void render_crew(Vessel v, vessel_info vi, List<ProtoCrewMember> crew) { // get degenerative rules List<Rule> degen_rules = Kerbalism.rules.FindAll(k => k.degeneration > 0.0); // do nothing if there are no degenerative rules if (degen_rules.Count == 0) return; // do nothing if there isn't a crew if (crew.Count == 0) return; // select a kerbal ProtoCrewMember kerbal = crew[crew_index % crew.Count]; // render it kerbal_data kd = DB.KerbalData(kerbal.name); render_title(Lib.Epsilon(kerbal.name.ToUpper(), 20), ref crew_index, crew.Count); foreach(Rule r in degen_rules) { var kmon = DB.KmonData(kerbal.name, r.name); var bar = Lib.ProgressBar(20, kmon.problem, r.warning_threshold, r.danger_threshold, r.fatal_threshold, kd.disabled > 0 ? "cyan" : ""); render_content(r.name.AddSpacesOnCaps().ToLower(), bar); } render_content("specialization", kerbal.trait); if (Kerbalism.detected_mods.DeepFreeze) render_content("hibernated", kd.disabled > 0 ? "yes" : "no"); if (Kerbalism.detected_mods.CLS) render_content("inside", v.isEVA ? "EVA" : Lib.Epsilon(kd.space_name.Length == 0 ? v.vesselName : kd.space_name, 24)); render_space(); }
void problem_kerbals(List<ProtoCrewMember> crew, ref List<Texture> icons, ref List<string> tooltips) { UInt32 health_severity = 0; UInt32 stress_severity = 0; foreach(ProtoCrewMember c in crew) { // get kerbal data kerbal_data kd = DB.KerbalData(c.name); // skip disabled kerbals if (kd.disabled == 1) continue; foreach(Rule r in Kerbalism.rules) { kmon_data kmon = DB.KmonData(c.name, r.name); if (kmon.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, " (", r.name, ")")); } else if (kmon.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, " (", r.name, ")")); } } } if (health_severity == 1) icons.Add(icon_health_warning); else if (health_severity == 2) icons.Add(icon_health_danger); if (stress_severity == 1) icons.Add(icon_stress_warning); else if (stress_severity == 2) icons.Add(icon_stress_danger); }
// hook: InjectRadiation() public static void hook_InjectRadiation(string k_name, double amount) { if (!DB.Ready()) { return; } if (!DB.Kerbals().ContainsKey(k_name)) { return; } kerbal_data kd = DB.KerbalData(k_name); foreach (Rule r in Kerbalism.rules) { if (r.modifier.Contains("radiation")) { var kmon = DB.KmonData(k_name, r.name); kmon.problem = Math.Max(kmon.problem + amount, 0.0); } } }
public static void applyRules(Vessel v, vessel_info vi, vessel_data vd, vessel_resources resources, double elapsed_s) { // get crew List<ProtoCrewMember> crew = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew(); // get breathable modifier double breathable = vi.breathable ? 0.0 : 1.0; // get temp diff modifier double temp_diff = v.altitude < 2000.0 && v.mainBody == FlightGlobals.GetHomeBody() ? 0.0 : Sim.TempDiff(vi.temperature); // for each rule foreach(Rule r in Kerbalism.rules) { // get resource handler resource_info res = r.resource_name.Length > 0 ? resources.Info(v, r.resource_name) : null; // if a resource is specified if (res != null) { // get data from db vmon_data vmon = DB.VmonData(v.id, r.name); // message obey user config bool show_msg = (r.resource_name == "ElectricCharge" ? vd.cfg_ec > 0 : vd.cfg_supply > 0); // no messages with no capacity if (res.capacity > double.Epsilon) { // manned/probe message variant uint variant = crew.Count > 0 ? 0 : 1u; // manage messages if (res.level <= double.Epsilon && vmon.message < 2) { if (r.empty_message.Length > 0 && show_msg) Message.Post(Severity.danger, Lib.ExpandMsg(r.empty_message, v, null, variant)); vmon.message = 2; } else if (res.level < r.low_threshold && vmon.message < 1) { if (r.low_message.Length > 0 && show_msg) Message.Post(Severity.warning, Lib.ExpandMsg(r.low_message, v, null, variant)); vmon.message = 1; } else if (res.level > r.low_threshold && vmon.message > 0) { if (r.refill_message.Length > 0 && show_msg) Message.Post(Severity.relax, Lib.ExpandMsg(r.refill_message, v, null, variant)); vmon.message = 0; } } } // for each crew foreach(ProtoCrewMember c in crew) { // get kerbal data kerbal_data kd = DB.KerbalData(c.name); // skip resque kerbals if (kd.resque == 1) continue; // skip disabled kerbals if (kd.disabled == 1) continue; // get supply data from db kmon_data kmon = DB.KmonData(c.name, r.name); // get product of all environment modifiers double k = 1.0; foreach(string modifier in r.modifier) { switch(modifier) { case "breathable": k *= breathable; break; case "temperature": k *= temp_diff; break; case "radiation": k *= vi.radiation * (1.0 - kd.shielding); break; case "qol": k /= QualityOfLife.Bonus(kd.living_space, kd.entertainment, vi.landed, vi.link.linked, vi.crew_count == 1); break; } } // if continuous double step; if (r.interval <= double.Epsilon) { // influence consumption by elapsed time step = elapsed_s; } // if interval-based else { // accumulate time kmon.time_since += elapsed_s; // determine number of steps step = Math.Floor(kmon.time_since / r.interval); // consume time kmon.time_since -= step * r.interval; // remember if a meal is consumed in this simulation step res.meal_consumed |= step > 0.99; } // if continuous, or if one or more intervals elapsed if (step > double.Epsilon) { // indicate if we must degenerate bool must_degenerate = true; // if there is a resource specified, and this isn't just a monitoring rule if (res != null && r.rate > double.Epsilon) { // determine amount of resource to consume double required = r.rate // rate per-second or per interval * k // product of environment modifiers * step; // seconds elapsed or number of steps // if there is no waste if (r.waste_name.Length == 0) { // simply consume (that is faster) res.Consume(required); } // if there is waste else { // transform resource into waste resource_recipe recipe = new resource_recipe(resource_recipe.rule_priority); recipe.Input(r.resource_name, required); recipe.Output(r.waste_name, required * r.waste_ratio); resources.Transform(recipe); } // reset degeneration when consumed, or when not required at all // note: evaluating amount from previous simulation step if (required <= double.Epsilon || res.amount > double.Epsilon) { // slowly recover instead of instant reset kmon.problem *= 1.0 / (1.0 + Math.Max(r.interval, 1.0) * step * 0.002); kmon.problem = Math.Max(kmon.problem, 0.0); // do not degenerate must_degenerate = false; } } // degenerate if this rule is resource-less, or if there was not enough resource in the vessel if (must_degenerate) { kmon.problem += r.degeneration // degeneration rate per-second or per-interval * k // product of environment modifiers * step // seconds elapsed or by number of steps * Variance(c, r.variance); // kerbal-specific variance } // determine message variant uint variant = vi.temperature < Settings.SurvivalTemperature ? 0 : 1u; // kill kerbal if necessary if (kmon.problem >= r.fatal_threshold) { if (r.fatal_message.Length > 0) Message.Post(r.breakdown ? Severity.breakdown : Severity.fatality, Lib.ExpandMsg(r.fatal_message, v, c, variant)); if (r.breakdown) { Kerbalism.Breakdown(v, c); kmon.problem = r.danger_threshold * 1.01; //< move back to danger threshold } else { Kerbalism.Kill(v, c); } } // show messages else if (kmon.problem >= r.danger_threshold && kmon.message < 2) { if (r.danger_message.Length > 0) Message.Post(Severity.danger, Lib.ExpandMsg(r.danger_message, v, c, variant)); kmon.message = 2; } else if (kmon.problem >= r.warning_threshold && kmon.message < 1) { if (r.warning_message.Length > 0) Message.Post(Severity.warning, Lib.ExpandMsg(r.warning_message, v, c, variant)); kmon.message = 1; } else if (kmon.problem < r.warning_threshold && kmon.message > 0) { if (r.relax_message.Length > 0) Message.Post(Severity.relax, Lib.ExpandMsg(r.relax_message, v, c, variant)); kmon.message = 0; } } } } }
void render_info() { // find vessel Vessel v = FlightGlobals.Vessels.Find(k => k.id == vessel_id); // forget vessel if it doesn't exist anymore, or if its a dead eva kerbal if (v == null || EVA.IsDead(v)) { vessel_id = Guid.Empty; return; } // get info from the cache vessel_info vi = Cache.VesselInfo(v); render_title("ENVIRONMENT"); render_content("Temperature:\t", Lib.HumanReadableTemp(vi.temperature)); render_content("Radiation:\t", Lib.HumanReadableRadiationRate(vi.env_radiation)); render_content("Atmosphere:\t", v.mainBody.atmosphere ? " yes" + (vi.breathable ? " <i>(breathable)</i>" : "") : "no"); render_space(); // render supplies if (Kerbalism.supply_rules.Count > 0 || Kerbalism.ec_rule != null) { render_title("SUPPLIES"); if (Kerbalism.ec_rule != null) { var vmon = vi.vmon[Kerbalism.ec_rule.name]; render_content(fix_title("Battery:"), vmon.level > double.Epsilon ? Lib.HumanReadableDuration(vmon.depletion) : "none"); } if (Lib.CrewCapacity(v) > 0) { foreach(Rule r in Kerbalism.supply_rules) { var vmon = vi.vmon[r.name]; render_content(fix_title(r.resource_name + ":"), vmon.level > double.Epsilon ? Lib.HumanReadableDuration(vmon.depletion) : "none"); } } render_space(); } // get crew var crew = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew(); // do not render internal spaces info for eva vessels if (!v.isEVA) { // collect set of spaces Dictionary<string, space_details> spaces = new Dictionary<string, space_details>(); foreach(var c in crew) { kerbal_data kd = DB.KerbalData(c.name); if (!spaces.ContainsKey(kd.space_name)) { space_details sd = new space_details(); sd.living_space = kd.living_space; sd.entertainment = kd.entertainment; sd.shielding = kd.shielding; spaces.Add(kd.space_name, sd); } ++(spaces[kd.space_name].crew_count); } // for each space foreach(var space in spaces) { string space_name = space.Key; space_details det = space.Value; string radiation_txt = vi.env_radiation > double.Epsilon ? " <i>(" + Lib.HumanReadableRadiationRate(vi.env_radiation * (1.0 - det.shielding)) + ")</i>" : ""; render_title(space_name.Length > 0 ? space_name.ToUpper() : v.isEVA ? "EVA" : "VESSEL"); render_content("Living space:\t", QualityOfLife.LivingSpaceToString(det.living_space)); render_content("Entertainment:\t", QualityOfLife.EntertainmentToString(det.entertainment)); render_content("Shielding:\t", Radiation.ShieldingToString(det.shielding) + radiation_txt); render_space(); } } // for each kerbal if (Kerbalism.rules.Count > 0) { foreach(var c in crew) { kerbal_data kd = DB.KerbalData(c.name); render_title(c.name.ToUpper()); foreach(var q in Kerbalism.rules) { Rule r = q.Value; if (r.degeneration > double.Epsilon) { var kmon = DB.KmonData(c.name, r.name); var bar = Lib.ProgressBar(23, kmon.problem, r.warning_threshold, r.danger_threshold, r.fatal_threshold, kd.disabled > 0 ? "cyan" : ""); render_content(fix_title(r.name + ":"), bar); } } if (kd.space_name.Length > 0 && !v.isEVA) render_content("Inside:\t\t", kd.space_name); if (kd.disabled > 0) render_content("Hibernated:\t", "yes"); render_space(); } } // for each greenhouse var greenhouses = Greenhouse.GetGreenhouses(v); foreach(var greenhouse in greenhouses) { render_title("GREENHOUSE"); render_content("Lighting:\t\t", (greenhouse.lighting * 100.0).ToString("F0") + "%"); render_content("Growth:\t\t", (greenhouse.growth * 100.0).ToString("F0") + "%"); render_content("Harvest:\t\t", Lib.HumanReadableDuration(greenhouse.growing > double.Epsilon ? 1.0 / greenhouse.growing : 0.0)); render_space(); } }