示例#1
0
        /// <summary>process the process and add/remove the resources from the simulator for the entire vessel at once</summary>
        private void Process_process_vessel_wide(Process pr, EnvironmentAnalyzer env, VesselAnalyzer va)
        {
            // evaluate modifiers
            double k = Modifiers.Evaluate(env, va, this, pr.modifiers);

            Process_process_inner_body(k, null, pr, env, va);
        }
示例#2
0
        /// <summary>determine if the resources involved are restricted to a part, and then process a rule</summary>
        public void Process_rule(List <Part> parts, Rule r, EnvironmentAnalyzer env, VesselAnalyzer va)
        {
            // evaluate modifiers
            double k = Modifiers.Evaluate(env, va, this, r.modifiers);

            Process_rule_inner_body(k, null, r, env, va);
        }
示例#3
0
        /// <summary>process a rule and add/remove the resources from the simulator</simulator>
        private void Process_rule_inner_body(double k, Part p, Rule r, EnvironmentAnalyzer env, VesselAnalyzer va)
        {
            // deduce rate per-second
            double rate = va.crew_count * (r.interval > 0.0 ? r.rate / r.interval : r.rate);

            // prepare recipe
            if (r.output.Length == 0)
            {
                Resource(r.input).Consume(rate * k, r.name);
            }
            else if (rate > double.Epsilon)
            {
                // simulate recipe if output_only is false
                if (!r.monitor)
                {
                    // - rules always dump excess overboard (because it is waste)
                    SimulatedRecipe recipe = new SimulatedRecipe(p, r.name);
                    recipe.Input(r.input, rate * k);
                    recipe.Output(r.output, rate * k * r.ratio, true);
                    recipes.Add(recipe);
                }
                // only simulate output
                else
                {
                    Resource(r.output).Produce(rate * k, r.name);
                }
            }
        }
示例#4
0
        void Process_solarPanel(SolarPanelFixer spf, EnvironmentAnalyzer env)
        {
            if (spf.part.editorStarted && spf.isInitialized && spf.isEnabled && spf.editorEnabled)
            {
                double editorOutput = 0.0;
                switch (Planner.Sunlight)
                {
                case Planner.SunlightState.SunlightNominal:
                    editorOutput = spf.nominalRate * (env.solar_flux / Sim.SolarFluxAtHome);
                    if (editorOutput > 0.0)
                    {
                        Resource("ElectricCharge").Produce(editorOutput, "solar panel (nominal)");
                    }
                    break;

                case Planner.SunlightState.SunlightSimulated:
                    // create a sun direction according to the shadows direction in the VAB / SPH
                    Vector3d sunDir         = EditorDriver.editorFacility == EditorFacility.VAB ? new Vector3d(1.0, 1.0, 0.0).normalized : new Vector3d(0.0, 1.0, -1.0).normalized;
                    string   occludingPart  = null;
                    double   effiencyFactor = spf.SolarPanel.GetCosineFactor(sunDir, true) * spf.SolarPanel.GetOccludedFactor(sunDir, out occludingPart, true);
                    double   distanceFactor = env.solar_flux / Sim.SolarFluxAtHome;
                    editorOutput = spf.nominalRate * effiencyFactor * distanceFactor;
                    if (editorOutput > 0.0)
                    {
                        Resource("ElectricCharge").Produce(editorOutput, "solar panel (estimated)");
                    }
                    break;
                }
            }
        }
