示例#1
0
		static void Render_supplies(Panel p, Vessel v, Vessel_info vi, Vessel_resources resources)
		{
			// for each supply
			int supplies = 0;
			foreach (Supply supply in Profile.supplies)
			{
				// get resource info
				Resource_info res = resources.Info(v, supply.resource);

				// only show estimate if the resource is present
				if (res.amount <= 1e-10) continue;

				// render panel title, if not done already
				if (supplies == 0) p.AddSection("SUPPLIES");

				// rate tooltip
				string rate_tooltip = Math.Abs(res.rate) >= 1e-10 ? Lib.BuildString
				(
				  res.rate > 0.0 ? "<color=#00ff00><b>" : "<color=#ffaa00><b>",
				  Lib.HumanReadableRate(Math.Abs(res.rate)),
				  "</b></color>"
				) : string.Empty;

				// determine label
				string label = supply.resource == "ElectricCharge"
				  ? "battery"
				  : Lib.SpacesOnCaps(supply.resource).ToLower();

				// finally, render resource supply
				p.AddContent(label, Lib.HumanReadableDuration(res.Depletion(vi.crew_count)), rate_tooltip);
				++supplies;
			}
		}
示例#2
0
        private void ExecuteRecipe(double k, Vessel_resources resources, double elapsed_s, Resource_recipe recipe)
        {
            // only execute processes if necessary
            if (Math.Abs(k) < double.Epsilon)
            {
                return;
            }

            foreach (var p in inputs)
            {
                recipe.Input(p.Key, p.Value * k * elapsed_s);
            }
            foreach (var p in outputs)
            {
                recipe.Output(p.Key, p.Value * k * elapsed_s, dump.Check(p.Key));
            }
            foreach (var p in cures)
            {
                // TODO this assumes that the cure modifies always put the resource first
                // works: modifier = _SickbayRDU,zerog works
                // fails: modifier = zerog,_SickbayRDU
                recipe.Cure(p.Key, p.Value * k * elapsed_s, modifiers[0]);
            }
            resources.Transform(recipe);
        }
示例#3
0
		bool Render_vessel(Panel p, Vessel v)
		{
			// get vessel info
			Vessel_info vi = Cache.VesselInfo(v);

			// skip invalid vessels
			if (!vi.is_valid) return false;

			// get data from db
			VesselData vd = DB.Vessel(v);

			// determine if filter must be shown
			show_filter |= vd.group.Length > 0 && vd.group != "NONE";

			// skip filtered vessels
			if (Filtered() && !Filter_match(vd.group)) return false;

			// get resource handler
			Vessel_resources resources = ResourceCache.Get(v);

			// get vessel crew
			List<ProtoCrewMember> crew = Lib.CrewList(v);

			// get vessel name
			string vessel_name = v.isEVA ? crew[0].name : v.vesselName;

			// get body name
			string body_name = v.mainBody.name.ToUpper();

			// render entry
			p.AddHeader
			(
			  Lib.BuildString("<b>",
			  Lib.Ellipsis(vessel_name, Styles.ScaleStringLength(((page == MonitorPage.data || page == MonitorPage.log || selected_id == Guid.Empty) && !Lib.IsFlight()) ? 50 : 30)),
			  "</b> <size=", Styles.ScaleInteger(9).ToString(),
			  "><color=#cccccc>", Lib.Ellipsis(body_name, Styles.ScaleStringLength(8)), "</color></size>"),
			  string.Empty,
			  () => { selected_id = selected_id != v.id ? v.id : Guid.Empty; }
			);

			// problem indicator
			Indicator_problems(p, v, vi, crew);

			// battery indicator
			Indicator_ec(p, v, vi);

			// supply indicator
			if (Features.Supplies) Indicator_supplies(p, v, vi);

			// reliability indicator
			if (Features.Reliability) Indicator_reliability(p, v, vi);

			// signal indicator
			if (RemoteTech.Enabled || HighLogic.fetch.currentGame.Parameters.Difficulty.EnableCommNet) Indicator_signal(p, v, vi);

			// done
			return true;
		}
示例#4
0
 public void Execute(Vessel v, Vessel_info vi, Vessel_resources resources, double elapsed_s)
 {
     if (restricted)
     {
         ExecutePerPart(v, vi, resources, elapsed_s);
     }
     else
     {
         ExecuteVesselWide(v, vi, resources, elapsed_s);
     }
 }
示例#5
0
        private void ExecuteVesselWide(Vessel v, Vessel_info vi, Vessel_resources resources, double elapsed_s)
        {
            // evaluate modifiers
            // if a given PartModule has a larger than 1 capacity for a process, then the multiplication happens here
            // remember that when a process is enabled the units of process are stored in the PartModule as a pseudo-resource
            double k = Modifiers.Evaluate(v, vi, resources, modifiers);

            Resource_recipe recipe = new Resource_recipe((Part)null);

            ExecuteRecipe(k, resources, elapsed_s, recipe);
        }
示例#6
0
        public static void TelemetryPanel(this Panel p, Vessel v)
        {
            // avoid corner-case when this is called in a lambda after scene changes
            v = FlightGlobals.FindVessel(v.id);

            // if vessel doesn't exist anymore, leave the panel empty
            if (v == null)
            {
                return;
            }

            // get info from the cache
            Vessel_info vi = Cache.VesselInfo(v);

            // if not a valid vessel, leave the panel empty
            if (!vi.is_valid)
            {
                return;
            }

            // set metadata
            p.Title(Lib.BuildString(Lib.Ellipsis(v.vesselName, Styles.ScaleStringLength(20)), " <color=#cccccc>TELEMETRY</color>"));
            p.Width(Styles.ScaleWidthFloat(355.0f));
            p.paneltype = Panel.PanelType.telemetry;

            // time-out simulation
            if (p.Timeout(vi))
            {
                return;
            }

            // get vessel data
            VesselData vd = DB.Vessel(v);

            // get resources
            Vessel_resources resources = ResourceCache.Get(v);

            // get crew
            var crew = Lib.CrewList(v);

            // draw the content
            Render_crew(p, crew);
            Render_greenhouse(p, vi);
            Render_supplies(p, v, vi, resources);
            Render_habitat(p, v, vi);
            Render_environment(p, v, vi);

            // collapse eva kerbal sections into one
            if (v.isEVA)
            {
                p.Collapse("EVA SUIT");
            }
        }
