Beispiel #1
0
        public TechLevel(TechLevel t)
        {
            atmosphereCurve = t.atmosphereCurve;
            velocityCurve = t.velocityCurve;
            TWR = t.TWR;
            thrustMultiplier = t.thrustMultiplier;
            massMultiplier = t.massMultiplier;
            gimbalRange = t.gimbalRange;
            techRequired = t.techRequired;
            minThrottleMultiplier = t.minThrottleMultiplier;
            costMult = t.costMult;

            LoadGlobals();
        }
        public override string GetInfo()
        {
            if (!compatible)
                return "";
            if (configs.Count < 2)
                return TLTInfo();

            string info = TLTInfo() + "\nAlternate configurations:\n";

            //Unused as yet
            /*TechLevel moduleTLInfo = new TechLevel();
            if (techNodes != null)
                moduleTLInfo.Load(techNodes, techLevel);
            else
                moduleTLInfo = null;*/

            foreach (ConfigNode config in configs) {

                TechLevel cTL = new TechLevel();
                if (!cTL.Load(config, techNodes, engineType, techLevel))
                    cTL = null;

                if(!config.GetValue ("name").Equals (configuration)) {
                    info += "   " + config.GetValue ("name") + "\n";
                    if(config.HasValue (thrustRating))
                        info += "    (" + (scale * ThrustTL(config.GetValue (thrustRating), config)).ToString("0.00") + " Thrust";
                    else
                        info += "    (Unknown Thrust";
                    float cst;
                    if(config.HasValue("cost") && float.TryParse(config.GetValue("cost"), out cst))
                        info += "    (" + (scale*cst).ToString("N0") + " extra cost)"; // FIXME should get cost from TL, but this should be safe
                    // because it will always be the cost for the original TL, and thus unmodified.

                    FloatCurve isp = new FloatCurve();
                    if(config.HasNode ("atmosphereCurve")) {
                        isp.Load (config.GetNode ("atmosphereCurve"));
                        info  += ", "
                            + isp.Evaluate (isp.maxTime).ToString() + "-"
                              + isp.Evaluate (isp.minTime).ToString() + "Isp";
                    }
                    else if (config.HasValue("IspSL") && config.HasValue("IspV"))
                    {
                        float ispSL = 1.0f, ispV = 1.0f;
                        float.TryParse(config.GetValue("IspSL"), out ispSL);
                        float.TryParse(config.GetValue("IspV"), out ispV);
                        if (cTL != null)
                        {
                            ispSL *= ispSLMult * cTL.AtmosphereCurve.Evaluate(1);
                            ispV *= ispVMult * cTL.AtmosphereCurve.Evaluate(0);
                            info += ", " + ispSL.ToString("0") + "-" + ispV.ToString("0") + "Isp";
                        }
                    }
                    float gimbalR = -1f;
                    if (config.HasValue("gimbalRange"))
                        gimbalR = float.Parse(config.GetValue("gimbalRange"));
                    // Don't do per-TL checks here, they're misleading.
                    /*else if (!gimbalTransform.Equals("") || useGimbalAnyway)
                    {
                        if (cTL != null)
                            gimbalR = cTL.GimbalRange;
                    }*/
                    if (gimbalR != -1f)
                        info += ", Gimbal " + gimbalR.ToString("N1");

                    if (config.HasValue("ullage"))
                        info += ", " + (config.GetValue("ullage").ToLower() == "true" ? "ullage" : "no ullage");
                    if (config.HasValue("pressureFed") && config.GetValue("pressureFed").ToLower() == "true")
                        info += ", pfed";

                    if (config.HasValue("ignitions"))
                    {
                        int ignitions;
                        if (int.TryParse(config.GetValue("ignitions"), out ignitions))
                        {
                            if (ignitions > 0)
                                info += ", " + ignitions + " ignition" + (ignitions > 1 ? "s" : "");
                            else
                                info += ", unl. ignitions";
                        }
                    }
                    info += ")\n";
                }

            }
            return info;
        }
        public virtual void DoConfig(ConfigNode cfg)
        {
            configMaxThrust = configMinThrust = configHeat = -1f;
            // Get thrusts
            if (config.HasValue(thrustRating))
            {
                float thr;
                if (float.TryParse(config.GetValue(thrustRating), out thr))
                    configMaxThrust = scale * thr;
            }
            if (config.HasValue("minThrust"))
            {
                float thr;
                if (float.TryParse(config.GetValue("minThrust"), out thr))
                    configMinThrust = scale * thr;
            }

            // Get, multiply heat
            if (cfg.HasValue("heatProduction"))
            {
                float heat;
                if(float.TryParse(cfg.GetValue("heatProduction"), out heat))
                    configHeat = (float)Math.Round(heat * RFSettings.Instance.heatMultiplier, 0);
            }

            // load throttle (for later)
            configThrottle = throttle;
            if (cfg.HasValue("throttle"))
                float.TryParse(cfg.GetValue("throttle"), out configThrottle);
            else if (configMinThrust >= 0f && configMaxThrust >= 0f)
                configThrottle = configMinThrust / configMaxThrust;

            float TLMassMult = 1.0f;

            float gimbal = -1f;
            if (cfg.HasValue("gimbalRange"))
                gimbal = float.Parse(cfg.GetValue("gimbalRange"));

            float cost = 0f;
            if (cfg.HasValue("cost"))
                cost = scale * float.Parse(cfg.GetValue("cost"));

            if (techLevel != -1)
            {
                // load techlevels
                TechLevel cTL = new TechLevel();
                cTL.Load(cfg, techNodes, engineType, techLevel);
                TechLevel oTL = new TechLevel();
                oTL.Load(cfg, techNodes, engineType, origTechLevel);

                // set atmosphereCurve
                if (cfg.HasValue("IspSL") && cfg.HasValue("IspV"))
                {
                    cfg.RemoveNode("atmosphereCurve");

                    ConfigNode curve = new ConfigNode("atmosphereCurve");

                    // get the multipliers
                    float ispSL = 1f, ispV = 1f;
                    float.TryParse(cfg.GetValue("IspSL"), out ispSL);
                    float.TryParse(cfg.GetValue("IspV"), out ispV);

                    // Mod the curve by the multipliers
                    FloatCurve newAtmoCurve = new FloatCurve();
                    newAtmoCurve = Utilities.Mod(cTL.AtmosphereCurve, ispSL, ispV);
                    newAtmoCurve.Save(curve);

                    cfg.AddNode(curve);
                }

                // set heatProduction
                if (configHeat > 0)
                {
                    configHeat = MassTL(configHeat);
                }

                // set thrust and throttle
                if (configMaxThrust >= 0)
                {
                    configMaxThrust = ThrustTL(configMaxThrust);
                    if (configMinThrust >= 0)
                    {
                        configMinThrust = ThrustTL(configMinThrust);
                    }
                    else if (thrustRating.Equals("thrusterPower"))
                    {
                        configMinThrust = configMaxThrust * 0.5f;
                    }
                    else
                    {
                        configMinThrust = configMaxThrust;
                        if (configThrottle > 1.0f)
                        {
                            if (techLevel >= configThrottle)
                                configThrottle = 1.0f;
                            else
                                configThrottle = -1.0f;
                        }
                        if (configThrottle >= 0.0f)
                        {
                            configThrottle = (float)((double)configThrottle * cTL.Throttle());
                            configMinThrust *= configThrottle;
                        }
                    }
                    configThrottle = configMinThrust / configMaxThrust;
                    if (origMass > 0)
                        TLMassMult = MassTL(1.0f);
                }
                // Don't want to change gimbals on TL-enabled engines willy-nilly
                // So we don't unless either a transform is specified, or we override.
                // We assume if it was specified in the CONFIG that we should use it anyway.
                if (gimbal < 0 && (!gimbalTransform.Equals("") || useGimbalAnyway))
                    gimbal = cTL.GimbalRange;
                if (gimbal >= 0)
                {
                    // allow local override of gimbal mult
                    if (cfg.HasValue("gimbalMult"))
                        gimbal *= float.Parse(cfg.GetValue("gimbalMult"));
                }

                // Cost (multiplier will be 1.0 if unspecified)
                cost = scale * CostTL(cost, cfg);
            }
            else
            {
                if (cfg.HasValue(thrustRating) && configThrottle > 0f && !cfg.HasValue("minThrust"))
                {
                    configMinThrust = configThrottle * configMaxThrust;
                }
            }

            // Now update the cfg from what we did.
            // thrust updates
            if(configMaxThrust >= 0f)
                cfg.SetValue(thrustRating, configMaxThrust.ToString("0.0000"));
            if(configMinThrust >= 0f)
                cfg.SetValue("minThrust", configMinThrust.ToString("0.0000")); // will be ignored by RCS, so what.

            // heat update
            if(configHeat >= 0f)
                cfg.SetValue("heatProduction", configHeat.ToString("0"));

            // mass change
            if (origMass > 0)
            {
                float ftmp;
                configMassMult = scale;
                if (cfg.HasValue("massMult"))
                    if (float.TryParse(cfg.GetValue("massMult"), out ftmp))
                        configMassMult *= ftmp;

                part.mass = origMass * configMassMult * RFSettings.Instance.EngineMassMultiplier * TLMassMult;
                massDelta = 0;
                if ((object)(part.partInfo) != null)
                    if ((object)(part.partInfo.partPrefab) != null)
                        massDelta = part.mass - part.partInfo.partPrefab.mass;
            }

            // KIDS integration
            if (cfg.HasNode("atmosphereCurve"))
            {
                ConfigNode newCurveNode = new ConfigNode("atmosphereCurve");
                FloatCurve oldCurve = new FloatCurve();
                oldCurve.Load(cfg.GetNode("atmosphereCurve"));
                FloatCurve newCurve = Utilities.Mod(oldCurve, ispSLMult, ispVMult);
                newCurve.Save(newCurveNode);
                cfg.RemoveNode("atmosphereCurve");
                cfg.AddNode(newCurveNode);
            }
            // gimbal change
            if (gimbal >= 0 && !cfg.HasValue("gimbalRange")) // if TL set a gimbal
            {
                // apply module-wide gimbal mult on top of any local ones
                cfg.AddValue("gimbalRange", (gimbal * gimbalMult).ToString("N4"));
            }
            if (cost != 0f)
            {
                if (cfg.HasValue("cost"))
                    cfg.SetValue("cost", cost.ToString("N3"));
                else
                    cfg.AddValue("cost", cost.ToString("N3"));
            }
        }
        private string TLTInfo()
        {
            string retStr = "";
            if (engineID != "")
                retStr += "Bound to " + engineID;
            if(moduleIndex >= 0)
                retStr += "Bound to engine " + moduleIndex + " in part";
            if(techLevel != -1)
            {
                TechLevel cTL = new TechLevel();
                if (!cTL.Load(config, techNodes, engineType, techLevel))
                    cTL = null;

                retStr =  "Type: " + engineType + ". Tech Level: " + techLevel + " (" + origTechLevel + "-" + maxTechLevel + ")";
                if (origMass > 0)
                    retStr += ", Mass: " + part.mass.ToString("N3") + " (was " + (origMass * RFSettings.Instance.EngineMassMultiplier).ToString("N3") + ")";
                if (configThrottle >= 0)
                    retStr += ", MinThr " + (100f * configThrottle).ToString("N0") + "%";

                float gimbalR = -1f;
                if (config.HasValue("gimbalRange"))
                    gimbalR = float.Parse(config.GetValue("gimbalRange"));
                else if (!gimbalTransform.Equals("") || useGimbalAnyway)
                {
                    if (cTL != null)
                        gimbalR = cTL.GimbalRange;
                }
                if (gimbalR != -1f)
                    retStr += ", Gimbal " + gimbalR.ToString("N1");

                return retStr;
            }
            else
                return "";
        }
        private double ThrustTL(ConfigNode cfg = null)
        {
            if (techLevel != -1 && !engineType.Contains("S"))
            {
                TechLevel oldTL = new TechLevel(), newTL = new TechLevel();
                if (!oldTL.Load(cfg == null ? config : cfg, techNodes, engineType, origTechLevel))
                    return 1.0;
                if (!newTL.Load(cfg == null ? config : cfg, techNodes, engineType, techLevel))
                    return 1.0;

                return newTL.Thrust(oldTL);
            }
            return 1.0;
        }
        private float CostTL(float cost, ConfigNode cfg = null)
        {
            TechLevel cTL = new TechLevel();
            TechLevel oTL = new TechLevel();
            if (cTL.Load(cfg, techNodes, engineType, techLevel) && oTL.Load(cfg, techNodes, engineType, origTechLevel) && part.partInfo != null)
            {
                // Bit of a dance: we have to figure out the total cost of the part, but doing so
                // also depends on us. So we zero out our contribution first
                // and then restore configCost.
                float oldCC = configCost;
                configCost = 0f;
                float totalCost = part.partInfo.cost + part.GetModuleCosts(part.partInfo.cost);
                configCost = oldCC;
                cost = (totalCost + cost) * (cTL.CostMult / oTL.CostMult) - totalCost;
            }

            return cost;
        }