示例#5
0
		private void Process_apiModule(PartModule m, EnvironmentAnalyzer env, VesselAnalyzer va)
		{
			List<KeyValuePair<string, double>> resourcesList = new List<KeyValuePair<string, double>>();

			Dictionary<string, double> environment = new Dictionary<string, double>();
			environment["altitude"] = env.altitude;
			environment["orbital_period"] = env.orbital_period;
			environment["shadow_period"] = env.shadow_period;
			environment["shadow_time"] = env.shadow_time;
			environment["albedo_flux"] = env.albedo_flux;
			environment["solar_flux"] = env.solar_flux;
			environment["sun_dist"] = env.sun_dist;
			environment["temperature"] = env.temperature;
			environment["total_flux"] = env.total_flux;
			environment["temperature"] = env.temperature;
			environment["sunlight"] = Planner.Sunlight == Planner.SunlightState.Shadow ? 0 : 1;

			Lib.Log("resource count before call " + resourcesList.Count);

			string title = apiDelegates[m.moduleName].Invoke(m, resourcesList, env.body, environment);

			Lib.Log("resource count after call " + resourcesList.Count);

			foreach (var p in resourcesList)
			{
				var res = Resource(p.Key);
				if (p.Value >= 0)
					res.Produce(p.Value, title);
				else
					res.Consume(-p.Value, title);
			}
		}
示例#6
0
        void Analyze_qol(List <Part> parts, ResourceSimulator sim, EnvironmentAnalyzer env)
        {
            // calculate living space factor
            living_space = Lib.Clamp((volume / Math.Max(crew_count, 1u)) / PreferencesComfort.Instance.livingSpace, 0.1, 1.0);

            // calculate comfort factor
            comforts = new Comforts(parts, env.landed, crew_count > 1, has_comms);
        }
示例#7
0
        void Process_curved_panel(Part p, PartModule m, EnvironmentAnalyzer env)
        {
            // note: assume half the components are in sunlight, and average inclination is half

            // get total rate
            double tot_rate = Lib.ReflectionValue <float>(m, "TotalEnergyRate");

            // get number of components
            int components = p.FindModelTransforms(Lib.ReflectionValue <string>(m, "PanelTransformName")).Length;

            // approximate output
            // 0.7071: average clamped cosine
            Resource("ElectricCharge").Produce(tot_rate * 0.7071 * env.solar_flux / Sim.SolarFluxAtHome(), "curved panel");
        }
示例#8
0
		/// <summary>process the process and add/remove the resources from the simulator</summary>
		private void Process_process_inner_body(double k, Part p, Process pr, EnvironmentAnalyzer env, VesselAnalyzer va)
		{
			// prepare recipe
			SimulatedRecipe recipe = new SimulatedRecipe(p, pr.name);
			foreach (KeyValuePair<string, double> input in pr.inputs)
			{
				recipe.Input(input.Key, input.Value * k);
			}
			foreach (KeyValuePair<string, double> output in pr.outputs)
			{
				recipe.Output(output.Key, output.Value * k, pr.dump.Check(output.Key));
			}
			recipes.Add(recipe);
		}
示例#9
0
        public void Analyze(List <Part> parts, ResourceSimulator sim, EnvironmentAnalyzer env)
        {
            // note: vessel analysis require resource analysis, but at the same time resource analysis
            // require vessel analysis, so we are using resource analysis from previous frame (that's okay)
            // in the past, it was the other way around - however that triggered a corner case when va.comforts
            // was null (because the vessel analysis was still never done) and some specific rule/process
            // in resource analysis triggered an exception, leading to the vessel analysis never happening
            // inverting their order avoided this corner-case

            Analyze_crew(parts);
            Analyze_habitat(parts, sim, env);
            Analyze_radiation(parts, sim);
            Analyze_reliability(parts);
            Analyze_qol(parts, sim, env);
            Analyze_comms(parts);
        }
