/// <summary> /// Manages the true condition which enables the "Fill EVA" button. /// </summary> /// <returns></returns> private bool EnableFillButton() { var info = EVALifeSupportTracker.GetEVALSInfo(kerbal.name); return(info.ls_current < info.ls_max && !FlightGlobals.ActiveVessel.isEVA && fillButtonEnable); }
public void OnSave(ConfigNode topnode) { Util.Log("Loader OnSave(..) in " + HighLogic.LoadedScene.ToString()); if (topnode.HasNode(TOPNAME)) { Util.Log("CheckThis -> Node " + TOPNAME + " already exists!"); } ConfigNode scenario_node = topnode.AddNode(TOPNAME); EVALifeSupportTracker.Save(scenario_node); ContractChecker.Save(scenario_node); }
private static void FillEVAResource(string kerbalName) { // This works right now because the tracker updates live. // May break in the future. var info = EVALifeSupportTracker.GetEVALSInfo(kerbalName); double eva_request = info.ls_max - info.ls_current; Cons2LSModule module = FlightGlobals.ActiveVessel.FindPartModuleImplementing <Cons2LSModule>(); double obtained = module.part.RequestResource(C.NAME_CONSUMABLES, C.CONS_PER_EVA_LS * eva_request); double add = obtained / C.CONS_PER_EVA_LS; EVALifeSupportTracker.AddEVAAmount(kerbalName, add, EVA_Resource.LifeSupport); Util.Log(" EVA Request = " + eva_request); Util.Log(" Amt Obtained = " + obtained); }
public void OnLoad(Game game) { Util.Log("Loader OnLoad(..) in " + HighLogic.LoadedScene.ToString()); ConfigNode scenario_node; if (game.config.HasNode(TOPNAME)) { scenario_node = game.config.GetNode(TOPNAME); } else { scenario_node = new ConfigNode(TOPNAME); } EVALifeSupportTracker.Load(scenario_node); ContractChecker.Load(scenario_node); }
public void FixedUpdate() { PartResource resource = part.Resources[C.NAME_EVA_LIFESUPPORT]; double initial_value = resource.amount; string kerbal_name = part.protoModuleCrew[0].name; // Update tracking info. // While Kerbal is in EVA, PartResource contains the "primary" value, // and tracking is only updated as a consequence. // It will be a frame behind, but that should be okay. EVALifeSupportTracker.SetCurrentAmount(kerbal_name, initial_value, EVA_Resource.LifeSupport); EVALifeSupportTracker.SetCurrentAmount(kerbal_name, part.Resources[C.NAME_EVA_PROPELLANT].amount, EVA_Resource.Propellant); // If Kerbal is below this altitude in an atmosphere with oxygen, // LifeSupport is irrelevant if (Util.BreathableAir(vessel)) { return; } // -- Reduce resource -- double retd = part.RequestResource(C.NAME_EVA_LIFESUPPORT, C.EVA_LS_DRAIN_PER_SEC * TimeWarp.fixedDeltaTime); if (initial_value > C.EVA_LS_30_SECONDS && resource.amount <= C.EVA_LS_30_SECONDS) { TimeWarp.SetRate(0, true); Util.PostUpperMessage(kerbal_name + " has 30 seconds to live!", 1); resource.amount = C.EVA_LS_30_SECONDS; } // Necessary to check if crew count > 0? if (retd == 0.0) { Util.KillKerbals(this); part.explode(); } }
private void UpdateGUI() { drewgui = true; Vessel vessel = FlightGlobals.ActiveVessel; int crewCount = vessel.GetCrewCount(); var parts = vessel.FindPartModulesImplementing <LifeSupportReportable>(); // Update live Kerbal numbers foreach (LifeSupportReportable module in parts) { bool empty; string timestr = module.ReportLifeSupport(out empty); if (emptyPartLabels.ContainsKey(module.part)) { emptyPartLabels[module.part].SetOptionText($"{ORANGE}{timestr}</color>"); continue; } foreach (ProtoCrewMember kerbal in module.part.protoModuleCrew) { double evaLS = EVALifeSupportTracker.GetEVALSInfo(kerbal.name).ls_current; if (compress) { if (!vessel.isEVA && !empty) { labelMap[kerbal.name].nameLabel.SetOptionText(labelMap[kerbal.name].compressNames[0]); labelMap[kerbal.name].shipLS.SetOptionText(timestr); } else { labelMap[kerbal.name].nameLabel.SetOptionText(labelMap[kerbal.name].compressNames[1]); string evastr = Util.DaysToString(evaLS / C.EVA_LS_DRAIN_PER_DAY); labelMap[kerbal.name].shipLS.SetOptionText(evastr); } } else { string evastr = Util.DaysToString(evaLS / C.EVA_LS_DRAIN_PER_DAY); labelMap[kerbal.name].shipLS.SetOptionText(timestr); labelMap[kerbal.name].evaLS.SetOptionText(evastr); } if (Config.DEBUG_SHOW_EVA) { var info = EVALifeSupportTracker.GetEVALSInfo(kerbal.name); labelMap[kerbal.name].evaLS_Value.SetOptionText(info.ls_current.ToString()); labelMap[kerbal.name].evaProp.SetOptionText(info.prop_current.ToString()); } } } // Any Kerbals not found above are assumed KIA // Would be great to move this to a GameEvent // such as GameEvents.onKerbalRemoved() if (crewCount < labelMap.Count) { List <string> fullcrew = vessel.GetVesselCrew().ConvertAll(a => a.name); foreach (string name in labelMap.Keys.ToArray()) { if (!fullcrew.Contains(name)) { labelMap[name].shipLS.SetOptionText("KIA"); labelMap[name].evaLS.SetOptionText("KIA"); labelMap.Remove(name); } } } // Update status double curr, max; vessel.GetConnectedResourceTotals(lsID, out curr, out max); string statusOne; // Status (no crew/breathable/active) string statusTwo; // Remaining Consumables/EVA Life Support string lsActive; if (crewCount == 0) { lsActive = "No crew"; } else if (Util.BreathableAir(vessel)) { lsActive = "Breathable air"; } else { lsActive = "ACTIVE"; } statusOne = $"{(compress ? "" : "Status: ")}{lsActive}"; if (FlightGlobals.ActiveVessel.isEVA) { vessel.GetConnectedResourceTotals(evapropID, out curr, out max); string prefix = ""; string suffix = "</color>"; if (curr < 0.5) { prefix = C.GUI_HARDWARN_COLOR; } else if (curr < 1.0) { prefix = C.GUI_LITEWARN_COLOR; } else { suffix = ""; } statusTwo = $"Propellant: {prefix}{string.Format("{0:0.00}", curr)}{suffix}"; } else { vessel.GetConnectedResourceTotals(consID, out curr, out max); double consDays = curr / C.CONS_PER_LS; statusTwo = $"{(compress ? "" : "Consumables: ")}{Util.DaysToString(consDays)}"; } statusLabel.SetOptionText($"{statusOne}\n{statusTwo}"); }
/// <summary> /// Handles LifeSupport, Consumables, and EVA LifeSupport drain on startup /// </summary> private void StartupMain() { bool skip_startup_request = false; showed_eva_warning = false; // Check if this ship belongs to a Rescue contract // and has not been set up with LifeSupport for (int i = 0; i < ContractChecker.Guids.Count; i++) { string contract_guid = ContractChecker.Guids[i]; if (part.flightID.ToString() == ContractChecker.GetPartID(contract_guid)) { Util.Log("For Contract GUID: " + contract_guid); Util.Log("Found PartID " + part.flightID + ", skipping startup request for " + vessel.name); // Remove guid from tracking, vessel will only transition to Owned once ContractChecker.Guids.Remove(contract_guid); part.Resources[C.NAME_LIFESUPPORT].amount = part.Resources[C.NAME_LIFESUPPORT].maxAmount / 2.0; skip_startup_request = true; break; } } double seconds_remaining = 0; // -- 1. Request primary LifeSupport resource and capture seconds unaccounted for -- if (!skip_startup_request) { // Use the seconds remaining to calculate how much EVA LifeSupport needs to be deducted seconds_remaining = Util.StartupRequest(this, C.NAME_LIFESUPPORT, C.LS_DRAIN_PER_SEC); } // Return early to avoid scanning all the parts // Just make sure all Kerbals are in tracking before exit, // otherwise this is taken care of in section 3 if (seconds_remaining < C.DOUBLE_MARGIN) { foreach (ProtoCrewMember kerbal in part.protoModuleCrew) { EVALifeSupportTracker.AddKerbalToTracking(kerbal.name); } return; } // -- 2. Deduct from Consumables if vessel has Converter -- bool has_manned_converter = false; List <Cons2LSModule> consParts = vessel.FindPartModulesImplementing <Cons2LSModule>(); foreach (Cons2LSModule converter in consParts) { if (converter.IsOperational()) { has_manned_converter = true; break; } } // Found Converter, convert Consumables. // Ignore ElectricCharge here. Too many escapes re. capacity, // charge rate, LOS to sun, etc. Assume Kerbals have been // converting slowly while player is away. if (has_manned_converter) { double cons_over_lifesupport = C.CONV_CONS_PER_SEC / C.CONV_LS_PER_SEC; double cons_per_sec = C.LS_DRAIN_PER_SEC * cons_over_lifesupport; // Initial Consumables request for time passed double request = seconds_remaining * cons_per_sec * part.protoModuleCrew.Count; double frac_obtained = part.RequestResource(C.NAME_CONSUMABLES, request) / request; seconds_remaining *= (1 - frac_obtained); // Now add a bit more LifeSupport double cons_extra_request = C.AUTO_LS_REFILL_EXTRA * cons_over_lifesupport * part.protoModuleCrew.Count; double frac_extra_obtained = part.RequestResource(C.NAME_CONSUMABLES, cons_extra_request) / cons_extra_request; part.RequestResource(C.NAME_LIFESUPPORT, -C.AUTO_LS_REFILL_EXTRA * frac_extra_obtained, C.FLOWMODE_LIFESUPPORT); } // -- 3. Finally, deduct from EVA LifeSupport -- double eva_diff = seconds_remaining * C.EVA_LS_DRAIN_PER_SEC; Util.Log(seconds_remaining + " seconds remaining for " + vessel.vesselName); if (seconds_remaining < C.DOUBLE_MARGIN) { eva_diff = 0; } Util.Log("Deducting " + eva_diff + " " + C.NAME_EVA_LIFESUPPORT); foreach (ProtoCrewMember kerbal in part.protoModuleCrew) { // If Kerbal isn't yet in tracking (i.e. mod was just installed), // add EVA LifeSupport but don't deduct anything. That seems unfair. if (!EVALifeSupportTracker.InTracking(kerbal.name)) { EVALifeSupportTracker.AddKerbalToTracking(kerbal.name); continue; } // Current EVA LifeSupport after draining from tracking double current = EVALifeSupportTracker.AddEVAAmount(kerbal.name, -eva_diff, EVA_Resource.LifeSupport); if (current < C.KILL_BUFFER) { EVALifeSupportTracker.SetCurrentAmount(kerbal.name, C.KILL_BUFFER, EVA_Resource.LifeSupport); } else if (current < C.EVA_LS_30_SECONDS) { Util.PostUpperMessage(kerbal.name + " has " + (int)(current / C.EVA_LS_DRAIN_PER_SEC) + " seconds to live!", 1); } // If Kerbal is about to die, messages will already be printed. // Don't clutter the screen with the "now on EVA LS" message too. if (current < C.EVA_LS_30_SECONDS) { showed_eva_warning = true; } } }
public void FixedUpdate() { // If part is unmanned, nothing to do if (part.protoModuleCrew.Count == 0) { showed_eva_warning = false; return; } // If vessel is below this altitude in an atmosphere with oxygen, // LifeSupport is irrelevant if (Util.BreathableAir(vessel)) { return; } int crew_count = part.protoModuleCrew.Count; // How much lifesupport to request double ls_request = crew_count * C.LS_DRAIN_PER_SEC * TimeWarp.fixedDeltaTime; // Request resource based on rates defined by constants double ret_rs = part.RequestResource(C.NAME_LIFESUPPORT, ls_request, C.FLOWMODE_LIFESUPPORT); // If LifeSupport exists or is restored, reset EVA warning and return if (ret_rs > 0.0) { showed_eva_warning = false; return; } // Otherwise, begin deducting EVA LifeSupport if (!showed_eva_warning) { TimeWarp.SetRate(0, true); string vessel_name = vessel.isActiveVessel ? part.partInfo.title : vessel.vesselName; showed_eva_warning = true; Util.PostUpperMessage("Crew in " + vessel_name + " has run out of " + C.NAME_LIFESUPPORT + ",\n is consuming " + C.NAME_EVA_LIFESUPPORT, 1); } // Modify crew list in place int i = 0; while (i < part.protoModuleCrew.Count) { ProtoCrewMember kerbal = part.protoModuleCrew[i]; double request = C.EVA_LS_DRAIN_PER_SEC * TimeWarp.fixedDeltaTime; double current_eva = EVALifeSupportTracker.AddEVAAmount(kerbal.name, -request, EVA_Resource.LifeSupport); if (current_eva + request > C.EVA_LS_30_SECONDS && current_eva <= C.EVA_LS_30_SECONDS) { TimeWarp.SetRate(0, true); Util.PostUpperMessage(kerbal.name + " has 30 seconds to live!", 1); // Set to 30 seconds in case of large timewarp. EVALifeSupportTracker.SetCurrentAmount(kerbal.name, C.EVA_LS_30_SECONDS, EVA_Resource.LifeSupport); } if (EVALifeSupportTracker.GetEVALSInfo(kerbal.name).ls_current < C.DOUBLE_MARGIN) { Util.KillKerbal(this, kerbal); continue; } i++; } }
public override void OnStart(StartState state) { Util.Log("EVALifeSupportModule OnStart()"); // Check if EVA Kerbal has exactly one ProtoCrewMember if (part.protoModuleCrew.Count == 0) { string msg = "0 PMCs found in EVA Kerbal: " + part.name; Util.Log(msg); throw new IndexOutOfRangeException(msg); } else if (part.protoModuleCrew.Count > 1) { Util.Log("Weird...multiple PMCs found in EVA Kerbal: " + part.name); } // To avoid conflicts with Unity variable "name" string kerbal_name = part.protoModuleCrew[0].name; PartResource ls_resource = null; foreach (PartResource pr in part.Resources) { if (pr.resourceName == C.NAME_EVA_LIFESUPPORT) { ls_resource = pr; break; } } // Kerbals assigned after this mod's installation should already be tracked, // but for Kerbals already in flight, add EVA LS according to current state // of astronaut complex EVALifeSupportTracker.AddKerbalToTracking(kerbal_name); if (ls_resource == null) { // If not found, add EVA LS resource to this PartModule. Util.Log("Adding " + C.NAME_EVA_LIFESUPPORT + " resource to " + part.name); var info = EVALifeSupportTracker.GetEVALSInfo(kerbal_name); ConfigNode resource_node = new ConfigNode("RESOURCE"); resource_node.AddValue("name", C.NAME_EVA_LIFESUPPORT); resource_node.AddValue("amount", info.ls_current.ToString()); resource_node.AddValue("maxAmount", info.ls_max.ToString()); ls_resource = part.AddResource(resource_node); Util.Log("Added EVA LS resource to " + part.name); } else { // If found, this EVA is already active - deduct LS. Util.StartupRequest(this, C.NAME_EVA_LIFESUPPORT, C.EVA_LS_DRAIN_PER_SEC); if (ls_resource.amount < C.KILL_BUFFER) { ls_resource.amount = C.KILL_BUFFER; } else if (ls_resource.amount < C.EVA_LS_30_SECONDS) { Util.PostUpperMessage(kerbal_name + " has " + (int)(ls_resource.amount / C.EVA_LS_DRAIN_PER_SEC) + " seconds to live!", 1); } } PartResource prop_resource = part.Resources[C.NAME_EVA_PROPELLANT]; // Necessary to override game's default behavior, which refills // EVA Propellant automatically every time EVA Kerbal is reset var eva_info = EVALifeSupportTracker.GetEVALSInfo(kerbal_name); prop_resource.maxAmount = eva_info.prop_max; prop_resource.amount = eva_info.prop_current; // Will this add safety Propellant on every load? // Should only be added when Kerbal first leaves ship // // Confirmed (1.0.5). At the time of this comment, the Propellant // threshold is brutally low (0.1), so it's okay, // but this should be changed in the future. if (prop_resource.amount < C.EVA_PROP_SAFE_MIN) { prop_resource.amount = C.EVA_PROP_SAFE_MIN; } // If difficulty option "Immediate Level Up" is selected, // immediately set this Kerbal's EVA to new max if (this.vessel.CanUpdateEVAStat(Config.EVA_MAX_UPDATE)) { ls_resource.maxAmount = Util.MaxAllowedEVA(EVA_Resource.LifeSupport); prop_resource.maxAmount = Util.MaxAllowedEVA(EVA_Resource.Propellant); } base.OnStart(state); }