Beispiel #7
0
        // MULTIPLIERS
        public double Thrust(TechLevel oldTL, bool constantMass = false)
        {
            if (oldTL.thrustMultiplier > 0 && thrustMultiplier > 0)
                return thrustMultiplier / oldTL.thrustMultiplier;

            if (constantMass)
                return TWR / oldTL.TWR;
            else
                return TWR / oldTL.TWR * oldTL.atmosphereCurve.Evaluate(0) / atmosphereCurve.Evaluate(0);
        }
Beispiel #8
0
        public double Mass(TechLevel oldTL, bool constantThrust = false)
        {
            if (oldTL.massMultiplier > 0 && massMultiplier > 0)
                return massMultiplier / oldTL.massMultiplier;

            if (constantThrust)
                return oldTL.TWR / TWR;
            else
                return oldTL.atmosphereCurve.Evaluate(0) / atmosphereCurve.Evaluate(0);
        }
Beispiel #9
0
 // Check if can switch to TL
 public static bool CanTL(ConfigNode cfg, ConfigNode mod, string type, int level)
 {
     TechLevel nTL = new TechLevel();
     if (!nTL.Load(cfg, mod, type, level))
         return false;
     return HighLogic.CurrentGame.Mode == Game.Modes.SANDBOX || nTL.techRequired.Equals("") || ResearchAndDevelopment.GetTechnologyState(nTL.techRequired) == RDTech.State.Available;
 }
        public virtual void DoConfig(ConfigNode cfg)
        {
            // fix propellant ratios to not be rounded
            if (cfg.HasNode("PROPELLANT"))
            {
                foreach (ConfigNode pNode in cfg.GetNodes("PROPELLANT"))
                {
                    if (pNode.HasValue("ratio"))
                    {
                        double dtmp;
                        if (double.TryParse(pNode.GetValue("ratio"), out dtmp))
                            pNode.SetValue("ratio", (dtmp * 100.0).ToString());
                    }
                }
            }
            float heat = -1;
            if (cfg.HasValue("heatProduction")) // ohai amsi: allow heat production to be changed by multiplier
            {
                heat = (float)Math.Round(float.Parse(cfg.GetValue("heatProduction")) * heatMult, 0);
                cfg.SetValue("heatProduction", heat.ToString());
            }

            // load throttle (for later)
            curThrottle = throttle;
            if (cfg.HasValue("throttle"))
                float.TryParse(cfg.GetValue("throttle"), out curThrottle);
            else if(cfg.HasValue("minThrust") && cfg.HasValue("maxThrust"))
                curThrottle = float.Parse(cfg.GetValue("minThrust")) / float.Parse(cfg.GetValue("maxThrust"));
            float TLMassMult = 1.0f;

            float gimbal = -1f;
            if (cfg.HasValue("gimbalRange"))
                gimbal = float.Parse(cfg.GetValue("gimbalRange"));

            float cost = 0f;
            if(cfg.HasValue("cost"))
                cost = float.Parse(cfg.GetValue("cost"));

            if (techLevel != -1)
            {
                // load techlevels
                TechLevel cTL = new TechLevel();
                //print("For engine " + part.name + ", config " + configuration + ", max TL: " + TechLevel.MaxTL(cfg, techNodes, engineType));
                cTL.Load(cfg, techNodes, engineType, techLevel);
                TechLevel oTL = new TechLevel();
                oTL.Load(cfg, techNodes, engineType, origTechLevel);

                // set atmosphereCurve
                if (cfg.HasValue("IspSL") && cfg.HasValue("IspV"))
                {
                    cfg.RemoveNode("atmosphereCurve");
                    ConfigNode curve = new ConfigNode("atmosphereCurve");
                    float ispSL, ispV;
                    float.TryParse(cfg.GetValue("IspSL"), out ispSL);
                    float.TryParse(cfg.GetValue("IspV"), out ispV);
                    FloatCurve aC = new FloatCurve();
                    aC = Mod(cTL.AtmosphereCurve, ispSL, ispV);
                    aC.Save(curve);
                    cfg.AddNode(curve);
                }

                // set heatProduction and dissipation
                if (heat > 0)
                {
                    cfg.SetValue("heatProduction", MassTL(heat).ToString("0"));
                    part.heatDissipation = 0.12f / MassTL(1.0f);
                }

                // set thrust and throttle
                if (cfg.HasValue(thrustRating))
                {
                    float thr;
                    float.TryParse(cfg.GetValue(thrustRating), out thr);
                    configMaxThrust = ThrustTL(thr);
                    cfg.SetValue(thrustRating, configMaxThrust.ToString("0.0000"));
                    if (cfg.HasValue("minThrust"))
                    {
                        float.TryParse(cfg.GetValue("minThrust"), out thr);
                        configMinThrust = ThrustTL(thr);
                        cfg.SetValue("minThrust", configMinThrust.ToString("0.0000"));
                    }
                    else
                    {
                        if (thrustRating.Equals("thrusterPower"))
                        {
                            configMinThrust = configMaxThrust * 0.5f;
                        }
                        else
                        {
                            configMinThrust = configMaxThrust;
                            if (curThrottle > 1.0f)
                            {
                                if (techLevel >= curThrottle)
                                    curThrottle = 1.0f;
                                else
                                    curThrottle = -1.0f;
                            }
                            if (curThrottle >= 0.0f)
                            {
                                curThrottle = (float)((double)curThrottle * cTL.Throttle());
                                configMinThrust *= curThrottle;
                            }
                            cfg.SetValue("minThrust", configMinThrust.ToString("0.0000"));
                        }
                    }
                    curThrottle = configMinThrust / configMaxThrust;
                    if(origMass > 0)
                         TLMassMult =  MassTL(1.0f);
                }
                // Don't want to change gimbals on TL-enabled engines willy-nilly
                // So we don't unless either a transform is specified, or we override.
                // We assume if it was specified in the CONFIG that we should use it anyway.
                if (gimbal < 0 && (!gimbalTransform.Equals("") || useGimbalAnyway))
                    gimbal = cTL.GimbalRange;
                if (gimbal >= 0)
                {
                    // allow local override of gimbal mult
                    if (cfg.HasValue("gimbalMult"))
                        gimbal *= float.Parse(cfg.GetValue("gimbalMult"));
                }

                // Cost (multiplier will be 1.0 if unspecified)
                cost = CostTL(cost, cfg);
            }
            else
            {
                if(cfg.HasValue(thrustRating) && curThrottle > 0f && !cfg.HasValue("minThrust"))
                {
                    configMinThrust = curThrottle * float.Parse(cfg.GetValue(thrustRating));
                    cfg.SetValue("minThrust", configMinThrust.ToString("0.0000"));
                }
            }
            // mass change
            if (origMass > 0)
            {
                float ftmp;
                configMassMult = 1.0f;
                if (cfg.HasValue("massMult"))
                    if (float.TryParse(cfg.GetValue("massMult"), out ftmp))
                        configMassMult = ftmp;

                part.mass = origMass * configMassMult * massMult * TLMassMult;
                massDelta = 0;
                if((object)(part.partInfo) != null)
                    if((object)(part.partInfo.partPrefab) != null)
                        massDelta = part.mass - part.partInfo.partPrefab.mass;
            }
            // KIDS integration
            if(cfg.HasNode("atmosphereCurve"))
            {
                ConfigNode newCurveNode = new ConfigNode("atmosphereCurve");
                FloatCurve oldCurve = new FloatCurve();
                oldCurve.Load(cfg.GetNode("atmosphereCurve"));
                FloatCurve newCurve = Mod(oldCurve, ispSLMult, ispVMult);
                newCurve.Save(newCurveNode);
                cfg.RemoveNode("atmosphereCurve");
                cfg.AddNode(newCurveNode);
            }
            // gimbal change
            if (gimbal >= 0 && !cfg.HasValue("gimbalRange")) // if TL set a gimbal
            {
                // apply module-wide gimbal mult on top of any local ones
                cfg.AddValue("gimbalRange", (gimbal * gimbalMult).ToString("N4"));
            }
            if (cost != 0f)
            {
                if (cfg.HasValue("cost"))
                    cfg.SetValue("cost", cost.ToString("N3"));
                else
                    cfg.AddValue("cost", cost.ToString("N3"));
            }
        }
        public string GetConfigInfo(ConfigNode config)
        {
            TechLevel cTL = new TechLevel();
            if (!cTL.Load(config, techNodes, engineType, techLevel))
                cTL = null;

            string info = "   " + config.GetValue("name") + "\n";
            if (config.HasValue("description"))
                info += "    " + config.GetValue("description") + "\n";
            if (config.HasValue(thrustRating))
            {
                info += "    " + (scale * ThrustTL(config.GetValue(thrustRating), config)).ToString("G3") + " kN";
                // add throttling info if present
                if (config.HasValue("minThrust"))
                    info += ", min " + (float.Parse(config.GetValue("minThrust")) / float.Parse(config.GetValue(thrustRating)) * 100f).ToString("N0") + "%";
                else if (config.HasValue("throttle"))
                    info += ", min " + (float.Parse(config.GetValue("throttle")) * 100f).ToString("N0") + "%";
            }
            else
                info += "    Unknown Thrust";

            if (origMass > 0f)
            {
                float cMass = scale;
                float ftmp;
                if (config.HasValue("massMult"))
                    if (float.TryParse(config.GetValue("massMult"), out ftmp))
                        cMass *= ftmp;

                cMass = origMass * cMass * RFSettings.Instance.EngineMassMultiplier;

                info += ", " + cMass.ToString("N3") + "t";
            }
            info += "\n";

            FloatCurve isp = new FloatCurve();
            if (config.HasNode("atmosphereCurve"))
            {
                isp.Load(config.GetNode("atmosphereCurve"));
                info += "    Isp: "
                    + isp.Evaluate(isp.maxTime).ToString() + " - "
                      + isp.Evaluate(isp.minTime).ToString() + "s\n";
            }
            else if (config.HasValue("IspSL") && config.HasValue("IspV"))
            {
                float ispSL = 1.0f, ispV = 1.0f;
                float.TryParse(config.GetValue("IspSL"), out ispSL);
                float.TryParse(config.GetValue("IspV"), out ispV);
                if (cTL != null)
                {
                    ispSL *= ispSLMult * cTL.AtmosphereCurve.Evaluate(1);
                    ispV *= ispVMult * cTL.AtmosphereCurve.Evaluate(0);
                    info += "    Isp: " + ispSL.ToString("0") + " - " + ispV.ToString("0") + "s\n";
                }
            }
            float gimbalR = -1f;
            if (config.HasValue("gimbalRange"))
                gimbalR = float.Parse(config.GetValue("gimbalRange"));
            // Don't do per-TL checks here, they're misleading.
            /*else if (!gimbalTransform.Equals("") || useGimbalAnyway)
            {
                if (cTL != null)
                    gimbalR = cTL.GimbalRange;
            }*/
            if (gimbalR != -1f)
                info += "    Gimbal " + gimbalR.ToString("N1") + "d\n";

            if (config.HasValue("ullage") || config.HasValue("ignitions") || config.HasValue("pressureFed"))
            {
                info += "    ";
                bool comma = false;
                if (config.HasValue("ullage"))
                {
                    info += (config.GetValue("ullage").ToLower() == "true" ? "ullage" : "no ullage");
                    comma = true;
                }
                if (config.HasValue("pressureFed") && config.GetValue("pressureFed").ToLower() == "true")
                {
                    if (comma)
                        info += ", ";
                    info += "pfed";
                    comma = true;
                }

                if (config.HasValue("ignitions"))
                {
                    int ignitions;
                    if (int.TryParse(config.GetValue("ignitions"), out ignitions))
                    {
                        if (comma)
                            info += ", ";
                        if (ignitions > 0)
                            info += ignitions + " ignition" + (ignitions > 1 ? "s" : "");
                        else
                            info += "unl. ignitions";
                    }
                }
                info += "\n";
            }
            float cst;
            if (config.HasValue("cost") && float.TryParse(config.GetValue("cost"), out cst))
                info += "    (" + (scale * cst).ToString("N0") + " extra cost)\n"; // FIXME should get cost from TL, but this should be safe

            return info;
        }
        //TODO
        public virtual void DoDEVConfig(ConfigNode cfg)
        {
            float TLMassMult = 1.0f;

            float gimbal = -1f;
            if (cfg.HasValue("gimbalRange"))
                gimbal = float.Parse(cfg.GetValue("gimbalRange"));

            float cost = 0f;
            if (cfg.HasValue("cost"))
                cost = scale * float.Parse(cfg.GetValue("cost"));

            if (techLevel != -1) {
                // load techlevels
                TechLevel cTL = new TechLevel();
                cTL.Load(cfg, techNodes, engineType, techLevel);
                TechLevel oTL = new TechLevel();
                oTL.Load(cfg, techNodes, engineType, origTechLevel);

                // set heatProduction
                if (configHeat > 0) {
                    configHeat = MassTL(configHeat);
                }
                // Don't want to change gimbals on TL-enabled engines willy-nilly
                // So we don't unless either a transform is specified, or we override.
                // We assume if it was specified in the CONFIG that we should use it anyway.
                if (gimbal < 0 && (!gimbalTransform.Equals("") || useGimbalAnyway))
                    gimbal = cTL.GimbalRange;
                if (gimbal >= 0) {
                    // allow local override of gimbal mult
                    if (cfg.HasValue("gimbalMult"))
                        gimbal *= float.Parse(cfg.GetValue("gimbalMult"));
                }

                // Cost (multiplier will be 1.0 if unspecified)
                cost = scale * CostTL(cost, cfg);
            } else {
                if (cfg.HasValue(thrustRating) && configThrottle > 0f && !cfg.HasValue("minThrust")) {
                    configMinThrust = configThrottle * configMaxThrust;
                }
            }

            // heat update
            if (configHeat >= 0f)
                cfg.SetValue("heatProduction", configHeat.ToString("0"));

            // mass change
            if (origMass > 0) {
                float ftmp;
                configMassMult = scale;
                if (cfg.HasValue("massMult"))
                    if (float.TryParse(cfg.GetValue("massMult"), out ftmp))
                        configMassMult *= ftmp;

                part.mass = origMass * configMassMult * RFSettings.Instance.EngineMassMultiplier * TLMassMult;
                massDelta = 0;
                if ((object)(part.partInfo) != null)
                    if ((object)(part.partInfo.partPrefab) != null)
                        massDelta = part.mass - part.partInfo.partPrefab.mass;
            }

            // gimbal change
            if (gimbal >= 0 && !cfg.HasValue("gimbalRange")) // if TL set a gimbal
            {
                // apply module-wide gimbal mult on top of any local ones
                cfg.AddValue("gimbalRange", (gimbal * gimbalMult).ToString("N4"));
            }
            if (cost != 0f) {
                if (cfg.HasValue("cost"))
                    cfg.SetValue("cost", cost.ToString("N3"));
                else
                    cfg.AddValue("cost", cost.ToString("N3"));
            }
        }