示例#10
0
        void Analyze_habitat(List <Part> parts, ResourceSimulator sim, EnvironmentAnalyzer env)
        {
            // calculate total volume
            volume = sim.Resource("Atmosphere").capacity / 1e3;

            // calculate total surface
            surface = sim.Resource("Shielding").capacity;

            // determine if the vessel has pressure control capabilities
            pressurized = sim.Resource("Atmosphere").produced > 0.0 || env.breathable;

            // determine number of EVA's using available Nitrogen
            evas = (uint)(Math.Max(0, sim.Resource("Nitrogen").amount - 330) / PreferencesLifeSupport.Instance.evaAtmoLoss);

            // determine if the vessel has scrubbing capabilities
            scrubbed = sim.Resource("WasteAtmosphere").consumed > 0.0 || env.breathable;

            // determine if the vessel has humidity control capabilities
            humid = sim.Resource("MoistAtmosphere").consumed > 0.0 || env.breathable;

            // scan the parts
            double max_pressure = 1.0;

            foreach (Part p in parts)
            {
                // for each module
                foreach (PartModule m in p.Modules)
                {
                    // skip disabled modules
                    if (!m.isEnabled)
                    {
                        continue;
                    }

                    if (m.moduleName == "Habitat")
                    {
                        Habitat h = m as Habitat;
                        max_pressure = Math.Min(max_pressure, h.max_pressure);
                    }
                }
            }

            pressurized &= max_pressure >= Settings.PressureThreshold;
        }
示例#11
0
		/// <summary>
		/// run simulator to get statistics a fraction of a second after the vessel would spawn
		/// in the configured environment (celestial body, orbit height and presence of sunlight)
		/// </summary>
		public void Analyze(List<Part> parts, EnvironmentAnalyzer env, VesselAnalyzer va)
		{
			// reach steady state, so all initial resources like WasteAtmosphere are produced
			// it is assumed that one cycle is needed to produce things that don't need inputs
			// another cycle is needed for processes to pick that up
			// another cycle may be needed for results of those processes to be picked up
			// two additional cycles are for having some margin
			for (int i = 0; i < 5; i++)
			{
				RunSimulator(parts, env, va);
			}

			// Do the actual run people will see from the simulator UI
			foreach (SimulatedResource r in resources.Values)
			{
				r.ResetSimulatorDisplayValues();
			}
			RunSimulator(parts, env, va);
		}
示例#12
0
        void Analyze_habitat(ResourceSimulator sim, EnvironmentAnalyzer env)
        {
            // calculate total volume
            volume = sim.Resource("Atmosphere").capacity / 1e3;

            // calculate total surface
            surface = sim.Resource("Shielding").capacity;

            // determine if the vessel has pressure control capabilities
            pressurized = sim.Resource("Atmosphere").produced > 0.0 || env.breathable;

            // determine number of EVA's using available Nitrogen
            evas = (uint)(Math.Max(0, sim.Resource("Nitrogen").amount - 330) / PreferencesLifeSupport.Instance.evaAtmoLoss);

            // determine if the vessel has scrubbing capabilities
            scrubbed = sim.Resource("WasteAtmosphere").consumed > 0.0 || env.breathable;

            // determine if the vessel has humidity control capabilities
            humid = sim.Resource("MoistAtmosphere").consumed > 0.0 || env.breathable;
        }
示例#13
0
        void Analyze_habitat(List <Part> parts, ResourceSimulator sim, EnvironmentAnalyzer env)
        {
            // calculate total volume
            volume = sim.Resource("Atmosphere").capacity / 1e3;

            // calculate total surface
            surface = sim.Resource("Shielding").capacity;

            // determine if the vessel has pressure control capabilities
            pressurized = sim.Resource("Atmosphere").produced > 0.0 || env.breathable;

            // determine if the vessel has scrubbing capabilities
            scrubbed = sim.Resource("WasteAtmosphere").consumed > 0.0 || env.breathable;

            // scan the parts
            double max_pressure = 1.0;

            foreach (Part p in parts)
            {
                // for each module
                foreach (PartModule m in p.Modules)
                {
                    // skip disabled modules
                    if (!m.isEnabled)
                    {
                        continue;
                    }

                    if (m.moduleName == "Habitat")
                    {
                        Habitat h = m as Habitat;
                        max_pressure = Math.Min(max_pressure, h.max_pressure);
                    }
                }
            }

            pressurized &= max_pressure >= Settings.PressureThreshold;
        }
示例#14
0
        void Process_panel(ModuleDeployableSolarPanel panel, EnvironmentAnalyzer env)
        {
            double generated = panel.resHandler.outputResources[0].rate * env.solar_flux / Sim.SolarFluxAtHome();

            Resource("ElectricCharge").Produce(generated, "solar panel");
        }
