protected float Sample(ProfileableResource resource, Vector3 worldPos, float noiseScalar) { float abundance = 0f; AbundanceRequest req = new AbundanceRequest(); double alt; double lat; double lon; part.vessel.mainBody.GetLatLonAlt(new Vector3d(worldPos.x, worldPos.y, worldPos.z), out lat, out lon, out alt); req.BodyId = FlightGlobals.GetBodyIndex(part.vessel.mainBody); req.ResourceName = resource.resourceName; req.Latitude = lat; req.Altitude = alt; req.Longitude = lon; // Sample atmo req.ResourceType = HarvestTypes.Atmospheric; abundance += ResourceMap.Instance.GetAbundance(req); if (resource.useAtmo) { abundance = abundance * (float)part.vessel.mainBody.GetPressure(alt); } // Sample exo req.ResourceType = HarvestTypes.Exospheric; abundance = abundance + ResourceMap.Instance.GetAbundance(req); //Utils.Log(String.Format("[ModuleProfilingScanner]: Sampling position {0}, geocentric alt {1}, lat {2} lon {3}\n Noise: {4} Result: {5}", worldPos.ToString(), alt, lat, lon, noiseScalar, abundance)); abundance = abundance + noiseScalar * UnityEngine.Random.Range(-1.0f, 1.0f); return(Mathf.Clamp(abundance, 0f, 10000f)); }
static void ProcessHarvester(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleResourceHarvester harvester, vessel_resources resources, double elapsed_s) { // note: ignore stock temperature mechanic of harvesters // note: ignore autoshutdown // note: ignore depletion (stock seem to do the same) // note: using hard-coded crew bonus values from the wiki because the module data make zero sense (DERP ALERT) // note: 'undo' stock behaviour by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation) // if active if (Lib.Proto.GetBool(m, "IsActivated")) { // do nothing if full // note: comparing against previous amount if (resources.Info(v, harvester.ResourceName).level < harvester.FillAmount - double.Epsilon) { // get malfunction penalty double penalty = Reliability.Penalty(p, "Harvester"); // deduce crew bonus int exp_level = -1; if (harvester.UseSpecialistBonus) { foreach (ProtoCrewMember c in v.protoVessel.GetVesselCrew()) { exp_level = Math.Max(exp_level, c.trait == harvester.Specialty ? c.experienceLevel : -1); } } double exp_bonus = exp_level < 0 ? 1.0 : 5.0 + (double)exp_level * 4.0; // detect amount of ore in the ground AbundanceRequest request = new AbundanceRequest { Altitude = v.altitude, BodyId = v.mainBody.flightGlobalsIndex, CheckForLock = false, Latitude = v.latitude, Longitude = v.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) { // create and commit recipe resource_recipe recipe = new resource_recipe(resource_recipe.harvester_priority); foreach (var ir in harvester.inputList) { recipe.Input(ir.ResourceName, ir.Ratio * elapsed_s); } recipe.Output(harvester.ResourceName, abundance * harvester.Efficiency * exp_bonus * penalty * elapsed_s); resources.Transform(recipe); } } // undo stock behaviour by forcing last_update_time to now Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime()); } }
public void Update() { // in editor and flight, update ui button label Events["Toggle"].guiName = Lib.StatusToggle(title, running ? "running" : "stopped"); // if in flight, and the stock planet resource system is online if (Lib.IsFlight() && ResourceMap.Instance != null) { // shortcut CelestialBody body = vessel.mainBody; // determine if overall situation is valid for extraction bool valid = false; if (deployed) { switch (type) { case 0: valid = vessel.Landed && GroundContact(); break; case 1: valid = body.ocean && (vessel.Splashed || vessel.altitude < 0.0); break; case 2: valid = body.atmosphere && vessel.altitude < body.atmosphereDepth; break; case 3: valid = vessel.altitude > body.atmosphereDepth || !body.atmosphere; break; } } // if situation is valid double abundance = 0.0; if (valid) { // get abundance AbundanceRequest request = new AbundanceRequest { ResourceType = (HarvestTypes)type, ResourceName = resource, BodyId = body.flightGlobalsIndex, Latitude = vessel.latitude, Longitude = vessel.longitude, Altitude = vessel.altitude, CheckForLock = false }; abundance = ResourceMap.Instance.GetAbundance(request); } // determine if resources can be harvested // - invalid conditions // - abundance below threshold // - pressure below threshold starved = !valid || abundance <= min_abundance || (type == 2 && body.GetPressure(vessel.altitude) <= min_pressure); // update ui Events["Toggle"].guiActive = valid; Fields["Abundance"].guiActive = valid; Fields["Abundance"].guiName = Lib.BuildString(resource, " abundance"); Abundance = abundance > double.Epsilon ? Lib.HumanReadablePerc(abundance, "F2") : "none"; } }
public ResourceAbundanceTracker(ModuleResourceScanner scanner) { request = new AbundanceRequest(); request.CheckForLock = scanner.RequiresUnlock; request.ResourceType = (HarvestTypes)scanner.ScannerType; request.ResourceName = scanner.ResourceName; abundance = 0; }
private static double GetAbundance(AbundanceRequest request) { // retrieve and convert to double double abundance = (double)(decimal)ResourceMap.Instance.GetAbundance(request) * 100; if (abundance < 1) { abundance = Math.Pow(abundance, 3); } return(abundance); }
public static int GenerateAbundanceSeed(AbundanceRequest request, CBAttributeMapSO.MapAttribute biome, CelestialBody body) { var seed = RegolithScenario.Instance.gameSettings.Seed; seed *= (request.BodyId + 1); seed += request.ResourceName.Length * request.ResourceName.Substring(1).ToCharArray().First(); seed += body.bodyName.Length * body.bodyName.Substring(1).ToCharArray().First(); if (biome != null) { seed += Convert.ToInt32(biome.mapColor.grayscale * 4096) * ((int)request.ResourceType + 1); } return seed; }
public double GetAmount(string ResourceName, RPLocation location, double rate) { AbundanceRequest request = new AbundanceRequest { Altitude = location.altitude, BodyId = location.bodyIndex, CheckForLock = false, Latitude = location.latitude, Longitude = location.longitude, ResourceType = 0, ResourceName = ResourceName }; return ResourceMap.Instance.GetAbundance(request) * rate; }
public static float GetAbundance(double latitude, double longitude, string resourceName, int bodyId, int resType, int altitude, bool checkForLock = true) { var abRequest = new AbundanceRequest { Latitude = latitude, Longitude = longitude, BodyId = bodyId, ResourceName = resourceName, ResourceType = (HarvestTypes)resType, Altitude = altitude, CheckForLock = checkForLock //TRUE = means that biome unlocking is active }; return GetAbundance(abRequest); }
public double GetAmount(string ResourceName, RPLocation location, double rate) { AbundanceRequest request = new AbundanceRequest { Altitude = location.altitude, BodyId = location.bodyIndex, CheckForLock = false, Latitude = location.latitude, Longitude = location.longitude, ResourceType = 0, ResourceName = ResourceName }; return(ResourceMap.Instance.GetAbundance(request) * rate); }
// return resource abundance at vessel position private static double SampleAbundance(Vessel v, Harvester harvester) { // get abundance AbundanceRequest request = new AbundanceRequest { ResourceType = (HarvestTypes)harvester.type, ResourceName = harvester.resource, BodyId = v.mainBody.flightGlobalsIndex, Latitude = v.latitude, Longitude = v.longitude, Altitude = v.altitude, CheckForLock = false }; return ResourceMap.Instance.GetAbundance(request); }
// return resource abundance at vessel position double SampleAbundance() { // get abundance AbundanceRequest request = new AbundanceRequest { ResourceType = (HarvestTypes)type, ResourceName = resource, BodyId = vessel.mainBody.flightGlobalsIndex, Latitude = vessel.latitude, Longitude = vessel.longitude, Altitude = vessel.altitude, CheckForLock = false }; return(ResourceMap.Instance.GetAbundance(request)); }
///<summary> ///returns the amount of a given resource for a body and place /// takes the args <body> <geocoordinates> and <resource-string> in any order ///</summary> public ScalarDoubleValue GetResourceByName(params Structure[] args) { if (args.Length != 3) { return(null); } BodyTarget body = args.Where(s => s.GetType() == typeof(BodyTarget)).Cast <BodyTarget>().First(); GeoCoordinates coordinate = args.Where(s => s.GetType() == typeof(GeoCoordinates)).Cast <GeoCoordinates>().First(); StringValue s_type = args.Where(s => s.GetType() == typeof(StringValue)).Cast <StringValue>().First(); if (resourceNames == null) { resourceNames = ResourceMap.Instance.FetchAllResourceNames(HarvestTypes.Planetary); } var resourceName = resourceNames.FirstOrDefault(name => string.Equals(name, s_type, StringComparison.InvariantCultureIgnoreCase)); if (resourceName == null) { throw new kOS.Safe.Exceptions.KOSException("invalid resource type"); } if ((SCANWrapper.IsCovered(coordinate.Longitude, coordinate.Latitude, body.Body, "ResourceHiRes")) || ((HasKerbNet("Resource") && (IsInKerbNetFoV(body.Body, coordinate.Longitude, coordinate.Latitude))))) { float amount = 0f; var aRequest = new AbundanceRequest { Latitude = coordinate.Latitude, Longitude = coordinate.Longitude, BodyId = body.Body.flightGlobalsIndex, ResourceName = resourceName, ResourceType = HarvestTypes.Planetary, Altitude = 0, CheckForLock = SCANWrapper.GetResourceBiomeLock(), BiomeName = ScienceUtil.GetExperimentBiome(body.Body, coordinate.Latitude, coordinate.Longitude), ExcludeVariance = false, }; amount = ResourceMap.Instance.GetAbundance(aRequest); return(amount); } else { return(-1.0); } }
protected override ConversionRecipe PrepareRecipe(double deltaTime) { if (HighLogic.LoadedSceneIsFlight && this.HarvesterType == 3) { // Handle resource generation here. var request = new AbundanceRequest { Altitude = this.vessel.altitude, BodyId = FlightGlobals.currentMainBody.flightGlobalsIndex, CheckForLock = false, // BiomeName = ?? Latitude = this.vessel.latitude, Longitude = this.vessel.longitude, ResourceType = HarvestTypes.Exospheric, ResourceName = this.ResourceName }; var abundance = ResourceMap.Instance.GetAbundance(request); if (abundance < 1E-09 || abundance < this.HarvestThreshold) { this.status = "missing resource"; this.IsActivated = false; return(null); } else { // we found a resource! if (counter % 10 == 0) { ScreenMessages.PostScreenMessage("Found " + this.ResourceName); } this.status = "harvesting"; var conversionRate = abundance * this.Efficiency * this.GetCrewBonus(); var orbitalSpeed = this.vessel.orbit.vel.magnitude + 1; var intakeRate = conversionRate * orbitalSpeed; return(GenerateRecipie(intakeRate)); } } else { return(base.PrepareRecipe(deltaTime)); } }
internal static float ResourceOverlay(double lat, double lon, string name, CelestialBody body) { float amount = 0f; var aRequest = new AbundanceRequest { Latitude = lat, Longitude = lon, BodyId = body.flightGlobalsIndex, ResourceName = name, ResourceType = HarvestTypes.Planetary, Altitude = 0, CheckForLock = SCANcontroller.controller.resourceBiomeLock, BiomeName = getBiomeName(body, lon, lat), ExcludeVariance = false, }; amount = ResourceMap.Instance.GetAbundance(aRequest); return(amount); }
public static float GetAbundance(string resourceName, CelestialBody body, double lat, double lon) { var biome = ScienceUtil.GetExperimentBiome(body, lat, lon); var key = new Key(resourceName, biome, lat, lon); if (!cache.ContainsKey(key)) { var req = new AbundanceRequest { Latitude = lat, Longitude = lon, BodyId = body.flightGlobalsIndex, ResourceName = resourceName, ResourceType = HarvestTypes.Planetary, Altitude = 0, CheckForLock = false, BiomeName = biome, ExcludeVariance = false, }; cache[key] = ResourceMap.Instance.GetAbundance(req); } return(cache[key]); }
///<summary> ///returns the amount of a given resource for a body and place /// takes the args <body> <geocoordinates> and <resource-string> in any order ///</summary> public ScalarDoubleValue GetResourceByName(params Structure[] args) { if (args.Length != 3) { return(null); } BodyTarget body = args.Where(s => s.GetType() == typeof(BodyTarget)).Cast <BodyTarget>().First(); // BodyTarget body = args[0] as BodyTarget; GeoCoordinates coordinate = args.Where(s => s.GetType() == typeof(GeoCoordinates)).Cast <GeoCoordinates>().First(); StringValue s_type = args.Where(s => s.GetType() == typeof(StringValue)).Cast <StringValue>().First(); if ((SCANWrapper.IsCovered(coordinate.Longitude, coordinate.Latitude, body.Body, s_type)) || ((HasKerbNet("Resource") && (IsInKerbNetFoV(body.Body, coordinate.Longitude, coordinate.Latitude))))) { float amount = 0f; var aRequest = new AbundanceRequest { Latitude = coordinate.Latitude, Longitude = coordinate.Longitude, BodyId = body.Body.flightGlobalsIndex, ResourceName = s_type, ResourceType = HarvestTypes.Planetary, Altitude = 0, CheckForLock = SCANWrapper.GetResourceBiomeLock(), BiomeName = ScienceUtil.GetExperimentBiome(body.Body, coordinate.Latitude, coordinate.Longitude), ExcludeVariance = false, }; amount = ResourceMap.Instance.GetAbundance(aRequest); return(amount); } else { return(-1.0); } }
protected string getAbundance(string resourceName) { AbundanceRequest request = new AbundanceRequest(); double lattitude = ResourceUtilities.Deg2Rad(this.part.vessel.latitude); double longitude = ResourceUtilities.Deg2Rad(this.part.vessel.longitude); request.BiomeName = Utils.GetCurrentBiome(this.part.vessel).name; request.BodyId = this.part.vessel.mainBody.flightGlobalsIndex; request.Longitude = longitude; request.Latitude = lattitude; request.CheckForLock = true; request.ResourceName = resourceName; float abundance = ResourceMap.Instance.GetAbundance(request) * 100.0f; if (abundance > 0.001) { return(string.Format("{0:f2}%", abundance)); } else { return("Unknown"); } }
private void DrawLogistics() { var vessels = LogisticsTools.GetNearbyVessels(2000, true, _model, true); List <LogisticsResource> resources = new List <LogisticsResource>(); Dictionary <string, float> dr = new Dictionary <string, float>(); int kerbals = 0; foreach (Vessel v in vessels) { // Storage foreach (var r in v.GetStorage()) { getResourceFromList(resources, r.resourceName).maxAmount += r.amount; } // Amount foreach (var r in v.GetResourceAmounts()) { getResourceFromList(resources, r.resourceName).amount += r.amount; } // Production of converters foreach (var r in v.GetProduction()) { getResourceFromList(resources, r.resourceName).change += r.amount; } // Consumption of Converters foreach (var r in v.GetProduction(false)) { getResourceFromList(resources, r.resourceName).change -= r.amount; } // Drills foreach (Part drill in v.Parts.Where(p => p.Modules.Contains("ModuleResourceHarvester"))) { foreach (ModuleResourceHarvester m in drill.FindModulesImplementing <ModuleResourceHarvester>().Where(mod => mod.IsActivated)) { if (v.Parts.Exists(p => p.Resources.list.Exists(r => r.resourceName == m.ResourceName && r.amount < r.maxAmount))) // test if storage for this resource on this vessel is not full { AbundanceRequest ar = new AbundanceRequest { Altitude = v.altitude, BodyId = FlightGlobals.currentMainBody.flightGlobalsIndex, CheckForLock = false, Latitude = v.latitude, Longitude = v.longitude, ResourceType = HarvestTypes.Planetary, ResourceName = m.ResourceName }; getResourceFromList(resources, m.ResourceName).change += (double)ResourceMap.Instance.GetAbundance(ar); getResourceFromList(resources, "ElectricCharge").change -= 6; } } } // Life Support kerbals += v.GetCrewCount(); } if (_usils) { getResourceFromList(resources, "Supplies").change -= kerbals * 0.00005; getResourceFromList(resources, "Mulch").change += kerbals * 0.00005; getResourceFromList(resources, "ElectricCharge").change -= kerbals * 0.01; } resources.Sort(new LogisticsResourceComparer()); foreach (LogisticsResource r in resources) { GUILayout.Label(r.resourceName + ": " + numberToOut(r.amount, -1, false) + "/" + Math.Round(r.maxAmount, 5) + " (" + numberToOut(r.change, r.change > 0 ? r.maxAmount - r.amount : r.amount) + ")"); } }
static void ProcessHarvester(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleResourceHarvester harvester, vessel_resources resources, double elapsed_s) { // 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) // if active if (Lib.Proto.GetBool(m, "IsActivated")) { // do nothing if full // note: comparing against previous amount if (resources.Info(v, harvester.ResourceName).level < harvester.FillAmount - double.Epsilon) { // deduce crew bonus int exp_level = -1; if (harvester.UseSpecialistBonus) { foreach (ProtoCrewMember c in Lib.CrewList(v)) { if (c.experienceTrait.Effects.Find(k => k.Name == harvester.ExperienceEffect) != null) { exp_level = Math.Max(exp_level, c.experienceLevel); } } } double exp_bonus = exp_level < 0 ? harvester.EfficiencyBonus * harvester.SpecialistBonusBase : harvester.EfficiencyBonus * (harvester.SpecialistBonusBase + (harvester.SpecialistEfficiencyFactor * (exp_level + 1))); // detect amount of ore in the ground AbundanceRequest request = new AbundanceRequest { Altitude = v.altitude, BodyId = v.mainBody.flightGlobalsIndex, CheckForLock = false, Latitude = v.latitude, Longitude = v.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) { // create and commit recipe resource_recipe recipe = new resource_recipe(); foreach (var ir in harvester.inputList) { recipe.Input(ir.ResourceName, ir.Ratio * elapsed_s); } recipe.Output(harvester.ResourceName, abundance * harvester.Efficiency * exp_bonus * elapsed_s, true); resources.Transform(recipe); } } // undo stock behaviour by forcing last_update_time to now Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime()); } }
private void DrawLogistics() { var vessels = LogisticsTools.GetNearbyVessels(2000, true, _model, true); List <LogisticsResource> resources = new List <LogisticsResource>(); Dictionary <string, float> dr = new Dictionary <string, float>(); int kerbals = 0; foreach (Vessel v in vessels) { // Storage foreach (var r in v.GetStorage()) { getResourceFromList(resources, r.resourceName).maxAmount += r.amount; } // Amount foreach (var r in v.GetResourceAmounts()) { getResourceFromList(resources, r.resourceName).amount += r.amount; } // Production of converters foreach (var r in v.GetProduction()) { getResourceFromList(resources, r.resourceName).change += r.amount; } // Consumption of Converters foreach (var r in v.GetProduction(false)) { getResourceFromList(resources, r.resourceName).change -= r.amount; } // Drills foreach (Part drill in v.Parts.Where(p => p.Modules.Contains("ModuleResourceHarvester"))) { foreach (ModuleResourceHarvester m in drill.FindModulesImplementing <ModuleResourceHarvester>().Where(mod => mod.IsActivated)) { bool hasResource = false; foreach (var p in v.Parts) { var res = p.Resources.Get(m.ResourceName); if (res == null) { continue; } if (res.amount <= res.maxAmount) { hasResource = true; } } if (hasResource) // test if storage for this resource on this vessel is not full { AbundanceRequest ar = new AbundanceRequest { Altitude = v.altitude, BodyId = FlightGlobals.currentMainBody.flightGlobalsIndex, CheckForLock = false, Latitude = v.latitude, Longitude = v.longitude, ResourceType = HarvestTypes.Planetary, ResourceName = m.ResourceName }; getResourceFromList(resources, m.ResourceName).change += (double)ResourceMap.Instance.GetAbundance(ar); getResourceFromList(resources, "ElectricCharge").change -= 6; } } } // Life Support kerbals += v.GetCrewCount(); } if (_usils) { getResourceFromList(resources, "Supplies").change -= kerbals * 0.00005; getResourceFromList(resources, "Mulch").change += kerbals * 0.00005; getResourceFromList(resources, "ElectricCharge").change -= kerbals * 0.01; } if (_tacls) { // TAC-LS consumption rates are a bit complex to calculate, so here is an approximated calculation where // - Kerbals on EVA are not handled // - BaseElectricityConsumptionRate is not taken into account getResourceFromList(resources, "Oxygen").change -= kerbals * 0.001713537562385; getResourceFromList(resources, "Food").change -= kerbals * 0.000016927083333; getResourceFromList(resources, "Water").change -= kerbals * 0.000011188078704; getResourceFromList(resources, "CarbonDioxide").change += kerbals * 0.00148012889876; getResourceFromList(resources, "Waste").change += kerbals * 0.000001539351852; getResourceFromList(resources, "WasteWater").change += kerbals * 0.000014247685185; getResourceFromList(resources, "ElectricCharge").change -= kerbals * 0.014166666666667; // Values are based on TAC-LS Version 0.11.1.20 } // Consumption rates for Snacks are not calculated resources.Sort(new LogisticsResourceComparer()); foreach (LogisticsResource r in resources) { GUILayout.Label(r.resourceName + ": " + numberToOut(r.amount, -1, false) + "/" + Math.Round(r.maxAmount, 5) + " (" + numberToOut(r.change, r.change > 0 ? r.maxAmount - r.amount : r.amount) + ")"); } }
// 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 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) { // 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) else if (module.moduleName == "ModuleResourceConverter") { // determine if active bool activated = Convert.ToBoolean(module.moduleValues.GetValue("IsActivated")); // if active if (activated) { // get module from prefab ModuleResourceConverter converter = part_prefab.Modules.GetModules<ModuleResourceConverter>()[converter_index++]; // 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]; // 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); } } double crew_bonus = 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]; // 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); } } double crew_bonus = 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()); } } // SCANSAT support (new version) // TODO: enable better 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; // if it was scanning if (SCANsat.wasScanning(module)) { // 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.15) //< re-enable at 15% 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(Severity.relax, "SCANsat> sensor on <b>" + vessel.vesselName + "</b> resumed operations", "we got enough ElectricCharge"); } } // if it is scanning if (SCANsat.isScanning(module)) { // 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, and remember it SCANsat.stopScanner(vessel, module, part_prefab); // give the user some feedback if (DB.VesselData(vessel.id).cfg_ec == 1) Message.Post(Severity.warning, "SCANsat sensor was disabled on <b>" + vessel.vesselName + "</b>", "for lack of ElectricCharge"); } } }*/ // SCANSAT support (old version) // note: this one doesn't support re-activation, is a bit slower and less clean // waiting for DMagic to fix a little bug else if (module.moduleName == "SCANsat" || module.moduleName == "ModuleSCANresourceScanner") { // determine if scanning bool scanning = Convert.ToBoolean(module.moduleValues.GetValue("scanning")); // consume ec if (scanning) { // get ec consumption PartModule scansat = part_prefab.Modules[module.moduleName]; double power = Lib.ReflectionValue<float>(scansat, "power"); // consume ec double ec_required = power * TimeWarp.fixedDeltaTime; 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 using reflection foreach(var a in AssemblyLoader.loadedAssemblies) { if (a.name == "SCANsat") { Type controller_type = a.assembly.GetType("SCANsat.SCANcontroller"); System.Object controller = controller_type.GetProperty("controller", BindingFlags.Public | BindingFlags.Static).GetValue(null, null); controller_type.InvokeMember("removeVessel", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, controller, new System.Object[]{vessel}); } } // disable scanning module.moduleValues.SetValue("scanning", false.ToString()); // give the user some feedback if (DB.VesselData(vessel.id).cfg_ec == 1) Message.Post(Severity.warning, "SCANsat sensor was disabled on <b>" + vessel.vesselName + "</b>", "for lack of ElectricCharge"); } } } // NearFutureSolar support // note: we assume deployed, this is a current limitation else if (module.moduleName == "ModuleCurvedSolarPanel") { // [unused] determine if extended //string state = module.moduleValues.GetValue("SavedState"); //bool extended = state == ModuleDeployableSolarPanel.panelStates.EXTENDED.ToString(); // if in sunlight if (info.sunlight) { // produce electric charge double output = CurvedPanelOutput(vessel, part, part_prefab, info.sun_dir, info.sun_dist, atmo_factor) * Malfunction.Penalty(part); Lib.RequestResource(vessel, "ElectricCharge", -output * 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 == "Malfunction") { Malfunction.BackgroundUpdate(vessel, part.flightID); } } } } }
protected string getAbundance(string resourceName) { AbundanceRequest request = new AbundanceRequest(); double lattitude = ResourceUtilities.Deg2Rad(this.part.vessel.latitude); double longitude = ResourceUtilities.Deg2Rad(this.part.vessel.longitude); request.BiomeName = Utils.GetCurrentBiome(this.part.vessel).name; request.BodyId = this.part.vessel.mainBody.flightGlobalsIndex; request.Longitude = longitude; request.Latitude = lattitude; request.CheckForLock = true; request.ResourceName = resourceName; float abundance = ResourceMap.Instance.GetAbundance(request) * 100.0f; if (abundance > 0.001) return string.Format("{0:f2}%", abundance); else return "Unknown"; }
public static void updateNetworkResources(double dt) { // print("updateNwres " + dt); //if (dt < 0) return; dt *= 4; if(currentPipe == 0) { // print("update vessels!"); foreach(Vessel v in myNetwork) { if (v.loaded) continue; //ignore loaded vessel //only generators will be simulated! Must add generators from all known mods! //everyone is different too! //supported generators will be processed in real time, unsupported will not foreach(var part in v.protoVessel.protoPartSnapshots) { ConfigNode node = findPartConfig(part.partName); if (node == null) continue; // print(node); var modules = node.GetNodes("MODULE"); int moduleID = 0; //solar power efficiency boost: double solarOut = 0; double panelAltitude = v.mainBody.RevealAltitude(); double sunMultiplier = sunScale(v, out panelAltitude); bool hasSolar = false; double altitudeMultiplier = 0; double chargeRate = 0; string solarResourceName = "ElectricCharge"; foreach(var module in part.modules) { // print("module id " + moduleID); if (moduleID > modules.Length) //found unexpected module!... there won't be any new generators here continue; switch(module.moduleName) { case "ModuleGenerator": { try { // print("found ModuleGenerator" + moduleID.ToString()); ConfigNode nonPersistantValues = modules[moduleID]; var outputResource = nonPersistantValues.GetNode("OUTPUT_RESOURCE"); var inputResource = nonPersistantValues.GetNode("INPUT_RESOURCE"); string resource = outputResource.GetValue("name"); double rate = double.Parse(outputResource.GetValue("rate")); string iResource = ""; double iRate = 0; if(inputResource != null) { iResource = inputResource.GetValue("name"); iRate = double.Parse(inputResource.GetValue("rate")); double input = networkResourceRequest(v, iResource, iRate * dt); double iScale = input / (iRate * dt); double output = networkResourceRequest(v, resource, -rate * iScale * dt); double oscale = output / (-rate * iScale * dt); double irefund = input * (1 - oscale); networkResourceRequest(v, iResource, irefund); } } catch { print("Failed to update ModuleGenerator module, check for updates"); } } break; // case "KolonyConverter": case "REGO_ModuleResourceConverter": case "REGO_ModuleResourceHarvester": { try { ConfigNode nonPersistantValues = modules[moduleID]; string recipeInputs = nonPersistantValues.GetValue("RecipeInputs"); string recipeOutputs = nonPersistantValues.GetValue("RecipeOutputs"); double groundEfficiency = 1; if(module.moduleName == "REGO_ModuleResourceHarvester") { string resourcename = nonPersistantValues.GetValue("ResourceName"); recipeOutputs = resourcename + ",1"; var abRequest = new AbundanceRequest { Altitude = v.altitude, BodyId = FlightGlobals.currentMainBody.flightGlobalsIndex, CheckForLock = false, Latitude = v.latitude, Longitude = v.longitude, ResourceType = (HarvestTypes)0, ResourceName = resourcename }; var abundance = ResourceMap.Instance.GetAbundance(abRequest); groundEfficiency = abundance; } string RequiredResources = nonPersistantValues.GetValue("RequiredResources"); if (recipeInputs == null) recipeInputs = ""; if (recipeOutputs == null) recipeOutputs = ""; if (RequiredResources == null) RequiredResources = ""; // print(recipeInputs); // print(recipeOutputs); // print(RequiredResources); string isEnabled = module.moduleValues.GetValue("IsActivated"); double efficiency = double.Parse(module.moduleValues.GetValue("EfficiencyBonus")); bool harvesterOK = true; if (isEnabled.ToLower() == "true" && harvesterOK) { var localdelta = processRegolithGenerator(v, dt * efficiency * groundEfficiency, recipeInputs, recipeOutputs, RequiredResources); foreach (string k in localdelta.Keys) networkResourceRequest(v, k, -localdelta[k]); } module.moduleValues.SetValue("lastUpdateTime", Planetarium.GetUniversalTime().ToString()); /* * Regolith generators use these isEnabled = True EfficiencyBonus = 1 IsActivated = False DirtyFlag = False lastUpdateTime = 49904539.6623243 */ } catch (Exception exc) { print("Failed to update regolith module, check for updates:" + exc.ToString()); } } break; case "NetworkBroker2": { //network broker tries to take network input from the network. // print("found NetworkBroker2"); try { ConfigNode nonPersistantValues = modules[moduleID]; string NetworkInput = nonPersistantValues.GetValue("NetworkInput"); string isEnabled = module.moduleValues.GetValue("IsActivated"); bool IsActivated = bool.Parse(isEnabled); string[] NWIn = NetworkInput.Split(','); double inputRequest = double.Parse(NWIn[1]) * dt; string NetworkResource = NWIn[0]; double NetworkDraw = 0; if (/*connected*/true && IsActivated && inputRequest > 0) { double nwdraw = xPipeLine2.fullNetworkRequest(v, NetworkResource, inputRequest); NetworkDraw = nwdraw / dt; double stored = xPipeLine2.networkResourceRequest(v,NWIn[0], -nwdraw); double excess = nwdraw + stored; // print("NetworkBroker2 " + nwdraw.ToString() + " store " + stored.ToString() + " excess " + excess.ToString()); xPipeLine2.fullNetworkRequest(v, NWIn[0], -excess); } } catch { print("Failed to update NetworkBroker2 module, check for updates"); } } break; case "xPipeLine2": //update current time on pipes so regolith doesn't simulate them! { try { ConfigNode nonPersistantValues = modules[moduleID]; module.moduleValues.SetValue("lastUpdateTime", Planetarium.GetUniversalTime().ToString()); } catch { print("Failed to update xPipeLine2 module, check for updates"); } } break; case "ModuleDeployableSolarPanel": { try { ConfigNode nonPersistantValues = modules[moduleID]; bool sunTracking = true; if (!bool.TryParse(getNonPersistantValue(nonPersistantValues, "sunTracking"), out sunTracking)) sunTracking = true; //squad doesn't mark suntracking on sun tracking panels! solarResourceName = getNonPersistantValue(nonPersistantValues, "resourceName"); chargeRate += double.Parse(getNonPersistantValue(nonPersistantValues, "chargeRate")); ConfigNode cpowerCurve = nonPersistantValues.GetNode("powerCurve"); string[] keys = cpowerCurve.GetValues("key"); powerCurveFunction func = new powerCurveFunction(); func.setFunction(keys); double multiplier = func.getMultiplier(panelAltitude); if(!hasSolar) { altitudeMultiplier = multiplier; } multiplier *= sunMultiplier; //simple half duty cycle for unfocused solar panels solarOut++; /*MODULE { name = ModuleDeployableSolarPanel sunTracking = false raycastTransformName = suncatcher pivotName = suncatcher isBreakable = false resourceName = ElectricCharge chargeRate = 0.75 powerCurve { key = 206000000000 0 0 0 key = 13599840256 1 0 0 key = 68773560320 0.5 0 0 key = 0 10 0 0 } }*/ } catch (Exception exc) { print("Failed to update solar module, check for updates" + exc.ToString()); } } break; default: break; } moduleID++; } if(hasSolar) { networkResourceRequest(v, solarResourceName, -chargeRate * dt * altitudeMultiplier * sunMultiplier); } } } } currentPipe++; if (currentPipe > pipeCount) currentPipe = 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 static Dictionary <string, int> GetResourceAbundance( int bodyIndex, double altitude, double latitude, double longitude, HarvestTypes[] harvestTypes, Configuration config, bool forHomeworld = false) { if (config == null) { config = new Configuration(); } if (config.AllowedHarvestableResources == null || config.AllowedHarvestableResources.Count < 1) { config.SetHarvestableResources(""); } var allowedResources = forHomeworld ? config.AllowedHarvestableResourcesOnHomeworld : config.AllowedHarvestableResources; var abundanceRequest = new AbundanceRequest { Altitude = altitude, BodyId = bodyIndex, CheckForLock = false, Latitude = latitude, Longitude = longitude }; var radiusMultiplier = Math.Sqrt(FlightGlobals.Bodies[bodyIndex].Radius) / RESOURCE_ABUNDANCE_RADIUS_MULT; var bodyMultiplier = 5; if (!FlightGlobals.currentMainBody.isHomeWorld) { bodyMultiplier = Math.Max(5, WOLF_GameParameters.ResourceAbundanceMultiplierValue); } var resourceList = new Dictionary <string, int>(); foreach (var harvestType in harvestTypes.Distinct()) { abundanceRequest.ResourceType = harvestType; var harvestableResources = ResourceMap.Instance.FetchAllResourceNames(harvestType); foreach (var resource in harvestableResources) { abundanceRequest.ResourceName = resource; var baseAbundance = ResourceMap.Instance.GetAbundance(abundanceRequest); int abundance = (int)Math.Round( baseAbundance * RESOURCE_ABUNDANCE_MULTIPLIER * radiusMultiplier * bodyMultiplier, MidpointRounding.AwayFromZero ); if (abundance > RESOURCE_ABUNDANCE_FLOOR) { abundance = Math.Min(abundance, RESOURCE_ABUNDANCE_CEILING); } else { abundance = 0; } // Make abundance a multiple of 5 if (abundance > 0 && abundance % 5 != 0) { abundance = 5 * (int)Math.Round(abundance / 5d, MidpointRounding.AwayFromZero); } if (allowedResources.Contains(resource)) { var wolfResourceName = resource + WOLF_DepotModule.HARVESTABLE_RESOURCE_SUFFIX; if (resourceList.ContainsKey(wolfResourceName)) { resourceList[wolfResourceName] += abundance; } else { resourceList.Add(wolfResourceName, abundance); } } } } return(resourceList); }
public static float GetAbundance(AbundanceRequest request) { try { var northing = Utilities.Deg2Rad(Utilities.fixLat(request.Latitude)); var easting = Utilities.Deg2Rad(Utilities.fixLong(request.Longitude)); var body = FlightGlobals.Bodies.FirstOrDefault(b => b.flightGlobalsIndex == request.BodyId); var biome = Utilities.GetBiome(northing, easting, body); var seed = GenerateAbundanceSeed(request, biome, body); var biomeName = GetDefaultSituation(request.ResourceType); if (biome != null) { biomeName = biome.name; } //We need to determine our data set for randomization. //Is there biome data? DistributionData distro = null; var biomeConfigs = BiomeResources.Where( r => r.PlanetName == body.bodyName && r.BiomeName == biomeName && r.ResourceName == request.ResourceName && r.ResourceType == (int)request.ResourceType).ToList(); var planetConfigs = PlanetaryResources.Where( r => r.PlanetName == body.bodyName && r.ResourceName == request.ResourceName && r.ResourceType == (int)request.ResourceType).ToList(); var globalConfigs = GlobalResources.Where( r => r.ResourceName == request.ResourceName && r.ResourceType == (int)request.ResourceType).ToList(); //Extrapolate based on matching overrides if (biomeConfigs.Any()) { distro = GetBestResourceData(biomeConfigs); seed *= 2; } else if (planetConfigs.Any()) { distro = GetBestResourceData(planetConfigs); seed *= 3; } else if (globalConfigs.Any()) { distro = GetBestResourceData(globalConfigs); seed *= 4; } else { return 0f; } var rand = new Random(seed); //Our Simplex noise: var noiseSeed = new int[8]; for (var ns = 0; ns < 8; ns++) { noiseSeed[ns] = rand.Next(); } var spx = new NoiseGenerator(noiseSeed); var noiseX = (float) northing * distro.Variance / 10f; var noiseY = (float)easting * distro.Variance / 10f; var noiseZ = (rand.Next(100)) / 100f; var noise = spx.noise(noiseX, noiseY, noiseZ); var presenceRoll = rand.Next(100); var isPresent = (presenceRoll <= distro.PresenceChance); if (!isPresent) return 0f; //Abundance begins with a starting range. var min = (int)(distro.MinAbundance * 1000); var max = (int)(distro.MaxAbundance * 1000); //In case someone is silly if (min > max) max = min + 1; var abundance = (rand.Next(min, max))/1000f; var baseAbuncance = abundance; var minAbundance = Math.Max(0.01f,min)/1000f; //Applies to all but interplanetary if (request.ResourceType != HarvestTypes.Interplanetary) { //Lets add some noise... float swing = abundance*(distro.Variance/100f); abundance = abundance - swing + (swing*noise*2); //You should only be able to hit zero if someohe sets their //variance >= 100 if (abundance < 0) abundance = 0; } //Altitude band - only applies to atmospheric and interplanetary if ( (request.ResourceType == HarvestTypes.Atmospheric || request.ResourceType == HarvestTypes.Interplanetary) && distro.HasVariableAltitude()) { var rad = body.Radius; var ideal = ((rad*distro.MinAltitude) + (rad*distro.MaxAltitude))/2; //print("REGO: IDEAL = " + ideal); var range = rand.Next((int)(rad * distro.MinRange), (int)(rad * distro.MaxRange)); var diff = Math.Abs(ideal - request.Altitude); var rangePerc = diff / range; var modifier = 1d - rangePerc; abundance *= (float)modifier; } if (abundance <= Utilities.FLOAT_TOLERANCE) return 0f; //Now for our return scenarios. var trueAbundance = abundance / 100; baseAbuncance = baseAbuncance / 100; minAbundance = minAbundance / 100; //Biome unlocked or no lock check if (!request.CheckForLock || RegolithScenario.Instance.gameSettings.IsBiomeUnlocked(request.BodyId, biomeName)) { return trueAbundance; } //Planet unlocked else if (RegolithScenario.Instance.gameSettings.IsPlanetUnlocked(request.BodyId)) { return baseAbuncance; } //Default is just our basic data else { return minAbundance; } } catch (Exception e) { print("[REGO] - Error in - RegolithResourceMap_GetAbundance - " + e.Message); return 0f; } }
internal static float ResourceOverlay(double lat, double lon, string name, CelestialBody body) { float amount = 0f; var aRequest = new AbundanceRequest { Latitude = lat, Longitude = lon, BodyId = body.flightGlobalsIndex, ResourceName = name, ResourceType = HarvestTypes.Planetary, Altitude = 0, CheckForLock = SCANcontroller.controller.resourceBiomeLock, BiomeName = getBiomeName(body, lon, lat), ExcludeVariance = false, }; amount = ResourceMap.Instance.GetAbundance(aRequest); return amount; }