Example #1
0
        private void Start()
        {
            List <AvailablePart> parts = PartLoader.LoadedPartsList.Where
                                             (p => p.partPrefab.Modules.GetModules <ModuleScienceConverter>().Any() &&
                                             p.partPrefab.Modules.GetModules <ModuleScienceLab>().Any()).ToList();

            foreach (AvailablePart part in parts)
            {
                List <ModuleScienceConverter> modulesSC = part.partPrefab.Modules.GetModules <ModuleScienceConverter>();
                List <ModuleScienceLab>       modulesSL = part.partPrefab.Modules.GetModules <ModuleScienceLab>();

                if (modulesSC.Count != 1)
                {
                    continue;
                }
                if (modulesSL.Count != 1)
                {
                    continue;
                }

                ModuleScienceConverter moduleSC = modulesSC[0];
                ModuleScienceLab       moduleSL = modulesSL[0];

                moduleSL.Fields.ToString();

                List <AvailablePart.ModuleInfo> modinfos = part.moduleInfos;

                foreach (AvailablePart.ModuleInfo modinfo in modinfos)
                {
                    if (modinfo.moduleName == moduleSL.GUIName)
                    {
                        double lab_modifier = moduleSC.dataProcessingMultiplier / 0.5 * Math.Pow(10, 7 - moduleSC.researchTime);

                        modinfo.info =
                            Localizer.Format("#SLI_ScientistsRequired", moduleSL.crewsRequired)
                            + Localizer.Format("#SLI_PowerUsage", moduleSC.powerRequirement)
                            + Localizer.Format("#SLI_DataStorage", moduleSL.dataStorage)
                            + Localizer.Format("#SLI_ScienceStorage", moduleSC.scienceCap)
                            + Localizer.Format("#SLI_DatatoScience", moduleSC.scienceMultiplier)

                            + Localizer.Format("#SLI_ResearchSpeed")
                            + " " + Localizer.Format("#SLI_TheLabModifier", lab_modifier.ToString("#0.0#"))
                            + " " + Localizer.Format("#SLI_ScientistLevelBonus", (moduleSC.scientistBonus).ToString("+#0%;-#0%"))

                            + Localizer.Format("#SLI_DataBonuses")
                            + " " + Localizer.Format("#SLI_SurfaceBonus", moduleSL.SurfaceBonus.ToString("+#0%;-#0%"))
                            + " " + Localizer.Format("#SLI_ContextBonus", moduleSL.ContextBonus.ToString("+#0%;-#0%"))
                            + " " + Localizer.Format("#SLI_HomeworldBonus", (moduleSL.homeworldMultiplier - 1).ToString("+#0%;-#0%"));
                    }
                }
            }
        }
        public void Start()
        {
            List <ModuleScienceConverter> listMSC = part.Modules.GetModules <ModuleScienceConverter>();
            List <ModuleScienceLab>       listMSL = part.Modules.GetModules <ModuleScienceLab>();

            if (listMSC.Count != 1)
            {
                return;
            }
            if (listMSL.Count != 1)
            {
                return;
            }

            ModuleSC = listMSC[0];
            ModuleSL = listMSL[0];

            // --- Hide Stock Fields
            ModuleSC.Fields["sciString"].guiActive    = false;
            ModuleSC.Fields["datString"].guiActive    = false;
            ModuleSC.Fields["rateString"].guiActive   = false;
            ModuleSC.Fields["status"].guiActive       = false;
            ModuleSC.Fields["status"].guiActiveEditor = false;
            ModuleSL.Fields["statusText"].guiActive   = false;

            DataToScience_str = "1:" + ModuleSC.scienceMultiplier;

            PowerConsumption_str = ModuleSC.powerRequirement + " " + Localizer.Format("#autoLOC_7001414"); // EC/s

            if (HighLogic.LoadedScene == GameScenes.EDITOR)
            {
                UpdateScientistFieldsInEditor();
            }
            else if (HighLogic.LoadedScene == GameScenes.FLIGHT)
            {
                UpdateScientistFieldsInFlight();
            }

            double labModifier = ModuleSC.dataProcessingMultiplier / 0.5 * Math.Pow(10, 7 - ModuleSC.researchTime);

            LabModifier_str = String.Format("×{0:F2}", labModifier);

            if (labModifier == 1.0)
            {
                Fields["LabModifier_str"].guiActive       = false;
                Fields["LabModifier_str"].guiActiveEditor = false;
            }


            GameEvents.onEditorScreenChange.Add(OnEditorScreenChange);
            GameEvents.onVesselCrewWasModified.Add(OnVesselCrewWasModified);
        }
