// implement scrubber mechanics for unloaded vessels public static void BackgroundUpdate(Vessel vessel, uint flight_id) { // get data ProtoPartModuleSnapshot m = Lib.GetProtoModule(vessel, flight_id, "Scrubber"); bool is_enabled = Lib.GetProtoValue<bool>(m, "is_enabled"); double ec_rate = Lib.GetProtoValue<double>(m, "ec_rate"); double co2_rate = Lib.GetProtoValue<double>(m, "co2_rate"); double efficiency = Lib.GetProtoValue<double>(m, "efficiency"); // if for some reason efficiency wasn't set, default to 50% // note: for example, resque vessels scrubbers get background update without prelaunch if (efficiency <= double.Epsilon) efficiency = 0.5; // get time elapsed from last update double elapsed_s = TimeWarp.fixedDeltaTime; // if inside breathable atmosphere if (LifeSupport.BreathableAtmosphere(vessel)) { // produce oxygen from the intake Lib.RequestResource(vessel, "Oxygen", -Settings.IntakeOxygenRate * elapsed_s); } // if outside breathable atmosphere and enabled else if (is_enabled) { // recycle CO2+EC into oxygen double co2_required = co2_rate * elapsed_s; double co2 = Lib.RequestResource(vessel, "CO2", co2_required); double ec_required = ec_rate * elapsed_s * (co2 / co2_required); double ec = Lib.RequestResource(vessel, "ElectricCharge", ec_required); Lib.RequestResource(vessel, "Oxygen", -co2 * efficiency); } }
// implement scrubber mechanics public void FixedUpdate() { // do nothing in the editor if (HighLogic.LoadedSceneIsEditor) return; // deduce quality from technological level if necessary // note: done at prelaunch to avoid problems with start()/load() and the tech tree being not consistent if (vessel.situation == Vessel.Situations.PRELAUNCH) efficiency = DeduceEfficiency(); // if for some reason efficiency wasn't set, default to 50% // note: for example, resque vessels never get to prelaunch if (efficiency <= double.Epsilon) efficiency = 0.5; // get time elapsed from last update double elapsed_s = TimeWarp.fixedDeltaTime; // if inside breathable atmosphere if (LifeSupport.BreathableAtmosphere(this.vessel)) { // produce oxygen from the intake this.part.RequestResource("Oxygen", -Settings.IntakeOxygenRate * elapsed_s); // set status Status = "Intake"; } // if outside breathable atmosphere and enabled else if (is_enabled) { // recycle CO2+EC into oxygen double co2_required = co2_rate * elapsed_s; double co2 = this.part.RequestResource("CO2", co2_required); double ec_required = ec_rate * elapsed_s * (co2 / co2_required); double ec = this.part.RequestResource("ElectricCharge", ec_required); this.part.RequestResource("Oxygen", -co2 * efficiency); // set status Status = co2 <= double.Epsilon ? "No CO2" : ec <= double.Epsilon ? "No Power" : "Running"; } // if outside breathable atmosphere and disabled else { // set status Status = "Off"; } // add efficiency to status Status += " (Efficiency: " + (efficiency * 100.0).ToString("F0") + "%)"; }
GUIContent indicator_supplies(Vessel v, List<Scrubber> scrubbers, List<Greenhouse> greenhouses) { // get food & oxygen info double food_amount = Lib.GetResourceAmount(v, "Food"); double food_capacity = Lib.GetResourceCapacity(v, "Food"); double food_level = food_capacity > 0.0 ? food_amount / food_capacity : 1.0; double oxygen_amount = Lib.GetResourceAmount(v, "Oxygen"); double oxygen_capacity = Lib.GetResourceCapacity(v, "Oxygen"); double oxygen_level = oxygen_capacity > 0.0 ? oxygen_amount / oxygen_capacity : 1.0; double level = Math.Min(food_level, oxygen_level); // store the icon and tooltip GUIContent state = new GUIContent(); // choose an icon if (level <= Settings.ResourceDangerThreshold) state.image = icon_supplies_danger; else if (level <= Settings.ResourceWarningThreshold) state.image = icon_supplies_warning; else state.image = icon_supplies_nominal; // if there is someone on board List<string> tooltips = new List<string>(); int crew_count = Lib.CrewCount(v); if (crew_count > 0) { // get oxygen recycled by scrubbers double oxygen_recycled = 0.0; double ec_left = Lib.GetResourceAmount(v, "ElectricCharge"); double co2_left = Lib.GetResourceAmount(v, "CO2"); foreach(Scrubber scrubber in scrubbers) { if (scrubber.is_enabled) { double co2_consumed = Math.Max(co2_left, scrubber.co2_rate); ec_left -= scrubber.ec_rate; co2_left -= co2_consumed; if (ec_left > -double.Epsilon && co2_left > -double.Epsilon) oxygen_recycled += co2_consumed * scrubber.efficiency; else break; } } // calculate time until depletion for food double food_consumption = (double)crew_count * Settings.FoodPerMeal / Settings.MealFrequency; if (food_capacity > double.Epsilon && food_consumption > double.Epsilon) { double food_depletion = food_amount / food_consumption; tooltips.Add(food_amount / food_capacity > Settings.ResourceDangerThreshold ? "Food: <b>" + (food_level * 100.0).ToString("F0") + "%, </b>deplete in <b>" + Lib.HumanReadableDuration(food_depletion) + "</b>" : "Food: <b>depleted</b>"); } // calculate time until depletion for oxygen double oxygen_consumption = !LifeSupport.BreathableAtmosphere(v) ? (double)crew_count * Settings.OxygenPerSecond - oxygen_recycled : 0.0; if (oxygen_capacity > double.Epsilon && oxygen_consumption > double.Epsilon) { double oxygen_depletion = oxygen_amount / oxygen_consumption; tooltips.Add(oxygen_amount / oxygen_capacity > Settings.ResourceDangerThreshold ? "Oxygen: <b>" + (oxygen_level * 100.0).ToString("F0") + "%, </b>deplete in <b>" + Lib.HumanReadableDuration(oxygen_depletion) + "</b>" : "Oxygen: <b>depleted</b>"); } } state.tooltip = string.Join("\n", tooltips.ToArray()); return state; }
void toEVA(GameEvents.FromToAction <Part, Part> data) { // determine if inside breathable atmosphere bool breathable = LifeSupport.BreathableAtmosphere(data.from.vessel); // get total crew in the origin vessel double tot_crew = (double)data.from.vessel.GetVesselCrew().Count + 1.0; // add resource definitions to EVA vessel part Lib.SetupResource(data.to, "ElectricCharge", 0.0, Settings.ElectricChargeOnEVA); if (!breathable) { Lib.SetupResource(data.to, "Oxygen", 0.0, Settings.OxygenOnEVA); } // determine how much MonoPropellant to get // note: never more that the 'share' of this kerbal double monoprop = Math.Min(Lib.GetResourceAmount(data.from.vessel, "MonoPropellant") / tot_crew, Settings.MonoPropellantOnEVA); // determine how much ElectricCharge to get // note: never more that the 'share' of this kerbal // note: always keep half the ec in the vessel double ec = Math.Min(Lib.GetResourceAmount(data.from.vessel, "ElectricCharge") / (tot_crew * 2.0), Settings.ElectricChargeOnEVA); // EVA vessels start with 5 units of eva fuel, remove them data.to.RequestResource("EVA Propellant", 5.0); // transfer monoprop data.to.RequestResource("EVA Propellant", -data.from.RequestResource("MonoPropellant", monoprop)); // transfer ec data.to.RequestResource("ElectricCharge", -data.from.RequestResource("ElectricCharge", ec)); // if outside breathable atmosphere if (!breathable) { // determine how much Oxygen to get // note: never more that the 'share' of this kerbal double oxygen = Math.Min(Lib.GetResourceAmount(data.from.vessel, "Oxygen") / tot_crew, Settings.OxygenOnEVA); // transfer oxygen data.to.RequestResource("Oxygen", -data.from.RequestResource("Oxygen", oxygen)); } // get KerbalEVA KerbalEVA kerbal = data.to.FindModuleImplementing <KerbalEVA>(); // turn off headlamp light, to avoid stock bug that show the light for a split second when going on eva EVA.SetHeadlamp(kerbal, false); EVA.SetFlares(kerbal, false); // remove the helmet if inside breathable atmosphere // note: done in EVA::FixedUpdate(), but also done here avoid 'popping' of the helmet when going on eva EVA.SetHelmet(kerbal, !breathable); // remember if the kerbal has an helmet in the EVA module data.to.FindModuleImplementing <EVA>().has_helmet = !breathable; // show warning if there isn't monoprop in the eva suit if (monoprop <= double.Epsilon && !Lib.Landed(data.from.vessel)) { Message.Post(Severity.danger, "There isn't any <b>MonoPropellant</b> in the EVA suit", "Don't let the ladder go!"); } }