示例#7
0
        public void Execute(Vessel v, VesselData vd, Vessel_resources resources)
        {
            // get crew
            List <ProtoCrewMember> crew = Lib.CrewList(v);

            // get resource handler
            Resource_info res = resources.Info(v, resource);

            // get data from db
            SupplyData sd = DB.Vessel(v).Supply(resource);

            // message obey user config
            bool show_msg = resource == "ElectricCharge" ? vd.cfg_ec : vd.cfg_supply;

            // messages are shown only if there is some capacity and the vessel is manned
            // special case: ElectricCharge related messages are shown for unmanned vessels too
            if (res.capacity > double.Epsilon && (crew.Count > 0 || resource == "ElectricCharge"))
            {
                // manned/probe message variant
                uint variant = crew.Count > 0 ? 0 : 1u;

                // manage messages
                if (res.level <= double.Epsilon && sd.message < 2)
                {
                    if (empty_message.Length > 0 && show_msg)
                    {
                        Message.Post(Severity.danger, Lib.ExpandMsg(empty_message, v, null, variant));
                    }
                    sd.message = 2;
                }
                else if (res.level < low_threshold && sd.message < 1)
                {
                    if (low_message.Length > 0 && show_msg)
                    {
                        Message.Post(Severity.warning, Lib.ExpandMsg(low_message, v, null, variant));
                    }
                    sd.message = 1;
                }
                else if (res.level > low_threshold && sd.message > 0)
                {
                    if (refill_message.Length > 0 && show_msg)
                    {
                        Message.Post(Severity.relax, Lib.ExpandMsg(refill_message, v, null, variant));
                    }
                    sd.message = 0;
                }
            }
        }
示例#8
0
        private static void RunProcessTick(Vessel v, double elapsed_s,
                                           double ec_produced, List <KeyValuePair <string, double> > resourcesProduced,
                                           double ec_consumed, List <KeyValuePair <string, double> > resourcesConsumed,
                                           Resource_info ec, Vessel_resources resources)
        {
            // evaluate process rate
            double rate = 1;

            if (ec_consumed < ec.amount)
            {
                rate = ec.amount / ec_consumed;
            }

            foreach (var consumed in resourcesConsumed)
            {
                var ri = resources.Info(v, consumed.Key);
                rate = Math.Min(rate, Lib.Clamp(ri.amount / (consumed.Value * elapsed_s), 0, 1));
            }

            foreach (var produced in resourcesProduced)
            {
                var ri = resources.Info(v, produced.Key);
                var capacityAvailable = ri.capacity - ri.amount;
                var amountProduced    = produced.Value * elapsed_s;
                if (capacityAvailable < amountProduced)
                {
                    rate = Math.Min(rate, Lib.Clamp(capacityAvailable / amountProduced, 0, 1));
                }
            }

            // produce/consume according to rate
            if (rate < double.Epsilon)
            {
                return;
            }

            ec.Consume(ec_consumed * elapsed_s * rate, "module process");
            ec.Produce(ec_produced * elapsed_s * rate, "module process");

            foreach (var consumed in resourcesConsumed)
            {
                resources.Info(v, consumed.Key).Consume(consumed.Value * elapsed_s * rate, "module process");
            }
            foreach (var produced in resourcesProduced)
            {
                resources.Info(v, produced.Key).Produce(produced.Value * elapsed_s * rate, "module process");
            }
        }
示例#9
0
        void ToEVA(GameEvents.FromToAction <Part, Part> data)
        {
            // get total crew in the origin vessel
            double tot_crew = (double)Lib.CrewCount(data.from.vessel) + 1.0;

            // get vessel resources handler
            Vessel_resources resources = ResourceCache.Get(data.from.vessel);

            // setup supply resources capacity in the eva kerbal
            Profile.SetupEva(data.to);

            // for each resource in the kerbal
            for (int i = 0; i < data.to.Resources.Count; ++i)
            {
                // get the resource
                PartResource res = data.to.Resources[i];

                // determine quantity to take
                double quantity = Math.Min(resources.Info(data.from.vessel, res.resourceName).amount / tot_crew, res.maxAmount);

                // remove resource from vessel
                quantity = data.from.RequestResource(res.resourceName, quantity);

                // add resource to eva kerbal
                data.to.RequestResource(res.resourceName, -quantity);
            }

            // show warning if there isn't monoprop in the eva suit
            string prop_name = Lib.EvaPropellantName();

            if (Lib.Amount(data.to, prop_name) <= double.Epsilon && !Lib.Landed(data.from.vessel))
            {
                Message.Post(Severity.danger, Lib.BuildString("There isn't any <b>", prop_name, "</b> in the EVA suit"), "Don't let the ladder go!");
            }

            // turn off headlamp light, to avoid stock bug that show them for a split second when going on eva
            KerbalEVA kerbal = data.to.FindModuleImplementing <KerbalEVA>();

            EVA.HeadLamps(kerbal, false);

            // execute script
            DB.Vessel(data.from.vessel).computer.Execute(data.from.vessel, ScriptType.eva_out);
        }
示例#10
0
        // return resource cache for a vessel
        public static Vessel_resources Get(Vessel v)
        {
            // try to get existing entry if any
            Vessel_resources entry;

            if (entries.TryGetValue(v.id, out entry))
            {
                return(entry);
            }

            // create new entry
            entry = new Vessel_resources();

            // remember new entry
            entries.Add(v.id, entry);

            // return new entry
            return(entry);
        }
示例#11
0
        public static void Execute(Vessel v, Vessel_info vi, VesselData vd, Vessel_resources resources, double elapsed_s)
        {
            // execute all supplies
            foreach (Supply supply in supplies)
            {
                supply.Execute(v, vd, resources);
            }

            // execute all rules
            foreach (Rule rule in rules)
            {
                rule.Execute(v, vi, resources, elapsed_s);
            }

            // execute all processes
            foreach (Process process in processes)
            {
                process.Execute(v, vi, resources, elapsed_s);
            }
        }
示例#12
0
        public static void Execute(Vessel v, Vessel_info vi, VesselData vd, Vessel_resources resources, double elapsed_s)
        {
            // execute all supplies
            foreach (Supply supply in supplies)
            {
                // this will just show warning messages if resources get low
                supply.Execute(v, vd, resources);
            }

            // execute all rules
            foreach (Rule rule in rules)
            {
                rule.Execute(v, vi, resources, elapsed_s);
            }

            // execute all processes
            foreach (Process process in processes)
            {
                process.Execute(v, vi, resources, elapsed_s);
            }
        }
示例#13
0
        public void Execute(Vessel v, Vessel_info vi, Vessel_resources resources, double elapsed_s)
        {
            // evaluate modifiers
            double k = Modifiers.Evaluate(v, vi, resources, modifiers);

            // only execute processes if necessary
            if (k > double.Epsilon)
            {
                // prepare recipe
                Resource_recipe recipe = new Resource_recipe();
                foreach (var p in inputs)
                {
                    recipe.Input(p.Key, p.Value * k * elapsed_s);
                }
                foreach (var p in outputs)
                {
                    recipe.Output(p.Key, p.Value * k * elapsed_s, dump.Check(p.Key));
                }
                resources.Transform(recipe);
            }
        }