Example #3
0
        public override void RedecorateModule(bool loadTemplateResources = true)
        {
            base.RedecorateModule(loadTemplateResources);
            bool enableMPLModules = false;

            if (CurrentTemplate.HasValue("enableMPLModules"))
            {
                enableMPLModules = bool.Parse(CurrentTemplate.GetValue("enableMPLModules"));
            }

            ModuleScienceLab sciLab = this.part.FindModuleImplementing <ModuleScienceLab>();

            if (sciLab != null)
            {
                if (HighLogic.LoadedSceneIsEditor)
                {
                    sciLab.isEnabled = false;
                    sciLab.enabled   = false;
                }

                else if (enableMPLModules)
                {
                    sciLab.isEnabled     = true;
                    sciLab.enabled       = true;
                    sciLab.crewsRequired = originalCrewsRequired;
                }
                else
                {
                    sciLab.crewsRequired = 2000.0f;
                    sciLab.isEnabled     = false;
                    sciLab.enabled       = false;
                }
            }

            ModuleScienceConverter converter = this.part.FindModuleImplementing <ModuleScienceConverter>();

            if (converter != null)
            {
                if (HighLogic.LoadedSceneIsEditor)
                {
                    converter.isEnabled = enableMPLModules;
                    converter.enabled   = enableMPLModules;
                }

                else
                {
                    converter.isEnabled = enableMPLModules;
                    converter.enabled   = enableMPLModules;
                }
            }
        }
Example #4
0
        public void Start()
        {
            List <ModuleScienceConverter> listMSC = part.Modules.GetModules <ModuleScienceConverter>();

            if (listMSC.Count != 1)
            {
                return;
            }

            ModuleSC = listMSC[0];

            Fields["ScientistsModifier_str"].guiActive    = true;
            Fields["DataScienceConversion_str"].guiActive = true;
            DataScienceConversion_str = "1:" + ModuleSC.scienceMultiplier;

            double labModifier = ModuleSC.dataProcessingMultiplier / 0.5 * Math.Pow(10, 7 - ModuleSC.researchTime);

            if (labModifier != 1.0)
            {
                Fields["LabModifier_str"].guiActive = true;
                LabModifier_str = String.Format("×{0:F2}", labModifier);
            }
        }
Example #5
0
        static void ProcessLab(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleScienceConverter lab, resource_info ec, double elapsed_s)
        {
            // note: we are only simulating the EC consumption
            // note: there is no easy way to 'stop' the lab when there isn't enough EC

            // if active
            if (Lib.Proto.GetBool(m, "IsActivated"))
            {
                // consume ec
                ec.Consume(lab.powerRequirement * elapsed_s);
            }
        }
		void Process_stocklab(ModuleScienceConverter lab)
		{
			Resource("ElectricCharge").Consume(lab.powerRequirement, "lab");
		}
