// return true if the vessel is a rescue mission public static bool IsRescueMission(Vessel v) { // if at least one of the crew is flagged as rescue, consider it a rescue mission foreach (var c in Lib.CrewList(v)) { if (DB.Kerbal(c.name).rescue) { return(true); } } // not a rescue mission return(false); }
// inject instant radiation dose to the specified kerbal (can use negative amounts) public static void InjectRadiation(string k_name, double amount) { if (!DB.ContainsKerbal(k_name)) { return; } KerbalData kd = DB.Kerbal(k_name); foreach (Rule rule in Profile.rules) { if (rule.modifiers.Contains("radiation")) { RuleData rd = kd.rules[rule.name]; rd.problem = Math.Max(rd.problem + amount, 0.0); } } }
internal void RemovePatient(string patientName) { if (!patientList.Contains(patientName)) { return; } patientList.Remove(patientName); KerbalData kd = DB.Kerbal(patientName); string key = resource + ","; int p = kd.sickbay.IndexOf(key, 0, StringComparison.Ordinal); if (p >= 0) { kd.sickbay = kd.sickbay.Remove(p, key.Length); } patients = string.Join(",", patientList.ToArray()); if (running) { running = patientList.Count > 0 && (slots > 0 || cureEverybody); } }
// return true if the vessel is a rescue mission public static bool IsRescueMission(Vessel v) { // avoid corner-case situation on the first update : rescue vessel handling code is called // after the VesselData creation, causing Vesseldata evaluation to be delayed, causing anything // that rely on it to fail on its first update or in OnStart if (v.situation == Vessel.Situations.PRELAUNCH) { return(false); } // if at least one of the crew is flagged as rescue, consider it a rescue mission foreach (var c in Lib.CrewList(v)) { if (DB.Kerbal(c.name).rescue) { return(true); } } // not a rescue mission return(false); }
void Problem_kerbals(List <ProtoCrewMember> crew, ref List <Texture2D> icons, ref List <string> tooltips) { UInt32 health_severity = 0; UInt32 stress_severity = 0; foreach (ProtoCrewMember c in crew) { // get kerbal data KerbalData kd = DB.Kerbal(c.name); // skip disabled kerbals if (kd.disabled) { continue; } foreach (Rule r in Profile.rules) { RuleData rd = kd.Rule(r.name); if (rd.problem > r.danger_threshold) { if (!r.breakdown) { health_severity = Math.Max(health_severity, 2); } else { stress_severity = Math.Max(stress_severity, 2); } tooltips.Add(Lib.BuildString(c.name, ": <b>", r.name, "</b>")); } else if (rd.problem > r.warning_threshold) { if (!r.breakdown) { health_severity = Math.Max(health_severity, 1); } else { stress_severity = Math.Max(stress_severity, 1); } tooltips.Add(Lib.BuildString(c.name, ": <b>", r.name, "</b>")); } } } if (health_severity == 1) { icons.Add(Icons.health_yellow); } else if (health_severity == 2) { icons.Add(Icons.health_red); } if (stress_severity == 1) { icons.Add(Icons.brain_yellow); } else if (stress_severity == 2) { icons.Add(Icons.brain_red); } }
// return true if the vessel is a kerbal eva, and is flagged as dead public static bool IsDead(Vessel v) { if (!v.isEVA) return false; return DB.Kerbal(Lib.CrewList(v)[0].name).eva_dead; }
static void Render_crew(Panel p, List <ProtoCrewMember> crew) { // do nothing if there isn't a crew, or if there are no rules if (crew.Count == 0 || Profile.rules.Count == 0) { return; } // panel section p.AddSection(Local.TELEMETRY_VITALS); //"VITALS" // for each crew foreach (ProtoCrewMember kerbal in crew) { // get kerbal data from DB KerbalData kd = DB.Kerbal(kerbal.name); // analyze issues UInt32 health_severity = 0; UInt32 stress_severity = 0; // generate tooltip List <string> tooltips = new List <string>(); foreach (Rule r in Profile.rules) { // get rule data RuleData rd = kd.Rule(r.name); // add to the tooltip tooltips.Add(Lib.BuildString("<b>", Lib.HumanReadablePerc(rd.problem / r.fatal_threshold), "</b>\t", r.title)); // analyze issue if (rd.problem > r.danger_threshold) { if (!r.breakdown) { health_severity = Math.Max(health_severity, 2); } else { stress_severity = Math.Max(stress_severity, 2); } } else if (rd.problem > r.warning_threshold) { if (!r.breakdown) { health_severity = Math.Max(health_severity, 1); } else { stress_severity = Math.Max(stress_severity, 1); } } } string tooltip = Lib.BuildString("<align=left />", String.Join("\n", tooltips.ToArray())); // generate kerbal name string name = kerbal.name.ToLower().Replace(" kerman", string.Empty); // render selectable title p.AddContent(Lib.Ellipsis(name, Styles.ScaleStringLength(30)), kd.disabled ? Lib.Color(Local.TELEMETRY_HYBERNATED, Lib.Kolor.Cyan) : string.Empty); //"HYBERNATED" p.AddRightIcon(health_severity == 0 ? Textures.health_white : health_severity == 1 ? Textures.health_yellow : Textures.health_red, tooltip); p.AddRightIcon(stress_severity == 0 ? Textures.brain_white : stress_severity == 1 ? Textures.brain_yellow : Textures.brain_red, tooltip); } }
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); } }
// kill a kerbal // note: you can't kill a kerbal while iterating over vessel crew list, do it outside the loop public static void Kill(Vessel v, ProtoCrewMember c) { // if on pod if (!v.isEVA) { // forget kerbal data DB.kerbals.Remove(c.name); // if vessel is loaded if (v.loaded) { // find part Part part = null; foreach (Part p in v.parts) { if (p.protoModuleCrew.Find(k => k.name == c.name) != null) { part = p; break; } } // remove kerbal from part part.RemoveCrewmember(c); // and from vessel v.RemoveCrew(c); // then kill it c.Die(); } // if vessel is not loaded else { // find proto part ProtoPartSnapshot part = null; foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { if (p.HasCrew(c.name)) { part = p; break; } } // remove from part part.RemoveCrew(c.name); // and from vessel v.protoVessel.RemoveCrew(c); // flag as dead c.rosterStatus = ProtoCrewMember.RosterStatus.Dead; } } // else it must be an eva death else { // flag as eva death DB.Kerbal(c.name).eva_dead = true; // rename vessel v.vesselName = c.name + "'s body"; } // remove reputation if (HighLogic.CurrentGame.Mode == Game.Modes.CAREER) { Reputation.Instance.AddReputation(-Settings.DeathReputation, TransactionReasons.Any); } }
public void Execute(Vessel v, VesselData vd, VesselResources resources, double elapsed_s) { // store list of crew to kill List <ProtoCrewMember> deferred_kills = new List <ProtoCrewMember>(); // get input resource handler ResourceInfo res = input.Length > 0 ? resources.GetResource(v, input) : null; // determine message variant uint variant = vd.EnvTemperature < Settings.LifeSupportSurvivalTemperature ? 0 : 1u; // get product of all environment modifiers double k = Modifiers.Evaluate(v, vd, resources, modifiers); bool lifetime_enabled = PreferencesRadiation.Instance.lifetime; // 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); rd.lifetime = lifetime_enabled && lifetime; // influence consumption by elapsed time double step = elapsed_s; // if interval-based if (interval > 0.0) { // accumulate time rd.time_since += elapsed_s; // determine number of intervals that has passed (can be 2 or more if elapsed_s > interval * 2) step = Math.Floor(rd.time_since / interval); // consume time rd.time_since -= step * interval; } // if there is a resource specified if (res != null && rate > double.Epsilon) { // get rate including per-kerbal variance double resRate = rate // consumption rate * Variance(name, c, individuality) // kerbal-specific variance * k; // product of environment modifiers // determine amount of resource to consume double required = resRate * step; // seconds elapsed or interval amount // remember if a meal is consumed/produced in this simulation step if (interval > 0.0) { double ratePerStep = resRate / interval; res.UpdateIntervalRule(-required, -ratePerStep, name); if (output.Length > 0) { ResourceCache.GetResource(v, output).UpdateIntervalRule(required * ratio, ratePerStep * ratio, name); } } // if continuous, or if one or more intervals elapsed if (step > 0.0) { // if there is no output if (output.Length == 0) { // simply consume (that is faster) res.Consume(required, name); } // if there is an output else { // transform input into output resource // - rules always dump excess overboard (because it is waste) ResourceRecipe recipe = new ResourceRecipe(name); recipe.AddInput(input, required); recipe.AddOutput(output, required * ratio, true); resources.AddRecipe(recipe); } } } // if continuous, or if one or more intervals elapsed if (step > 0.0) { // degenerate: // - if the environment modifier is not telling to reset (by being zero) // - if this rule is resource-less, or if there was not enough resource in the vessel if (k > 0.0 && (input.Length == 0 || res.Amount <= double.Epsilon)) { rd.problem += degeneration // degeneration rate per-second or per-interval * k // product of environment modifiers * step // seconds elapsed or by number of steps * Variance(name, c, variance); // kerbal-specific variance } // else slowly recover else { rd.problem *= 1.0 / (1.0 + Math.Max(interval, 1.0) * step * 0.002); } } bool do_breakdown = false; if (breakdown) { // don't do breakdowns and don't show stress message if disabled if (!PreferencesComfort.Instance.stressBreakdowns) { return; } // stress level double breakdown_probability = rd.problem / warning_threshold; breakdown_probability = Lib.Clamp(breakdown_probability, 0.0, 1.0); // use the stupidity of a kerbal. // however, nobody is perfect - not even a kerbal with a stupidity of 0. breakdown_probability *= c.stupidity * 0.6 + 0.4; // apply the weekly error rate breakdown_probability *= PreferencesComfort.Instance.stressBreakdownRate; // now we have the probability for one failure per week, based on the // individual stupidity and stress level of the kerbal. breakdown_probability = (breakdown_probability * elapsed_s) / (Lib.DaysInYear * Lib.HoursInDay * 3600); if (breakdown_probability > Lib.RandomDouble()) { do_breakdown = true; // we're stressed out and just made a major mistake, this further increases the stress level... rd.problem += warning_threshold * 0.05; // add 5% of the warning treshold to current stress level } } // kill kerbal if necessary if (rd.problem >= fatal_threshold) { #if DEBUG || DEVBUILD Lib.Log("Rule " + name + " kills " + c.name + " at " + rd.problem + " " + degeneration + "/" + k + "/" + step + "/" + Variance(name, c, variance)); #endif if (fatal_message.Length > 0) { Message.Post(breakdown ? Severity.breakdown : Severity.fatality, Lib.ExpandMsg(fatal_message, v, c, variant)); } if (breakdown) { do_breakdown = true; // move back between warning and danger level rd.problem = (warning_threshold + danger_threshold) * 0.5; // make sure next danger message 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; } if (do_breakdown) { // trigger breakdown event Misc.Breakdown(v, c); } } // execute the deferred kills foreach (ProtoCrewMember c in deferred_kills) { Misc.Kill(v, c); } }
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 < PreferencesLifeSupport.Instance.survivalTemperature ? 0 : 1u; // get product of all environment modifiers double k = Modifiers.Evaluate(v, vi, resources, modifiers); bool lifetime_enabled = PreferencesBasic.Instance.lifetime; // 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); rd.lifetime = lifetime_enabled && lifetime; // 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 if (step > 0.99) { res.SetMealHappened(); } if (output.Length > 0 && step > 0.99) { ResourceCache.Info(v, output).SetMealHappened(); } } // if continuous, or if one or more intervals elapsed if (step > double.Epsilon) { double r = rate * Variance(name, c, individuality); // kerbal-specific variance // if there is a resource specified if (res != null && r > double.Epsilon) { // determine amount of resource to consume double required = r // 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 monitor is false else if (!monitor) { // transform input into output resource // - rules always dump excess overboard (because it is waste) Resource_recipe recipe = new Resource_recipe((Part)null); // kerbals are not associated with a part recipe.Input(input, required); recipe.Output(output, required * ratio, true); resources.Transform(recipe); } // if monitor then do not consume input resource and only produce output if resource percentage + monitor_offset is < 100% else if ((res.amount / res.capacity) + monitor_offset < 1.0) { // simply produce (that is faster) resources.Produce(v, output, required * ratio); } } // 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) + monitor_offset >= 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(name, c, variance); // kerbal-specific variance } // else slowly recover else { rd.problem *= 1.0 / (1.0 + Math.Max(interval, 1.0) * step * 0.002); } } bool do_breakdown = false; if (breakdown && PreferencesBasic.Instance.stressBreakdowns) { // stress level double breakdown_probability = rd.problem / warning_threshold; breakdown_probability = Lib.Clamp(breakdown_probability, 0.0, 1.0); // use the stupidity of a kerbal. // however, nobody is perfect - not even a kerbal with a stupidity of 0. breakdown_probability *= c.stupidity * 0.6 + 0.4; // apply the weekly error rate breakdown_probability *= PreferencesBasic.Instance.stressBreakdownRate; // now we have the probability for one failure per week, based on the // individual stupidity and stress level of the kerbal. breakdown_probability = (breakdown_probability * elapsed_s) / (Lib.DaysInYear() * Lib.HoursInDay() * 3600); if (breakdown_probability > Lib.RandomDouble()) { do_breakdown = true; // we're stressed out and just made a major mistake, this further increases the stress level... rd.problem += warning_threshold * 0.05; // add 5% of the warning treshold to current stress level } } // 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) { do_breakdown = true; // move back between warning and danger level rd.problem = (warning_threshold + danger_threshold) * 0.5; // make sure next danger message 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; } if (do_breakdown) { // trigger breakdown event Misc.Breakdown(v, c); } } // execute the deferred kills foreach (ProtoCrewMember c in deferred_kills) { Misc.Kill(v, c); } }
/// <summary> /// Execute the recipe and record deferred consumption/production for inputs/ouputs. /// This need to be called multiple times until left <= 0.0 for complete execution of the recipe. /// return true if recipe execution is completed, false otherwise /// </summary> private bool ExecuteRecipeStep(Vessel v, VesselResources resources) { // determine worst input ratio // - pure input recipes can just underflow double worst_input = left; if (outputs.Count > 0) { for (int i = 0; i < inputs.Count; ++i) { Entry e = inputs[i]; ResourceInfo res = resources.GetResource(v, e.name); // handle combined inputs if (e.combined != null) { // is combined resource the primary if (e.combined != "") { Entry sec_e = inputs.Find(x => x.name.Contains(e.combined)); ResourceInfo sec = resources.GetResource(v, sec_e.name); double pri_worst = Lib.Clamp((res.Amount + res.Deferred) * e.inv_quantity, 0.0, worst_input); if (pri_worst > 0.0) { worst_input = pri_worst; } else { worst_input = Lib.Clamp((sec.Amount + sec.Deferred) * sec_e.inv_quantity, 0.0, worst_input); } } } else { worst_input = Lib.Clamp((res.Amount + res.Deferred) * e.inv_quantity, 0.0, worst_input); } } } // determine worst output ratio // - pure output recipes can just overflow double worst_output = left; if (inputs.Count > 0) { for (int i = 0; i < outputs.Count; ++i) { Entry e = outputs[i]; if (!e.dump) // ignore outputs that can dump overboard { ResourceInfo res = resources.GetResource(v, e.name); worst_output = Lib.Clamp((res.Capacity - (res.Amount + res.Deferred)) * e.inv_quantity, 0.0, worst_output); } } } // determine worst-io double worst_io = Math.Min(worst_input, worst_output); // consume inputs for (int i = 0; i < inputs.Count; ++i) { Entry e = inputs[i]; ResourceInfo res = resources.GetResource(v, e.name); // handle combined inputs if (e.combined != null) { // is combined resource the primary if (e.combined != "") { Entry sec_e = inputs.Find(x => x.name.Contains(e.combined)); ResourceInfo sec = resources.GetResource(v, sec_e.name); double need = (e.quantity * worst_io) + (sec_e.quantity * worst_io); // do we have enough primary to satisfy needs, if so don't consume secondary if (res.Amount + res.Deferred >= need) { resources.Consume(v, e.name, need, name); } // consume primary if any available and secondary else { need -= res.Amount + res.Deferred; res.Consume(res.Amount + res.Deferred, name); sec.Consume(need, name); } } } else { res.Consume(e.quantity * worst_io, name); } } // produce outputs for (int i = 0; i < outputs.Count; ++i) { Entry e = outputs[i]; ResourceInfo res = resources.GetResource(v, e.name); res.Produce(e.quantity * worst_io, name); } // produce cures for (int i = 0; i < cures.Count; ++i) { Entry entry = cures[i]; List <RuleData> curingRules = new List <RuleData>(); foreach (ProtoCrewMember crew in v.GetVesselCrew()) { KerbalData kd = DB.Kerbal(crew.name); if (kd.sickbay.IndexOf(entry.combined + ",", StringComparison.Ordinal) >= 0) { curingRules.Add(kd.Rule(entry.name)); } } foreach (RuleData rd in curingRules) { rd.problem -= entry.quantity * worst_io / curingRules.Count; rd.problem = Math.Max(rd.problem, 0); } } // update amount left to execute left -= worst_io; // the recipe was executed, at least partially return(worst_io > double.Epsilon); }