void VesselDestroyed(Vessel v) { // for each part foreach (Part p in v.parts) { // forget all potential vessel data DB.vessels.Remove(p.flightID); } // rescan the damn kerbals // - vessel crew is empty at destruction time // - we can't even use the flightglobal roster, because sometimes it isn't updated yet at this point HashSet<string> kerbals_alive = new HashSet<string>(); HashSet<string> kerbals_dead = new HashSet<string>(); foreach (Vessel ov in FlightGlobals.Vessels) { foreach (ProtoCrewMember c in Lib.CrewList(ov)) kerbals_alive.Add(c.name); } foreach (var p in DB.kerbals) { if (!kerbals_alive.Contains(p.Key)) kerbals_dead.Add(p.Key); } foreach (string n in kerbals_dead) DB.kerbals.Remove(n); // purge the caches Cache.Purge(v); ResourceCache.Purge(v); }
static void ProcessHarvester(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleResourceHarvester harvester, vessel_resources resources, double elapsed_s) { // note: ignore stock temperature mechanic of harvesters // note: ignore autoshutdown // note: ignore depletion (stock seem to do the same) // note: using hard-coded crew bonus values from the wiki because the module data make zero sense (DERP ALERT) // note: 'undo' stock behaviour by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation) // if active if (Lib.Proto.GetBool(m, "IsActivated")) { // do nothing if full // note: comparing against previous amount if (resources.Info(v, harvester.ResourceName).level < harvester.FillAmount - double.Epsilon) { // deduce crew bonus int exp_level = -1; if (harvester.UseSpecialistBonus) { foreach (ProtoCrewMember c in Lib.CrewList(v)) { if (c.experienceTrait.Effects.Find(k => k.Name == harvester.ExperienceEffect) != null) { exp_level = Math.Max(exp_level, c.experienceLevel); } } } double exp_bonus = exp_level < 0 ? 1.0 : 5.0 + (double)exp_level * 4.0; // detect amount of ore in the ground AbundanceRequest request = new AbundanceRequest { Altitude = v.altitude, BodyId = v.mainBody.flightGlobalsIndex, CheckForLock = false, Latitude = v.latitude, Longitude = v.longitude, ResourceType = (HarvestTypes)harvester.HarvesterType, ResourceName = harvester.ResourceName }; double abundance = ResourceMap.Instance.GetAbundance(request); // if there is actually something (should be if active when unloaded) if (abundance > harvester.HarvestThreshold) { // create and commit recipe resource_recipe recipe = new resource_recipe(); foreach (var ir in harvester.inputList) { recipe.Input(ir.ResourceName, ir.Ratio * elapsed_s); } recipe.Output(harvester.ResourceName, abundance * harvester.Efficiency * exp_bonus * elapsed_s); resources.Transform(recipe); } } // undo stock behaviour by forcing last_update_time to now Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime()); } }
void VesselDestroyed(Vessel v) { // rescan the damn kerbals // - vessel crew is empty at destruction time // - we can't even use the flightglobal roster, because sometimes it isn't updated yet at this point HashSet <string> kerbals_alive = new HashSet <string>(); HashSet <string> kerbals_dead = new HashSet <string>(); foreach (Vessel ov in FlightGlobals.Vessels) { foreach (ProtoCrewMember c in Lib.CrewList(ov)) { kerbals_alive.Add(c.name); } } foreach (string key in DB.Kerbals().Keys) { if (!kerbals_alive.Contains(key)) { kerbals_dead.Add(key); } } foreach (string n in kerbals_dead) { // we don't know if the kerbal really is dead, or if it is just not currently assigned to a mission DB.KillKerbal(n, false); } // purge the caches ResourceCache.Purge(v); // works with loaded and unloaded vessels //Drive.Purge(v); // works with loaded and unloaded vessels Cache.PurgeVesselCaches(v); // works with loaded and unloaded vessels }
// 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); }
public static void manageRescueMission(Vessel v) { // true if we detected this was a rescue mission vessel bool detected = false; // deal with rescue missions foreach (ProtoCrewMember c in Lib.CrewList(v)) { // get kerbal data KerbalData kd = DB.Kerbal(c.name); // flag the kerbal as not rescue at prelaunch if (v.situation == Vessel.Situations.PRELAUNCH) { kd.rescue = false; } // if the kerbal belong to a rescue mission if (kd.rescue) { // remember it detected = true; // flag the kerbal as non-rescue // note: enable life support mechanics for the kerbal kd.rescue = false; // 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 propellant usable on eva string monoprop_name = Lib.EvaPropellantName(); double monoprop_amount = Lib.EvaPropellantCapacity(); foreach (var part in parts) { if (part.CrewCapacity > 0 || part.FindModuleImplementing <KerbalEVA>() != null) { if (Lib.Capacity(part, monoprop_name) <= double.Epsilon) { Lib.AddResource(part, monoprop_name, 0.0, monoprop_amount); } break; } } ResourceCache.Produce(v, monoprop_name, monoprop_amount); // give the vessel some supplies Profile.SetupRescue(v); } }
bool render_vessel(Panel p, Vessel v) { // get vessel info vessel_info vi = Cache.VesselInfo(v); // skip invalid vessels if (!vi.is_valid) return false; // get data from db VesselData vd = DB.Vessel(v); // determine if filter must be shown show_filter |= vd.group.Length > 0 && vd.group != "NONE"; // skip filtered vessels if (filtered() && vd.group != filter) return false; // get resource handler vessel_resources resources = ResourceCache.Get(v); // get vessel crew List<ProtoCrewMember> crew = Lib.CrewList(v); // get vessel name string vessel_name = v.isEVA ? crew[0].name : v.vesselName; // get body name string body_name = v.mainBody.name.ToUpper(); // render entry p.header ( Lib.BuildString("<b>", Lib.Ellipsis(vessel_name, Styles.ScaleStringLength(((page == MonitorPage.data || page == MonitorPage.log || selected_id == Guid.Empty) && !Lib.IsFlight()) ? 50 : 30)), "</b> <size=", Styles.ScaleInteger(9).ToString(), "><color=#cccccc>", Lib.Ellipsis(body_name, Styles.ScaleStringLength(8)), "</color></size>"), string.Empty, () => { selected_id = selected_id != v.id ? v.id : Guid.Empty; } ); // problem indicator indicator_problems(p, v, vi, crew); // battery indicator indicator_ec(p, v, vi); // supply indicator if (Features.Supplies) indicator_supplies(p, v, vi); // reliability indicator if (Features.Reliability) indicator_reliability(p, v, vi); // signal indicator if (RemoteTech.Enabled() || HighLogic.fetch.currentGame.Parameters.Difficulty.EnableCommNet) indicator_signal(p, v, vi); // done return true; }
static void ProcessConverter(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleResourceConverter converter, vessel_resources resources, double elapsed_s) { // note: ignore stock temperature mechanic of converters // note: ignore autoshutdown // note: using hard-coded crew bonus values from the wiki because the module data make zero sense (DERP ALERT) // note: non-mandatory resources 'dynamically scale the ratios', that is exactly what mandatory resources do too (DERP ALERT) // note: 'undo' stock behaviour by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation) // note: support PlanetaryBaseSystem converters // note: support NearFuture reactors // note: assume dump overboard is false for all outputs // if active if (Lib.Proto.GetBool(m, "IsActivated")) { // determine if vessel is full of all output resources // note: comparing against previous amount bool full = true; foreach (var or in converter.outputList) { resource_info res = resources.Info(v, or.ResourceName); full &= (res.level >= converter.FillAmount - double.Epsilon); } // if not full if (!full) { // deduce crew bonus int exp_level = -1; if (converter.UseSpecialistBonus) { foreach (ProtoCrewMember c in Lib.CrewList(v)) { if (c.experienceTrait.Effects.Find(k => k.Name == converter.ExperienceEffect) != null) { exp_level = Math.Max(exp_level, c.experienceLevel); } } } double exp_bonus = exp_level < 0 ? 1.0 : 5.0 + (double)exp_level * 4.0; // create and commit recipe resource_recipe recipe = new resource_recipe(); foreach (var ir in converter.inputList) { recipe.Input(ir.ResourceName, ir.Ratio * elapsed_s); } foreach (var or in converter.outputList) { recipe.Output(or.ResourceName, or.Ratio * exp_bonus * elapsed_s); } resources.Transform(recipe); } // undo stock behaviour by forcing last_update_time to now Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime()); } }
static void ProcessConverter(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleResourceConverter converter, VesselResources resources, double elapsed_s) { // note: ignore stock temperature mechanic of converters // note: ignore auto shutdown // note: non-mandatory resources 'dynamically scale the ratios', that is exactly what mandatory resources do too (DERP ALERT) // note: 'undo' stock behavior by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation) // if active if (Lib.Proto.GetBool(m, "IsActivated")) { // determine if vessel is full of all output resources // note: comparing against previous amount bool full = true; foreach (var or in converter.outputList) { ResourceInfo res = resources.GetResource(v, or.ResourceName); full &= (res.Level >= converter.FillAmount - double.Epsilon); } // if not full if (!full) { // deduce crew bonus int exp_level = -1; if (converter.UseSpecialistBonus) { foreach (ProtoCrewMember c in Lib.CrewList(v)) { if (c.experienceTrait.Effects.Find(k => k.Name == converter.ExperienceEffect) != null) { exp_level = Math.Max(exp_level, c.experienceLevel); } } } double exp_bonus = exp_level < 0 ? converter.EfficiencyBonus * converter.SpecialistBonusBase : converter.EfficiencyBonus * (converter.SpecialistBonusBase + (converter.SpecialistEfficiencyFactor * (exp_level + 1))); // create and commit recipe ResourceRecipe recipe = new ResourceRecipe(ResourceBroker.StockConverter); foreach (var ir in converter.inputList) { recipe.AddInput(ir.ResourceName, ir.Ratio * exp_bonus * elapsed_s); } foreach (var or in converter.outputList) { recipe.AddOutput(or.ResourceName, or.Ratio * exp_bonus * elapsed_s, or.DumpExcess); } resources.AddRecipe(recipe); } // undo stock behavior by forcing last_update_time to now Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime()); } }
bool render_vessel(Panel p, Vessel v) { // get vessel info vessel_info vi = Cache.VesselInfo(v); // skip invalid vessels if (!vi.is_valid) return false; // get data from db VesselData vd = DB.Vessel(v); // determine if filter must be shown show_filter |= vd.group.Length > 0 && vd.group != "NONE"; // skip filtered vessels if (filtered() && vd.group != filter) return false; // get resource handler vessel_resources resources = ResourceCache.Get(v); // get vessel crew List<ProtoCrewMember> crew = Lib.CrewList(v); // get vessel name string vessel_name = v.isEVA ? crew[0].name : v.vesselName; // get body name string body_name = v.mainBody.name.ToUpper(); // render entry p.header ( Lib.BuildString("<b>", Lib.Ellipsis(vessel_name, 20), "</b> <size=9><color=#cccccc>", Lib.Ellipsis(body_name, 8), "</color></size>"), string.Empty, () => { selected_id = selected_id != v.id ? v.id : Guid.Empty; } ); // problem indicator indicator_problems(p, v, vi, crew); // battery indicator indicator_ec(p, v, vi); // supply indicator if (Features.Supplies) indicator_supplies(p, v, vi); // reliability indicator if (Features.Reliability) indicator_reliability(p, v, vi); // signal indicator if (Features.Signal) indicator_signal(p, v, vi); // done return true; }
public static void TelemetryPanel(this Panel p, Vessel v) { // avoid corner-case when this is called in a lambda after scene changes v = FlightGlobals.FindVessel(v.id); // if vessel doesn't exist anymore, leave the panel empty if (v == null) { return; } // get vessel data VesselData vd = v.KerbalismData(); // if not a valid vessel, leave the panel empty if (!vd.IsSimulated) { return; } // set metadata p.Title(Lib.BuildString(Lib.Ellipsis(v.vesselName, Styles.ScaleStringLength(20)), " ", Lib.Color(Local.TELEMETRY_title, Lib.Kolor.LightGrey))); //"TELEMETRY" p.Width(Styles.ScaleWidthFloat(355.0f)); p.paneltype = Panel.PanelType.telemetry; // time-out simulation if (p.Timeout(vd)) { return; } // get resources VesselResources resources = ResourceCache.Get(v); // get crew var crew = Lib.CrewList(v); // draw the content Render_crew(p, crew); if (Features.Science) { Render_science(p, v, vd); } Render_greenhouse(p, vd); Render_supplies(p, v, vd, resources); Render_habitat(p, v, vd); Render_environment(p, v, vd); // collapse eva kerbal sections into one if (v.isEVA) { p.Collapse(Local.TELEMETRY_EVASUIT); //"EVA SUIT" } }
public static void TelemetryPanel(this Panel p, Vessel v) { // avoid corner-case when this is called in a lambda after scene changes v = FlightGlobals.FindVessel(v.id); // if vessel doesn't exist anymore, leave the panel empty if (v == null) { return; } // get info from the cache Vessel_info vi = Cache.VesselInfo(v); // if not a valid vessel, leave the panel empty if (!vi.is_valid) { return; } // set metadata p.Title(Lib.BuildString(Lib.Ellipsis(v.vesselName, Styles.ScaleStringLength(20)), " <color=#cccccc>TELEMETRY</color>")); p.Width(Styles.ScaleWidthFloat(355.0f)); p.paneltype = Panel.PanelType.telemetry; // time-out simulation if (p.Timeout(vi)) { return; } // get vessel data VesselData vd = DB.Vessel(v); // get resources Vessel_resources resources = ResourceCache.Get(v); // get crew var crew = Lib.CrewList(v); // draw the content Render_crew(p, crew); Render_greenhouse(p, vi); Render_supplies(p, v, vi, resources); Render_habitat(p, v, vi); Render_environment(p, v, vi); // collapse eva kerbal sections into one if (v.isEVA) { p.Collapse("EVA SUIT"); } }
// 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); }
private static void ResourceUpdate(Vessel v, Harvester harvester, double min_abundance, double elapsed_s) { double abundance = SampleAbundance(v, harvester); if (abundance > min_abundance) { ResourceRecipe recipe = new ResourceRecipe(ResourceBroker.Harvester); recipe.AddInput("ElectricCharge", harvester.ec_rate * elapsed_s); recipe.AddOutput( harvester.resource, Harvester.AdjustedRate(harvester, engineer_cs, Lib.CrewList(v), abundance) * elapsed_s, dump: false); ResourceCache.AddRecipe(v, recipe); } }
public void Execute(Vessel v, VesselData vd, VesselResources resources) { // get crew List <ProtoCrewMember> crew = Lib.CrewList(v); // get resource handler ResourceInfo res = resources.GetResource(v, resource); // get data from db SupplyData sd = v.KerbalismData().Supply(resource); // message obey user config bool show_msg = resource == "ElectricCharge" ? vd.cfg_ec : vd.cfg_supply; // messages are shown only if there is some capacity and the vessel is manned // special case: ElectricCharge related messages are shown for unmanned vessels too if (res.Capacity > double.Epsilon && (crew.Count > 0 || resource == "ElectricCharge")) { // manned/probe message variant uint variant = crew.Count > 0 ? 0 : 1u; // manage messages if (res.Level <= double.Epsilon && sd.message < 2) { if (empty_message.Length > 0 && show_msg) { Message.Post(Severity.danger, Lib.ExpandMsg(empty_message, v, null, variant)); } sd.message = 2; } else if (res.Level < low_threshold && sd.message < 1) { if (low_message.Length > 0 && show_msg) { Message.Post(Severity.warning, Lib.ExpandMsg(low_message, v, null, variant)); } sd.message = 1; } else if (res.Level > low_threshold && sd.message > 0) { if (refill_message.Length > 0 && show_msg) { Message.Post(Severity.relax, Lib.ExpandMsg(refill_message, v, null, variant)); } sd.message = 0; } } }
void VesselDestroyed(Vessel v) { // for each part foreach (Part p in v.parts) { // forget all potential vessel data DB.vessels.Remove(p.flightID); } // rescan the damn kerbals // - vessel crew is empty at destruction time // - we can't even use the flightglobal roster, because sometimes it isn't updated yet at this point HashSet <string> kerbals_alive = new HashSet <string>(); HashSet <string> kerbals_dead = new HashSet <string>(); foreach (Vessel ov in FlightGlobals.Vessels) { foreach (ProtoCrewMember c in Lib.CrewList(ov)) { kerbals_alive.Add(c.name); } } foreach (KeyValuePair <string, KerbalData> p in DB.Kerbals()) { if (!kerbals_alive.Contains(p.Key)) { kerbals_dead.Add(p.Key); } } foreach (string n in kerbals_dead) { // we don't know if the kerbal really is dead, or if it is just not currently assigned to a mission DB.KillKerbal(n, false); } // purge the caches Cache.Purge(v); ResourceCache.Purge(v); }
// 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); }
public static void telemetry(this Panel p, Vessel v) { // if vessel doesn't exist anymore if (FlightGlobals.FindVessel(v.id) == null) return; // get info from the cache vessel_info vi = Cache.VesselInfo(v); // if not a valid vessel if (!vi.is_valid) return; // set metadata p.title(Lib.BuildString(Lib.Ellipsis(v.vesselName, 20), " <color=#cccccc>TELEMETRY</color>")); // time-out simulation if (p.timeout(vi)) return; // get vessel data VesselData vd = DB.Vessel(v); // get resources vessel_resources resources = ResourceCache.Get(v); // get crew var crew = Lib.CrewList(v); // draw the content render_crew(p, crew); render_greenhouse(p, vi); render_supplies(p, v, vi, resources); render_habitat(p, v, vi); render_environment(p, v, vi); // collapse eva kerbal sections into one if (v.isEVA) p.collapse("EVA SUIT"); }
static void ProcessAsteroidDrill(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleAsteroidDrill asteroid_drill, vessel_resources resources, double elapsed_s) { // note: untested // note: ignore stock temperature mechanic of asteroid drills // note: ignore autoshutdown // note: 'undo' stock behaviour by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation) // if active if (Lib.Proto.GetBool(m, "IsActivated")) { // get asteroid data ProtoPartModuleSnapshot asteroid_info = null; ProtoPartModuleSnapshot asteroid_resource = null; foreach (ProtoPartSnapshot pp in v.protoVessel.protoPartSnapshots) { if (asteroid_info == null) { asteroid_info = pp.modules.Find(k => k.moduleName == "ModuleAsteroidInfo"); } if (asteroid_resource == null) { asteroid_resource = pp.modules.Find(k => k.moduleName == "ModuleAsteroidResource"); } } // if there is actually an asteroid attached to this active asteroid drill (it should) if (asteroid_info != null && asteroid_resource != null) { // get some data double mass_threshold = Lib.Proto.GetDouble(asteroid_info, "massThresholdVal"); double mass = Lib.Proto.GetDouble(asteroid_info, "currentMassVal"); double abundance = Lib.Proto.GetDouble(asteroid_resource, "abundance"); string res_name = Lib.Proto.GetString(asteroid_resource, "resourceName"); double res_density = PartResourceLibrary.Instance.GetDefinition(res_name).density; // if asteroid isn't depleted if (mass > mass_threshold && abundance > double.Epsilon) { // deduce crew bonus int exp_level = -1; if (asteroid_drill.UseSpecialistBonus) { foreach (ProtoCrewMember c in Lib.CrewList(v)) { if (c.experienceTrait.Effects.Find(k => k.Name == asteroid_drill.ExperienceEffect) != null) { exp_level = Math.Max(exp_level, c.experienceLevel); } } } double exp_bonus = exp_level < 0 ? asteroid_drill.EfficiencyBonus * asteroid_drill.SpecialistBonusBase : asteroid_drill.EfficiencyBonus * (asteroid_drill.SpecialistBonusBase + (asteroid_drill.SpecialistEfficiencyFactor * (exp_level + 1))); // determine resource extracted double res_amount = abundance * asteroid_drill.Efficiency * exp_bonus * elapsed_s; // transform EC into mined resource resource_recipe recipe = new resource_recipe(); recipe.Input("ElectricCharge", asteroid_drill.PowerConsumption * elapsed_s); recipe.Output(res_name, res_amount, true); resources.Transform(recipe); // if there was ec // note: comparing against amount in previous simulation step if (resources.Info(v, "ElectricCharge").amount > double.Epsilon) { // consume asteroid mass Lib.Proto.Set(asteroid_info, "currentMassVal", (mass - res_density * res_amount)); } } } // undo stock behaviour by forcing last_update_time to now Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime()); } }
/// <summary> /// return true if the crew of specified vessel satisfy the specs /// </summary> public bool Check(Vessel v) { return(Check(Lib.CrewList(v))); }
/// <summary> /// Returns the total crew level bonus (= how many levels above required minimum is the crew). /// </summary> public int Bonus(Vessel v, int requiredLevel = Int16.MinValue) { return(Bonus(Lib.CrewList(v), requiredLevel)); }
bool Render_vessel(Panel p, Vessel v, bool selected = false) { // get vessel info Vessel_info vi = Cache.VesselInfo(v); // skip invalid vessels if (!vi.is_valid) { return(false); } // get data from db VesselData vd = DB.Vessel(v); // get vessel crew List <ProtoCrewMember> crew = Lib.CrewList(v); // get vessel name string vessel_name = v.isEVA ? crew[0].name : v.vesselName; // get body name string body_name = v.mainBody.name.ToUpper(); // skip filtered vessels if (!Filter_match(v.vesselType, vd.group + " " + body_name + " " + vessel_name)) { return(false); } // render entry p.AddHeader ( Lib.BuildString("<b>", Lib.Ellipsis(vessel_name, Styles.ScaleStringLength(((page == MonitorPage.data || page == MonitorPage.log || selected_id == Guid.Empty) && !Lib.IsFlight()) ? 45 : 25)), "</b> <size=", Styles.ScaleInteger(9).ToString(), ">", Lib.Color("#cccccc", Lib.Ellipsis(body_name, Styles.ScaleStringLength(8))), "</size>"), string.Empty, () => { selected_id = selected_id != v.id ? v.id : Guid.Empty; } ); // vessel type icon if (!selected) { p.SetIcon(GetVesselTypeIcon(v.vesselType), v.vesselType.displayDescription(), () => { selected_id = selected_id != v.id ? v.id : Guid.Empty; }); } else { if (FlightGlobals.ActiveVessel != v) { if (Lib.IsFlight()) { p.SetIcon(GetVesselTypeIcon(v.vesselType), "Go to vessel!", () => Lib.Popup ("Warning!", Lib.BuildString("Do you really want go to ", vessel_name, " vessel?"), new DialogGUIButton("Go", () => { GotoVessel.JumpToVessel(v); }), new DialogGUIButton("Target", () => { GotoVessel.SetVesselAsTarget(v); }), new DialogGUIButton("Stay", () => { }))); } else { p.SetIcon(GetVesselTypeIcon(v.vesselType), "Go to vessel!", () => Lib.Popup ("Warning!", Lib.BuildString("Do you really want go to ", vessel_name, " vessel?"), new DialogGUIButton("Go", () => { GotoVessel.JumpToVessel(v); }), new DialogGUIButton("Stay", () => { }))); } } else { p.SetIcon(GetVesselTypeIcon(v.vesselType), v.vesselType.displayDescription(), () => { }); } } // problem indicator Indicator_problems(p, v, vi, crew); // battery indicator Indicator_ec(p, v, vi); // supply indicator if (Features.Supplies) { Indicator_supplies(p, v, vi); } // reliability indicator if (Features.Reliability) { Indicator_reliability(p, v, vi); } // signal indicator if (API.Comm.handlers.Count > 0 || HighLogic.fetch.currentGame.Parameters.Difficulty.EnableCommNet) { Indicator_signal(p, v, vi); } // done return(true); }
// 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; }
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); } }
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 < 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); } }