Example #7
0
        public override void OnStart(StartState state)
        {
            moduleScienceLab       = part.FindModuleImplementing <ModuleScienceLab>();
            moduleScienceConverter = part.FindModuleImplementing <ModuleScienceConverter>();

            if (moduleScienceConverter != null && moduleScienceLab != null)
            {
                DetermineTechLevel();

                var scienceCap = GetScienceCap();
                moduleScienceLab.dataStorage      = scienceCap;
                moduleScienceConverter.scienceCap = scienceCap;

                var deminishingScienceModifier = moduleScienceLab.dataStored >= baseDataStorage ? 1 : moduleScienceLab.dataStored / baseDataStorage;

                dataProcessingMultiplier = moduleScienceLab.dataStored < float.Epsilon ? 0.5f
                    : deminishingScienceModifier * (baseDataStorage / moduleScienceLab.dataStorage) * (moduleScienceLab.dataStorage / moduleScienceLab.dataStored) * 0.5f;
                moduleScienceConverter.dataProcessingMultiplier = dataProcessingMultiplier;
            }

            if (state == StartState.Editor)
            {
                if (this.HasTechsRequiredToUpgrade())
                {
                    isupgraded = true;
                    upgradePartModule();
                }
                return;
            }

            if (isupgraded)
            {
                upgradePartModule();
            }
            else
            {
                if (this.HasTechsRequiredToUpgrade())
                {
                    hasrequiredupgrade = true;
                }
            }

            // update gui names

            /*
             * Events["BeginResearch"].guiName = beginResearchName;
             */

            reprocessor = new NuclearFuelReprocessor();
            reprocessor.Initialize(part, null);
            antimatterGenerator = new AntimatterGenerator(part, 1, PartResourceLibrary.Instance.GetDefinition(ResourceSettings.Config.AntiProtium));

            Debug.Log("[KSPI]: ScienceModule on " + part.name + " was Force Activated");
            part.force_activate();

            anim  = part.FindModelAnimators(animName1).FirstOrDefault();
            anim2 = part.FindModelAnimators(animName2).FirstOrDefault();
            if (anim != null && anim2 != null)
            {
                if (IsEnabled)
                {
                    //anim [animName1].normalizedTime = 1f;
                    //anim2 [animName2].normalizedTime = 1f;
                    //anim [animName1].speed = -1f;
                    //anim2 [animName2].speed = -1f;
                    anim.Blend(animName1, 1, 0);
                    anim2.Blend(animName2, 1, 0);
                }
                else
                {
                    //anim [animName1].normalizedTime = 0f;
                    //anim2 [animName2].normalizedTime = 0f;
                    //anim [animName1].speed = 1f;
                    //anim2 [animName2].speed = 1f;
                    //anim.Blend (animName1, 0, 0);global_rate_multipliers
                    //anim2.Blend (animName2, 0, 0);
                    play_down = false;
                }
                //anim.Play ();
                //anim2.Play ();
            }

            if (IsEnabled && last_active_time != 0)
            {
                double globalRateMultipliers = 1;
                crew_capacity_ratio   = part.protoModuleCrew.Count / (float)part.CrewCapacity;
                globalRateMultipliers = globalRateMultipliers * crew_capacity_ratio;

                /*
                 * if (active_mode == 0) // Science persistence
                 * {
                 *  var time_diff = Planetarium.GetUniversalTime() - last_active_time;
                 *  var altitude_multiplier = Math.Max((vessel.altitude / (vessel.mainBody.Radius)), 1);
                 *  var kerbalResearchSkillFactor = part.protoModuleCrew.Sum(proto_crew_member => GetKerbalScienceFactor(proto_crew_member) / 2f);
                 *
                 *  double science_to_increment = kerbalResearchSkillFactor * GameConstants.baseScienceRate * time_diff
                 *      / PluginHelper.SecondsInDay * electrical_power_ratio * global_rate_multipliers * PluginHelper.getScienceMultiplier(vessel)
                 *      / (Math.Sqrt(altitude_multiplier));
                 *
                 *  science_to_increment = (double.IsNaN(science_to_increment) || double.IsInfinity(science_to_increment)) ? 0 : science_to_increment;
                 *  science_to_add += science_to_increment;
                 *
                 * }
                 * else
                 */
                if (active_mode == 2) // Antimatter persistence
                {
                    var deltaTime = Planetarium.GetUniversalTime() - last_active_time;

                    var electricalPowerProvidedInMegajoules = electrical_power_ratio * globalRateMultipliers * powerReqMult * PluginSettings.Config.BaseAMFPowerConsumption * deltaTime;

                    antimatterGenerator.Produce(electricalPowerProvidedInMegajoules);
                }
            }
        }
 public override bool Initialize(PartModule pm)
 {
     base.Initialize(pm);
     lab = (ModuleScienceConverter)pm;
     return(true);
 }
