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