public static void update(Vessel v, vessel_info vi, vessel_data vd, vessel_resources resources, double elapsed_s) { // get most used resource handlers resource_info ec = resources.Info(v, "ElectricCharge"); // for each part foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { // a part can contain multiple resource converters int converter_index = 0; // get part prefab (required for module properties) Part part_prefab = PartLoader.getPartInfoByName(p.partName).partPrefab; // for each module foreach (ProtoPartModuleSnapshot m in p.modules) { // get the module prefab PartModule module_prefab = Lib.FindModule(part_prefab, m.moduleName); // if the prefab doesn't contain this module, skip it if (!module_prefab) { continue; } // process modules switch (m.moduleName) { case "Reliability": Reliability.BackgroundUpdate(v, m, module_prefab as Reliability, elapsed_s); break; case "Scrubber": Scrubber.BackgroundUpdate(v, m, module_prefab as Scrubber, vi, resources, elapsed_s); break; case "Recycler": Recycler.BackgroundUpdate(v, m, module_prefab as Recycler, resources, elapsed_s); break; case "Greenhouse": Greenhouse.BackgroundUpdate(v, p, m, module_prefab as Greenhouse, vi, resources, elapsed_s); break; case "GravityRing": GravityRing.BackgroundUpdate(v, p, m, module_prefab as GravityRing, resources, elapsed_s); break; case "Emitter": Emitter.BackgroundUpdate(v, p, m, module_prefab as Emitter, ec, elapsed_s); break; case "ModuleCommand": ProcessCommand(v, p, m, module_prefab as ModuleCommand, resources, elapsed_s); break; case "ModuleDeployableSolarPanel": ProcessPanel(v, p, m, module_prefab as ModuleDeployableSolarPanel, vi, ec, elapsed_s); break; case "ModuleGenerator": ProcessGenerator(v, p, m, module_prefab as ModuleGenerator, resources, elapsed_s); break; case "ModuleResourceConverter": case "ModuleKPBSConverter": case "FissionReactor": ProcessConverter(v, p, m, part_prefab, converter_index++, resources, elapsed_s); break; case "ModuleResourceHarvester": ProcessHarvester(v, p, m, module_prefab as ModuleResourceHarvester, resources, elapsed_s); break; case "ModuleAsteroidDrill": ProcessAsteroidDrill(v, p, m, module_prefab as ModuleAsteroidDrill, resources, elapsed_s); break; case "ModuleScienceConverter": ProcessLab(v, p, m, module_prefab as ModuleScienceConverter, ec, elapsed_s); break; case "ModuleLight": case "ModuleColoredLensLight": case "ModuleMultiPointSurfaceLight": ProcessLight(v, p, m, module_prefab as ModuleLight, ec, elapsed_s); break; case "SCANsat": case "ModuleSCANresourceScanner": ProcessScanner(v, p, m, module_prefab, part_prefab, vd, ec, elapsed_s); break; case "ModuleCurvedSolarPanel": ProcessCurvedPanel(v, p, m, module_prefab, part_prefab, vi, ec, elapsed_s); break; case "FissionGenerator": ProcessFissionGenerator(v, p, m, module_prefab, ec, elapsed_s); break; case "ModuleRadioisotopeGenerator": ProcessRadioisotopeGenerator(v, p, m, module_prefab, ec, elapsed_s); break; case "ModuleCryoTank": ProcessCryoTank(v, p, m, module_prefab, resources, elapsed_s); break; } } } }
public static void update(Vessel v, vessel_info vi, VesselData vd, vessel_resources resources, double elapsed_s) { // get most used resource handlers resource_info ec = resources.Info(v, "ElectricCharge"); // store data required to support multiple modules of same type in a part var PD = new Dictionary <string, Lib.module_prefab_data>(); // for each part foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { // get part prefab (required for module properties) Part part_prefab = PartLoader.getPartInfoByName(p.partName).partPrefab; // get all module prefabs var module_prefabs = part_prefab.FindModulesImplementing <PartModule>(); // clear module indexes PD.Clear(); // for each module foreach (ProtoPartModuleSnapshot m in p.modules) { // get module type // if the type is unknown, skip it module_type type = ModuleType(m.moduleName); if (type == module_type.Unknown) { continue; } // get the module prefab // if the prefab doesn't contain this module, skip it PartModule module_prefab = Lib.ModulePrefab(module_prefabs, m.moduleName, PD); if (!module_prefab) { continue; } // if the module is disabled, skip it // note: this must be done after ModulePrefab is called, so that indexes are right if (!Lib.Proto.GetBool(m, "isEnabled")) { continue; } // process modules // note: this should be a fast switch, possibly compiled to a jump table switch (type) { case module_type.Reliability: Reliability.BackgroundUpdate(v, p, m, module_prefab as Reliability); break; case module_type.Experiment: Experiment.BackgroundUpdate(v, m, module_prefab as Experiment, ec, elapsed_s); break; case module_type.Greenhouse: Greenhouse.BackgroundUpdate(v, m, module_prefab as Greenhouse, vi, resources, elapsed_s); break; case module_type.GravityRing: GravityRing.BackgroundUpdate(v, p, m, module_prefab as GravityRing, ec, elapsed_s); break; case module_type.Emitter: Emitter.BackgroundUpdate(v, p, m, module_prefab as Emitter, ec, elapsed_s); break; case module_type.Harvester: Harvester.BackgroundUpdate(v, m, module_prefab as Harvester, elapsed_s); break; case module_type.Laboratory: Laboratory.BackgroundUpdate(v, p, m, module_prefab as Laboratory, ec, elapsed_s); break; case module_type.Command: ProcessCommand(v, p, m, module_prefab as ModuleCommand, resources, elapsed_s); break; case module_type.Panel: ProcessPanel(v, p, m, module_prefab as ModuleDeployableSolarPanel, vi, ec, elapsed_s); break; case module_type.Generator: ProcessGenerator(v, p, m, module_prefab as ModuleGenerator, resources, elapsed_s); break; case module_type.Converter: ProcessConverter(v, p, m, module_prefab as ModuleResourceConverter, resources, elapsed_s); break; case module_type.Drill: ProcessHarvester(v, p, m, module_prefab as ModuleResourceHarvester, resources, elapsed_s); break; case module_type.AsteroidDrill: ProcessAsteroidDrill(v, p, m, module_prefab as ModuleAsteroidDrill, resources, elapsed_s); break; case module_type.StockLab: ProcessStockLab(v, p, m, module_prefab as ModuleScienceConverter, ec, elapsed_s); break; case module_type.Light: ProcessLight(v, p, m, module_prefab as ModuleLight, ec, elapsed_s); break; case module_type.Scanner: ProcessScanner(v, p, m, module_prefab, part_prefab, vd, ec, elapsed_s); break; case module_type.CurvedPanel: ProcessCurvedPanel(v, p, m, module_prefab, part_prefab, vi, ec, elapsed_s); break; case module_type.FissionGenerator: ProcessFissionGenerator(v, p, m, module_prefab, ec, elapsed_s); break; case module_type.RadioisotopeGenerator: ProcessRadioisotopeGenerator(v, p, m, module_prefab, ec, elapsed_s); break; case module_type.CryoTank: ProcessCryoTank(v, p, m, module_prefab, resources, elapsed_s); break; } } } }
public static void Update(Vessel v, Vessel_info vi, VesselData vd, Vessel_resources resources, double elapsed_s) { if (!Lib.IsVessel(v)) { return; } // get most used resource handlers Resource_info ec = resources.Info(v, "ElectricCharge"); // This is basically handled in cache. However, when accelerating time warp while // the vessel is in shadow, the cache logic doesn't kick in soon enough. So we double-check here if (TimeWarp.CurrentRate > 1000.0f || elapsed_s > 150) // we're time warping fast... { vi.highspeedWarp(v); } foreach (var e in Background_PMs(v)) { switch (e.type) { case Module_type.Reliability: Reliability.BackgroundUpdate(v, e.p, e.m, e.module_prefab as Reliability); break; case Module_type.Experiment: Experiment.BackgroundUpdate(v, e.m, e.module_prefab as Experiment, ec, resources, elapsed_s); break; case Module_type.Greenhouse: Greenhouse.BackgroundUpdate(v, e.m, e.module_prefab as Greenhouse, vi, resources, elapsed_s); break; case Module_type.GravityRing: GravityRing.BackgroundUpdate(v, e.p, e.m, e.module_prefab as GravityRing, ec, elapsed_s); break; case Module_type.Emitter: Emitter.BackgroundUpdate(v, e.p, e.m, e.module_prefab as Emitter, ec, elapsed_s); break; case Module_type.Harvester: Harvester.BackgroundUpdate(v, e.m, e.module_prefab as Harvester, elapsed_s); break; // Kerbalism ground and air harvester module case Module_type.Laboratory: Laboratory.BackgroundUpdate(v, e.p, e.m, e.module_prefab as Laboratory, ec, elapsed_s); break; case Module_type.Command: ProcessCommand(v, e.p, e.m, e.module_prefab as ModuleCommand, resources, elapsed_s); break; case Module_type.Panel: ProcessPanel(v, e.p, e.m, e.module_prefab as ModuleDeployableSolarPanel, vi, ec, elapsed_s); break; case Module_type.Generator: ProcessGenerator(v, e.p, e.m, e.module_prefab as ModuleGenerator, resources, elapsed_s); break; case Module_type.Converter: ProcessConverter(v, e.p, e.m, e.module_prefab as ModuleResourceConverter, resources, elapsed_s); break; case Module_type.Drill: ProcessDrill(v, e.p, e.m, e.module_prefab as ModuleResourceHarvester, resources, elapsed_s); break; // Stock ground harvester module case Module_type.AsteroidDrill: ProcessAsteroidDrill(v, e.p, e.m, e.module_prefab as ModuleAsteroidDrill, resources, elapsed_s); break; // Stock asteroid harvester module case Module_type.StockLab: ProcessStockLab(v, e.p, e.m, e.module_prefab as ModuleScienceConverter, ec, elapsed_s); break; case Module_type.Light: ProcessLight(v, e.p, e.m, e.module_prefab as ModuleLight, ec, elapsed_s); break; case Module_type.Scanner: KerbalismScansat.BackgroundUpdate(v, e.p, e.m, e.module_prefab as KerbalismScansat, e.part_prefab, vd, ec, elapsed_s); break; case Module_type.CurvedPanel: ProcessCurvedPanel(v, e.p, e.m, e.module_prefab, e.part_prefab, vi, ec, elapsed_s); break; case Module_type.FissionGenerator: ProcessFissionGenerator(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break; case Module_type.RadioisotopeGenerator: ProcessRadioisotopeGenerator(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break; case Module_type.CryoTank: ProcessCryoTank(v, e.p, e.m, e.module_prefab, resources, ec, elapsed_s); break; case Module_type.FNGenerator: ProcessFNGenerator(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break; case Module_type.NonRechargeBattery: ProcessNonRechargeBattery(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break; case Module_type.KerbalismProcess: KerbalismProcess.BackgroundUpdate(v, e.m, e.module_prefab as KerbalismProcess, ec, resources, elapsed_s); break; } } }
public static void Update(Vessel v, VesselData vd, VesselResources resources, double elapsed_s) { if (!Lib.IsVessel(v)) { return; } // get most used resource handlers ResourceInfo ec = resources.GetResource(v, "ElectricCharge"); List <ResourceInfo> allResources = resources.GetAllResources(v); Dictionary <string, double> availableResources = new Dictionary <string, double>(); foreach (var ri in allResources) { availableResources[ri.ResourceName] = ri.Amount; } List <KeyValuePair <string, double> > resourceChangeRequests = new List <KeyValuePair <string, double> >(); foreach (var e in Background_PMs(v)) { switch (e.type) { case Module_type.Reliability: Reliability.BackgroundUpdate(v, e.p, e.m, e.module_prefab as Reliability, elapsed_s); break; case Module_type.Experiment: (e.module_prefab as Experiment).BackgroundUpdate(v, vd, e.m, ec, resources, elapsed_s); break; // experiments use the prefab as a singleton instead of a static method case Module_type.Greenhouse: Greenhouse.BackgroundUpdate(v, e.m, e.module_prefab as Greenhouse, vd, resources, elapsed_s); break; case Module_type.GravityRing: GravityRing.BackgroundUpdate(v, e.p, e.m, e.module_prefab as GravityRing, ec, elapsed_s); break; case Module_type.Harvester: Harvester.BackgroundUpdate(v, e.m, e.module_prefab as Harvester, elapsed_s); break; // Kerbalism ground and air harvester module case Module_type.Laboratory: Laboratory.BackgroundUpdate(v, e.p, e.m, e.module_prefab as Laboratory, ec, elapsed_s); break; case Module_type.Command: ProcessCommand(v, e.p, e.m, e.module_prefab as ModuleCommand, resources, elapsed_s); break; case Module_type.Generator: ProcessGenerator(v, e.p, e.m, e.module_prefab as ModuleGenerator, resources, elapsed_s); break; case Module_type.Converter: ProcessConverter(v, e.p, e.m, e.module_prefab as ModuleResourceConverter, resources, elapsed_s); break; case Module_type.Drill: ProcessDrill(v, e.p, e.m, e.module_prefab as ModuleResourceHarvester, resources, elapsed_s); break; // Stock ground harvester module // case Module_type.AsteroidDrill: ProcessAsteroidDrill(v, e.p, e.m, e.module_prefab as ModuleAsteroidDrill, resources, elapsed_s); break; // Stock asteroid harvester module case Module_type.StockLab: ProcessStockLab(v, e.p, e.m, e.module_prefab as ModuleScienceConverter, ec, elapsed_s); break; case Module_type.Light: ProcessLight(v, e.p, e.m, e.module_prefab as ModuleLight, ec, elapsed_s); break; case Module_type.Scanner: KerbalismScansat.BackgroundUpdate(v, e.p, e.m, e.module_prefab as KerbalismScansat, e.part_prefab, vd, ec, elapsed_s); break; case Module_type.FissionGenerator: ProcessFissionGenerator(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break; case Module_type.RadioisotopeGenerator: ProcessRadioisotopeGenerator(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break; case Module_type.CryoTank: ProcessCryoTank(v, e.p, e.m, e.module_prefab, resources, ec, elapsed_s); break; case Module_type.FNGenerator: ProcessFNGenerator(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break; case Module_type.SolarPanelFixer: SolarPanelFixer.BackgroundUpdate(v, e.m, e.module_prefab as SolarPanelFixer, vd, ec, elapsed_s); break; case Module_type.KerbalismSentinel: KerbalismSentinel.BackgroundUpdate(v, e.m, e.module_prefab as KerbalismSentinel, vd, ec, elapsed_s); break; case Module_type.APIModule: ProcessApiModule(v, e.p, e.m, e.part_prefab, e.module_prefab, resources, availableResources, resourceChangeRequests, elapsed_s); break; } } }
// 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); } } } } }