示例#15
0
		/// <summary>run a single timestamp of the simulator</summary>
		private void RunSimulator(List<Part> parts, EnvironmentAnalyzer env, VesselAnalyzer va)
		{
			// clear previous resource state
			resources.Clear();

			// get amount and capacity from parts
			foreach (Part p in parts)
			{
				for (int i = 0; i < p.Resources.Count; ++i)
				{
					Process_part(p, p.Resources[i].resourceName);
#if DEBUG_RESOURCES
					p.Resources[i].isVisible = true;
					p.Resources[i].isTweakable = true;
#endif
				}
			}

			// process all rules
			foreach (Rule r in Profile.rules)
			{
				if (r.input.Length > 0 && r.rate > 0.0)
				{
					Process_rule(parts, r, env, va);
				}
			}

			// process all processes
			foreach (Process p in Profile.processes)
			{
				Process_process(parts, p, env, va);
			}

			// process all modules
			foreach (Part p in parts)
			{
				// get planner controller in the part
				PlannerController ctrl = p.FindModuleImplementing<PlannerController>();

				// ignore all modules in the part if specified in controller
				if (ctrl != null && !ctrl.considered)
					continue;

				// for each module
				foreach (PartModule m in p.Modules)
				{
					// skip disabled modules
					// rationale: the Selector disable non-selected modules in this way
					if (!m.isEnabled)
						continue;

					if (IsApiModule(m))
					{
						Process_apiModule(m, env, va);
					}
					else
					{
						switch (m.moduleName)
						{
							case "Greenhouse":
								Process_greenhouse(m as Greenhouse, env, va);
								break;
							case "GravityRing":
								Process_ring(m as GravityRing);
								break;
							case "Emitter":
								Process_emitter(m as Emitter);
								break;
							case "Laboratory":
								Process_laboratory(m as Laboratory);
								break;
							case "Experiment":
								Process_experiment(m as Experiment);
								break;
							case "ModuleCommand":
								Process_command(m as ModuleCommand);
								break;
							case "ModuleGenerator":
								Process_generator(m as ModuleGenerator, p);
								break;
							case "ModuleResourceConverter":
								Process_converter(m as ModuleResourceConverter, va);
								break;
							case "ModuleKPBSConverter":
								Process_converter(m as ModuleResourceConverter, va);
								break;
							case "ModuleResourceHarvester":
								Process_harvester(m as ModuleResourceHarvester, va);
								break;
							case "ModuleScienceConverter":
								Process_stocklab(m as ModuleScienceConverter);
								break;
							case "ModuleActiveRadiator":
								Process_radiator(m as ModuleActiveRadiator);
								break;
							case "ModuleWheelMotor":
								Process_wheel_motor(m as ModuleWheelMotor);
								break;
							case "ModuleWheelMotorSteering":
								Process_wheel_steering(m as ModuleWheelMotorSteering);
								break;
							case "ModuleLight":
							case "ModuleColoredLensLight":
							case "ModuleMultiPointSurfaceLight":
								Process_light(m as ModuleLight);
								Process_light(m as ModuleLight);
								Process_light(m as ModuleLight);
								break;
							case "KerbalismScansat":
								Process_scanner(m as KerbalismScansat);
								break;
							case "FissionGenerator":
								Process_fission_generator(p, m);
								break;
							case "ModuleRadioisotopeGenerator":
								Process_radioisotope_generator(p, m);
								break;
							case "ModuleCryoTank":
								Process_cryotank(p, m);
								break;
							case "ModuleRTAntennaPassive":
							case "ModuleRTAntenna":
								Process_rtantenna(m);
								break;
							case "ModuleDataTransmitter":
								Process_datatransmitter(m as ModuleDataTransmitter);
								break;
							case "ModuleEngines":
								Process_engines(m as ModuleEngines);
								break;
							case "ModuleEnginesFX":
								Process_enginesfx(m as ModuleEnginesFX);
								break;
							case "ModuleRCS":
								Process_rcs(m as ModuleRCS);
								break;
							case "ModuleRCSFX":
								Process_rcsfx(m as ModuleRCSFX);
								break;
							case "SolarPanelFixer":
								Process_solarPanel(m as SolarPanelFixer, env);
								break;
						}
					}
				}
			}

			// execute all possible recipes
			bool executing = true;
			while (executing)
			{
				executing = false;
				for (int i = 0; i < recipes.Count; ++i)
				{
					SimulatedRecipe recipe = recipes[i];
					if (recipe.left > double.Epsilon)
					{
						executing |= recipe.Execute(this);
					}
				}
			}
			recipes.Clear();

			// clamp all resources
			foreach (KeyValuePair<string, SimulatedResource> pair in resources)
				pair.Value.Clamp();
		}