示例#14
0
 private void ExecutePerPart(Vessel v, Vessel_info vi, Vessel_resources resources, double elapsed_s)
 {
     if (v.loaded)
     {
         foreach (Part p in v.Parts)
         {
             double          k      = Modifiers.Evaluate(v, vi, resources, modifiers, p, null);
             Resource_recipe recipe = new Resource_recipe(p);
             ExecuteRecipe(k, resources, elapsed_s, recipe);
         }
     }
     else
     {
         foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots)
         {
             double          k      = Modifiers.Evaluate(v, vi, resources, modifiers, null, p);
             Resource_recipe recipe = new Resource_recipe(p);
             ExecuteRecipe(k, resources, elapsed_s, recipe);
         }
     }
 }
示例#15
0
        public static void BackgroundUpdate(Vessel v, ProtoPartModuleSnapshot m, Experiment experiment, Resource_info ec, Vessel_resources resources, double elapsed_s)
        {
            bool   didPrepare          = Lib.Proto.GetBool(m, "didPrepare", false);
            bool   shrouded            = Lib.Proto.GetBool(m, "shrouded", false);
            string last_subject_id     = Lib.Proto.GetString(m, "last_subject_id", "");
            double remainingSampleMass = Lib.Proto.GetDouble(m, "remainingSampleMass", 0);
            bool   broken      = Lib.Proto.GetBool(m, "broken", false);
            bool   forcedRun   = Lib.Proto.GetBool(m, "forcedRun", false);
            bool   recording   = Lib.Proto.GetBool(m, "recording", false);
            uint   privateHdId = Lib.Proto.GetUInt(m, "privateHdId", 0);

            string issue = TestForIssues(v, ec, experiment, privateHdId, broken,
                                         remainingSampleMass, didPrepare, shrouded, last_subject_id);

            if (string.IsNullOrEmpty(issue))
            {
                issue = TestForResources(v, KerbalismProcess.ParseResources(experiment.resources), elapsed_s, resources);
            }

            Lib.Proto.Set(m, "issue", issue);

            if (!string.IsNullOrEmpty(issue))
            {
                return;
            }

            var subject_id = Science.Generate_subject_id(experiment.experiment_id, v);

            Lib.Proto.Set(m, "last_subject_id", subject_id);

            double dataSampled = Lib.Proto.GetDouble(m, "dataSampled");

            if (last_subject_id != subject_id)
            {
                dataSampled = 0;
                Lib.Proto.Set(m, "forcedRun", false);
            }

            double scienceValue = Science.Value(last_subject_id);

            Lib.Proto.Set(m, "scienceValue", scienceValue);

            var state = GetState(scienceValue, issue, recording, forcedRun);

            if (state != State.RUNNING)
            {
                return;
            }
            if (dataSampled >= Science.Experiment(subject_id).max_amount)
            {
                return;
            }

            var stored = DoRecord(experiment, subject_id, v, ec, privateHdId,
                                  resources, KerbalismProcess.ParseResources(experiment.resources),
                                  remainingSampleMass, dataSampled, out dataSampled, out remainingSampleMass);

            if (!stored)
            {
                Lib.Proto.Set(m, "issue", insufficient_storage);
            }

            Lib.Proto.Set(m, "dataSampled", dataSampled);
            Lib.Proto.Set(m, "remainingSampleMass", remainingSampleMass);
        }
示例#16
0
        private static double Rate(Vessel v, double chunkSize, double maxCapacity, double elapsed, Resource_info ec, double ec_rate, Vessel_resources resources, List <KeyValuePair <string, double> > resourceDefs)
        {
            double result = Lib.Clamp(maxCapacity / chunkSize, 0, 1);

            result = Math.Min(result, Lib.Clamp(ec.amount / (ec_rate * elapsed), 0, 1));

            foreach (var p in resourceDefs)
            {
                var ri = resources.Info(v, p.Key);
                result = Math.Min(result, Lib.Clamp(ri.amount / (p.Value * elapsed), 0, 1));
            }

            return(result);
        }
示例#17
0
        private static bool DoRecord(Experiment experiment, string subject_id, Vessel vessel, Resource_info ec, uint hdId,
                                     Vessel_resources resources, List <KeyValuePair <string, double> > resourceDefs,
                                     double remainingSampleMass, double dataSampled,
                                     out double sampledOut, out double remainingSampleMassOut)
        {
            // default output values for early returns
            sampledOut             = dataSampled;
            remainingSampleMassOut = remainingSampleMass;

            var exp = Science.Experiment(subject_id);

            if (Done(exp, dataSampled))
            {
                return(true);
            }

            double elapsed   = Kerbalism.elapsed_s;
            double chunkSize = Math.Min(experiment.data_rate * elapsed, exp.max_amount);
            double massDelta = experiment.sample_mass * chunkSize / exp.max_amount;

            Drive drive = GetDrive(experiment, vessel, hdId, chunkSize, subject_id);

            // on high time warp this chunk size could be too big, but we could store a sizable amount if we process less
            bool   isFile      = experiment.sample_mass < float.Epsilon;
            double maxCapacity = isFile ? drive.FileCapacityAvailable() : drive.SampleCapacityAvailable(subject_id);

            Drive warpCacheDrive = null;

            if (isFile)
            {
                if (drive.GetFileSend(subject_id))
                {
                    warpCacheDrive = Cache.WarpCache(vessel);
                }
                if (warpCacheDrive != null)
                {
                    maxCapacity += warpCacheDrive.FileCapacityAvailable();
                }
            }

            double factor = Rate(vessel, chunkSize, maxCapacity, elapsed, ec, experiment.ec_rate, resources, resourceDefs);

            if (factor < double.Epsilon)
            {
                return(false);
            }

            chunkSize *= factor;
            massDelta *= factor;
            elapsed   *= factor;

            bool stored = false;

            if (chunkSize > double.Epsilon)
            {
                if (isFile)
                {
                    if (warpCacheDrive != null)
                    {
                        double s = Math.Min(chunkSize, warpCacheDrive.FileCapacityAvailable());
                        stored = warpCacheDrive.Record_file(subject_id, s, true);

                        if (chunkSize > s)                        // only write to persisted drive if the data cannot be transmitted in this tick
                        {
                            stored &= drive.Record_file(subject_id, chunkSize - s, true);
                        }
                    }
                    else
                    {
                        stored = drive.Record_file(subject_id, chunkSize, true);
                    }
                }
                else
                {
                    stored = drive.Record_sample(subject_id, chunkSize, massDelta);
                }
            }

            if (!stored)
            {
                return(false);
            }

            // consume resources
            ec.Consume(experiment.ec_rate * elapsed, "experiment");
            foreach (var p in resourceDefs)
            {
                resources.Consume(vessel, p.Key, p.Value * elapsed, "experiment");
            }

            dataSampled += chunkSize;
            dataSampled  = Math.Min(dataSampled, exp.max_amount);
            sampledOut   = dataSampled;
            if (!experiment.sample_collecting)
            {
                remainingSampleMass -= massDelta;
                remainingSampleMass  = Math.Max(remainingSampleMass, 0);
            }
            remainingSampleMassOut = remainingSampleMass;
            return(true);
        }
