// return true if the vessel is a resque mission public static bool IsResqueMission(Vessel v) { // if db isn't ready, assume a resque mission if (!DB.Ready()) { return(true); } // avoid re-creating dead eva kerbals in the db // note: no extra cost if vessel is not eva if (EVA.IsDead(v)) { return(true); } // if at least one of the crew is flagged as resque, consider it a resque mission var crew_members = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew(); foreach (var c in crew_members) { kerbal_data kd = DB.KerbalData(c.name); if (kd.resque == 1) { return(true); } } // not a resque mission return(false); }
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); }
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; // health if (kd.starved > Settings.StarvedDangerThreshold) { health_severity = Math.Max(health_severity, 2); tooltips.Add(c.name + " is starving"); } else if (kd.starved > Settings.StarvedWarningThreshold) { health_severity = Math.Max(health_severity, 1); tooltips.Add(c.name + " is hungry"); } if (kd.deprived > Settings.DeprivedDangerThreshold) { health_severity = Math.Max(health_severity, 2); tooltips.Add(c.name + " is suffocating"); } else if (kd.deprived > Settings.DeprivedWarningThreshold) { health_severity = Math.Max(health_severity, 1); tooltips.Add(c.name + " is gasping"); } if (kd.temperature < -Settings.TemperatureDangerThreshold) { health_severity = Math.Max(health_severity, 2); tooltips.Add(c.name + " is freezing"); } else if (kd.temperature < -Settings.TemperatureWarningThreshold) { health_severity = Math.Max(health_severity, 1); tooltips.Add(c.name + " feels cold"); } else if (kd.temperature > Settings.TemperatureDangerThreshold) { health_severity = Math.Max(health_severity, 2); tooltips.Add(c.name + " is burning"); } else if (kd.temperature > Settings.TemperatureWarningThreshold) { health_severity = Math.Max(health_severity, 1); tooltips.Add(c.name + " feels hot"); } // radiation if (kd.radiation > Settings.RadiationDangerThreshold) { health_severity = Math.Max(health_severity, 2); tooltips.Add(c.name + " exposed to extreme radiation"); } else if (kd.radiation > Settings.RadiationWarningThreshold) { health_severity = Math.Max(health_severity, 1); tooltips.Add(c.name + " exposed to intense radiation"); } // stress if (kd.stressed > Settings.StressedDangerThreshold) { stress_severity = Math.Max(stress_severity, 2); tooltips.Add(c.name + " mind is breaking"); } else if (kd.stressed > Settings.StressedWarningThreshold) { stress_severity = Math.Max(stress_severity, 1); tooltips.Add(c.name + " is stressed"); } } 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); }
// return quality-of-life bonus public static double Bonus(Vessel v, string k_name) { // get QoL data from db kerbal_data kd = DB.KerbalData(k_name); // calculate quality of life bonus return Bonus(kd.living_space, kd.entertainment, Lib.Landed(v), Signal.Link(v).linked, Lib.CrewCount(v) < 2u); }
// 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); kd.radiation = Math.Max(kd.radiation + amount, 0.0); }
public override string info() { kerbal_data kd = DB.KerbalData(crew.name); int worst = 0; foreach(Rule r in Kerbalism.rules) { double problem = kd.kmon[r.name].problem; if (problem > r.danger_threshold) worst = Math.Max(worst, 2); else if (problem > r.warning_threshold) worst = Math.Max(worst, 1); } switch(worst) { case 2: return "<color=red>danger</color>"; case 1: return "<color=yellow>warning</color>"; default: return "<color=green>healthy</color>"; } }
public static void update(Vessel v) { // get kerbal data from db kerbal_data kd = KerbalData(v); // get KerbalEVA module KerbalEVA kerbal = v.FindPartModulesImplementing<KerbalEVA>()[0]; // show/hide helmet, play nice with KIS if (!Kerbalism.detected_mods.KIS) { SetHelmet(kerbal, kd.has_helmet); } // synchronize has_helmet state with KIS (for the headlights) else { kd.has_helmet = HasHelmet(kerbal); } // get resource handler resource_info ec = ResourceCache.Info(v, "ElectricCharge"); // consume EC for the headlamp if (kd.has_helmet && kerbal.lampOn) ec.Consume(Settings.HeadlightCost * Kerbalism.elapsed_s); //< ignore time dilation // force the headlamp lights on/off depending on ec amount left and if it has an helmet // synchronize helmet flares with headlamp state // support case when there is no ec rule (or no profile at all) bool b = kd.has_helmet && kerbal.lampOn && (ec.amount > double.Epsilon || ec.capacity <= double.Epsilon); SetHeadlamp(kerbal, b); SetFlares(kerbal, b); // if dead if (kd.eva_dead) { // enforce freezed state SetFreezed(kerbal); // remove plant flag action kerbal.flagItems = 0; // remove experiment actions (game engine keeps readding them) RemoveExperiments(kerbal); } }
void updateConnectedSpaces(Vessel v, vessel_info vi) { // get CLS handler var cls = CLS.get(); // calculate whole-space if (cls == null) { double living_space = QualityOfLife.LivingSpace((uint)vi.crew_count, (uint)vi.crew_capacity); double entertainment = QualityOfLife.Entertainment(v); double shielding = Radiation.Shielding(v); foreach (var c in v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew()) { kerbal_data kd = DB.KerbalData(c.name); kd.living_space = living_space; kd.entertainment = entertainment; kd.shielding = shielding; kd.space_name = ""; } } // calculate connected-space // note: avoid problem at scene change else if (cls.Vessel != null && cls.Vessel.Spaces.Count > 0) { // calculate internal spaces foreach (var space in cls.Vessel.Spaces) { double living_space = QualityOfLife.LivingSpace(space); double entertainment = QualityOfLife.Entertainment(v, space); double shielding = Radiation.Shielding(space); foreach (var c in space.Crew) { kerbal_data kd = DB.KerbalData(c.Kerbal.name); kd.living_space = living_space; kd.entertainment = entertainment; kd.shielding = shielding; kd.space_name = space.Name; } } } }
// 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); } } }
void render_internal_space(Vessel v, vessel_info vi, List<ProtoCrewMember> crew) { // do not render internal space info for eva vessels if (v.isEVA) return; // if there is no crew, no space will be found, so do nothing in that case if (crew.Count == 0) return; // collect set of spaces // note: this is guaranteed to get at least a space (because there is at least one crew member) List<space_details> spaces = new List<space_details>(); foreach(var c in crew) { kerbal_data kd = DB.KerbalData(c.name); space_details sd = spaces.Find(k => k.name == kd.space_name); if (sd == null) { sd = new space_details(); sd.name = kd.space_name; sd.living_space = kd.living_space; sd.entertainment = kd.entertainment; sd.shielding = kd.shielding; spaces.Add(sd); } ++sd.crew_count; } // select a space space_details space = spaces[space_index % spaces.Count]; // render it string radiation_txt = vi.radiation > double.Epsilon ? Lib.BuildString(" <i>(", Lib.HumanReadableRadiationRate(vi.radiation * (1.0 - space.shielding)), ")</i>") : ""; render_title(space.name.Length > 0 && spaces.Count > 1 ? Lib.Epsilon(space.name.ToUpper(), 20) : "VESSEL", ref space_index, spaces.Count); render_content("living space", QualityOfLife.LivingSpaceToString(space.living_space)); render_content("entertainment", QualityOfLife.EntertainmentToString(space.entertainment)); render_content("shielding", Lib.BuildString(Radiation.ShieldingToString(space.shielding), radiation_txt)); render_space(); }
void manageResqueMission(Vessel v) { // skip eva dead kerbals // rationale: getting the kerbal data will create it again, leading to spurious resque mission detection if (EVA.IsDead(v)) { return; } // deal with resque missions foreach (ProtoCrewMember c in v.GetVesselCrew()) { // get kerbal data kerbal_data kd = DB.KerbalData(c.name); // flag the kerbal as not resque at prelaunch if (v.situation == Vessel.Situations.PRELAUNCH) { kd.resque = 0; } // if the kerbal belong to a resque mission if (kd.resque == 1) { // give the vessel some supply Lib.RequestResource(v, v.isEVA ? "EVA Propellant" : "MonoPropellant", -Settings.ResqueMonoPropellant); Lib.RequestResource(v, "ElectricCharge", -Settings.ResqueElectricCharge); Lib.RequestResource(v, "Food", -Settings.ResqueFood); Lib.RequestResource(v, "Oxygen", -Settings.ResqueOxygen); // flag the kerbal as non-resque // note: enable life support mechanics for the kerbal kd.resque = 0; // show a message Message.Post("We found <b>" + c.name + "</b>", (c.gender == ProtoCrewMember.Gender.Male ? "He" : "She") + "'s still alive!"); } } }
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 manageResqueMission(Vessel v) { // true if we detected this was a resque mission vessel bool detected = false; // deal with resque missions foreach (ProtoCrewMember c in v.GetVesselCrew()) { // get kerbal data kerbal_data kd = DB.KerbalData(c.name); // flag the kerbal as not resque at prelaunch if (v.situation == Vessel.Situations.PRELAUNCH) { kd.resque = 0; } // if the kerbal belong to a resque mission if (kd.resque == 1) { // remember it detected = true; // flag the kerbal as non-resque // note: enable life support mechanics for the kerbal kd.resque = 0; // show a message Message.Post(Lib.BuildString("We found <b>", c.name, "</b>"), Lib.BuildString((c.gender == ProtoCrewMember.Gender.Male ? "He" : "She"), "'s still alive!")); } } // gift resources if (detected) { var reslib = PartResourceLibrary.Instance.resourceDefinitions; var parts = Lib.GetPartsRecursively(v.rootPart); // give the vessel some monoprop string monoprop_name = v.isEVA ? "EVA Propellant" : detected_mods.RealFuels ? "Hydrazine" : "MonoPropellant"; foreach (var part in parts) { if (part.CrewCapacity > 0 || part.FindModuleImplementing <KerbalEVA>() != null) { if (part.Resources.list.Find(k => k.resourceName == monoprop_name) == null) { Lib.SetupResource(part, monoprop_name, 0.0, Settings.MonoPropellantOnResque); } break; } } ResourceCache.Produce(v, monoprop_name, Settings.MonoPropellantOnResque); // give the vessel some supplies foreach (Rule r in rules) { if (r.resource_name.Length == 0 || r.on_resque <= double.Epsilon || !reslib.Contains(r.resource_name)) { continue; } foreach (var part in parts) { if (part.CrewCapacity > 0 || part.FindModuleImplementing <KerbalEVA>() != null) { if (part.Resources.list.Find(k => k.resourceName == r.resource_name) == null) { Lib.SetupResource(part, r.resource_name, 0.0, r.on_resque); } break; } } ResourceCache.Produce(v, r.resource_name, r.on_resque); } } }
// implement life support mechanics public void FixedUpdate() { // avoid case when DB isn't ready for whatever reason if (!DB.Ready()) return; // do nothing in the editors and the menus if (!Lib.SceneIsGame()) return; // do nothing if paused if (Lib.IsPaused()) return; // get time elapsed from last update double elapsed_s = TimeWarp.fixedDeltaTime; // for each vessel foreach(Vessel v in FlightGlobals.Vessels) { // skip invalid vessels if (!Lib.IsVessel(v)) continue; // skip dead eva kerbals if (EVA.IsDead(v)) continue; // get crew List<ProtoCrewMember> crew = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew(); // get vessel info from the cache vessel_info info = Cache.VesselInfo(v); // get temperature difference // note: for gameplay reasons, climatization doesn't consume anything landed at home bool landed_home = Lib.Landed(v) && v.mainBody == FlightGlobals.GetHomeBody(); double temp_diff = landed_home ? 0.0 : Math.Abs(info.temperature - Settings.SurvivalTemperature); double temp_sign = landed_home ? 1.0 : info.temperature > Settings.SurvivalTemperature ? 1.0 : -1.0; // determine if inside breathable atmosphere bool breathable = BreathableAtmosphere(v); // 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; // consume ec for climate control double ec_required = temp_diff * Settings.ElectricChargePerSecond * elapsed_s; double ec_consumed = Lib.RequestResource(v, "ElectricCharge", ec_required); double ec_perc = ec_required > 0.0 ? ec_consumed / ec_required : 0.0; // reset kerbal temperature, if necessary if (ec_required <= double.Epsilon || ec_perc >= 1.0 - double.Epsilon) { kd.temperature = 0.0; } else { // degenerate kerbal temperature kd.temperature += Settings.TemperatureDegradationRate * elapsed_s * (1.0 - ec_perc) * temp_diff * temp_sign; // kill kerbal if necessary if (kd.temperature <= -Settings.TemperatureFatalThreshold) { Message.Post(Severity.fatality, KerbalEvent.climate_low, v, c); Kerbalism.Kill(v, c); } else if (kd.temperature >= Settings.TemperatureFatalThreshold) { Message.Post(Severity.fatality, KerbalEvent.climate_high, v, c); Kerbalism.Kill(v, c); } // show warnings else if (kd.temperature <= -Settings.TemperatureDangerThreshold && kd.msg_freezing < 2) { Message.Post(Severity.danger, KerbalEvent.climate_low, v, c); kd.msg_freezing = 2; } else if (kd.temperature <= -Settings.TemperatureWarningThreshold && kd.msg_freezing < 1) { Message.Post(Severity.warning, KerbalEvent.climate_low, v, c); kd.msg_freezing = 1; } else if (kd.temperature > -Settings.TemperatureWarningThreshold && kd.msg_freezing > 0) { Message.Post(Severity.relax, KerbalEvent.climate_low, v, c); kd.msg_freezing = 0; } else if (kd.temperature >= Settings.TemperatureDangerThreshold && kd.msg_burning < 2) { Message.Post(Severity.danger, KerbalEvent.climate_high, v, c); kd.msg_burning = 2; } else if (kd.temperature >= Settings.TemperatureWarningThreshold && kd.msg_burning < 1) { Message.Post(Severity.warning, KerbalEvent.climate_high, v, c); kd.msg_burning = 1; } else if (kd.temperature < Settings.TemperatureWarningThreshold && kd.msg_burning > 0) { Message.Post(Severity.relax, KerbalEvent.climate_high, v, c); kd.msg_burning = 0; } } // if its meal time for this kerbal kd.time_since_food += elapsed_s; if (kd.time_since_food >= Settings.MealFrequency) { // consume food const double food_required = Settings.FoodPerMeal; double food_consumed = Lib.RequestResource(v, "Food", food_required); double food_perc = food_consumed / food_required; // reset kerbal starvation, if necessary if (food_perc >= 1.0 - double.Epsilon) { kd.starved = 0.0; kd.time_since_food = 0.0; } else { // assure piecewise consumption Lib.RequestResource(v, "Food", -food_consumed); food_consumed = 0.0; // degenerate kerbal starvation kd.starved += Settings.StarvedDegradationRate * elapsed_s; // kill kerbal if necessary if (kd.starved >= Settings.StarvedFatalThreshold) { Message.Post(Severity.fatality, KerbalEvent.food, v, c); Kerbalism.Kill(v, c); } // show warnings else if (kd.starved >= Settings.StarvedDangerThreshold && kd.msg_starved < 2) { Message.Post(Severity.danger, KerbalEvent.food, v, c); kd.msg_starved = 2; } else if (kd.starved >= Settings.StarvedWarningThreshold && kd.msg_starved < 1) { Message.Post(Severity.warning, KerbalEvent.food, v, c); kd.msg_starved = 1; } else if (kd.starved < Settings.StarvedWarningThreshold && kd.msg_starved > 0) { Message.Post(Severity.relax, KerbalEvent.food, v, c); kd.msg_starved = 0; } } // produce waste Lib.RequestResource(v, "Waste", -food_consumed); } // if not inside a breathable atmosphere if (!breathable) { // consume oxygen double oxygen_required = Settings.OxygenPerSecond * elapsed_s; double oxygen_consumed = Lib.RequestResource(v, "Oxygen", oxygen_required); double oxygen_perc = oxygen_consumed / oxygen_required; // reset kerbal deprivation, if necessary if (oxygen_perc >= 1.0 - double.Epsilon) { kd.deprived = 0.0; } else { // degenerate kerbal deprivation kd.deprived += Settings.DeprivedDegradationRate * elapsed_s * (1.0 - oxygen_perc); // kill kerbal if necessary if (kd.deprived >= Settings.DeprivedFatalThreshold) { Message.Post(Severity.fatality, KerbalEvent.oxygen, v, c); Kerbalism.Kill(v, c); } // show warnings else if (kd.deprived >= Settings.DeprivedDangerThreshold && kd.msg_deprived < 2) { Message.Post(Severity.danger, KerbalEvent.oxygen, v, c); kd.msg_deprived = 2; } else if (kd.deprived >= Settings.DeprivedWarningThreshold && kd.msg_deprived < 1) { Message.Post(Severity.warning, KerbalEvent.oxygen, v, c); kd.msg_deprived = 1; } else if (kd.deprived < Settings.DeprivedWarningThreshold && kd.msg_deprived > 0) { Message.Post(Severity.relax, KerbalEvent.oxygen, v, c); kd.msg_deprived = 0; } } // produce CO2 Lib.RequestResource(v, "CO2", -oxygen_consumed); } } } }
// implement quality-of-life mechanics public void FixedUpdate() { // avoid case when DB isn't ready for whatever reason if (!DB.Ready()) return; // do nothing in the editors and the menus if (!Lib.SceneIsGame()) return; // do nothing if paused if (Lib.IsPaused()) return; // get time elapsed from last update double elapsed_s = TimeWarp.fixedDeltaTime; // for each vessel foreach(Vessel v in FlightGlobals.Vessels) { // skip invalid vessels if (!Lib.IsVessel(v)) continue; // skip dead eva kerbals if (EVA.IsDead(v)) continue; // get crew List<ProtoCrewMember> crew = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew(); // calculate quality-of-life bonus double qol = Bonus(v); // 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; // accumulate stress kd.stressed += Settings.StressedDegradationRate * elapsed_s / (qol * Variance(c)); // in case of breakdown if (kd.stressed >= Settings.StressedEventThreshold) { // trigger breakdown event Breakdown(v, c); // reset stress halfway between danger and event threshold kd.stressed = (Settings.StressedDangerThreshold + Settings.StressedEventThreshold) * 0.5; } // show warning messages else if (kd.stressed >= Settings.StressedDangerThreshold && kd.msg_stressed < 2) { Message.Post(Severity.danger, KerbalEvent.stress, v, c); kd.msg_stressed = 2; } else if (kd.stressed >= Settings.StressedWarningThreshold && kd.msg_stressed < 1) { Message.Post(Severity.warning, KerbalEvent.stress, v, c); kd.msg_stressed = 1; } // note: no recovery from stress } } }
public override void OnSave(ConfigNode node) { // save current version node.AddValue("version", current_version); ConfigNode kerbals_node = node.AddNode("kerbals"); foreach(var p in kerbals) { kerbal_data kd = p.Value; ConfigNode kerbal_node = kerbals_node.AddNode(p.Key.Replace(" ", "___")); kerbal_node.AddValue("temperature", kd.temperature); kerbal_node.AddValue("starved", kd.starved); kerbal_node.AddValue("deprived", kd.deprived); kerbal_node.AddValue("stressed", kd.stressed); kerbal_node.AddValue("radiation", kd.radiation); kerbal_node.AddValue("time_since_food", kd.time_since_food); kerbal_node.AddValue("msg_freezing", kd.msg_freezing); kerbal_node.AddValue("msg_burning", kd.msg_burning); kerbal_node.AddValue("msg_starved", kd.msg_starved); kerbal_node.AddValue("msg_deprived", kd.msg_deprived); kerbal_node.AddValue("msg_stressed", kd.msg_stressed); kerbal_node.AddValue("msg_radiation", kd.msg_radiation); kerbal_node.AddValue("resque", kd.resque); kerbal_node.AddValue("disabled", kd.disabled); } ConfigNode vessels_node = node.AddNode("vessels"); foreach(var p in vessels) { vessel_data vd = p.Value; ConfigNode vessel_node = vessels_node.AddNode(p.Key.ToString()); vessel_node.AddValue("msg_ec", vd.msg_ec); vessel_node.AddValue("msg_food", vd.msg_food); vessel_node.AddValue("msg_oxygen", vd.msg_oxygen); vessel_node.AddValue("msg_signal", vd.msg_signal); vessel_node.AddValue("msg_belt", vd.msg_belt); vessel_node.AddValue("cfg_ec", vd.cfg_ec); vessel_node.AddValue("cfg_supply", vd.cfg_supply); vessel_node.AddValue("cfg_malfunction", vd.cfg_malfunction); vessel_node.AddValue("cfg_signal", vd.cfg_signal); vessel_node.AddValue("notes", vd.notes.Replace("\n", "$NEWLINE")); vessel_node.AddValue("group", vd.group); } ConfigNode bodies_node = node.AddNode("bodies"); foreach(var p in bodies) { body_data bd = p.Value; ConfigNode body_node = bodies_node.AddNode(p.Key.Replace(" ", "___")); body_node.AddValue("storm_time", bd.storm_time); body_node.AddValue("storm_age", bd.storm_age); body_node.AddValue("storm_state", bd.storm_state); body_node.AddValue("msg_storm", bd.msg_storm); } ConfigNode notifications_node = node.AddNode("notifications"); notifications_node.AddValue("next_death_report", notifications.next_death_report.ToString()); notifications_node.AddValue("next_tutorial", notifications.next_tutorial.ToString()); notifications_node.AddValue("death_counter", notifications.death_counter.ToString()); notifications_node.AddValue("last_death_counter", notifications.last_death_counter.ToString()); notifications_node.AddValue("first_belt_crossing", notifications.first_belt_crossing.ToString()); notifications_node.AddValue("first_signal_loss", notifications.first_signal_loss.ToString()); notifications_node.AddValue("first_malfunction", notifications.first_malfunction.ToString()); }
public override void OnLoad(ConfigNode node) { // get version of the savegame // note: if there isn't a version this is either a new game, or the first public release (that didn't have versioning) string version = node.HasValue("version") ? node.GetValue("version") : node.HasNode("kerbals") ? "0.9.9.0" : current_version; kerbals.Clear(); if (node.HasNode("kerbals")) { ConfigNode kerbals_node = node.GetNode("kerbals"); foreach(ConfigNode kerbal_node in kerbals_node.GetNodes()) { kerbal_data kd = new kerbal_data(); kd.temperature = Convert.ToDouble( kerbal_node.GetValue("temperature") ); kd.starved = Convert.ToDouble( kerbal_node.GetValue("starved") ); kd.deprived = Convert.ToDouble( kerbal_node.GetValue("deprived") ); kd.stressed = Convert.ToDouble( kerbal_node.GetValue("stressed") ); kd.radiation = Convert.ToDouble( kerbal_node.GetValue("radiation") ); kd.time_since_food = Convert.ToDouble( kerbal_node.GetValue("time_since_food") ); kd.msg_freezing = Convert.ToUInt32( kerbal_node.GetValue("msg_freezing") ); kd.msg_burning = Convert.ToUInt32( kerbal_node.GetValue("msg_burning") ); kd.msg_starved = Convert.ToUInt32( kerbal_node.GetValue("msg_starved") ); kd.msg_deprived = Convert.ToUInt32( kerbal_node.GetValue("msg_deprived") ); kd.msg_stressed = Convert.ToUInt32( kerbal_node.GetValue("msg_stressed") ); kd.msg_radiation = Convert.ToUInt32( kerbal_node.GetValue("msg_radiation") ); kd.resque = Convert.ToUInt32( kerbal_node.GetValue("resque") ); kd.disabled = Convert.ToUInt32( kerbal_node.GetValue("disabled") ); kerbals.Add(kerbal_node.name.Replace("___", " "), kd); } } vessels.Clear(); if (node.HasNode("vessels")) { ConfigNode vessels_node = node.GetNode("vessels"); foreach(ConfigNode vessel_node in vessels_node.GetNodes()) { vessel_data vd = new vessel_data(); vd.msg_ec = Convert.ToUInt32( vessel_node.GetValue("msg_ec") ); vd.msg_food = Convert.ToUInt32( vessel_node.GetValue("msg_food") ); vd.msg_oxygen = Convert.ToUInt32( vessel_node.GetValue("msg_oxygen") ); vd.msg_signal = Convert.ToUInt32( vessel_node.GetValue("msg_signal") ); vd.msg_belt = Convert.ToUInt32( vessel_node.GetValue("msg_belt") ); vd.cfg_ec = Convert.ToUInt32( vessel_node.GetValue("cfg_ec") ); vd.cfg_supply = Convert.ToUInt32( vessel_node.GetValue("cfg_supply") ); vd.cfg_malfunction = Convert.ToUInt32( vessel_node.GetValue("cfg_malfunction") ); vd.cfg_signal = Convert.ToUInt32( vessel_node.GetValue("cfg_signal") ); vd.notes = vessel_node.GetValue("notes").Replace("$NEWLINE", "\n"); vd.group = string.CompareOrdinal(version, "0.9.9.0") > 0 ? vessel_node.GetValue("group") : "NONE"; vessels.Add(new Guid(vessel_node.name), vd); } } bodies.Clear(); if (node.HasNode("bodies")) { ConfigNode bodies_node = node.GetNode("bodies"); foreach(ConfigNode body_node in bodies_node.GetNodes()) { body_data bd = new body_data(); bd.storm_time = Convert.ToDouble( body_node.GetValue("storm_time") ); bd.storm_age = Convert.ToDouble( body_node.GetValue("storm_age") ); bd.storm_state = Convert.ToUInt32( body_node.GetValue("storm_state") ); bd.msg_storm = Convert.ToUInt32( body_node.GetValue("msg_storm") ); bodies.Add(body_node.name.Replace("___", " "), bd); } } notifications = new notification_data(); if (node.HasNode("notifications")) { ConfigNode notifications_node = node.GetNode("notifications"); notifications.next_death_report = Convert.ToUInt32( notifications_node.GetValue("next_death_report") ); notifications.next_tutorial = Convert.ToUInt32( notifications_node.GetValue("next_tutorial") ); notifications.death_counter = Convert.ToUInt32( notifications_node.GetValue("death_counter") ); notifications.last_death_counter = Convert.ToUInt32( notifications_node.GetValue("last_death_counter") ); notifications.first_belt_crossing = Convert.ToUInt32( notifications_node.GetValue("first_belt_crossing") ); notifications.first_signal_loss = Convert.ToUInt32( notifications_node.GetValue("first_signal_loss") ); notifications.first_malfunction = Convert.ToUInt32( notifications_node.GetValue("first_malfunction") ); } // if an old savegame was imported, log some debug info if (version != current_version) Lib.Log("savegame converted from version " + version); }
public override void OnSave(ConfigNode node) { // save current version node.AddValue("version", current_version); ConfigNode kerbals_node = node.AddNode("kerbals"); foreach(var p in kerbals) { kerbal_data kd = p.Value; ConfigNode kerbal_node = kerbals_node.AddNode(p.Key.Replace(" ", "___")); kerbal_node.AddValue("resque", kd.resque); kerbal_node.AddValue("disabled", kd.disabled); kerbal_node.AddValue("living_space", kd.living_space); kerbal_node.AddValue("entertainment", kd.entertainment); kerbal_node.AddValue("shielding", kd.shielding); kerbal_node.AddValue("space_name", kd.space_name); var kmon_node = kerbal_node.AddNode("kmon"); foreach(var q in kd.kmon) { var kmon_subnode = kmon_node.AddNode(q.Key); kmon_subnode.AddValue("problem", q.Value.problem); kmon_subnode.AddValue("message", q.Value.message); kmon_subnode.AddValue("time_since", q.Value.time_since); } } ConfigNode vessels_node = node.AddNode("vessels"); foreach(var p in vessels) { vessel_data vd = p.Value; ConfigNode vessel_node = vessels_node.AddNode(p.Key.ToString()); vessel_node.AddValue("msg_signal", vd.msg_signal); vessel_node.AddValue("msg_belt", vd.msg_belt); vessel_node.AddValue("cfg_ec", vd.cfg_ec); vessel_node.AddValue("cfg_supply", vd.cfg_supply); vessel_node.AddValue("cfg_signal", vd.cfg_signal); vessel_node.AddValue("cfg_malfunction", vd.cfg_malfunction); vessel_node.AddValue("cfg_storm", vd.cfg_storm); vessel_node.AddValue("cfg_highlights", vd.cfg_highlights); vessel_node.AddValue("cfg_showlink", vd.cfg_showlink); vessel_node.AddValue("notes", vd.notes.Replace("\n", "$NEWLINE")); vessel_node.AddValue("group", vd.group); var vmon_node = vessel_node.AddNode("vmon"); foreach(var q in vd.vmon) { var vmon_subnode = vmon_node.AddNode(q.Key); vmon_subnode.AddValue("message", q.Value.message); } foreach(uint id in vd.scansat_id) { vessel_node.AddValue("scansat_id", id.ToString()); } } ConfigNode bodies_node = node.AddNode("bodies"); foreach(var p in bodies) { body_data bd = p.Value; ConfigNode body_node = bodies_node.AddNode(p.Key.Replace(" ", "___")); body_node.AddValue("storm_time", bd.storm_time); body_node.AddValue("storm_age", bd.storm_age); body_node.AddValue("storm_state", bd.storm_state); body_node.AddValue("msg_storm", bd.msg_storm); } ConfigNode notifications_node = node.AddNode("notifications"); notifications_node.AddValue("next_death_report", notifications.next_death_report.ToString()); notifications_node.AddValue("next_tutorial", notifications.next_tutorial.ToString()); notifications_node.AddValue("death_counter", notifications.death_counter.ToString()); notifications_node.AddValue("last_death_counter", notifications.last_death_counter.ToString()); notifications_node.AddValue("first_belt_crossing", notifications.first_belt_crossing.ToString()); notifications_node.AddValue("first_signal_loss", notifications.first_signal_loss.ToString()); notifications_node.AddValue("first_malfunction", notifications.first_malfunction.ToString()); }
public override void OnLoad(ConfigNode node) { // get version of the savegame // note: if there isn't a version this is either a new game, or the first public release (that didn't have versioning) string version = node.HasValue("version") ? node.GetValue("version") : node.HasNode("kerbals") ? "0.9.9.0" : current_version; // this is an unsupported version, attempt a total clean up and pray // note: currently unused if (string.CompareOrdinal(version, "0.9.9.0") < 0) { Lib.Log("loading save from unsupported version " + version); kerbals.Clear(); vessels.Clear(); bodies.Clear(); notifications = new notification_data(); return; } kerbals.Clear(); if (node.HasNode("kerbals")) { ConfigNode kerbals_node = node.GetNode("kerbals"); foreach(ConfigNode kerbal_node in kerbals_node.GetNodes()) { kerbal_data kd = new kerbal_data(); kd.resque = Lib.ConfigValue(kerbal_node, "resque", 1u); kd.disabled = Lib.ConfigValue(kerbal_node, "disabled", 0u); kd.living_space = Lib.ConfigValue(kerbal_node, "living_space", 1.0); // since 0.9.9.4 kd.entertainment = Lib.ConfigValue(kerbal_node, "entertainment", 1.0); // since 0.9.9.4 kd.shielding = Lib.ConfigValue(kerbal_node, "shielding", 0.0); // since 0.9.9.4 kd.space_name = Lib.ConfigValue(kerbal_node, "space_name", ""); // since 0.9.9.4 kd.kmon = new Dictionary<string, kmon_data>(); if (kerbal_node.HasNode("kmon")) // since 0.9.9.5 { foreach(var cfg in kerbal_node.GetNode("kmon").GetNodes()) { kmon_data kmon = new kmon_data(); kmon.problem = Lib.ConfigValue(cfg, "problem", 0.0); kmon.message = Lib.ConfigValue(cfg, "message", 0u); kmon.time_since = Lib.ConfigValue(cfg, "time_since", 0.0); kd.kmon.Add(cfg.name, kmon); } } kerbals.Add(kerbal_node.name.Replace("___", " "), kd); } } vessels.Clear(); if (node.HasNode("vessels")) { ConfigNode vessels_node = node.GetNode("vessels"); foreach(ConfigNode vessel_node in vessels_node.GetNodes()) { vessel_data vd = new vessel_data(); vd.msg_signal = Lib.ConfigValue(vessel_node, "msg_signal", 0u); vd.msg_belt = Lib.ConfigValue(vessel_node, "msg_belt", 0u); vd.cfg_ec = Lib.ConfigValue(vessel_node, "cfg_ec", 1u); vd.cfg_supply = Lib.ConfigValue(vessel_node, "cfg_supply", 1u); vd.cfg_signal = Lib.ConfigValue(vessel_node, "cfg_signal", 1u); vd.cfg_malfunction = Lib.ConfigValue(vessel_node, "cfg_malfunction", 1u); vd.cfg_storm = Lib.ConfigValue(vessel_node, "cfg_storm", 1u); // since 0.9.9.5 vd.cfg_highlights = Lib.ConfigValue(vessel_node, "cfg_highlights", 1u); // since 0.9.9.5 vd.cfg_showlink = Lib.ConfigValue(vessel_node, "cfg_showlink", 0u); // since 0.9.9.8 vd.notes = Lib.ConfigValue(vessel_node, "notes", "").Replace("$NEWLINE", "\n"); // since 0.9.9.1 vd.group = Lib.ConfigValue(vessel_node, "group", "NONE"); // since 0.9.9.1 vd.vmon = new Dictionary<string, vmon_data>(); if (vessel_node.HasNode("vmon")) // since 0.9.9.5 { foreach(var cfg in vessel_node.GetNode("vmon").GetNodes()) { vmon_data vmon = new vmon_data(); vmon.message = Lib.ConfigValue(cfg, "message", 0u); vd.vmon.Add(cfg.name, vmon); } } vd.scansat_id = new List<uint>(); foreach(string s in vessel_node.GetValues("scansat_id")) // since 0.9.9.5 { vd.scansat_id.Add(Convert.ToUInt32(s)); } vessels.Add(new Guid(vessel_node.name), vd); } } bodies.Clear(); if (node.HasNode("bodies")) { ConfigNode bodies_node = node.GetNode("bodies"); foreach(ConfigNode body_node in bodies_node.GetNodes()) { body_data bd = new body_data(); bd.storm_time = Lib.ConfigValue(body_node, "storm_time", 0.0); bd.storm_age = Lib.ConfigValue(body_node, "storm_age", 0.0); bd.storm_state = Lib.ConfigValue(body_node, "storm_state", 0u); bd.msg_storm = Lib.ConfigValue(body_node, "msg_storm", 0u); bodies.Add(body_node.name.Replace("___", " "), bd); } } notifications = new notification_data(); if (node.HasNode("notifications")) { ConfigNode n_node = node.GetNode("notifications"); notifications.next_death_report = Lib.ConfigValue(n_node, "next_death_report", 0u); notifications.next_tutorial = Lib.ConfigValue(n_node, "next_tutorial", 0u); notifications.death_counter = Lib.ConfigValue(n_node, "death_counter", 0u); notifications.last_death_counter = Lib.ConfigValue(n_node, "last_death_counter", 0u); notifications.first_belt_crossing = Lib.ConfigValue(n_node, "first_belt_crossing", 0u); notifications.first_signal_loss = Lib.ConfigValue(n_node, "first_signal_loss", 0u); notifications.first_malfunction = Lib.ConfigValue(n_node, "first_malfunction", 0u); } // versions before 0.9.9.5 used a different structure to remember message sent // mute the message system for a few seconds to avoid the user being bombarded by messages if (string.CompareOrdinal(version, "0.9.9.5") < 0) { Message.MuteInternal(); base.StartCoroutine(CallbackUtil.DelayedCallback(10.0f, Message.UnmuteInternal)); } // versions before 0.9.9.5 didn't have profiles, and didn't use CRP food/oxygen values // scale all amounts of them in existing vessels, to not break savegames if (string.CompareOrdinal(version, "0.9.9.5") < 0) { foreach(Vessel v in FlightGlobals.Vessels.FindAll(k => !k.loaded)) { foreach(var part in v.protoVessel.protoPartSnapshots) { var food = part.resources.Find(k => k.resourceName == "Food"); if (food != null) { food.resourceValues.SetValue("amount", (Lib.ConfigValue(food.resourceValues, "amount", 0.0f) * 10.0f).ToString()); food.resourceValues.SetValue("maxAmount", (Lib.ConfigValue(food.resourceValues, "maxAmount", 0.0f) * 10.0f).ToString()); } var oxygen = part.resources.Find(k => k.resourceName == "Oxygen"); if (oxygen != null) { oxygen.resourceValues.SetValue("amount", (Lib.ConfigValue(oxygen.resourceValues, "amount", 0.0f) * 1000.0f).ToString()); oxygen.resourceValues.SetValue("maxAmount", (Lib.ConfigValue(oxygen.resourceValues, "maxAmount", 0.0f) * 1000.0f).ToString()); } } } } // if an old savegame was imported, log some debug info if (version != current_version) Lib.Log("savegame converted from version " + version); }
// implement radiation mechanics public void FixedUpdate() { // avoid case when DB isn't ready for whatever reason if (!DB.Ready()) { return; } // do nothing in the editors and the menus if (!Lib.SceneIsGame()) { return; } // do nothing if paused if (Lib.IsPaused()) { return; } // get time elapsed from last update double elapsed_s = TimeWarp.fixedDeltaTime; // for each vessel foreach (Vessel v in FlightGlobals.Vessels) { // skip invalid vessels if (!Lib.IsVessel(v)) { continue; } // skip dead eva kerbals if (EVA.IsDead(v)) { continue; } // get crew List <ProtoCrewMember> crew = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew(); // get crew count int crew_count = Lib.CrewCount(v); // get vessel info from the cache vessel_info info = Cache.VesselInfo(v); // get vessel data vessel_data vd = DB.VesselData(v.id); // belt warnings // note: we only show it for manned vesssels, but the first time we also show it for probes if (crew_count > 0 || DB.NotificationData().first_belt_crossing == 0) { if (InsideBelt(v) && vd.msg_belt < 1) { Message.Post("<b>" + v.vesselName + "</b> is crossing <i>" + v.mainBody.bodyName + " radiation belt</i>", "Exposed to extreme radiation"); vd.msg_belt = 1; DB.NotificationData().first_belt_crossing = 1; //< record first belt crossing } else if (!InsideBelt(v) && vd.msg_belt > 0) { // no message after crossing the belt vd.msg_belt = 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; } // accumulate radiation kd.radiation += info.radiation * elapsed_s; // kill kerbal if necessary if (kd.radiation >= Settings.RadiationFatalThreshold) { Message.Post(Severity.fatality, KerbalEvent.radiation, v, c); Kerbalism.Kill(v, c); } // show warnings else if (kd.radiation >= Settings.RadiationDangerThreshold && kd.msg_radiation < 2) { Message.Post(Severity.danger, KerbalEvent.radiation, v, c); kd.msg_radiation = 2; } else if (kd.radiation >= Settings.RadiationWarningThreshold && kd.msg_radiation < 1) { Message.Post(Severity.danger, KerbalEvent.radiation, v, c); kd.msg_radiation = 1; } // note: no recovery from radiations } } }
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(); } }
public override void OnSave(ConfigNode node) { // save current version node.AddValue("version", current_version); ConfigNode kerbals_node = node.AddNode("kerbals"); foreach(var p in kerbals) { kerbal_data kd = p.Value; ConfigNode kerbal_node = kerbals_node.AddNode(p.Key.Replace(" ", "___")); kerbal_node.AddValue("resque", kd.resque); kerbal_node.AddValue("disabled", kd.disabled); kerbal_node.AddValue("living_space", kd.living_space); kerbal_node.AddValue("entertainment", kd.entertainment); kerbal_node.AddValue("shielding", kd.shielding); kerbal_node.AddValue("space_name", kd.space_name); kerbal_node.AddValue("eva_dead", kd.eva_dead); kerbal_node.AddValue("has_helmet", kd.has_helmet); var kmon_node = kerbal_node.AddNode("kmon"); foreach(var q in kd.kmon) { var kmon_subnode = kmon_node.AddNode(q.Key); kmon_subnode.AddValue("problem", q.Value.problem); kmon_subnode.AddValue("message", q.Value.message); kmon_subnode.AddValue("time_since", q.Value.time_since); } } ConfigNode vessels_node = node.AddNode("vessels"); foreach(var p in vessels) { vessel_data vd = p.Value; ConfigNode vessel_node = vessels_node.AddNode(p.Key.ToString()); vessel_node.AddValue("msg_signal", vd.msg_signal); vessel_node.AddValue("msg_belt", vd.msg_belt); vessel_node.AddValue("cfg_ec", vd.cfg_ec); vessel_node.AddValue("cfg_supply", vd.cfg_supply); vessel_node.AddValue("cfg_signal", vd.cfg_signal); vessel_node.AddValue("cfg_malfunction", vd.cfg_malfunction); vessel_node.AddValue("cfg_storm", vd.cfg_storm); vessel_node.AddValue("cfg_highlights", vd.cfg_highlights); vessel_node.AddValue("cfg_showlink", vd.cfg_showlink); vessel_node.AddValue("storm_time", vd.storm_time); vessel_node.AddValue("storm_age", vd.storm_age); vessel_node.AddValue("storm_state", vd.storm_state); vessel_node.AddValue("group", vd.group); var vmon_node = vessel_node.AddNode("vmon"); foreach(var q in vd.vmon) { var vmon_subnode = vmon_node.AddNode(q.Key); vmon_subnode.AddValue("message", q.Value.message); } foreach(uint id in vd.scansat_id) { vessel_node.AddValue("scansat_id", id.ToString()); } ConfigNode computer_node = vessel_node.AddNode("computer"); vd.computer.save(computer_node); } ConfigNode bodies_node = node.AddNode("bodies"); foreach(var p in bodies) { body_data bd = p.Value; ConfigNode body_node = bodies_node.AddNode(p.Key.Replace(" ", "___")); body_node.AddValue("storm_time", bd.storm_time); body_node.AddValue("storm_age", bd.storm_age); body_node.AddValue("storm_state", bd.storm_state); body_node.AddValue("msg_storm", bd.msg_storm); } ConfigNode landmarks_node = node.AddNode("landmarks"); landmarks_node.AddValue("belt_crossing", landmarks.belt_crossing); landmarks_node.AddValue("manned_orbit", landmarks.manned_orbit); landmarks_node.AddValue("space_harvest", landmarks.space_harvest); }
public override void OnLoad(ConfigNode node) { // reset caches to deal with minor issues on flight revert and scene changes Engine.ResetCache(); // get version of the savegame // note: if there isn't a version this is either a new game, or the first public release (that didn't have versioning) string version = node.HasValue("version") ? node.GetValue("version") : node.HasNode("kerbals") ? "0.9.9.0" : current_version; // this is an unsupported version, attempt a total clean up and pray if (string.CompareOrdinal(version, "0.9.9.5") < 0) { Lib.Log("loading save from unsupported version " + version); kerbals.Clear(); vessels.Clear(); bodies.Clear(); landmarks = new landmarks_data(); return; } kerbals.Clear(); if (node.HasNode("kerbals")) { ConfigNode kerbals_node = node.GetNode("kerbals"); foreach(ConfigNode kerbal_node in kerbals_node.GetNodes()) { kerbal_data kd = new kerbal_data(); kd.resque = Lib.ConfigValue(kerbal_node, "resque", 1u); kd.disabled = Lib.ConfigValue(kerbal_node, "disabled", 0u); kd.living_space = Lib.ConfigValue(kerbal_node, "living_space", 1.0); kd.entertainment = Lib.ConfigValue(kerbal_node, "entertainment", 1.0); kd.shielding = Lib.ConfigValue(kerbal_node, "shielding", 0.0); kd.space_name = Lib.ConfigValue(kerbal_node, "space_name", ""); kd.eva_dead = Lib.ConfigValue(kerbal_node, "eva_dead", false); kd.has_helmet = Lib.ConfigValue(kerbal_node, "has_helmet", false); if (kerbal_node.HasNode("kmon")) { foreach(var cfg in kerbal_node.GetNode("kmon").GetNodes()) { kmon_data kmon = new kmon_data(); kmon.problem = Lib.ConfigValue(cfg, "problem", 0.0); kmon.message = Lib.ConfigValue(cfg, "message", 0u); kmon.time_since = Lib.ConfigValue(cfg, "time_since", 0.0); kd.kmon.Add(cfg.name, kmon); } } kerbals.Add(kerbal_node.name.Replace("___", " "), kd); } } vessels.Clear(); if (node.HasNode("vessels")) { ConfigNode vessels_node = node.GetNode("vessels"); foreach(ConfigNode vessel_node in vessels_node.GetNodes()) { vessel_data vd = new vessel_data(); vd.msg_signal = Lib.ConfigValue(vessel_node, "msg_signal", 0u); vd.msg_belt = Lib.ConfigValue(vessel_node, "msg_belt", 0u); vd.cfg_ec = Lib.ConfigValue(vessel_node, "cfg_ec", 1u); vd.cfg_supply = Lib.ConfigValue(vessel_node, "cfg_supply", 1u); vd.cfg_signal = Lib.ConfigValue(vessel_node, "cfg_signal", 1u); vd.cfg_malfunction = Lib.ConfigValue(vessel_node, "cfg_malfunction", 1u); vd.cfg_storm = Lib.ConfigValue(vessel_node, "cfg_storm", 1u); vd.cfg_highlights = Lib.ConfigValue(vessel_node, "cfg_highlights", 1u); vd.cfg_showlink = Lib.ConfigValue(vessel_node, "cfg_showlink", 0u); vd.storm_time = Lib.ConfigValue(vessel_node, "storm_time", 0.0); vd.storm_age = Lib.ConfigValue(vessel_node, "storm_age", 0.0); vd.storm_state = Lib.ConfigValue(vessel_node, "storm_state", 0u); vd.group = Lib.ConfigValue(vessel_node, "group", "NONE"); if (vessel_node.HasNode("vmon")) { foreach(var cfg in vessel_node.GetNode("vmon").GetNodes()) { vmon_data vmon = new vmon_data(); vmon.message = Lib.ConfigValue(cfg, "message", 0u); vd.vmon.Add(cfg.name, vmon); } } foreach(string s in vessel_node.GetValues("scansat_id")) { vd.scansat_id.Add(Lib.Parse.ToUInt(s)); } if (vessel_node.HasNode("computer")) { vd.computer = new Computer(vessel_node.GetNode("computer")); } // import old notes into new computer system else if (string.CompareOrdinal(version, "1.1.1.0") < 0) { vd.computer.files["doc/notes"].content = Lib.ConfigValue(vessel_node, "notes", "").Replace("$NEWLINE", "\n"); } vessels.Add(new Guid(vessel_node.name), vd); } } bodies.Clear(); if (node.HasNode("bodies")) { ConfigNode bodies_node = node.GetNode("bodies"); foreach(ConfigNode body_node in bodies_node.GetNodes()) { body_data bd = new body_data(); bd.storm_time = Lib.ConfigValue(body_node, "storm_time", 0.0); bd.storm_age = Lib.ConfigValue(body_node, "storm_age", 0.0); bd.storm_state = Lib.ConfigValue(body_node, "storm_state", 0u); bd.msg_storm = Lib.ConfigValue(body_node, "msg_storm", 0u); bodies.Add(body_node.name.Replace("___", " "), bd); } } landmarks = new landmarks_data(); if (node.HasNode("landmarks")) { ConfigNode landmarks_node = node.GetNode("landmarks"); landmarks.belt_crossing = Lib.ConfigValue(landmarks_node, "belt_crossing", 0u); landmarks.manned_orbit = Lib.ConfigValue(landmarks_node, "manned_orbit", 0u); landmarks.space_harvest = Lib.ConfigValue(landmarks_node, "space_harvest", 0u); } // import old notifications data into new landmark system else if (string.CompareOrdinal(version, "1.1.2.0") < 0) { if (node.HasNode("notifications")) { ConfigNode n_node = node.GetNode("notifications"); landmarks.belt_crossing = Lib.ConfigValue(n_node, "first_belt_crossing", 0u); landmarks.manned_orbit = Lib.ConfigValue(n_node, "manned_orbit_contract", 0u); landmarks.space_harvest = Lib.ConfigValue(n_node, "first_space_harvest", 0u); } } // if an old savegame was imported, log some debug info if (version != current_version) Lib.Log("savegame converted from version " + version); }