Example #9
0
  // called at every simulation step
  public void FixedUpdate()
  {
    // do nothing if paused
    if (Lib.IsPaused()) return;

    // do nothing if DB isn't ready
    if (!DB.Ready()) return;

    // for each vessel
    foreach(Vessel vessel in FlightGlobals.Vessels)
    {
      // skip invalid vessels
      if (!Lib.IsVessel(vessel)) continue;

      // skip loaded vessels
      if (vessel.loaded) continue;

      // get vessel data from the db
      vessel_data vd = DB.VesselData(vessel.id);

      // get vessel info from the cache
      vessel_info info = Cache.VesselInfo(vessel);

      // calculate atmospheric factor (proportion of flux not blocked by atmosphere)
      double atmo_factor = Sim.AtmosphereFactor(vessel.mainBody, info.position, info.sun_dir);

      // for each part
      foreach(ProtoPartSnapshot part in vessel.protoVessel.protoPartSnapshots)
      {
        // get part prefab (required for module properties)
        Part part_prefab = PartLoader.getPartInfoByName(part.partName).partPrefab;

        // store index of ModuleResourceConverter to process
        // rationale: a part can contain multiple resource converters
        int converter_index = 0;

        // for each module
        foreach(ProtoPartModuleSnapshot module in part.modules)
        {
          // something weird is going on, skip this
          if (!part_prefab.Modules.Contains(module.moduleName)) continue;

          // command module
          if (module.moduleName == "ModuleCommand")
          {
            // get module from prefab
            ModuleCommand command = part_prefab.Modules.GetModules<ModuleCommand>()[0];

            // do not consume if this is a MCM with no crew
            // rationale: for consistency, the game doesn't consume resources for MCM without crew in loaded vessels
            //            this make some sense: you left a vessel with some battery and nobody on board, you expect it to not consume EC
            if (command.minimumCrew == 0 || part.protoModuleCrew.Count > 0)
            {
              // for each input resource
              foreach(ModuleResource ir in command.inputResources)
              {
                // consume the resource
                Lib.RequestResource(vessel, ir.name, ir.rate * TimeWarp.fixedDeltaTime);
              }
            }
          }
          // solar panel
          else if (module.moduleName == "ModuleDeployableSolarPanel")
          {
            // determine if extended
            bool extended = module.moduleValues.GetValue("stateString") == ModuleDeployableSolarPanel.panelStates.EXTENDED.ToString();

            // if in sunlight and extended
            if (info.sunlight && extended)
            {
              // get module from prefab
              ModuleDeployableSolarPanel panel = part_prefab.Modules.GetModules<ModuleDeployableSolarPanel>()[0];

              // produce electric charge
              Lib.RequestResource(vessel, "ElectricCharge", -PanelOutput(vessel, part, panel, info.sun_dir, info.sun_dist, atmo_factor) * TimeWarp.fixedDeltaTime * Malfunction.Penalty(part));
            }
          }
          // generator
          // note: assume generators require all input
          else if (module.moduleName == "ModuleGenerator")
          {
            // determine if active
            bool activated = Convert.ToBoolean(module.moduleValues.GetValue("generatorIsActive"));

            // if active
            if (activated)
            {
              // get module from prefab
              ModuleGenerator generator = part_prefab.Modules.GetModules<ModuleGenerator>()[0];

              // determine if vessel is full of all output resources
              bool full = true;
              foreach(var or in generator.outputList)
              {
                double amount = Lib.GetResourceAmount(vessel, or.name);
                double capacity = Lib.GetResourceCapacity(vessel, or.name);
                double perc = capacity > 0.0 ? amount / capacity : 0.0;
                full &= (perc >= 1.0 - double.Epsilon);
              }

              // if not full
              if (!full)
              {
                // calculate worst required resource percentual
                double worst_input = 1.0;
                foreach(var ir in generator.inputList)
                {
                  double required = ir.rate * TimeWarp.fixedDeltaTime;
                  double amount = Lib.GetResourceAmount(vessel, ir.name);
                  worst_input = Math.Min(worst_input, amount / required);
                }

                // for each input resource
                foreach(var ir in generator.inputList)
                {
                  // consume the resource
                  Lib.RequestResource(vessel, ir.name, ir.rate * worst_input * TimeWarp.fixedDeltaTime);
                }

                // for each output resource
                foreach(var or in generator.outputList)
                {
                  // produce the resource
                  Lib.RequestResource(vessel, or.name, -or.rate * worst_input * TimeWarp.fixedDeltaTime * Malfunction.Penalty(part));
                }
              }
            }
          }
          // converter
          // note: support multiple resource converters
          // note: ignore stock temperature mechanic of converters
          // note: ignore autoshutdown
          // note: ignore crew experience bonus (seem that stock ignore it too)
          // note: 'undo' stock behaviour by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation)
          // note: support PlanetaryBaseSystem converters
          // note: support NearFuture reactors
          else if (module.moduleName == "ModuleResourceConverter" || module.moduleName == "ModuleKPBSConverter" || module.moduleName == "FissionReactor")
          {
            // get module from prefab
            ModuleResourceConverter converter = part_prefab.Modules.GetModules<ModuleResourceConverter>()[converter_index++];

            // determine if active
            bool activated = Convert.ToBoolean(module.moduleValues.GetValue("IsActivated"));

            // if active
            if (activated)
            {
              // determine if vessel is full of all output resources
              bool full = true;
              foreach(var or in converter.outputList)
              {
                double amount = Lib.GetResourceAmount(vessel, or.ResourceName);
                double capacity = Lib.GetResourceCapacity(vessel, or.ResourceName);
                double perc = capacity > 0.0 ? amount / capacity : 0.0;
                full &= (perc >= converter.FillAmount - double.Epsilon);
              }

              // if not full
              if (!full)
              {
                // calculate worst required resource percentual
                double worst_input = 1.0;
                foreach(var ir in converter.inputList)
                {
                  double required = ir.Ratio * TimeWarp.fixedDeltaTime;
                  double amount = Lib.GetResourceAmount(vessel, ir.ResourceName);
                  worst_input = Math.Min(worst_input, amount / required);
                }

                // for each input resource
                foreach(var ir in converter.inputList)
                {
                  // consume the resource
                  Lib.RequestResource(vessel, ir.ResourceName, ir.Ratio * worst_input * TimeWarp.fixedDeltaTime);
                }

                // for each output resource
                foreach(var or in converter.outputList)
                {
                  // produce the resource
                  Lib.RequestResource(vessel, or.ResourceName, -or.Ratio * worst_input * TimeWarp.fixedDeltaTime * Malfunction.Penalty(part));
                }
              }

              // undo stock behaviour by forcing last_update_time to now
              module.moduleValues.SetValue("lastUpdateTime", Planetarium.GetUniversalTime().ToString());
            }
          }
          // drill
          // note: ignore stock temperature mechanic of harvesters
          // note: ignore autoshutdown
          // note: ignore depletion (stock seem to do the same)
          // note: 'undo' stock behaviour by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation)
          else if (module.moduleName == "ModuleResourceHarvester")
          {
            // determine if active
            bool activated = Convert.ToBoolean(module.moduleValues.GetValue("IsActivated"));

            // if active
            if (activated)
            {
              // get module from prefab
              ModuleResourceHarvester harvester = part_prefab.Modules.GetModules<ModuleResourceHarvester>()[0];

              // [disabled] reason: not working
              // deduce crew bonus
              /*double experience_bonus = 0.0;
              if (harvester.UseSpecialistBonus)
              {
                foreach(ProtoCrewMember c in vessel.protoVessel.GetVesselCrew())
                {
                  experience_bonus = Math.Max(experience_bonus, (c.trait == harvester.Specialty) ? (double)c.experienceLevel : 0.0);
                }
              }*/
              const double crew_bonus = 1.0; //harvester.SpecialistBonusBase + (experience_bonus + 1.0) * harvester.SpecialistEfficiencyFactor;

              // detect amount of ore in the ground
              AbundanceRequest request = new AbundanceRequest
              {
                Altitude = vessel.altitude,
                BodyId = vessel.mainBody.flightGlobalsIndex,
                CheckForLock = false,
                Latitude = vessel.latitude,
                Longitude = vessel.longitude,
                ResourceType = (HarvestTypes)harvester.HarvesterType,
                ResourceName = harvester.ResourceName
              };
              double abundance = ResourceMap.Instance.GetAbundance(request);

              // if there is actually something (should be if active when unloaded)
              if (abundance > harvester.HarvestThreshold)
              {
                // calculate worst required resource percentual
                double worst_input = 1.0;
                foreach(var ir in harvester.inputList)
                {
                  double required = ir.Ratio * TimeWarp.fixedDeltaTime;
                  double amount = Lib.GetResourceAmount(vessel, ir.ResourceName);
                  worst_input = Math.Min(worst_input, amount / required);
                }

                // for each input resource
                foreach(var ir in harvester.inputList)
                {
                  // consume the resource
                  Lib.RequestResource(vessel, ir.ResourceName, ir.Ratio * worst_input * TimeWarp.fixedDeltaTime);
                }

                // determine resource produced
                double res = abundance * harvester.Efficiency * crew_bonus * worst_input * Malfunction.Penalty(part);

                // accumulate ore
                Lib.RequestResource(vessel, harvester.ResourceName, -res * TimeWarp.fixedDeltaTime);
              }

              // undo stock behaviour by forcing last_update_time to now
              module.moduleValues.SetValue("lastUpdateTime", Planetarium.GetUniversalTime().ToString());
            }
          }
          // asteroid drill
          // note: untested
          // note: ignore stock temperature mechanic of asteroid drills
          // note: ignore autoshutdown
          // note: 'undo' stock behaviour by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation)
          else if (module.moduleName == "ModuleAsteroidDrill")
          {
            // determine if active
            bool activated = Convert.ToBoolean(module.moduleValues.GetValue("IsActivated"));

            // if active
            if (activated)
            {
              // get module from prefab
              ModuleAsteroidDrill asteroid_drill = part_prefab.Modules.GetModules<ModuleAsteroidDrill>()[0];

              // [disabled] reason: not working
              // deduce crew bonus
              /*double experience_bonus = 0.0;
              if (asteroid_drill.UseSpecialistBonus)
              {
                foreach(ProtoCrewMember c in vessel.protoVessel.GetVesselCrew())
                {
                  experience_bonus = Math.Max(experience_bonus, (c.trait == asteroid_drill.Specialty) ? (double)c.experienceLevel : 0.0);
                }
              }*/
              const double crew_bonus = 1.0; //asteroid_drill.SpecialistBonusBase + (experience_bonus + 1.0) * asteroid_drill.SpecialistEfficiencyFactor;

              // get asteroid data
              ProtoPartModuleSnapshot asteroid_info = null;
              ProtoPartModuleSnapshot asteroid_resource = null;
              foreach(ProtoPartSnapshot p in vessel.protoVessel.protoPartSnapshots)
              {
                if (asteroid_info == null) asteroid_info = p.modules.Find(k => k.moduleName == "ModuleAsteroidInfo");
                if (asteroid_resource == null) asteroid_resource = p.modules.Find(k => k.moduleName == "ModuleAsteroidResource");
              }

              // if there is actually an asteroid attached to this active asteroid drill (it should)
              if (asteroid_info != null && asteroid_resource != null)
              {
                // get some data
                double mass_threshold = Convert.ToDouble(asteroid_info.moduleValues.GetValue("massThresholdVal"));
                double mass = Convert.ToDouble(asteroid_info.moduleValues.GetValue("currentMassVal"));
                double abundance = Convert.ToDouble(asteroid_resource.moduleValues.GetValue("abundance"));
                string res_name = asteroid_resource.moduleValues.GetValue("resourceName");
                double res_density = PartResourceLibrary.Instance.GetDefinition(res_name).density;

                // if asteroid isn't depleted
                if (mass > mass_threshold && abundance > double.Epsilon)
                {
                  // consume EC
                  double ec_required = asteroid_drill.PowerConsumption * TimeWarp.fixedDeltaTime;
                  double ec_consumed = Lib.RequestResource(vessel, "ElectricCharge", ec_required);
                  double ec_ratio = ec_consumed / ec_required;

                  // determine resource extracted
                  double res_amount = abundance * asteroid_drill.Efficiency * crew_bonus * ec_ratio * TimeWarp.fixedDeltaTime;

                  // produce mined resource
                  Lib.RequestResource(vessel, res_name, -res_amount);

                  // consume asteroid mass
                  asteroid_info.moduleValues.SetValue("currentMassVal", (mass - res_density * res_amount).ToString());
                }
              }

              // undo stock behaviour by forcing last_update_time to now
              module.moduleValues.SetValue("lastUpdateTime", Planetarium.GetUniversalTime().ToString());
            }
          }
          // science lab
          // note: we are only simulating the EC consumption
          // note: there is no easy way to 'stop' the lab when there isn't enough EC
          else if (module.moduleName == "ModuleScienceConverter")
          {
            // get module from prefab
            ModuleScienceConverter lab = part_prefab.Modules.GetModules<ModuleScienceConverter>()[0];

            // determine if active
            bool activated = Convert.ToBoolean(module.moduleValues.GetValue("IsActivated"));

            // if active
            if (activated)
            {
              Lib.RequestResource(vessel, "ElectricCharge", lab.powerRequirement * TimeWarp.fixedDeltaTime);
            }
          }
          // SCANSAT support
          else if (module.moduleName == "SCANsat" || module.moduleName == "ModuleSCANresourceScanner")
          {
            // get ec consumption rate
            PartModule scansat = part_prefab.Modules[module.moduleName];
            double power = Lib.ReflectionValue<float>(scansat, "power");
            double ec_required = power * TimeWarp.fixedDeltaTime;
            bool is_scanning = Lib.GetProtoValue<bool>(module, "scanning");
            bool was_disabled = vd.scansat_id.Contains(part.flightID);

            // if its scanning
            if (Lib.GetProtoValue<bool>(module, "scanning"))
            {
              // consume ec
              double ec_consumed = Lib.RequestResource(vessel, "ElectricCharge", ec_required);

              // if there isn't enough ec
              if (ec_consumed < ec_required * 0.99 && ec_required > double.Epsilon)
              {
                // unregister scanner
                SCANsat.stopScanner(vessel, module, part_prefab);

                // remember disabled scanner
                vd.scansat_id.Add(part.flightID);

                // give the user some feedback
                if (DB.VesselData(vessel.id).cfg_ec == 1)
                  Message.Post("SCANsat sensor was disabled on <b>" + vessel.vesselName + "</b>");
              }
            }
            // if it was disabled
            else if (vd.scansat_id.Contains(part.flightID))
            {
              // if there is enough ec
              double ec_amount = Lib.GetResourceAmount(vessel, "ElectricCharge");
              double ec_capacity = Lib.GetResourceCapacity(vessel, "ElectricCharge");
              if (ec_capacity > double.Epsilon && ec_amount / ec_capacity > 0.25) //< re-enable at 25% EC
              {
                // re-enable the scanner
                SCANsat.resumeScanner(vessel, module, part_prefab);

                // give the user some feedback
                if (DB.VesselData(vessel.id).cfg_ec == 1)
                  Message.Post("SCANsat sensor resumed operations on <b>" + vessel.vesselName + "</b>");
              }
            }

            // forget active scanners
            if (Lib.GetProtoValue<bool>(module, "scanning")) vd.scansat_id.Remove(part.flightID);
          }
          // NearFutureSolar support
          // note: we assume deployed, this is a current limitation
          else if (module.moduleName == "ModuleCurvedSolarPanel")
          {
            // if in sunlight
            if (info.sunlight)
            {
              PartModule curved_panel = part_prefab.Modules[module.moduleName];
              double output = CurvedPanelOutput(vessel, part, part_prefab, curved_panel, info.sun_dir, info.sun_dist, atmo_factor) * Malfunction.Penalty(part);
              Lib.RequestResource(vessel, "ElectricCharge", -output * TimeWarp.fixedDeltaTime);
            }
          }
          // NearFutureElectrical support
          // note: fission generator ignore heat
          // note: radioisotope generator doesn't support easy mode
          else if (module.moduleName == "FissionGenerator")
          {
            PartModule generator = part_prefab.Modules[module.moduleName];
            double power = Lib.ReflectionValue<float>(generator, "PowerGeneration");

            // get fission reactor tweakable, will default to 1.0 for other modules
            var reactor = part.modules.Find(k => k.moduleName == "FissionReactor");
            double tweakable = reactor == null ? 1.0 : Lib.ConfigValue(reactor.moduleValues, "CurrentPowerPercent", 100.0) * 0.01;
            Lib.RequestResource(vessel, "ElectricCharge", -power * tweakable * TimeWarp.fixedDeltaTime);
          }
          else if (module.moduleName == "ModuleRadioisotopeGenerator")
          {
            double mission_time = vessel.missionTime / (3600.0 * Lib.HoursInDay() * Lib.DaysInYear());
            PartModule generator = part_prefab.Modules[module.moduleName];
            double half_life = Lib.ReflectionValue<float>(generator, "HalfLife");
            double remaining = Math.Pow(2.0, (-mission_time) / half_life);
            double power = Lib.ReflectionValue<float>(generator, "BasePower");
            Lib.RequestResource(vessel, "ElectricCharge", -power * remaining * TimeWarp.fixedDeltaTime);
          }
          // KERBALISM modules
          else if (module.moduleName == "Scrubber") { Scrubber.BackgroundUpdate(vessel, part.flightID); }
          else if (module.moduleName == "Greenhouse") { Greenhouse.BackgroundUpdate(vessel, part.flightID); }
          else if (module.moduleName == "GravityRing") { GravityRing.BackgroundUpdate(vessel, part.flightID); }
          else if (module.moduleName == "Malfunction") { Malfunction.BackgroundUpdate(vessel, part.flightID); }
        }
      }
    }
  }
        public override void OnStart(StartState state)
        {
            moduleScienceLab       = part.FindModuleImplementing <ModuleScienceLab>();
            moduleScienceConverter = part.FindModuleImplementing <ModuleScienceConverter>();

            if (moduleScienceConverter != null && moduleScienceLab != null)
            {
                DetermineTechLevel();

                var scienceCap = GetScienceCap();
                moduleScienceLab.dataStorage      = scienceCap;
                moduleScienceConverter.scienceCap = scienceCap;

                var deminishingScienceModifier = moduleScienceLab.dataStored >= baseDataStorage ? 1 : moduleScienceLab.dataStored / baseDataStorage;

                dataProcessingMultiplier = moduleScienceLab.dataStored < float.Epsilon ? 0.5f
                    : deminishingScienceModifier * (baseDataStorage / moduleScienceLab.dataStorage) * (moduleScienceLab.dataStorage / moduleScienceLab.dataStored) * 0.5f;
                moduleScienceConverter.dataProcessingMultiplier = dataProcessingMultiplier;
            }

            if (state == StartState.Editor)
            {
                if (this.HasTechsRequiredToUpgrade())
                {
                    isupgraded = true;
                    upgradePartModule();
                }
                return;
            }

            if (isupgraded)
            {
                upgradePartModule();
            }
            else
            {
                if (this.HasTechsRequiredToUpgrade())
                {
                    hasRequiredUpgrade = true;
                }
            }

            // update gui names

            /*
             * Events["BeginResearch"].guiName = beginResearchName;
             */

            reprocessor = new NuclearFuelReprocessor();
            reprocessor.Initialize(part);
            antimatterGenerator = new AntimatterGenerator(part, 1, PartResourceLibrary.Instance.GetDefinition(KITResourceSettings.AntiProtium));

            Debug.Log("[KSPI]: ScienceModule on " + part.name + " was Force Activated");
            part.force_activate();

            anim  = part.FindModelAnimators(animName1).FirstOrDefault();
            anim2 = part.FindModelAnimators(animName2).FirstOrDefault();
            if (anim != null && anim2 != null)
            {
                if (IsEnabled)
                {
                    //anim [animName1].normalizedTime = 1f;
                    //anim2 [animName2].normalizedTime = 1f;
                    //anim [animName1].speed = -1f;
                    //anim2 [animName2].speed = -1f;
                    anim.Blend(animName1, 1, 0);
                    anim2.Blend(animName2, 1, 0);
                }
                else
                {
                    //anim [animName1].normalizedTime = 0f;
                    //anim2 [animName2].normalizedTime = 0f;
                    //anim [animName1].speed = 1f;
                    //anim2 [animName2].speed = 1f;
                    //anim.Blend (animName1, 0, 0);global_rate_multipliers
                    //anim2.Blend (animName2, 0, 0);
                    play_down = false;
                }
                //anim.Play ();
                //anim2.Play ();
            }
        }