示例#18
0
		public static double Evaluate(Vessel v, Vessel_info vi, Vessel_resources resources, List<string> modifiers, Part p = null, ProtoPartSnapshot pp = null)
		{
			double k = 1.0;
			foreach (string mod in modifiers)
			{
				switch (mod)
				{
					case "zerog":
						k *= vi.zerog ? 1.0 : 0.0;
						break;

					case "landed":
						k *= vi.landed ? 1.0 : 0.0;
						break;

					case "breathable":
						k *= vi.breathable ? 1.0 : 0.0;
						break;

					case "non_breathable":
						k *= vi.breathable ? 0.0 : 1.0;
						break;

					case "temperature":
						k *= vi.temp_diff;
						break;

					case "radiation":
						k *= vi.radiation;
						break;

					case "shielding":
						k *= 1.0 - vi.shielding;
						break;

					case "volume":
						k *= vi.volume;
						break;

					case "surface":
						k *= vi.surface;
						break;

					case "living_space":
						k /= vi.living_space;
						break;

					case "comfort":
						k /= vi.comforts.factor;
						break;

					case "pressure":
						k *= vi.pressure > Settings.PressureThreshold ? 1.0 : Settings.PressureFactor;
						break;

					case "poisoning":
						k *= vi.poisoning > Settings.PoisoningThreshold ? 1.0 : Settings.PoisoningFactor;
						break;

					case "humidity":
						k *= vi.humidity > Settings.HumidityThreshold ? 1.0 : Settings.HumidityFactor;
						break;

					case "per_capita":
						k /= (double)Math.Max(vi.crew_count, 1);
						break;

					default:
						// If a psuedo resource is flowable, per part processing would result in every part producing the entire capacity of the vessel
						if (!Lib.IsResourceImpossibleToFlow(mod)) throw new Exception("psuedo-resource " + mod + " must be NO_FLOW");
						if (p != null) 			k *= resources.Info(v, mod).GetResourceInfoView(p).amount;  // loaded part
						else if (pp != null)	k *= resources.Info(v, mod).GetResourceInfoView(pp).amount; // unloaded part
						else					k *= resources.Info(v, mod).amount;                         // vessel-wide
						break;
				}
			}
			return k;
		}
示例#19
0
        public static void BackgroundUpdate(Vessel v, ProtoPartModuleSnapshot m, Greenhouse g,
                                            Vessel_info vi, Vessel_resources resources, double elapsed_s)
        {
            // get protomodule data
            bool   active = Lib.Proto.GetBool(m, "active");
            double growth = Lib.Proto.GetDouble(m, "growth");

            // if enabled and not ready for harvest
            if (active && growth < 0.99)
            {
                // get resource handler
                Resource_info ec = resources.Info(v, "ElectricCharge");

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

                // consume EC for the lamps, scaled by artificial light intensity
                if (artificial > double.Epsilon)
                {
                    ec.Consume(g.ec_rate * (artificial / g.light_tolerance) * elapsed_s, "greenhouse");
                }

                // reset artificial lighting if there is no ec left
                // note: comparing against amount in previous simulation step
                if (ec.amount <= double.Epsilon)
                {
                    artificial = 0.0;
                }

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

                // determine environment conditions
                bool lighting  = natural + artificial >= g.light_tolerance;
                bool pressure  = g.pressure_tolerance <= double.Epsilon || vi.pressure >= g.pressure_tolerance;
                bool radiation = g.radiation_tolerance <= double.Epsilon || vi.radiation * (1.0 - vi.shielding) < g.radiation_tolerance;

                // determine inputs conditions
                // note: comparing against amounts in previous simulation step
                bool   inputs      = true;
                string missing_res = string.Empty;
                bool   dis_WACO2   = false;
                foreach (ModuleResource input in g.resHandler.inputResources)
                {
                    // combine WasteAtmosphere and CO2 if both exist
                    if (input.name == "WasteAtmosphere" || input.name == "CarbonDioxide")
                    {
                        if (dis_WACO2 || vi.breathable)
                        {
                            continue;                                                        // skip if already checked or atmosphere is breathable
                        }
                        if (g.WACO2)
                        {
                            if (resources.Info(v, "WasteAtmosphere").amount <= double.Epsilon && resources.Info(v, "CarbonDioxide").amount <= double.Epsilon)
                            {
                                inputs      = false;
                                missing_res = "CarbonDioxide";
                                break;
                            }
                            dis_WACO2 = true;
                            continue;
                        }
                    }
                    if (resources.Info(v, input.name).amount <= double.Epsilon)
                    {
                        inputs      = false;
                        missing_res = input.name;
                        break;
                    }
                }

                // if growing
                if (lighting && pressure && radiation && inputs)
                {
                    // increase growth
                    growth += g.crop_rate * elapsed_s;
                    growth  = Math.Min(growth, 1.0);

                    // notify the user when crop can be harvested
                    if (growth >= 0.99)
                    {
                        Message.Post(Lib.BuildString("On <b>", v.vesselName, "</b> the crop is ready to be harvested"));
                        growth = 1.0;
                    }
                }

                // update time-to-harvest
                double tta = (1.0 - growth) / g.crop_rate;

                // update issues
                string issue =
                    !inputs?Lib.BuildString("missing ", missing_res)
                        : !lighting ? "insufficient lighting"
                                : !pressure ? "insufficient pressure"
                                : !radiation ? "excessive radiation"
                                : string.Empty;

                // update protomodule data
                Lib.Proto.Set(m, "natural", natural);
                Lib.Proto.Set(m, "artificial", artificial);
                Lib.Proto.Set(m, "tta", tta);
                Lib.Proto.Set(m, "issue", issue);
                Lib.Proto.Set(m, "growth", growth);
            }
        }
