public void FixedUpdate() { // do nothing in the editor if (Lib.IsEditor()) { return; } // if enabled, and there is ec consumption if (running && ec_rate > double.Epsilon) { // get resource cache Resource_info ec = ResourceCache.Info(vessel, "ElectricCharge"); // consume EC ec.Consume(ec_rate * Kerbalism.elapsed_s, "emitter"); } }
public void SetupRescue(Vessel v) { // do nothing if no resource on resque if (on_rescue <= double.Epsilon) return; // if the vessel has no capacity if (ResourceCache.Info(v, resource).capacity <= double.Epsilon) { // find the first useful part Part p = v.parts.Find(k => k.CrewCapacity > 0 || k.FindModuleImplementing<KerbalEVA>() != null); // add capacity Lib.AddResource(p, resource, 0.0, on_rescue); } // add resource to the vessel ResourceCache.Produce(v, resource, on_rescue); }
public void FixedUpdate() { // do nothing in the editor if (Lib.IsEditor()) { return; } // if has any animation playing, consume energy. if (Is_consuming_energy()) { // get resource handler Resource_info ec = ResourceCache.Info(vessel, "ElectricCharge"); // consume ec ec.Consume(ec_rate * Kerbalism.elapsed_s); } }
void indicator_ec(Panel p, Vessel v) { Texture image; string tooltip; resource_info ec = ResourceCache.Info(v, "ElectricCharge"); tooltip = ec.capacity > 0.0 ? "EC: " + Lib.HumanReadablePerc(ec.level) : ""; image = Icons.battery_white; Supply supply = Profile.supplies.Find(k => k.resource == "ElectricCharge"); double low_threshold = supply != null ? supply.low_threshold : 0.15; if (ec.level <= 0.005) image = Icons.battery_red; else if (ec.level <= low_threshold) image = Icons.battery_yellow; p.icon(image, tooltip); }
void Indicator_Supplies(Panel p, Vessel v, Vessel_Info vi) { List <string> tooltips = new List <string>(); uint max_severity = 0; if (vi.crew_count > 0) { foreach (Supply supply in Profile.supplies.FindAll(k => k.resource != "ElectricCharge")) { Resource_Info res = ResourceCache.Info(v, supply.resource); double depletion = res.Depletion(vi.crew_count); if (res.capacity > double.Epsilon) { if (tooltips.Count == 0) { tooltips.Add("<align=left /><b>name\t\tlevel\tduration</b>"); } tooltips.Add(Lib.BuildString ( res.level <= 0.005 ? "<color=#ff0000>" : res.level <= supply.low_threshold ? "<color=#ffff00>" : "<color=#cccccc>", supply.resource, supply.resource != "Ammonia" ? "\t\t" : "\t", //< hack: make ammonia fit damn it Lib.HumanReadablePerc(res.level), "\t", depletion <= double.Epsilon ? "depleted" : Lib.HumanReadableDuration(depletion), "</color>" )); uint severity = res.level <= 0.005 ? 2u : res.level <= supply.low_threshold ? 1u : 0; max_severity = Math.Max(max_severity, severity); } } } Texture image = max_severity == 2 ? Icons.box_red : max_severity == 1 ? Icons.box_yellow : Icons.box_white; p.SetIcon(image, string.Join("\n", tooltips.ToArray())); }
State venting() { // in flight if (Lib.IsFlight()) { // shortcuts resource_info vessel_atmo = ResourceCache.Info(vessel, "Atmosphere"); PartResource hab_atmo = part.Resources["Atmosphere"]; // get amount of atmosphere in part double hab_amount = hab_atmo.amount; // venting succeeded if the amount reached zero if (hab_amount <= double.Epsilon) { return(State.disabled); } // determine venting speed double amount = volume * equalize_speed * Kerbalism.elapsed_s; // consume from the part, clamp amount to what's available in the part amount = Math.Min(amount, hab_atmo.amount); hab_atmo.amount -= amount; // produce in all enabled habs in the vessel // (attempt recovery, but dump overboard if there is no capacity left) vessel_atmo.Produce(amount); // venting still in progress return(State.venting); } // in the editors else { // set amount to zero part.Resources["Atmosphere"].amount = 0.0; // return new state return(State.disabled); } }
public void FixedUpdate() { if (Lib.IsEditor()) { return; } if (ResourceCache.Info(vessel, "ElectricCharge").amount <= double.Epsilon && running) { Events["Toggle"].Invoke(); } if (deployed && running && issue.Length == 0) { Resource_Recipe recipe = new Resource_Recipe(); recipe.Input("ElectricCharge", ec_rate * Kerbalism.elapsed_s); recipe.Output(resource, rate * Kerbalism.elapsed_s, true); ResourceCache.Transform(vessel, recipe); } }
public virtual void FixedUpdate() { if (FixGame(thisModule)) { return; } hasEC = ResourceCache.Info(part.vessel, "ElectricCharge").amount > double.Epsilon; if (GetIsActive()) { part.ModulesOnUpdate(); // get resource cache vessel_resources resources = ResourceCache.Get(part.vessel); resources.Consume(part.vessel, "ElectricCharge", actualECCost * Kerbalism.elapsed_s); } else { actualECCost = 0; } }
public static void Update(Vessel v) { // do nothing if not an eva kerbal if (!v.isEVA) { return; } // get KerbalEVA module KerbalEVA kerbal = Lib.FindModules <KerbalEVA>(v)[0]; // get resource handler Resource_Info ec = ResourceCache.Info(v, "ElectricCharge"); // determine if headlamps need ec // - not required if there is no EC capacity in eva kerbal (no ec supply in profile) // - not required if no EC cost for headlamps is specified (set by the user) bool need_ec = ec.capacity > double.Epsilon && Settings.HeadLampsCost > double.Epsilon; // consume EC for the headlamps if (need_ec && kerbal.lampOn) { ec.Consume(Settings.HeadLampsCost * Kerbalism.elapsed_s); } // force the headlamps on/off HeadLamps(kerbal, kerbal.lampOn && (!need_ec || ec.amount > double.Epsilon)); // if dead if (IsDead(v)) { // enforce freezed state Freeze(kerbal); // disable modules DisableModules(kerbal); // remove plant flag action kerbal.flagItems = 0; } }
void problem_recyclers(Vessel v, List<Recycler.partial_data> recyclers, ref List<Texture> icons, ref List<string> tooltips) { if (recyclers.Count == 0) return; bool no_ec_left = ResourceCache.Info(v, "ElectricCharge").amount <= double.Epsilon; bool disabled = false; foreach(var recycler in recyclers) { disabled |= !recycler.is_enabled; } if (no_ec_left) { icons.Add(icon_scrubber_danger); tooltips.Add("Recycler has no power"); } else if (disabled) { icons.Add(icon_scrubber_warning); tooltips.Add("Recycler disabled"); } }
void render_supplies(Vessel v, vessel_info vi) { if (Kerbalism.supply_rules.Count > 0 || Kerbalism.ec_rule != null) { render_title("SUPPLIES"); if (Kerbalism.ec_rule != null) { resource_info res = ResourceCache.Info(v, "ElectricCharge"); render_content("battery", res.level > double.Epsilon ? Lib.HumanReadableDuration(Kerbalism.ec_rule.Depletion(v, res)) : "none", res.rate); } if (vi.crew_capacity > 0) { foreach(Rule r in Kerbalism.supply_rules) { resource_info res = ResourceCache.Info(v, r.resource_name); render_content(r.resource_name.AddSpacesOnCaps().ToLower(), res.level > double.Epsilon ? Lib.HumanReadableDuration(r.Depletion(v, res)) : "none", res.rate); } } render_space(); } }
void IScienceDataTransmitter.TransmitData(List<ScienceData> dataQueue) { // if there is no signal if (!can_transmit) { // show a message to the user Message.Post(Severity.warning, "No signal", "We can't send the data"); // return data to the containers ReturnData(dataQueue); // do not transmit the data return; } // calculate total ec cost of transmission double total_amount = 0.0; foreach(ScienceData sd in dataQueue) total_amount += sd.dataAmount; double total_cost = total_amount * this.packetResourceCost; // if there is no EC to transmit the data // note: comparing against amount in previous simulation step if (total_cost > ResourceCache.Info(vessel, "ElectricCharge").amount) { // show a message to the user Message.Post(Severity.warning, Lib.BuildString("Not enough power, <b>", total_cost.ToString("F0"), " ElectricCharge</b> required"), "We can't send the data"); // return data to the containers ReturnData(dataQueue); // do not transmit the data return; } // transmit the data ModuleDataTransmitter transmitter = (ModuleDataTransmitter)this; transmitter.TransmitData(dataQueue); }
void indicator_supplies(Panel p, Vessel v, vessel_info vi) { List<string> tooltips = new List<string>(); uint max_severity = 0; if (vi.crew_count > 0) { var supplies = Profile.supplies.FindAll(k => k.resource != "ElectricCharge"); foreach(Supply supply in supplies) { resource_info res = ResourceCache.Info(v, supply.resource); if (res.capacity > double.Epsilon) { double depletion = res.Depletion(vi.crew_count); string deplete_str = depletion <= double.Epsilon ? ", depleted" : double.IsNaN(depletion) ? "" : Lib.BuildString(", deplete in <b>", Lib.HumanReadableDuration(depletion), "</b>"); tooltips.Add(Lib.BuildString(supply.resource, ": <b>", Lib.HumanReadablePerc(res.level), "</b>", deplete_str)); uint severity = res.level <= 0.005 ? 2u : res.level <= supply.low_threshold ? 1u : 0; max_severity = Math.Max(max_severity, severity); } } } Texture image = Icons.box_white; switch(max_severity) { case 0: image = Icons.box_white; break; case 1: image = Icons.box_yellow; break; case 2: image = Icons.box_red; break; } string tooltip = string.Join("\n", tooltips.ToArray()); p.icon(image, tooltip); }
public void FixedUpdate() { // in any scene: update the RMB ui Status = Lib.HumanReadableRadiationRate(Math.Abs(radiation) * intensity); // do nothing else in the editor if (HighLogic.LoadedSceneIsEditor) return; // if there is ec consumption if (ec_rate > double.Epsilon) { // get vessel info from the cache vessel_info vi = Cache.VesselInfo(vessel); // do nothing if vessel is invalid if (!vi.is_valid) return; // get resource cache resource_info ec = ResourceCache.Info(vessel, "ElectricCharge"); // get elapsed time double elapsed_s = Kerbalism.elapsed_s * vi.time_dilation; // if there is enough EC // note: comparing against amount in previous simulation step if (ec.amount > double.Epsilon) { // consume EC ec.Consume(ec_rate * intensity * elapsed_s); } // else disable it else { intensity = 0.0f; } } }
public void FixedUpdate() { // in flight if (Lib.IsFlight()) { // if we are transmitting using the stock system if (stream.transmitting()) { // get ec resource handler resource_info ec = ResourceCache.Info(vessel, "ElectricCharge"); // if we are still linked, and there is ec left if (CanTransmit() && ec.amount > double.Epsilon) { // compression factor // - used to avoid making the user wait too much for transmissions that // don't happen in background, while keeping transmission rates realistic const double compression = 16.0; // transmit using the data stream stream.update(DataRate * Kerbalism.elapsed_s * compression, vessel); // consume ec ec.Consume(DataResourceCost * Kerbalism.elapsed_s); } else { // abort transmission, return data to the vessel stream.abort(vessel); // inform the user ScreenMessages.PostScreenMessage("Transmission aborted", 5.0f, ScreenMessageStyle.UPPER_LEFT); } } } }
public void Execute(Vessel v, ScriptType type) { // do nothing if there is no EC left on the vessel Resource_Info ec = ResourceCache.Info(v, "ElectricCharge"); if (ec.amount <= double.Epsilon) { return; } // get the script if (scripts.TryGetValue(type, out Script script)) { // execute the script script.Execute(Boot(v)); // show message to the user // - unless the script is empty (can happen when being edited) if (script.states.Count > 0 && DB.Vessel(v).cfg_script) { Message.Post(Lib.BuildString("Script called on vessel <b>", v.vesselName, "</b>")); } } }
public void Update() { // in editor, merely update ui button label if (Lib.IsEditor()) { Events["Toggle"].guiName = Lib.StatusToggle(title, running ? "running" : "stopped"); } // if in flight, and the stock planet resource system is online if (Lib.IsFlight() && ResourceMap.Instance != null) { // sample abundance double abundance = SampleAbundance(); // determine if resource can be extracted issue = DetectIssue(abundance); // TODO: Change Review double ecLeft = ResourceCache.Info(part.vessel, "ElectricCharge").amount; // update ui Events["Toggle"].guiActive = deployed && ecLeft > double.Epsilon; Fields["Abundance"].guiActive = deployed && ecLeft > double.Epsilon; if (deployed) { string status = !running ? "stopped" : issue.Length == 0 ? "running" : Lib.BuildString("<color=yellow>", issue, "</color>"); Events["Toggle"].guiName = Lib.StatusToggle(title, status); Abundance = abundance > double.Epsilon ? Lib.HumanReadablePerc(abundance, "F2") : "none"; } } }
// return name of file being transmitted from vessel specified public static string Transmitting(Vessel v, bool linked) { // never transmitting if science system is disabled if (!Features.Science) { return(string.Empty); } // not transmitting if unlinked if (!linked) { return(string.Empty); } // not transmitting if there is no ec left if (ResourceCache.Info(v, "ElectricCharge").amount <= double.Epsilon) { return(string.Empty); } // get first file flagged for transmission, AND has a ts at least 5 seconds old or is > 0.001Mb in size foreach (var drive in DB.Vessel(v).drives.Values) { double now = Planetarium.GetUniversalTime(); foreach (var p in drive.files) { if (p.Value.send && (p.Value.ts + 3 < now || p.Value.size > 0.003)) { return(p.Key); } } } // no file flagged for transmission return(string.Empty); }
// return shielding factor in a vessel public static double shielding(Vessel v) { // the shielding factor is simply the level of shielding, scaled by the 'shielding efficiency' setting return ResourceCache.Info(v, "Shielding").level * Settings.ShieldingEfficiency; }
// return waste level in a vessel atmosphere public static double poisoning(Vessel v) { // the proportion of co2 in the atmosphere is simply the level of WasteAtmo return ResourceCache.Info(v, "WasteAtmosphere").level; }
// return normalized pressure in a vessel public static double pressure(Vessel v) { // the pressure is simply the atmosphere level return ResourceCache.Info(v, "Atmosphere").level; }
// return habitat surface in a vessel in m^2 public static double tot_surface(Vessel v) { // we use capacity: this mean that partially pressurized parts will still count, return ResourceCache.Info(v, "Shielding").capacity; }
// return habitat volume in a vessel in m^3 public static double tot_volume(Vessel v) { // we use capacity: this mean that partially pressurized parts will still count, return ResourceCache.Info(v, "Atmosphere").capacity; }
// trigger a random breakdown event public static void Breakdown(Vessel v, ProtoCrewMember c) { // constants const double res_penalty = 0.1; // proportion of food lost on 'depressed' and 'wrong_valve' // get info Rule supply = supply_rules.Count > 0 ? supply_rules[Lib.RandomInt(supply_rules.Count)] : null; resource_info res = supply != null?ResourceCache.Info(v, supply.resource_name) : null; // compile list of events with condition satisfied List <KerbalBreakdown> events = new List <KerbalBreakdown>(); events.Add(KerbalBreakdown.mumbling); //< do nothing, here so there is always something that can happen if (Lib.CrewCount(v) > 1) { events.Add(KerbalBreakdown.argument); //< do nothing, add some variation to messages } if (Lib.HasData(v)) { events.Add(KerbalBreakdown.fat_finger); } if (Reliability.CanMalfunction(v)) { events.Add(KerbalBreakdown.rage); } if (supply != null && res.amount > double.Epsilon) { events.Add(KerbalBreakdown.depressed); events.Add(KerbalBreakdown.wrong_valve); } // choose a breakdown event KerbalBreakdown breakdown = events[Lib.RandomInt(events.Count)]; // generate message string text = ""; string subtext = ""; switch (breakdown) { case KerbalBreakdown.mumbling: text = "$ON_VESSEL$KERBAL has been in space for too long"; subtext = "Mumbling incoherently"; break; case KerbalBreakdown.argument: text = "$ON_VESSEL$KERBAL had an argument with the rest of the crew"; subtext = "Morale is degenerating at an alarming rate"; break; case KerbalBreakdown.fat_finger: text = "$ON_VESSEL$KERBAL is pressing buttons at random on the control panel"; subtext = "Science data has been lost"; break; case KerbalBreakdown.rage: text = "$ON_VESSEL$KERBAL is possessed by a blind rage"; subtext = "A component has been damaged"; break; case KerbalBreakdown.depressed: text = "$ON_VESSEL$KERBAL is not respecting the rationing guidelines"; subtext = supply.resource_name + " has been lost"; break; case KerbalBreakdown.wrong_valve: text = "$ON_VESSEL$KERBAL opened the wrong valve"; subtext = supply.resource_name + " has been lost"; break; } // post message first so this one is shown before malfunction message Message.Post(Severity.breakdown, Lib.ExpandMsg(text, v, c), subtext); // trigger the event switch (breakdown) { case KerbalBreakdown.mumbling: break; // do nothing case KerbalBreakdown.argument: break; // do nothing case KerbalBreakdown.fat_finger: Lib.RemoveData(v); break; case KerbalBreakdown.rage: Reliability.CauseMalfunction(v); break; case KerbalBreakdown.depressed: case KerbalBreakdown.wrong_valve: res.Consume(res.amount * res_penalty); break; } // remove reputation if (HighLogic.CurrentGame.Mode == Game.Modes.CAREER) { Reputation.Instance.AddReputation(-Settings.BreakdownReputationPenalty, TransactionReasons.Any); } }
void toEVA(GameEvents.FromToAction <Part, Part> data) { // use Hydrazine instead of MonoPropellant if RealFuel is installed string monoprop_name = detected_mods.RealFuels ? "Hydrazine" : "MonoPropellant"; // determine if inside breathable atmosphere // note: the user can force the helmet + oxygen by pressing shift when going on eva bool breathable = Sim.Breathable(data.from.vessel) && !(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)); // get total crew in the origin vessel double tot_crew = (double)data.from.vessel.GetVesselCrew().Count + 1.0; // EVA vessels start with 5 units of eva fuel, remove them data.to.RequestResource("EVA Propellant", 5.0); // determine how much MonoPropellant to get // note: never more that the 'share' of this kerbal double monoprop = Math.Min(ResourceCache.Info(data.from.vessel, monoprop_name).amount / tot_crew, Settings.MonoPropellantOnEVA); // get monoprop from the vessel monoprop = data.from.RequestResource(monoprop_name, monoprop); // transfer monoprop to the EVA kerbal data.to.RequestResource("EVA Propellant", -monoprop); // show warning if there isn't monoprop in the eva suit if (monoprop <= double.Epsilon && !Lib.Landed(data.from.vessel)) { Message.Post(Severity.danger, Lib.BuildString("There isn't any <b>", monoprop_name, "</b> in the EVA suit", "Don't let the ladder go!")); } // manage resources from rules foreach (Rule r in rules) { if (r.resource_name.Length == 0 || r.on_eva <= double.Epsilon) { continue; } // determine amount to take, never more that his own share double amount = Math.Min(ResourceCache.Info(data.from.vessel, r.resource_name).amount / tot_crew, r.on_eva); // deal with breathable modifier if (breathable && r.modifier.Contains("breathable")) { continue; } // remove resource from the vessel amount = data.from.RequestResource(r.resource_name, amount); // create new resource in the eva kerbal Lib.SetupResource(data.to, r.resource_name, amount, r.on_eva); } // get KerbalEVA KerbalEVA kerbal = data.to.FindModuleImplementing <KerbalEVA>(); // turn off headlamp light, to avoid stock bug that show the light for a split second when going on eva EVA.SetHeadlamp(kerbal, false); EVA.SetFlares(kerbal, false); // remove the helmet if inside breathable atmosphere // note: done in EVA::FixedUpdate(), but also done here avoid 'popping' of the helmet when going on eva EVA.SetHelmet(kerbal, !breathable); // remember if the kerbal has an helmet EVA.KerbalData(data.to.vessel).has_helmet = !breathable; // execute script on vessel computer if (DB.Ready()) { DB.VesselData(data.from.vessel.id).computer.execute("run", "auto/eva_out", string.Empty, data.from.vessel); } // mute messages for a couple seconds to avoid warning messages from the vessel resource amounts Message.MuteInternal(); base.StartCoroutine(CallbackUtil.DelayedCallback(2.0f, Message.UnmuteInternal)); // if vessel info is open, switch to the eva kerbal // note: for a single tick, the EVA vessel is not valid (sun_dist is zero) // this make IsVessel() return false, that in turn close the vessel info instantly // for this reason, we wait a small amount of time before switching the info window if (Info.IsOpen()) { Info.Open(data.to.vessel); } }
public void FixedUpdate() { // do nothing in the editor if (Lib.IsEditor()) { return; } // if enabled if (running) { // if a researcher is not required, or the researcher is present if (!researcher_cs || researcher_cs.check(part.protoModuleCrew)) { // get next sample to analyze string sample_filename = next_sample(vessel); // if there is a sample to analyze if (sample_filename.Length > 0) { // consume EC resource_info ec = ResourceCache.Info(vessel, "ElectricCharge"); ec.Consume(ec_rate * Kerbalism.elapsed_s); // if there was ec // - comparing against amount in previous simulation step if (ec.amount > double.Epsilon) { // analyze the sample analyze(vessel, sample_filename, analysis_rate * Kerbalism.elapsed_s); // update status status = Science.experiment(sample_filename).name; } // if there was no ec else { // update status status = "<color=yellow>no electric charge</color>"; } } // if there is no sample to analyze else { // update status status = "no samples to analyze"; } } // if a researcher is required, but missing else { // update status status = Lib.BuildString("<color=yellow>", researcher_cs.warning(), "</color>"); } } // if disabled else { // update status status = "disabled"; } }
public void FixedUpdate() { // basic sanity checks if (Lib.IsEditor()) { return; } if (!Cache.VesselInfo(vessel).is_valid) { return; } if (next_check > Planetarium.GetUniversalTime()) { return; } // get ec handler Resource_info ec = ResourceCache.Info(vessel, "ElectricCharge"); shrouded = part.ShieldedFromAirstream; issue = TestForIssues(vessel, ec, this, privateHdId, broken, remainingSampleMass, didPrepare, shrouded, last_subject_id); if (string.IsNullOrEmpty(issue)) { issue = TestForResources(vessel, resourceDefs, Kerbalism.elapsed_s, ResourceCache.Get(vessel)); } scienceValue = Science.Value(last_subject_id, 0); state = GetState(scienceValue, issue, recording, forcedRun); if (!string.IsNullOrEmpty(issue)) { next_check = Planetarium.GetUniversalTime() + Math.Max(3, Kerbalism.elapsed_s * 3); return; } var subject_id = Science.Generate_subject_id(experiment_id, vessel); if (last_subject_id != subject_id) { dataSampled = 0; forcedRun = false; } last_subject_id = subject_id; if (state != State.RUNNING) { return; } var exp = Science.Experiment(experiment_id); if (dataSampled >= exp.max_amount) { return; } // if experiment is active and there are no issues DoRecord(ec, subject_id); }
public void Update() { // update RMB ui Events["Toggle"].guiName = deployed ? Localizer.Format("#KERBALISM_Generic_RETRACT") : Localizer.Format("#KERBALISM_Generic_DEPLOY"); Events["Toggle"].active = (deploy.Length > 0) && (part.FindModuleImplementing<Habitat>() == null) && !deploy_anim.Playing() && !waitRotation && ResourceCache.Info(vessel, "ElectricCharge").amount > ec_rate; // in flight if (Lib.IsFlight()) { // if deployed if (deployed) { // if there is no ec if (ResourceCache.Info(vessel, "ElectricCharge").amount < 0.01) { // pause rotate animation // - safe to pause multiple times Set_rotation(false); } // if there is enough ec instead and is not deploying else if (Should_start_rotation()) { // resume rotate animation // - safe to resume multiple times Set_rotation(true); } } // stop loop animation if exist and we are retracting else { // Call transform.stop() if it is rotating and the Stop method wasn't called. Set_rotation(false); } // When is not rotating if (waitRotation) { if (rotateIsTransform && !rotate_transf.IsRotating()) { // start retract animation in the correct direction, when is not rotating if (animBackwards) deploy_anim.Play(deployed, false); else deploy_anim.Play(!deployed, false); waitRotation = false; } else if (!rotateIsTransform && !rotate_anim.Playing()) { if (animBackwards) deploy_anim.Play(deployed, false); else deploy_anim.Play(!deployed, false); waitRotation = false; } } if (rotateIsTransform && rotate_transf != null) rotate_transf.DoSpin(); } }
public void Execute(Vessel v, vessel_info vi, vessel_resources resources, double elapsed_s) { // store list of crew to kill List <ProtoCrewMember> deferred_kills = new List <ProtoCrewMember>(); // get input resource handler resource_info res = input.Length > 0 ? resources.Info(v, input) : null; // determine message variant uint variant = vi.temperature < Settings.SurvivalTemperature ? 0 : 1u; // get product of all environment modifiers double k = Modifiers.evaluate(v, vi, resources, modifiers); // for each crew foreach (ProtoCrewMember c in Lib.CrewList(v)) { // get kerbal data KerbalData kd = DB.Kerbal(c.name); // skip rescue kerbals if (kd.rescue) { continue; } // skip disabled kerbals if (kd.disabled) { continue; } // get kerbal property data from db RuleData rd = kd.Rule(name); // if continuous double step; if (interval <= double.Epsilon) { // influence consumption by elapsed time step = elapsed_s; } // if interval-based else { // accumulate time rd.time_since += elapsed_s; // determine number of steps step = Math.Floor(rd.time_since / interval); // consume time rd.time_since -= step * interval; // remember if a meal is consumed/produced in this simulation step res.meal_happened |= step > 0.99; if (output.Length > 0) { ResourceCache.Info(v, output).meal_happened |= step > 0.99; } } // if continuous, or if one or more intervals elapsed if (step > double.Epsilon) { // if there is a resource specified if (res != null && rate > double.Epsilon) { // determine amount of resource to consume double required = rate // consumption rate * k // product of environment modifiers * step; // seconds elapsed or number of steps // if there is no output if (output.Length == 0) { // simply consume (that is faster) res.Consume(required); } // if there is an output and output_only is false else if (!output_only) { // transform input into output resource // - rules always dump excess overboard (because it is waste) resource_recipe recipe = new resource_recipe(); recipe.Input(input, required); recipe.Output(output, required * ratio, true); resources.Transform(recipe); } // if output_only then do not consume input resource else { // simply produce (that is faster) resources.Produce(v, output, required); } } // degenerate: // - if the environment modifier is not telling to reset (by being zero) // - if the input threshold is reached if used // - if this rule is resource-less, or if there was not enough resource in the vessel if (input_threshold >= double.Epsilon) { if (res.amount >= double.Epsilon && res.capacity >= double.Epsilon) { trigger = res.amount / res.capacity >= input_threshold; } else { trigger = false; } } else { trigger = input.Length == 0 || res.amount <= double.Epsilon; } if (k > 0.0 && trigger) { rd.problem += degeneration // degeneration rate per-second or per-interval * k // product of environment modifiers * step // seconds elapsed or by number of steps * Variance(c, variance); // kerbal-specific variance } // else slowly recover else { rd.problem *= 1.0 / (1.0 + Math.Max(interval, 1.0) * step * 0.002); rd.problem = Math.Max(rd.problem, 0.0); } } // kill kerbal if necessary if (rd.problem >= fatal_threshold) { if (fatal_message.Length > 0) { Message.Post(breakdown ? Severity.breakdown : Severity.fatality, Lib.ExpandMsg(fatal_message, v, c, variant)); } if (breakdown) { // trigger breakdown event Misc.Breakdown(v, c); // move back between warning and danger level rd.problem = (warning_threshold + danger_threshold) * 0.5; // make sure next danger messagen is shown rd.message = 1; } else { deferred_kills.Add(c); } } // show messages else if (rd.problem >= danger_threshold && rd.message < 2) { if (danger_message.Length > 0) { Message.Post(Severity.danger, Lib.ExpandMsg(danger_message, v, c, variant)); } rd.message = 2; } else if (rd.problem >= warning_threshold && rd.message < 1) { if (warning_message.Length > 0) { Message.Post(Severity.warning, Lib.ExpandMsg(warning_message, v, c, variant)); } rd.message = 1; } else if (rd.problem < warning_threshold && rd.message > 0) { if (relax_message.Length > 0) { Message.Post(Severity.relax, Lib.ExpandMsg(relax_message, v, c, variant)); } rd.message = 0; } } // execute the deferred kills foreach (ProtoCrewMember c in deferred_kills) { Misc.Kill(v, c); } }
public Comforts(Vessel v, bool env_firm_ground, bool env_not_alone, bool env_call_home) { // environment factors firm_ground = env_firm_ground; not_alone = env_not_alone; call_home = env_call_home; // if loaded if (v.loaded) { // scan parts for comfort foreach (Comfort c in Lib.FindModules <Comfort>(v)) { switch (c.bonus) { case "firm-ground": firm_ground = true; break; case "not-alone": not_alone = true; break; case "call-home": call_home = true; break; case "exercise": exercise = true; break; case "panorama": panorama = true; break; case "plants": plants = true; break; } } // scan parts for gravity ring if (ResourceCache.Info(v, "ElectricCharge").amount >= 0.01) { firm_ground |= Lib.HasModule <GravityRing>(v, k => k.deployed); } } // if not loaded else { // scan parts for comfort foreach (ProtoPartModuleSnapshot m in Lib.FindModules(v.protoVessel, "Comfort")) { switch (Lib.Proto.GetString(m, "bonus")) { case "firm-ground": firm_ground = true; break; case "not-alone": not_alone = true; break; case "call-home": call_home = true; break; case "exercise": exercise = true; break; case "panorama": panorama = true; break; case "plants": plants = true; break; } } // scan parts for gravity ring if (ResourceCache.Info(v, "ElectricCharge").amount >= 0.01) { firm_ground |= Lib.HasModule(v.protoVessel, "GravityRing", k => Lib.Proto.GetBool(k, "deployed")); } } // calculate factor factor = 0.1; if (firm_ground) { factor += PreferencesComfort.Instance.firmGround; } if (not_alone) { factor += PreferencesComfort.Instance.notAlone; } if (call_home) { factor += PreferencesComfort.Instance.callHome; } if (exercise) { factor += PreferencesComfort.Instance.exercise; } if (panorama) { factor += PreferencesComfort.Instance.panorama; } if (plants) { factor += PreferencesComfort.Instance.plants; } factor = Lib.Clamp(factor, 0.1, 1.0); }