public virtual void SetConfiguration(string newConfiguration = null, bool resetTechLevels = false)
        {
            if (newConfiguration == null)
                newConfiguration = configuration;

            ConfigSaveLoad();

            ConfigNode newConfig = configs.Find (c => c.GetValue ("name").Equals (newConfiguration));
            if (!UnlockedConfig(newConfig, part))
            {
                if(newConfig == null)
                    Debug.Log("*RFMEC* ERROR Can't find configuration " + newConfiguration + ", falling back to first tech-available config.");

                foreach(ConfigNode cfg in configs)
                    if (UnlockedConfig(cfg, part))
                    {
                        newConfig = cfg;
                        newConfiguration = cfg.GetValue("name");
                        break;
                    }
            }
            if (newConfig != null)
            {
                if (configuration != newConfiguration)
                {
                    if(resetTechLevels)
                        techLevel = origTechLevel;

                    while (techLevel > 0)
                    {
                        if (TechLevel.CanTL(newConfig, techNodes, engineType, techLevel))
                            break;
                        else
                            --techLevel;
                    }
                }

                // for asmi
                if (useConfigAsTitle)
                    part.partInfo.title = configuration;

                configuration = newConfiguration;
                config = new ConfigNode("MODULE");
                newConfig.CopyTo(config);
                config.name = "MODULE";

            #if DEBUG
                print ("replacing " + type + " with:");
                print (newConfig.ToString ());
            #endif

                pModule = null;
                // get correct module
                pModule = GetSpecifiedModule(part, engineID, moduleIndex, type, useWeakType);

                if ((object)pModule == null)
                {
                    Debug.Log("*RFMEC* Could not find appropriate module of type " + type + ", with ID=" + engineID + " and index " + moduleIndex);
                    return;
                }

                Type mType = pModule.GetType();
                config.SetValue("name", mType.Name);

                // clear all FloatCurves we need to clear (i.e. if our config has one, or techlevels are enabled)
                bool delAtmo = config.HasNode("atmosphereCurve") || techLevel >= 0;
                bool delDens = config.HasNode("atmCurve") || techLevel >= 0;
                bool delVel = config.HasNode("velCurve") || techLevel >= 0;
                foreach (FieldInfo field in mType.GetFields())
                {
                    if (field.FieldType == typeof(FloatCurve) &&
                        ((field.Name.Equals("atmosphereCurve") && delAtmo)
                        || (field.Name.Equals("atmCurve") && delDens)
                        || (field.Name.Equals("velCurve") && delVel)))
                    {
                        field.SetValue(pModule, new FloatCurve());
                    }
                }
                // clear propellant gauges
                foreach (FieldInfo field in mType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
                {
                    if (field.FieldType == typeof(Dictionary<Propellant, VInfoBox>))
                    {
                        Dictionary<Propellant, VInfoBox> boxes = (Dictionary<Propellant, VInfoBox>)(field.GetValue(pModule));
                        if (boxes == null)
                            continue;
                        foreach (VInfoBox v in boxes.Values)
                        {
                            if (v == null) //just in case...
                                continue;
                            try
                            {
                                part.stackIcon.RemoveInfo(v);
                            }
                            catch (Exception e)
                            {
                                Debug.Log("*RFMEC* Trying to remove info box: " + e.Message);
                            }
                        }
                        boxes.Clear();
                    }
                }
                if (type.Equals("ModuleRCS") || type.Equals("ModuleRCSFX"))
                {
                    ModuleRCS rcs = (ModuleRCS)pModule;
                    if (rcs != null)
                    {
                        DoConfig(config);
                        if (config.HasNode("PROPELLANT"))
                        {
                            rcs.propellants.Clear();
                        }
                        pModule.Load(config);
                    }
                }
                else
                { // is an ENGINE
                    ModuleEngines mE = (ModuleEngines)pModule;
                    if (mE != null)
                    {
                        if (config.HasNode("PROPELLANT"))
                        {
                            mE.propellants.Clear();
                        }
                    }

                    DoConfig(config);

                    // Handle Engine Ignitor
                    if (config.HasNode("ModuleEngineIgnitor"))
                    {
                        if (part.Modules.Contains("ModuleEngineIgnitor"))
                        {
                            ConfigNode eiNode = config.GetNode("ModuleEngineIgnitor");
                            if (eiNode.HasValue("ignitionsAvailable"))
                            {
                                int ignitions;
                                if (int.TryParse(eiNode.GetValue("ignitionsAvailable"), out ignitions))
                                {
                                    ignitions = ConfigIgnitions(ignitions);

                                    eiNode.SetValue("ignitionsAvailable", ignitions.ToString());
                                    if (eiNode.HasValue("ignitionsRemained"))
                                        eiNode.SetValue("ignitionsRemained", ignitions.ToString());
                                    else
                                        eiNode.AddValue("ignitionsRemained", ignitions.ToString());
                                }
                            }
                            if (!HighLogic.LoadedSceneIsEditor && !(HighLogic.LoadedSceneIsFlight && vessel != null && vessel.situation == Vessel.Situations.PRELAUNCH)) // fix for prelaunch
                            {
                                int remaining = (int)(part.Modules["ModuleEngineIgnitor"].GetType().GetField("ignitionsRemained").GetValue(part.Modules["ModuleEngineIgnitor"]));
                                if (eiNode.HasValue("ignitionsRemained"))
                                    eiNode.SetValue("ignitionsRemained", remaining.ToString());
                                else
                                    eiNode.AddValue("ignitionsRemained", remaining.ToString());
                            }
                            ConfigNode tNode = new ConfigNode("MODULE");
                            eiNode.CopyTo(tNode);
                            tNode.SetAddValue("name", "ModuleEngineIgnitor");
                            part.Modules["ModuleEngineIgnitor"].Load(tNode);
                        }
                        else // backwards compatible with EI nodes when using RF ullage etc.
                        {
                            ConfigNode eiNode = config.GetNode("ModuleEngineIgnitor");
                            if (eiNode.HasValue("ignitionsAvailable") && !config.HasValue("ignitions"))
                            {
                                config.AddValue("ignitions", eiNode.GetValue("ignitionsAvailable"));
                            }
                            if (eiNode.HasValue("useUllageSimulation") && !config.HasValue("ullage"))
                                config.AddValue("ullage", eiNode.GetValue("useUllageSimulation"));
                            if (eiNode.HasValue("isPressureFed") && !config.HasValue("pressureFed"))
                                config.AddValue("pressureFed", eiNode.GetValue("isPressureFed"));
                            if (!config.HasNode("IGNITOR_RESOURCE"))
                                foreach (ConfigNode resNode in eiNode.GetNodes("IGNITOR_RESOURCE"))
                                    config.AddNode(resNode);
                        }
                    }
                    if (config.HasValue("ignitions"))
                    {
                        int ignitions;
                        if ((!HighLogic.LoadedSceneIsFlight || (vessel != null && vessel.situation == Vessel.Situations.PRELAUNCH)))
                        {
                            if (int.TryParse(config.GetValue("ignitions"), out ignitions))
                            {
                                ignitions = ConfigIgnitions(ignitions);
                                config.SetValue("ignitions", ignitions.ToString());
                            }
                        }
                        else
                            config.RemoveValue("ignitions");
                    }

                    if (pModule is ModuleEnginesRF)
                        (pModule as ModuleEnginesRF).SetScale(1d);
                    pModule.Load(config);
                }
                // fix for editor NaN
                if (part.Resources.Contains("ElectricCharge") && part.Resources["ElectricCharge"].maxAmount < 0.1)
                { // hacking around a KSP bug here
                    part.Resources["ElectricCharge"].amount = 0;
                    part.Resources["ElectricCharge"].maxAmount = 0.1;
                }

                // set gimbal
                if (config.HasValue("gimbalRange"))
                {
                    float newGimbal = float.Parse(config.GetValue("gimbalRange"));
                    for (int m = 0; m < part.Modules.Count; ++m)
                    {
                        if (part.Modules[m] is ModuleGimbal)
                        {
                            ModuleGimbal g = part.Modules[m] as ModuleGimbal;
                            if (gimbalTransform.Equals("") || g.gimbalTransformName.Equals(gimbalTransform))
                            {
                                g.gimbalRange = newGimbal;
                                break;
                            }
                        }
                    }
                }
                if (config.HasValue("cost"))
                    configCost = scale * float.Parse(config.GetValue("cost"));
                else
                    configCost = 0f;

                UpdateOtherModules(config);

                // GUI disabled for now - UpdateTweakableMenu();

                // Finally, fire the modified event
                // more trouble than it is worth...
                /*if((object)(EditorLogic.fetch) != null && (object)(EditorLogic.fetch.ship) != null && HighLogic.LoadedSceneIsEditor)
                    GameEvents.onEditorShipModified.Fire(EditorLogic.fetch.ship);*/

                // fire config modified event
                /*if(HighLogic.LoadedSceneIsEditor || HighLogic.LoadedSceneIsFlight)
                    EngineConfigChanged();*/
                // do it manually
                List<Part> parts;
                if (HighLogic.LoadedSceneIsEditor && EditorLogic.fetch.ship != null)
                    parts = EditorLogic.fetch.ship.parts;
                else if (HighLogic.LoadedSceneIsFlight && vessel != null)
                    parts = vessel.parts;
                else parts = new List<Part>();
                for (int i = parts.Count - 1; i >= 0; --i)
                    parts[i].SendMessage("UpdateUsedBy");

                SetupFX();

                UpdateTFInterops(); // update TestFlight if it's installed

                if (config.HasValue("description"))
                    configDescription = config.GetValue("description");
                else
                    configDescription = "";
            }
            else
            {
                Debug.Log("*RFMEC* ERROR could not find configuration of name " + configuration + " and could find no fallback config.");
                Debug.Log("For part " + part.name + ", Current nodes:" + Utilities.PrintConfigs(configs));
            }
        }
        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.SetAddValue(thrustRating, configMaxThrust.ToString("0.0000"));
            if(configMinThrust >= 0f)
                cfg.SetAddValue("minThrust", configMinThrust.ToString("0.0000")); // will be ignored by RCS, so what.

            // heat update
            if(configHeat >= 0f)
                cfg.SetAddValue("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"));
            }
        }