示例#20
0
        // call scripts automatically when conditions are met
        public void Automate(Vessel v, Vessel_info vi, Vessel_resources resources)
        {
            // do nothing if automation is disabled
            if (!Features.Automation)
            {
                return;
            }

            // get current states
            Resource_info ec             = resources.Info(v, "ElectricCharge");
            bool          sunlight       = vi.sunlight > double.Epsilon;
            bool          power_low      = ec.level < 0.2;
            bool          power_high     = ec.level > 0.8;
            bool          radiation_low  = vi.radiation < 0.000005552;   //< 0.02 rad/h
            bool          radiation_high = vi.radiation > 0.00001388;    //< 0.05 rad/h
            bool          signal         = vi.connection.linked;

            // get current situation
            bool landed = false;
            bool atmo   = false;
            bool space  = false;

            switch (v.situation)
            {
            case Vessel.Situations.LANDED:
            case Vessel.Situations.SPLASHED:
                landed = true;
                break;

            case Vessel.Situations.FLYING:
                atmo = true;
                break;

            case Vessel.Situations.SUB_ORBITAL:
            case Vessel.Situations.ORBITING:
            case Vessel.Situations.ESCAPING:
                space = true;
                break;
            }


            // compile list of scripts that need to be called
            var to_exec = new List <Script>();

            foreach (var p in scripts)
            {
                ScriptType type   = p.Key;
                Script     script = p.Value;
                if (script.states.Count == 0)
                {
                    continue;                                           //< skip empty scripts (may happen during editing)
                }
                switch (type)
                {
                case ScriptType.landed:
                    if (landed && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = landed ? "1" : "0";
                    break;

                case ScriptType.atmo:
                    if (atmo && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = atmo ? "1" : "0";
                    break;

                case ScriptType.space:
                    if (space && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = space ? "1" : "0";
                    break;

                case ScriptType.sunlight:
                    if (sunlight && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = sunlight ? "1" : "0";
                    break;

                case ScriptType.shadow:
                    if (!sunlight && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = !sunlight ? "1" : "0";
                    break;

                case ScriptType.power_high:
                    if (power_high && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = power_high ? "1" : "0";
                    break;

                case ScriptType.power_low:
                    if (power_low && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = power_low ? "1" : "0";
                    break;

                case ScriptType.rad_low:
                    if (radiation_low && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = radiation_low ? "1" : "0";
                    break;

                case ScriptType.rad_high:
                    if (radiation_high && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = radiation_high ? "1" : "0";
                    break;

                case ScriptType.linked:
                    if (signal && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = signal ? "1" : "0";
                    break;

                case ScriptType.unlinked:
                    if (!signal && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = !signal ? "1" : "0";
                    break;
                }
            }

            // if there are scripts to call
            if (to_exec.Count > 0)
            {
                // get list of devices
                // - we avoid creating it when there are no scripts to be executed, making its overall cost trivial
                var devices = Boot(v);

                // execute all scripts
                foreach (Script script in to_exec)
                {
                    script.Execute(devices);
                }

                // show message to the user
                if (DB.Vessel(v).cfg_script)
                {
                    Message.Post(Lib.BuildString("Script called on vessel <b>", v.vesselName, "</b>"));
                }
            }
        }
示例#21
0
        static void ProcessAsteroidDrill(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleAsteroidDrill asteroid_drill, Vessel_resources resources, double elapsed_s)
        {
            // note: untested
            // note: ignore stock temperature mechanic of asteroid drills
            // note: ignore auto shutdown
            // note: 'undo' stock behavior by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation)

            // if active
            if (Lib.Proto.GetBool(m, "IsActivated"))
            {
                // get asteroid data
                ProtoPartModuleSnapshot asteroid_info     = null;
                ProtoPartModuleSnapshot asteroid_resource = null;
                foreach (ProtoPartSnapshot pp in v.protoVessel.protoPartSnapshots)
                {
                    if (asteroid_info == null)
                    {
                        asteroid_info = pp.modules.Find(k => k.moduleName == "ModuleAsteroidInfo");
                    }
                    if (asteroid_resource == null)
                    {
                        asteroid_resource = pp.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 = Lib.Proto.GetDouble(asteroid_info, "massThresholdVal");
                    double mass           = Lib.Proto.GetDouble(asteroid_info, "currentMassVal");
                    double abundance      = Lib.Proto.GetDouble(asteroid_resource, "abundance");
                    string res_name       = Lib.Proto.GetString(asteroid_resource, "resourceName");
                    double res_density    = PartResourceLibrary.Instance.GetDefinition(res_name).density;

                    // if asteroid isn't depleted
                    if (mass > mass_threshold && abundance > double.Epsilon)
                    {
                        // deduce crew bonus
                        int exp_level = -1;
                        if (asteroid_drill.UseSpecialistBonus)
                        {
                            foreach (ProtoCrewMember c in Lib.CrewList(v))
                            {
                                if (c.experienceTrait.Effects.Find(k => k.Name == asteroid_drill.ExperienceEffect) != null)
                                {
                                    exp_level = Math.Max(exp_level, c.experienceLevel);
                                }
                            }
                        }
                        double exp_bonus = exp_level < 0
                                                ? asteroid_drill.EfficiencyBonus * asteroid_drill.SpecialistBonusBase
                                                : asteroid_drill.EfficiencyBonus * (asteroid_drill.SpecialistBonusBase + (asteroid_drill.SpecialistEfficiencyFactor * (exp_level + 1)));

                        // determine resource extracted
                        double res_amount = abundance * asteroid_drill.Efficiency * exp_bonus * elapsed_s;

                        // transform EC into mined resource
                        Resource_recipe recipe = new Resource_recipe(p);
                        recipe.Input("ElectricCharge", asteroid_drill.PowerConsumption * elapsed_s);
                        recipe.Output(res_name, res_amount, true);
                        resources.Transform(recipe);

                        // if there was ec
                        // note: comparing against amount in previous simulation step
                        if (resources.Info(v, "ElectricCharge").amount > double.Epsilon)
                        {
                            // consume asteroid mass
                            Lib.Proto.Set(asteroid_info, "currentMassVal", (mass - res_density * res_amount));
                        }
                    }
                }

                // undo stock behavior by forcing last_update_time to now
                Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime());
            }
        }
示例#22
0
        static void ProcessCryoTank(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule cryotank, Vessel_resources resources, Resource_info ec, double elapsed_s)
        {
            // Note. Currently background simulation of Cryotanks has an irregularity in that boiloff of a fuel type in a tank removes resources from all tanks
            // but at least some simulation is better than none ;)

            // get list of fuels, do nothing if no fuels
            IList fuels = Lib.ReflectionValue <IList>(cryotank, "fuels");

            if (fuels == null)
            {
                return;
            }

            // is cooling available, note: comparing against amount in previous simulation step
            bool available = (Lib.Proto.GetBool(m, "CoolingEnabled") && ec.amount > double.Epsilon);

            // get cooling cost
            double cooling_cost = Lib.ReflectionValue <float>(cryotank, "CoolingCost");

            string fuel_name    = "";
            double amount       = 0.0;
            double total_cost   = 0.0;
            double boiloff_rate = 0.0;

            foreach (var item in fuels)
            {
                fuel_name = Lib.ReflectionValue <string>(item, "fuelName");
                // if fuel_name is null, don't do anything
                if (fuel_name == null)
                {
                    continue;
                }

                //get fuel resource
                Resource_info fuel = resources.Info(v, fuel_name);

                // if there is some fuel
                // note: comparing against amount in previous simulation step
                if (fuel.amount > double.Epsilon)
                {
                    // Try to find resource "fuel_name" in PartResources
                    ProtoPartResourceSnapshot proto_fuel = p.resources.Find(k => k.resourceName == fuel_name);

                    // If part doesn't have the fuel, don't do anything.
                    if (proto_fuel == null)
                    {
                        continue;
                    }

                    // get amount in the part
                    amount = proto_fuel.amount;

                    // if cooling is enabled and there is enough EC
                    if (available)
                    {
                        // calculate ec consumption
                        total_cost += cooling_cost * amount * 0.001;
                    }
                    // if cooling is disabled or there wasn't any EC
                    else
                    {
                        // get boiloff rate per-second
                        boiloff_rate = Lib.ReflectionValue <float>(item, "boiloffRate") / 360000.0f;

                        // let it boil off
                        fuel.Consume(amount * (1.0 - Math.Pow(1.0 - boiloff_rate, elapsed_s)));
                    }
                }
            }

            // apply EC consumption
            ec.Consume(total_cost * elapsed_s);
        }
示例#23
0
        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>();

            // 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);
            }

            // 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;                             // Kerbalism ground and air harvester module

                    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: ProcessDrill(v, p, m, module_prefab as ModuleResourceHarvester, resources, elapsed_s); break;                             // Stock ground harvester module

                    case Module_type.AsteroidDrill: ProcessAsteroidDrill(v, p, m, module_prefab as ModuleAsteroidDrill, resources, elapsed_s); break;                 // Stock asteroid harvester module

                    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, ec, elapsed_s); break;

                    case Module_type.FNGenerator: ProcessFNGenerator(v, p, m, module_prefab, ec, elapsed_s); break;
                    }
                }
            }
        }
示例#24
0
        void FixedUpdate()
        {
            // remove control locks in any case
            Misc.ClearLocks();

            // do nothing if paused
            if (Lib.IsPaused())
            {
                return;
            }

            // maintain elapsed_s, converting to double only once
            // and detect warp blending
            double fixedDeltaTime = TimeWarp.fixedDeltaTime;

            if (Math.Abs(fixedDeltaTime - elapsed_s) > double.Epsilon)
            {
                warp_blending = 0;
            }
            else
            {
                ++warp_blending;
            }
            elapsed_s = fixedDeltaTime;

            // evict oldest entry from vessel cache
            Cache.Update();

            // store info for oldest unloaded vessel
            double           last_time      = 0.0;
            Vessel           last_v         = null;
            Vessel_info      last_vi        = null;
            VesselData       last_vd        = null;
            Vessel_resources last_resources = null;

            // for each vessel
            foreach (Vessel v in FlightGlobals.Vessels)
            {
                // get vessel info from the cache
                Vessel_info vi = Cache.VesselInfo(v);

                // set locks for active vessel
                if (v.isActiveVessel)
                {
                    Misc.SetLocks(v, vi);
                }

                // maintain eva dead animation and helmet state
                if (v.loaded && v.isEVA)
                {
                    EVA.Update(v);
                }

                // keep track of rescue mission kerbals, and gift resources to their vessels on discovery
                if (v.loaded && vi.is_vessel)
                {
                    // manage rescue mission mechanics
                    Misc.ManageRescueMission(v);
                }

                // do nothing else for invalid vessels
                if (!vi.is_valid)
                {
                    continue;
                }

                // get vessel data from db
                VesselData vd = DB.Vessel(v);

                // get resource cache
                Vessel_resources resources = ResourceCache.Get(v);

                // if loaded
                if (v.loaded)
                {
                    // get most used resource
                    Resource_info ec = resources.Info(v, "ElectricCharge");

                    // show belt warnings
                    Radiation.BeltWarnings(v, vi, vd);

                    // update storm data
                    Storm.Update(v, vi, vd, elapsed_s);

                    Communications.Update(v, vi, vd, ec, elapsed_s);

                    // Habitat equalization
                    ResourceBalance.Equalizer(v);

                    // transmit science data
                    Science.Update(v, vi, vd, resources, elapsed_s);

                    // apply rules
                    Profile.Execute(v, vi, vd, resources, elapsed_s);

                    // apply deferred requests
                    resources.Sync(v, elapsed_s);

                    // call automation scripts
                    vd.computer.Automate(v, vi, resources);

                    // remove from unloaded data container
                    unloaded.Remove(vi.id);
                }
                // if unloaded
                else
                {
                    // get unloaded data, or create an empty one
                    Unloaded_data ud;
                    if (!unloaded.TryGetValue(vi.id, out ud))
                    {
                        ud = new Unloaded_data();
                        unloaded.Add(vi.id, ud);
                    }

                    // accumulate time
                    ud.time += elapsed_s;

                    // maintain oldest entry
                    if (ud.time > last_time)
                    {
                        last_time      = ud.time;
                        last_v         = v;
                        last_vi        = vi;
                        last_vd        = vd;
                        last_resources = resources;
                    }
                }
            }


            // at most one vessel gets background processing per physics tick
            // if there is a vessel that is not the currently loaded vessel, then
            // we will update the vessel whose most recent background update is the oldest
            if (last_v != null)
            {
                // get most used resource
                Resource_info last_ec = last_resources.Info(last_v, "ElectricCharge");

                // show belt warnings
                Radiation.BeltWarnings(last_v, last_vi, last_vd);

                // update storm data
                Storm.Update(last_v, last_vi, last_vd, last_time);

                Communications.Update(last_v, last_vi, last_vd, last_ec, last_time);

                // transmit science	data
                Science.Update(last_v, last_vi, last_vd, last_resources, last_time);

                // apply rules
                Profile.Execute(last_v, last_vi, last_vd, last_resources, last_time);

                // simulate modules in background
                Background.Update(last_v, last_vi, last_vd, last_resources, last_time);

                // apply deferred requests
                last_resources.Sync(last_v, last_time);

                // call automation scripts
                last_vd.computer.Automate(last_v, last_vi, last_resources);

                // remove from unloaded data container
                unloaded.Remove(last_vi.id);
            }

            // update storm data for one body per-step
            if (storm_bodies.Count > 0)
            {
                storm_bodies.ForEach(k => k.time += elapsed_s);
                Storm_data sd = storm_bodies[storm_index];
                Storm.Update(sd.body, sd.time);
                sd.time     = 0.0;
                storm_index = (storm_index + 1) % storm_bodies.Count;
            }
        }
示例#25
0
		public static double Evaluate(Vessel v, Vessel_info vi, Vessel_resources resources, List<string> modifiers)
		{
			double k = 1.0;
			foreach (string mod in modifiers)
			{
				switch (mod)
				{
					case "breathable":
						k *= vi.breathable ? 0.0 : 1.0;
						break;

					case "temperature":
						k *= vi.temp_diff;
						break;

					case "radiation":
						k *= vi.radiation;
						break;

					case "shielding":
						k *= 1.0 - vi.shielding;
						break;

					case "volume":
						k *= vi.volume;
						break;

					case "surface":
						k *= vi.surface;
						break;

					case "living_space":
						k /= vi.living_space;
						break;

					case "comfort":
						k /= vi.comforts.factor;
						break;

					case "pressure":
						k *= vi.pressure > Settings.PressureThreshold ? 1.0 : Settings.PressureFactor;
						break;

					case "poisoning":
						k *= vi.poisoning > Settings.PoisoningThreshold ? 1.0 : Settings.PoisoningFactor;
						break;

					case "humidity":
						k *= vi.humidity > Settings.HumidityThreshold ? 1.0 : Settings.HumidityFactor;
						break;

					case "per_capita":
						k /= (double)Math.Max(vi.crew_count, 1);
						break;

					default:
						k *= resources.Info(v, mod).amount;
						break;
				}
			}
			return k;
		}
示例#26
0
        private static string TestForResources(Vessel v, List <KeyValuePair <string, double> > defs, double elapsed_s, Vessel_resources res)
        {
            if (defs.Count < 1)
            {
                return(string.Empty);
            }

            // test if there are enough resources on the vessel
            foreach (var p in defs)
            {
                var ri = res.Info(v, p.Key);
                if (ri.amount < p.Value * elapsed_s)
                {
                    return("missing " + ri.resource_name);
                }
            }

            return(string.Empty);
        }
示例#27
0
        public void FixedUpdate()
        {
            // do nothing in the editor
            if (Lib.IsEditor())
            {
                return;
            }

            // if enabled and not ready for harvest
            if (active && growth < 0.99)
            {
                // get vessel info from the cache
                // - if the vessel is not valid (eg: flagged as debris) then solar flux will be 0 and landed false (but that's okay)
                Vessel_info vi = Cache.VesselInfo(vessel);

                // get resource cache
                Vessel_resources resources = ResourceCache.Get(vessel);
                Resource_info    ec        = resources.Info(vessel, "ElectricCharge");

                // deal with corner cases when greenhouse is assembled using KIS
                if (double.IsNaN(growth) || double.IsInfinity(growth))
                {
                    growth = 0.0;
                }

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

                // consume EC for the lamps, scaled by artificial light intensity
                if (artificial > double.Epsilon)
                {
                    ec.Consume(ec_rate * (artificial / light_tolerance) * Kerbalism.elapsed_s, "greenhouse");
                }

                // reset artificial lighting if there is no ec left
                // - comparing against amount in previous simulation step
                if (ec.amount <= double.Epsilon)
                {
                    artificial = 0.0;
                }

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

                // determine environment conditions
                bool lighting  = natural + artificial >= light_tolerance;
                bool pressure  = pressure_tolerance <= double.Epsilon || vi.pressure >= pressure_tolerance;
                bool radiation = radiation_tolerance <= double.Epsilon || vi.radiation * (1.0 - vi.shielding) < radiation_tolerance;

                // determine input resources conditions
                // - comparing against amounts in previous simulation step
                bool   inputs      = true;
                string missing_res = string.Empty;
                bool   dis_WACO2   = false;
                foreach (ModuleResource input in resHandler.inputResources)
                {
                    // combine WasteAtmosphere and CO2 if both exist
                    if (input.name == "WasteAtmosphere" || input.name == "CarbonDioxide")
                    {
                        if (dis_WACO2 || Cache.VesselInfo(vessel).breathable)
                        {
                            continue;                                                                              // skip if already checked or atmosphere is breathable
                        }
                        if (WACO2)
                        {
                            if (resources.Info(vessel, "WasteAtmosphere").amount <= double.Epsilon && resources.Info(vessel, "CarbonDioxide").amount <= double.Epsilon)
                            {
                                inputs      = false;
                                missing_res = "CarbonDioxide";
                                break;
                            }
                            dis_WACO2 = true;
                            continue;
                        }
                    }
                    if (resources.Info(vessel, input.name).amount <= double.Epsilon)
                    {
                        inputs      = false;
                        missing_res = input.name;
                        break;
                    }
                }

                // if growing
                if (lighting && pressure && radiation && inputs)
                {
                    // increase growth
                    growth += crop_rate * Kerbalism.elapsed_s;
                    growth  = Math.Min(growth, 1.0);

                    // notify the user when crop can be harvested
                    if (growth >= 0.99)
                    {
                        Message.Post(Lib.BuildString("On <b>", vessel.vesselName, "</b> the crop is ready to be harvested"));
                        growth = 1.0;
                    }
                }

                // update time-to-harvest
                tta = (1.0 - growth) / crop_rate;

                // update issues
                issue =
                    !inputs?Lib.BuildString("missing ", missing_res)
                        : !lighting ? "insufficient lighting"
                                : !pressure ? "insufficient pressure"
                                : !radiation ? "excessive radiation"
                                : string.Empty;
            }
        }
示例#28
0
        void ToEVA(GameEvents.FromToAction <Part, Part> data)
        {
            Cache.PurgeObjects(data.from.vessel);
            Cache.PurgeObjects(data.to.vessel);

            // get total crew in the origin vessel
            double tot_crew = Lib.CrewCount(data.from.vessel) + 1.0;

            // get vessel resources handler
            Vessel_resources resources = ResourceCache.Get(data.from.vessel);

            // setup supply resources capacity in the eva kerbal
            Profile.SetupEva(data.to);

            String prop_name = Lib.EvaPropellantName();

            // for each resource in the kerbal
            for (int i = 0; i < data.to.Resources.Count; ++i)
            {
                // get the resource
                PartResource res = data.to.Resources[i];

                // eva prop is handled differently
                if (res.resourceName == prop_name)
                {
                    continue;
                }

                double quantity = Math.Min(resources.Info(data.from.vessel, res.resourceName).amount / tot_crew, res.maxAmount);
                // remove resource from vessel
                quantity = data.from.RequestResource(res.resourceName, quantity);

                // add resource to eva kerbal
                data.to.RequestResource(res.resourceName, -quantity);
            }

            // take as much of the propellant as possible. just imagine: there are 1.3 units left, and 12 occupants
            // in the ship. you want to send out an engineer to fix the chemical plant that produces monoprop,
            // and have to get from one end of the station to the other with just 0.1 units in the tank...
            // nope.
            double evaPropQuantity = data.from.RequestResource(prop_name, Lib.EvaPropellantCapacity());

            // We can't just add the monoprop here, because that doesn't always work. It might be related
            // to the fact that stock KSP wants to add 5 units of monoprop to new EVAs. Instead of fighting KSP here,
            // we just let it do it's thing and set our amount later in EVA.cs - which seems to work just fine.
            // don't put that into Cache.VesselInfo because that can be deleted before we get there
            Cache.SetVesselObjectsCache(data.to.vessel, "eva_prop", evaPropQuantity);

            // Airlock loss
            resources.Consume(data.from.vessel, "Nitrogen", PreferencesLifeSupport.Instance.evaAtmoLoss, "airlock");

            // show warning if there is little or no EVA propellant in the suit
            if (evaPropQuantity <= 0.05 && !Lib.Landed(data.from.vessel))
            {
                Message.Post(Severity.danger,
                             Lib.BuildString("There isn't any <b>", prop_name, "</b> in the EVA suit"), "Don't let the ladder go!");
            }

            // turn off headlamp light, to avoid stock bug that show them for a split second when going on eva
            KerbalEVA kerbal = data.to.FindModuleImplementing <KerbalEVA>();

            EVA.HeadLamps(kerbal, false);

            // execute script
            DB.Vessel(data.from.vessel).computer.Execute(data.from.vessel, ScriptType.eva_out);
        }
示例#29
0
        // consume EC for transmission, and transmit science data
        public static void Update(Vessel v, Vessel_info vi, VesselData vd, Vessel_resources resources, double elapsed_s)
        {
            // do nothing if science system is disabled
            if (!Features.Science)
            {
                return;
            }

            // avoid corner-case when RnD isn't live during scene changes
            // - this avoid losing science if the buffer reach threshold during a scene change
            if (HighLogic.CurrentGame.Mode != Game.Modes.SANDBOX && ResearchAndDevelopment.Instance == null)
            {
                return;
            }

            // get connection info
            ConnectionInfo conn = vi.connection;

            if (conn == null)
            {
                return;
            }
            if (String.IsNullOrEmpty(vi.transmitting))
            {
                return;
            }

            Drive warpCache   = Cache.WarpCache(v);
            bool  isWarpCache = false;

            double transmitSize = conn.rate * elapsed_s;

            while (warpCache.files.Count > 0 ||            // transmit EVERYTHING in the cache, regardless of transmitSize.
                   (transmitSize > double.Epsilon && !String.IsNullOrEmpty(vi.transmitting)))
            {
                // get filename of data being downloaded
                var exp_filename = vi.transmitting;
                if (string.IsNullOrEmpty(exp_filename))
                {
                    break;
                }

                Drive drive = null;
                if (warpCache.files.ContainsKey(exp_filename))
                {
                    drive       = warpCache;
                    isWarpCache = true;
                }
                else
                {
                    drive       = FindDrive(v, exp_filename);
                    isWarpCache = false;
                }

                if (drive == null)
                {
                    break;
                }

                File file = drive.files[exp_filename];

                if (isWarpCache)
                {
                    file.buff     = file.size;
                    file.size     = 0;
                    transmitSize -= file.size;
                }
                else
                {
                    if (transmitSize < double.Epsilon)
                    {
                        break;
                    }

                    // determine how much data is transmitted
                    double transmitted = Math.Min(file.size, transmitSize);
                    transmitSize -= transmitted;

                    // consume data in the file
                    file.size -= transmitted;

                    // accumulate in the buffer
                    file.buff += transmitted;
                }

                // special case: file size on drive = 0 -> buffer is 0, so no need to do anyhting. just delete.
                if (file.buff > double.Epsilon)
                {
                    // this is the science value remaining for this experiment
                    var remainingValue = Value(exp_filename, 0);

                    // this is the science value of this sample
                    double dataValue = Value(exp_filename, file.buff);
                    bool   doCredit  = file.size <= double.Epsilon || dataValue > buffer_science_value;;

                    // if buffer science value is high enough or file was transmitted completely
                    if (doCredit)
                    {
                        var totalValue = TotalValue(exp_filename);

                        // collect the science data
                        Credit(exp_filename, file.buff, true, v.protoVessel);

                        // reset the buffer
                        file.buff = 0.0;

                        // this was the last useful bit, there is no more value in the experiment
                        if (remainingValue >= 0.1 && remainingValue - dataValue < 0.1)
                        {
                            Message.Post(
                                Lib.BuildString(Lib.HumanReadableScience(totalValue), " ", Experiment(exp_filename).FullName(exp_filename), " completed"),
                                Lib.TextVariant(
                                    "Our researchers will jump on it right now",
                                    "This cause some excitement",
                                    "These results are causing a brouhaha in R&D",
                                    "Our scientists look very confused",
                                    "The scientists won't believe these readings"
                                    ));
                        }
                    }
                }

                // if file was transmitted completely
                if (file.size <= double.Epsilon)
                {
                    // remove the file
                    drive.files.Remove(exp_filename);
                    vi.transmitting = Science.Transmitting(v, true);
                }
            }
        }
示例#30
0
        private static bool DoRecord(Experiment experiment, string subject_id, Vessel vessel, Resource_info ec, uint hdId,
                                     Vessel_resources resources, List <KeyValuePair <string, double> > resourceDefs,
                                     double remainingSampleMass, double dataSampled,
                                     out double sampledOut, out double remainingSampleMassOut)
        {
            var exp = Science.Experiment(subject_id);

            if (Done(exp, dataSampled))
            {
                sampledOut             = dataSampled;
                remainingSampleMassOut = remainingSampleMass;
                return(true);
            }

            double elapsed   = Kerbalism.elapsed_s;
            double chunkSize = Math.Min(experiment.data_rate * elapsed, exp.max_amount);
            double massDelta = experiment.sample_mass * chunkSize / exp.max_amount;

            Drive drive = GetDrive(experiment, vessel, hdId, chunkSize, subject_id);

            // on high time warp this chunk size could be too big, but we could store a sizable amount if we process less
            bool   isFile      = experiment.sample_mass < float.Epsilon;
            double maxCapacity = isFile ? drive.FileCapacityAvailable() : drive.SampleCapacityAvailable(subject_id);

            if (maxCapacity < chunkSize)
            {
                double factor = maxCapacity / chunkSize;
                chunkSize *= factor;
                massDelta *= factor;
                elapsed   *= factor;
            }

            foreach (var p in resourceDefs)
            {
                resources.Consume(vessel, p.Key, p.Value * elapsed, "experiment");
            }

            bool stored = false;

            if (isFile)
            {
                stored = drive.Record_file(subject_id, chunkSize, true);
            }
            else
            {
                stored = drive.Record_sample(subject_id, chunkSize, massDelta);
            }

            if (stored)
            {
                // consume ec
                ec.Consume(experiment.ec_rate * elapsed, "experiment");
                dataSampled += chunkSize;
                dataSampled  = Math.Min(dataSampled, exp.max_amount);
                sampledOut   = dataSampled;
                if (!experiment.sample_collecting)
                {
                    remainingSampleMass -= massDelta;
                    remainingSampleMass  = Math.Max(remainingSampleMass, 0);
                }
                remainingSampleMassOut = remainingSampleMass;
                return(true);
            }

            sampledOut             = dataSampled;
            remainingSampleMassOut = remainingSampleMass;
            return(false);
        }