示例#16
0
		void Process_greenhouse(Greenhouse g, EnvironmentAnalyzer env, VesselAnalyzer va)
		{
			// skip disabled greenhouses
			if (!g.active)
				return;

			// shortcut to resources
			SimulatedResource ec = Resource("ElectricCharge");
			SimulatedResource res = Resource(g.crop_resource);

			// calculate natural and artificial lighting
			double natural = env.solar_flux;
			double artificial = Math.Max(g.light_tolerance - natural, 0.0);

			// if lamps are on and artificial lighting is required
			if (artificial > 0.0)
			{
				// consume ec for the lamps
				ec.Consume(g.ec_rate * (artificial / g.light_tolerance), "greenhouse");
			}

			// execute recipe
			SimulatedRecipe recipe = new SimulatedRecipe(g.part, "greenhouse");
			foreach (ModuleResource input in g.resHandler.inputResources)
			{
				// WasteAtmosphere is primary combined input
				if (g.WACO2 && input.name == "WasteAtmosphere")
					recipe.Input(input.name, env.breathable ? 0.0 : input.rate, "CarbonDioxide");
				// CarbonDioxide is secondary combined input
				else if (g.WACO2 && input.name == "CarbonDioxide")
					recipe.Input(input.name, env.breathable ? 0.0 : input.rate, "");
				// if atmosphere is breathable disable WasteAtmosphere / CO2
				else if (!g.WACO2 && (input.name == "CarbonDioxide" || input.name == "WasteAtmosphere"))
					recipe.Input(input.name, env.breathable ? 0.0 : input.rate, "");
				else
					recipe.Input(input.name, input.rate);
			}
			foreach (ModuleResource output in g.resHandler.outputResources)
			{
				// if atmosphere is breathable disable Oxygen
				if (output.name == "Oxygen")
					recipe.Output(output.name, env.breathable ? 0.0 : output.rate, true);
				else
					recipe.Output(output.name, output.rate, true);
			}
			recipes.Add(recipe);

			// determine environment conditions
			bool lighting = natural + artificial >= g.light_tolerance;
			bool pressure = va.pressurized || g.pressure_tolerance <= double.Epsilon;
			bool radiation = (env.landed ? env.surface_rad : env.magnetopause_rad) * (1.0 - va.shielding) < g.radiation_tolerance;

			// if all conditions apply
			// note: we are assuming the inputs are satisfied, we can't really do otherwise here
			if (lighting && pressure && radiation)
			{
				// produce food
				res.Produce(g.crop_size * g.crop_rate, "greenhouse");

				// add harvest info
				res.harvests.Add(Lib.BuildString(g.crop_size.ToString("F0"), " in ", Lib.HumanReadableDuration(1.0 / g.crop_rate)));
			}
		}
示例#17
0
		/// <summary>
		/// determine if the resources involved are restricted to a part, and then process
		/// the process and add/remove the resources from the simulator
		/// </summary>
		/// <remarks>while rules are usually input or output only, processes transform input to output</remarks>
		public void Process_process(List<Part> parts, Process pr, EnvironmentAnalyzer env, VesselAnalyzer va)
		{
			Process_process_vessel_wide(pr, env, va);
		}