public void Repair() { // do nothing if something is wrong, or the eva kerbal is dead Vessel v = FlightGlobals.ActiveVessel; if (v == null || !v.isEVA || EVA.IsDead(v)) return; // if the kerbal isn't an engineer, show a message and do nothing if (v.GetVesselCrew()[0].trait != "Engineer") { Message.Post("Only <b>Engineers</b> can repair parts"); return; } // restore full functionality Apply(Math.Pow(2.0, (double)malfunctions)); malfunctions = 0; // show a message Message.Post(Severity.relax, PrepareMsg(repair_msg, v, malfunctions)); }
public static void SetLocks(Vessel v, Vessel_Info vi) { // lock controls for EVA death if (EVA.IsDead(v)) { InputLockManager.SetControlLock(ControlTypes.EVA_INPUT, "eva_dead_lock"); } // lock controls for probes without signal if (vi.is_valid && !vi.connection.linked && vi.crew_count == 0 && Settings.UnlinkedControl != UnlinkedCtrl.full) { // choose no controls, or only full/zero throttle and staging ControlTypes ctrl = Settings.UnlinkedControl == UnlinkedCtrl.none ? ControlTypes.ALL_SHIP_CONTROLS : ControlTypes.PARTIAL_SHIP_CONTROLS; InputLockManager.SetControlLock(ctrl, "no_signal_lock"); FlightInputHandler.state.mainThrottle = 0.0f; } }
[KSPEvent(guiActive = false, guiActiveUnfocused = true, guiActiveUncommand = true, guiName = "#KERBALISM_HardDrive_TransferData", active = true, groupName = "Science", groupDisplayName = "#KERBALISM_Group_Science")] //Science public void StoreData() { // disable for dead eva kerbals Vessel v = FlightGlobals.ActiveVessel; if (v == null || EVA.IsDead(v)) { return; } // transfer data if (!Drive.Transfer(v, drive, PreferencesScience.Instance.sampleTransfer || Lib.CrewCount(v) > 0)) { Message.Post ( Lib.Color(Lib.BuildString(Local.HardDrive_WARNING_title), Lib.Kolor.Red, true), //"WARNING: not evering copied" Lib.BuildString(Local.HardDrive_WARNING) //"Storage is at capacity" ); } }
public void TakeData() { // disable for dead eva kerbals Vessel v = FlightGlobals.ActiveVessel; if (v == null || EVA.IsDead(v)) { return; } // transfer data if (!Drive.Transfer(drive, v, PreferencesScience.Instance.sampleTransfer || Lib.CrewCount(v) > 0)) { Message.Post ( Lib.Color("red", Lib.BuildString("WARNING: not evering copied"), true), Lib.BuildString("Storage is at capacity") ); } }
void ToEVA(GameEvents.FromToAction<Part, Part> data) { // get total crew in the origin vessel double tot_crew = (double)Lib.CrewCount(data.from.vessel) + 1.0; // get vessel resources handler Vessel_resources resources = ResourceCache.Get(data.from.vessel); // setup supply resources capacity in the eva kerbal Profile.SetupEva(data.to); // for each resource in the kerbal for (int i = 0; i < data.to.Resources.Count; ++i) { // get the resource PartResource res = data.to.Resources[i]; // determine quantity to take double quantity = Math.Min(resources.Info(data.from.vessel, res.resourceName).amount / tot_crew, res.maxAmount); // remove resource from vessel quantity = data.from.RequestResource(res.resourceName, quantity); // add resource to eva kerbal data.to.RequestResource(res.resourceName, -quantity); } // show warning if there isn't monoprop in the eva suit string prop_name = Lib.EvaPropellantName(); if (Lib.Amount(data.to, prop_name) <= double.Epsilon && !Lib.Landed(data.from.vessel)) { Message.Post(Severity.danger, Lib.BuildString("There isn't any <b>", prop_name, "</b> in the EVA suit"), "Don't let the ladder go!"); } // turn off headlamp light, to avoid stock bug that show them for a split second when going on eva KerbalEVA kerbal = data.to.FindModuleImplementing<KerbalEVA>(); EVA.HeadLamps(kerbal, false); // execute script DB.Vessel(data.from.vessel).computer.Execute(data.from.vessel, ScriptType.eva_out); }
public void Update() { UpdateCapacity(); if (Lib.IsFlight()) { // show DATA UI button, with size info Events["ToggleUI"].guiName = Lib.StatusToggle("Data", drive.Empty() ? "empty" : drive.Size()); Events["ToggleUI"].active = true; // show TakeData eva action button, if there is something to take Events["TakeData"].active = !drive.Empty(); // show StoreData eva action button, if active vessel is an eva kerbal and there is something to store from it Vessel v = FlightGlobals.ActiveVessel; Events["StoreData"].active = v != null && v.isEVA && !EVA.IsDead(v) && drive.FilesSize() > double.Epsilon; // hide TransferLocation button Events["TransferData"].active = true; } }
public void Prepare() { // disable for dead eva kerbals Vessel v = FlightGlobals.ActiveVessel; if (v == null || EVA.IsDead(v)) { return; } if (prepare_cs == null) { return; } // check trait if (!prepare_cs.Check(v)) { Message.Post( Lib.TextVariant ( "I'm not qualified for this", "I will not even know where to start", "I'm afraid I can't do that" ), reset_cs.Warning() ); } didPrepare = true; Message.Post( "Preparation Complete", Lib.TextVariant ( "Ready to go", "Let's start doing some science!" ) ); }
public void Inspect() { // disable for dead eva kerbals Vessel v = FlightGlobals.ActiveVessel; if (v == null || EVA.IsDead(v)) { return; } // get normalized time to failure double time_k = (Planetarium.GetUniversalTime() - last) / (next - last); // notify user if (time_k < 0.2) { Message.Post("It is practically new"); } else if (time_k < 0.35) { Message.Post("It is in good shape"); } else { needMaintenance = true; if (time_k < 0.6) { Message.Post("It will keep working for some more time"); } else if (time_k < 0.8) { Message.Post("It is reaching its operational limits"); } else { Message.Post("It could fail at any moment now"); } } }
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 void Toggle() { if (Lib.IsFlight()) { // disable for dead eva kerbals Vessel v = FlightGlobals.ActiveVessel; if (v == null || EVA.IsDead(v)) { return; } if (!deploy_cs.Check(v)) { Message.Post ( Lib.TextVariant ( "I don't know how this works!" ), deploy_cs.Warning() ); return; } } // switch status deployed = !deployed; // play animation deploy_anim.Play(!deployed, false); // refresh VAB/SPH ui if (Lib.IsEditor()) { GameEvents.onEditorShipModified.Fire(EditorLogic.fetch.ship); } }
// 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 Vessel_info(Vessel v, UInt64 vessel_id, UInt64 inc) { // NOTE: anything used here can't in turn use cache, unless you know what you are doing // NOTE: you can't cache vessel position // at any point in time all vessel/body positions are relative to a different frame of reference // so comparing the current position of a vessel, with the cached one of another make no sense // associate with an unique incremental id this.inc = inc; // determine if this is a valid vessel is_vessel = Lib.IsVessel(v); if (!is_vessel) { return; } // determine if this is a rescue mission vessel is_rescue = Misc.IsRescueMission(v); if (is_rescue) { return; } // dead EVA are not valid vessels if (EVA.IsDead(v)) { return; } // shortcut for common tests is_valid = true; // generate id once id = vessel_id; // calculate crew info for the vessel crew_count = Lib.CrewCount(v); crew_capacity = Lib.CrewCapacity(v); // get vessel position Vector3d position = Lib.VesselPosition(v); // this should never happen again if (Vector3d.Distance(position, v.mainBody.position) < 1.0) { throw new Exception("Shit hit the fan for vessel " + v.vesselName); } // determine if there is enough EC for a powered state powered = ResourceCache.Info(v, "ElectricCharge").amount > double.Epsilon; // determine if in sunlight, calculate sun direction and distance sunlight = Sim.RaytraceBody(v, position, FlightGlobals.Bodies[0], out sun_dir, out sun_dist) ? 1.0 : 0.0; // environment stuff atmo_factor = Sim.AtmosphereFactor(v.mainBody, position, sun_dir); gamma_transparency = Sim.GammaTransparency(v.mainBody, v.altitude); underwater = Sim.Underwater(v); breathable = Sim.Breathable(v, underwater); landed = Lib.Landed(v); zerog = !landed && (!v.mainBody.atmosphere || v.mainBody.atmosphereDepth < v.altitude); if (v.mainBody.flightGlobalsIndex != 0 && TimeWarp.CurrentRate > 1000.0f) { highspeedWarp(v); } // temperature at vessel position temperature = Sim.Temperature(v, position, sunlight, atmo_factor, out solar_flux, out albedo_flux, out body_flux, out total_flux); temp_diff = Sim.TempDiff(temperature, v.mainBody, landed); // radiation radiation = Radiation.Compute(v, position, gamma_transparency, sunlight, out blackout, out magnetosphere, out inner_belt, out outer_belt, out interstellar); // extended atmosphere thermosphere = Sim.InsideThermosphere(v); exosphere = Sim.InsideExosphere(v); // malfunction stuff malfunction = Reliability.HasMalfunction(v); critical = Reliability.HasCriticalFailure(v); // communications info connection = new ConnectionInfo(v, powered, blackout); transmitting = Science.Transmitting(v, connection.linked && connection.rate > double.Epsilon); // habitat data volume = Habitat.Tot_volume(v); surface = Habitat.Tot_surface(v); pressure = Habitat.Pressure(v); evas = (uint)(Math.Max(0, ResourceCache.Info(v, "Nitrogen").amount - 330) / PreferencesLifeSupport.Instance.evaAtmoLoss); poisoning = Habitat.Poisoning(v); humidity = Habitat.Humidity(v); shielding = Habitat.Shielding(v); living_space = Habitat.Living_space(v); volume_per_crew = Habitat.Volume_per_crew(v); comforts = new Comforts(v, landed, crew_count > 1, connection.linked && connection.rate > double.Epsilon); // data about greenhouses greenhouses = Greenhouse.Greenhouses(v); // other stuff gravioli = Sim.Graviolis(v); }
void FixedUpdate() { // remove control locks in any case Misc.clearLocks(); // do nothing if paused if (Lib.IsPaused()) { return; } // maintain elapsed_s, converting to double only once // and detect warp blending double fixedDeltaTime = TimeWarp.fixedDeltaTime; if (Math.Abs(fixedDeltaTime - elapsed_s) > double.Epsilon) { warp_blending = 0; } else { ++warp_blending; } elapsed_s = fixedDeltaTime; // evict oldest entry from vessel cache Cache.update(); // store info for oldest unloaded vessel double last_time = 0.0; Vessel last_v = null; vessel_info last_vi = null; VesselData last_vd = null; vessel_resources last_resources = null; // for each vessel foreach (Vessel v in FlightGlobals.Vessels) { // get vessel info from the cache vessel_info vi = Cache.VesselInfo(v); // set locks for active vessel if (v.isActiveVessel) { Misc.setLocks(v, vi); } // maintain eva dead animation and helmet state if (v.loaded && v.isEVA) { EVA.update(v); } // keep track of rescue mission kerbals, and gift resources to their vessels on discovery if (v.loaded && vi.is_vessel) { // manage rescue mission mechanics Misc.manageRescueMission(v); } // do nothing else for invalid vessels if (!vi.is_valid) { continue; } // get vessel data from db VesselData vd = DB.Vessel(v); // get resource cache vessel_resources resources = ResourceCache.Get(v); // if loaded if (v.loaded) { // show belt warnings Radiation.beltWarnings(v, vi, vd); // update storm data Storm.update(v, vi, vd, elapsed_s); // show signal warnings Signal.update(v, vi, vd, elapsed_s); // consume ec for transmission, and transmit science data Science.update(v, vi, vd, resources, elapsed_s); // apply rules Profile.Execute(v, vi, vd, resources, elapsed_s); // apply deferred requests resources.Sync(v, elapsed_s); // call automation scripts vd.computer.automate(v, vi, resources); // remove from unloaded data container unloaded.Remove(vi.id); } // if unloaded else { // get unloaded data, or create an empty one unloaded_data ud; if (!unloaded.TryGetValue(vi.id, out ud)) { ud = new unloaded_data(); unloaded.Add(vi.id, ud); } // accumulate time ud.time += elapsed_s; // maintain oldest entry if (ud.time > last_time) { last_time = ud.time; last_v = v; last_vi = vi; last_vd = vd; last_resources = resources; } } } // if the oldest unloaded vessel was selected if (last_v != null) { // show belt warnings Radiation.beltWarnings(last_v, last_vi, last_vd); // update storm data Storm.update(last_v, last_vi, last_vd, last_time); // show signal warnings Signal.update(last_v, last_vi, last_vd, last_time); // consume ec for transmission, and transmit science Science.update(last_v, last_vi, last_vd, last_resources, last_time); // apply rules Profile.Execute(last_v, last_vi, last_vd, last_resources, last_time); // simulate modules in background Background.update(last_v, last_vi, last_vd, last_resources, last_time); // apply deferred requests last_resources.Sync(last_v, last_time); // call automation scripts last_vd.computer.automate(last_v, last_vi, last_resources); // remove from unloaded data container unloaded.Remove(last_vi.id); } // update storm data for one body per-step if (storm_bodies.Count > 0) { storm_bodies.ForEach(k => k.time += elapsed_s); storm_data sd = storm_bodies[storm_index]; Storm.update(sd.body, sd.time); sd.time = 0.0; storm_index = (storm_index + 1) % storm_bodies.Count; } }
public void Update() { if (drive == null) { return; } if (Lib.IsEditor()) { bool update = false; if (dataCapacities != null) { foreach (var c in dataCapacities) { if (c.Key == dataCapacityUI) { update |= effectiveDataCapacity != c.Value; effectiveDataCapacity = c.Value; } } } if (sampleCapacities != null) { foreach (var c in sampleCapacities) { if (c.Key == sampleCapacityUI) { update |= effectiveSampleCapacity != c.Value; effectiveSampleCapacity = c.Value; } } } drive.dataCapacity = effectiveDataCapacity; drive.sampleCapacity = effectiveSampleCapacity; Fields["sampleCapacityUI"].guiActiveEditor = sampleCapacity > 0; Fields["dataCapacityUI"].guiActiveEditor = dataCapacity > 0; if (update) { GameEvents.onEditorShipModified.Fire(EditorLogic.fetch.ship); UpdateCapacity(); } } if (Lib.IsFlight()) { // show DATA UI button, with size info Events["ToggleUI"].guiName = Lib.StatusToggle("Data", drive.Empty() ? "empty" : drive.Size()); Events["ToggleUI"].active = !IsPrivate(); // show TakeData eva action button, if there is something to take Events["TakeData"].active = !drive.Empty(); // show StoreData eva action button, if active vessel is an eva kerbal and there is something to store from it Vessel v = FlightGlobals.ActiveVessel; Events["StoreData"].active = !IsPrivate() && v != null && v.isEVA && !EVA.IsDead(v); // hide TransferLocation button var transferVisible = !IsPrivate(); if (transferVisible) { transferVisible = Drive.GetDrives(vessel, true).Count > 1; } Events["TransferData"].active = transferVisible; Events["TransferData"].guiActive = transferVisible; } }
void FixedUpdate() { // remove control locks in any case Misc.ClearLocks(); // do nothing if paused if (Lib.IsPaused()) { return; } // convert elapsed time to double only once double fixedDeltaTime = TimeWarp.fixedDeltaTime; // and detect warp blending if (Math.Abs(fixedDeltaTime - elapsed_s) < 0.001) { warp_blending = 0; } else { ++warp_blending; } // update elapsed time elapsed_s = fixedDeltaTime; // store info for oldest unloaded vessel double last_time = 0.0; Guid last_id = Guid.Empty; Vessel last_v = null; VesselData last_vd = null; VesselResources last_resources = null; foreach (VesselData vd in DB.VesselDatas) { vd.EarlyUpdate(); } // for each vessel foreach (Vessel v in FlightGlobals.Vessels) { // get vessel data VesselData vd = v.KerbalismData(); // update the vessel data validity vd.Update(v); // set locks for active vessel if (v.isActiveVessel) { Misc.SetLocks(v); } // maintain eva dead animation and helmet state if (v.loaded && v.isEVA) { EVA.Update(v); } // keep track of rescue mission kerbals, and gift resources to their vessels on discovery if (v.loaded && vd.is_vessel) { // manage rescue mission mechanics Misc.ManageRescueMission(v); } // do nothing else for invalid vessels if (!vd.IsSimulated) { continue; } // get resource cache VesselResources resources = ResourceCache.Get(v); // if loaded if (v.loaded) { //UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Loaded.VesselDataEval"); // update the vessel info vd.Evaluate(false, elapsed_s); //UnityEngine.Profiling.Profiler.EndSample(); // get most used resource ResourceInfo ec = resources.GetResource(v, "ElectricCharge"); UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Loaded.Radiation"); // show belt warnings Radiation.BeltWarnings(v, vd); // update storm data Storm.Update(v, vd, elapsed_s); UnityEngine.Profiling.Profiler.EndSample(); UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Loaded.Comms"); Communications.Update(v, vd, ec, elapsed_s); UnityEngine.Profiling.Profiler.EndSample(); // Habitat equalization ResourceBalance.Equalizer(v); UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Loaded.Science"); // transmit science data Science.Update(v, vd, ec, elapsed_s); UnityEngine.Profiling.Profiler.EndSample(); UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Loaded.Profile"); // apply rules Profile.Execute(v, vd, resources, elapsed_s); UnityEngine.Profiling.Profiler.EndSample(); UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Loaded.Profile"); // part module resource updates vd.ResourceUpdate(resources, elapsed_s); UnityEngine.Profiling.Profiler.EndSample(); UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Loaded.Resource"); // apply deferred requests resources.Sync(v, vd, elapsed_s); UnityEngine.Profiling.Profiler.EndSample(); // call automation scripts vd.computer.Automate(v, vd, resources); // remove from unloaded data container unloaded.Remove(vd.VesselId); } // if unloaded else { // get unloaded data, or create an empty one Unloaded_data ud; if (!unloaded.TryGetValue(vd.VesselId, out ud)) { ud = new Unloaded_data(); unloaded.Add(vd.VesselId, ud); } // accumulate time ud.time += elapsed_s; // maintain oldest entry if (ud.time > last_time) { last_time = ud.time; last_v = v; last_vd = vd; last_resources = resources; } } } // at most one vessel gets background processing per physics tick : // if there is a vessel that is not the currently loaded vessel, then // we will update the vessel whose most recent background update is the oldest if (last_v != null) { //UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Unloaded.VesselDataEval"); // update the vessel info (high timewarp speeds reevaluation) last_vd.Evaluate(false, last_time); //UnityEngine.Profiling.Profiler.EndSample(); // get most used resource ResourceInfo last_ec = last_resources.GetResource(last_v, "ElectricCharge"); UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Unloaded.Radiation"); // show belt warnings Radiation.BeltWarnings(last_v, last_vd); // update storm data Storm.Update(last_v, last_vd, last_time); UnityEngine.Profiling.Profiler.EndSample(); UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Unloaded.Comms"); Communications.Update(last_v, last_vd, last_ec, last_time); UnityEngine.Profiling.Profiler.EndSample(); UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Unloaded.Profile"); // apply rules Profile.Execute(last_v, last_vd, last_resources, last_time); UnityEngine.Profiling.Profiler.EndSample(); UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Unloaded.Background"); // simulate modules in background Background.Update(last_v, last_vd, last_resources, last_time); UnityEngine.Profiling.Profiler.EndSample(); UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Unloaded.Science"); // transmit science data Science.Update(last_v, last_vd, last_ec, last_time); UnityEngine.Profiling.Profiler.EndSample(); UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Unloaded.Resource"); // apply deferred requests last_resources.Sync(last_v, last_vd, last_time); UnityEngine.Profiling.Profiler.EndSample(); // call automation scripts last_vd.computer.Automate(last_v, last_vd, last_resources); // remove from unloaded data container unloaded.Remove(last_vd.VesselId); } // update storm data for one body per-step if (storm_bodies.Count > 0) { storm_bodies.ForEach(k => k.time += elapsed_s); Storm_data sd = storm_bodies[storm_index]; Storm.Update(sd.body, sd.time); sd.time = 0.0; storm_index = (storm_index + 1) % storm_bodies.Count; } }
public vessel_info(Vessel v, uint vessel_id, UInt64 inc) { // NOTE: anything used here can't in turn use cache, unless you know what you are doing // associate with an unique incremental id this.inc = inc; // determine if this is a valid vessel is_vessel = Lib.IsVessel(v); if (!is_vessel) return; // determine if this is a resque mission vessel is_resque = Lib.IsResqueMission(v); if (is_resque) return; // dead EVA are not valid vessels if (v.isEVA && EVA.KerbalData(v).eva_dead) return; // shortcut for common tests is_valid = true; // generate id once id = vessel_id; // calculate crew info for the vessel crew_count = Lib.CrewCount(v); crew_capacity = Lib.CrewCapacity(v); // get vessel position once position = Lib.VesselPosition(v); // determine if in sunlight, calculate sun direction and distance sunlight = Sim.RaytraceBody(v, position, FlightGlobals.Bodies[0], out sun_dir, out sun_dist) ? 1.0 : 0.0; // if the orbit length vs simulation step is lower than an acceptable threshold, use discrete sun visibility if (v.mainBody.flightGlobalsIndex != 0) { double orbit_period = Sim.OrbitalPeriod(v); if (orbit_period / Kerbalism.elapsed_s < 16.0) sunlight = 1.0 - Sim.ShadowPeriod(v) / orbit_period; } // calculate environment stuff atmo_factor = Sim.AtmosphereFactor(v.mainBody, position, sun_dir); gamma_transparency = Sim.GammaTransparency(v.mainBody, v.altitude); breathable = Sim.Breathable(v); landed = Lib.Landed(v); // calculate temperature at vessel position temperature = Sim.Temperature(v, position, sunlight, atmo_factor, out solar_flux, out albedo_flux, out body_flux, out total_flux); // calculate radiation radiation = Radiation.Compute(v, position, gamma_transparency, sunlight, out blackout, out inside_pause, out inside_belt); // calculate malfunction stuff max_malfunction = Reliability.MaxMalfunction(v); avg_quality = Reliability.AverageQuality(v); // calculate signal info antenna = new antenna_data(v); avoid_inf_recursion.Add(v.id); link = Signal.Link(v, position, antenna, blackout, avoid_inf_recursion); avoid_inf_recursion.Remove(v.id); // partial data about modules, used by vessel info/monitor scrubbers = Scrubber.PartialData(v); recyclers = Recycler.PartialData(v); greenhouses = Greenhouse.PartialData(v); // woot relativity time_dilation = Sim.TimeDilation(v); }
public void Update() { var exp = Science.Experiment(experiment_id); // in flight if (Lib.IsFlight()) { Vessel v = FlightGlobals.ActiveVessel; if (v == null || EVA.IsDead(v)) { return; } // get info from cache Vessel_info vi = Cache.VesselInfo(vessel); // do nothing if vessel is invalid if (!vi.is_valid) { return; } var sampleSize = exp.max_amount; var eta = data_rate < double.Epsilon || Done(exp, dataSampled) ? " done" : " " + Lib.HumanReadableCountdown((sampleSize - dataSampled) / data_rate); // update ui var title = Lib.Ellipsis(exp.name, Styles.ScaleStringLength(24)); if (scienceValue > 0.1) { title += " •<b>" + scienceValue.ToString("F1") + "</b>"; } string statusString = string.Empty; switch (state) { case State.ISSUE: statusString = Lib.Color("yellow", issue); break; case State.RUNNING: statusString = Lib.HumanReadablePerc(dataSampled / sampleSize) + eta; break; case State.WAITING: statusString = "waiting" + eta; break; case State.STOPPED: statusString = "stopped"; break; } Events["Toggle"].guiName = Lib.StatusToggle(title, statusString); Events["Toggle"].active = (prepare_cs == null || didPrepare); Events["Prepare"].guiName = Lib.BuildString("Prepare <b>", exp.name, "</b>"); Events["Prepare"].active = !didPrepare && prepare_cs != null && string.IsNullOrEmpty(last_subject_id); Events["Reset"].guiName = Lib.BuildString("Reset <b>", exp.name, "</b>"); // we need a reset either if we have recorded data or did a setup bool resetActive = (reset_cs != null || prepare_cs != null) && !string.IsNullOrEmpty(last_subject_id); Events["Reset"].active = resetActive; if (issue.Length > 0 && hide_when_unavailable && issue != insufficient_storage) { Events["Toggle"].active = false; } } // in the editor else if (Lib.IsEditor()) { // update ui Events["Toggle"].guiName = Lib.StatusToggle(exp.name, recording ? "recording" : "stopped"); Events["Reset"].active = false; Events["Prepare"].active = false; } }
// 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 } } }
public void Update() { if (drive == null) { return; } if (Lib.IsFlight()) { // show DATA UI button, with size info Events["ToggleUI"].guiName = Lib.StatusToggle("Data", drive.Empty() ? "empty" : drive.Size()); Events["ToggleUI"].active = !IsPrivate(); // show TakeData eva action button, if there is something to take Events["TakeData"].active = !drive.Empty(); // show StoreData eva action button, if active vessel is an eva kerbal and there is something to store from it Vessel v = FlightGlobals.ActiveVessel; Events["StoreData"].active = !IsPrivate() && v != null && v.isEVA && !EVA.IsDead(v); // hide TransferLocation button var transferVisible = !IsPrivate(); if (transferVisible) { transferVisible = Drive.GetDrives(vessel, true).Count > 1; } Events["TransferData"].active = transferVisible; Events["TransferData"].guiActive = transferVisible; } }
public vessel_info(Vessel v, uint vessel_id, UInt64 inc) { // NOTE: anything used here can't in turn use cache, unless you know what you are doing // NOTE: you can't cache vessel position // at any point in time all vessel/body positions are relative to a different frame of reference // so comparing the current position of a vessel, with the cached one of another make no sense // associate with an unique incremental id this.inc = inc; // determine if this is a valid vessel is_vessel = Lib.IsVessel(v); if (!is_vessel) return; // determine if this is a rescue mission vessel is_rescue = Misc.IsRescueMission(v); if (is_rescue) return; // dead EVA are not valid vessels if (EVA.IsDead(v)) return; // shortcut for common tests is_valid = true; // generate id once id = vessel_id; // calculate crew info for the vessel crew_count = Lib.CrewCount(v); crew_capacity = Lib.CrewCapacity(v); // get vessel position Vector3d position = Lib.VesselPosition(v); // this should never happen again if (Vector3d.Distance(position, v.mainBody.position) < 1.0) { throw new Exception("Shit hit the fan for vessel " + v.vesselName); } // determine if in sunlight, calculate sun direction and distance sunlight = Sim.RaytraceBody(v, position, FlightGlobals.Bodies[0], out sun_dir, out sun_dist) ? 1.0 : 0.0; // at the two highest timewarp speed, the number of sun visibility samples drop to the point that // the quantization error first became noticeable, and then exceed 100% // to solve this, we switch to an analytical estimation of the portion of orbit that was in sunlight // - we check against timewarp rate, instead of index, to avoid issues during timewarp blending if (v.mainBody.flightGlobalsIndex != 0 && TimeWarp.CurrentRate > 1000.0f) { sunlight = 1.0 - Sim.ShadowPeriod(v) / Sim.OrbitalPeriod(v); } // environment stuff atmo_factor = Sim.AtmosphereFactor(v.mainBody, position, sun_dir); gamma_transparency = Sim.GammaTransparency(v.mainBody, v.altitude); underwater = Sim.Underwater(v); breathable = Sim.Breathable(v, underwater); landed = Lib.Landed(v); // temperature at vessel position temperature = Sim.Temperature(v, position, sunlight, atmo_factor, out solar_flux, out albedo_flux, out body_flux, out total_flux); temp_diff = Sim.TempDiff(temperature, v.mainBody, landed); // radiation radiation = Radiation.Compute(v, position, gamma_transparency, sunlight, out blackout, out magnetosphere, out inner_belt, out outer_belt, out interstellar); // extended atmosphere thermosphere = Sim.InsideThermosphere(v); exosphere = Sim.InsideExosphere(v); // malfunction stuff malfunction = Reliability.HasMalfunction(v); critical = Reliability.HasCriticalFailure(v); // signal info antenna = new AntennaInfo(v); avoid_inf_recursion.Add(v.id); connection = Signal.connection(v, position, antenna, blackout, avoid_inf_recursion); transmitting = Science.transmitting(v, connection.linked); relaying = Signal.relaying(v, avoid_inf_recursion); avoid_inf_recursion.Remove(v.id); // habitat data volume = Habitat.tot_volume(v); surface = Habitat.tot_surface(v); pressure = Habitat.pressure(v); poisoning = Habitat.poisoning(v); shielding = Habitat.shielding(v); living_space = Habitat.living_space(v); comforts = new Comforts(v, landed, crew_count > 1, connection.linked); // data about greenhouses greenhouses = Greenhouse.Greenhouses(v); // other stuff gravioli = Sim.Graviolis(v); }
public void Repair() { // disable for dead eva kerbals Vessel v = FlightGlobals.ActiveVessel; if (v == null || EVA.IsDead(v)) { return; } // check trait if (!repair_cs.Check(v)) { Message.Post ( Lib.TextVariant ( "I'm not qualified for this", "I will not even know where to start", "I'm afraid I can't do that" ), repair_cs.Warning() ); return; } // flag as not broken broken = false; // reset times last = 0.0; next = 0.0; // re-enable module foreach (PartModule m in modules) { m.isEnabled = true; m.enabled = true; } // we need to reconfigure the module here, because if all modules of a type // share the broken state, and these modules are part of a configure setup, // then repairing will enable all of them, messing up with the configuration part.FindModulesImplementing <Configure>().ForEach(k => k.DoConfigure()); // type-specific hacks Apply(false); // notify user Message.Post ( Lib.BuildString("<b>", title, "</b> repaired"), Lib.TextVariant ( "A powerkick did the trick", "Duct tape, is there something it can't fix?", "Fully operational again", "We are back in business" ) ); }
void FixedUpdate() { // remove control locks in any case Misc.ClearLocks(); // do nothing if paused if (Lib.IsPaused()) { return; } // maintain elapsed_s, converting to double only once // and detect warp blending double fixedDeltaTime = TimeWarp.fixedDeltaTime; if (Math.Abs(fixedDeltaTime - elapsed_s) > double.Epsilon) { warp_blending = 0; } else { ++warp_blending; } elapsed_s = fixedDeltaTime; // evict oldest entry from vessel cache Cache.Update(); // vvvv------- This code tests for a theroy that could cause #313 // If KSP itself has the same vessel more than once in the // FlightGlobals.Vessels list, it would cause processes to run too many times. // The other possible cause was a Vessel ID collision in Lib.VesselID(), which // only used 4 bytes of a 16 byte GUID to create an ID from. // // If the BUG TRIGGERED message is never observed in the wild, // it is safe to remove this chunk of code. Dictionary <UInt64, Vessel> vessels = new Dictionary <UInt64, Vessel>(); foreach (Vessel v in FlightGlobals.Vessels) { if (vessels.ContainsKey(Lib.VesselID(v))) { Lib.Log("THIS SHOULD NOT BE HAPPENING: Vessel " + v.name + " already seen in FlightGlobals.Vessels"); Message.Post(Lib.BuildString(Lib.Color("red", "BUG TRIGGERED", true), "\n", v.name + " duplicated in FlightGlobals.Vessels. Please report this on GitHub.")); } else { vessels.Add(Lib.VesselID(v), v); } } // ^^^^-------- end theory test code // store info for oldest unloaded vessel double last_time = 0.0; Vessel last_v = null; Vessel_info last_vi = null; VesselData last_vd = null; Vessel_resources last_resources = null; // for each vessel //foreach (Vessel v in FlightGlobals.Vessels) foreach (Vessel v in vessels.Values) { // get vessel info from the cache Vessel_info vi = Cache.VesselInfo(v); // set locks for active vessel if (v.isActiveVessel) { Misc.SetLocks(v, vi); } // maintain eva dead animation and helmet state if (v.loaded && v.isEVA) { EVA.Update(v); } // keep track of rescue mission kerbals, and gift resources to their vessels on discovery if (v.loaded && vi.is_vessel) { // manage rescue mission mechanics Misc.ManageRescueMission(v); } // do nothing else for invalid vessels if (!vi.is_valid) { continue; } // get vessel data from db VesselData vd = DB.Vessel(v); // get resource cache Vessel_resources resources = ResourceCache.Get(v); // if loaded if (v.loaded) { // get most used resource Resource_info ec = resources.Info(v, "ElectricCharge"); // show belt warnings Radiation.BeltWarnings(v, vi, vd); // update storm data Storm.Update(v, vi, vd, elapsed_s); Communications.Update(v, vi, vd, ec, elapsed_s); // Habitat equalization ResourceBalance.Equalizer(v); // transmit science data Science.Update(v, vi, vd, resources, elapsed_s); // apply rules Profile.Execute(v, vi, vd, resources, elapsed_s); // apply deferred requests resources.Sync(v, elapsed_s); // call automation scripts vd.computer.Automate(v, vi, resources); // remove from unloaded data container unloaded.Remove(vi.id); } // if unloaded else { // get unloaded data, or create an empty one Unloaded_data ud; if (!unloaded.TryGetValue(vi.id, out ud)) { ud = new Unloaded_data(); unloaded.Add(vi.id, ud); } // accumulate time ud.time += elapsed_s; // maintain oldest entry if (ud.time > last_time) { last_time = ud.time; last_v = v; last_vi = vi; last_vd = vd; last_resources = resources; } } } // at most one vessel gets background processing per physics tick // if there is a vessel that is not the currently loaded vessel, then // we will update the vessel whose most recent background update is the oldest if (last_v != null) { // get most used resource Resource_info last_ec = last_resources.Info(last_v, "ElectricCharge"); // show belt warnings Radiation.BeltWarnings(last_v, last_vi, last_vd); // update storm data Storm.Update(last_v, last_vi, last_vd, last_time); Communications.Update(last_v, last_vi, last_vd, last_ec, last_time); // transmit science data Science.Update(last_v, last_vi, last_vd, last_resources, last_time); // apply rules Profile.Execute(last_v, last_vi, last_vd, last_resources, last_time); // simulate modules in background Background.Update(last_v, last_vi, last_vd, last_resources, last_time); // apply deferred requests last_resources.Sync(last_v, last_time); // call automation scripts last_vd.computer.Automate(last_v, last_vi, last_resources); // remove from unloaded data container unloaded.Remove(last_vi.id); } // update storm data for one body per-step if (storm_bodies.Count > 0) { storm_bodies.ForEach(k => k.time += elapsed_s); Storm_data sd = storm_bodies[storm_index]; Storm.Update(sd.body, sd.time); sd.time = 0.0; storm_index = (storm_index + 1) % storm_bodies.Count; } }
void ToEVA(GameEvents.FromToAction <Part, Part> data) { OnVesselModified(data.from.vessel); OnVesselModified(data.to.vessel); // get total crew in the origin vessel double tot_crew = Lib.CrewCount(data.from.vessel) + 1.0; // get vessel resources handler VesselResources resources = ResourceCache.Get(data.from.vessel); // setup supply resources capacity in the eva kerbal Profile.SetupEva(data.to); String prop_name = Lib.EvaPropellantName(); // for each resource in the kerbal for (int i = 0; i < data.to.Resources.Count; ++i) { // get the resource PartResource res = data.to.Resources[i]; // eva prop is handled differently if (res.resourceName == prop_name) { continue; } double quantity = Math.Min(resources.GetResource(data.from.vessel, res.resourceName).Amount / tot_crew, res.maxAmount); // remove resource from vessel quantity = data.from.RequestResource(res.resourceName, quantity); // add resource to eva kerbal data.to.RequestResource(res.resourceName, -quantity); } // take as much of the propellant as possible. just imagine: there are 1.3 units left, and 12 occupants // in the ship. you want to send out an engineer to fix the chemical plant that produces monoprop, // and have to get from one end of the station to the other with just 0.1 units in the tank... // nope. double evaPropQuantity = data.from.RequestResource(prop_name, Lib.EvaPropellantCapacity()); // We can't just add the monoprop here, because that doesn't always work. It might be related // to the fact that stock KSP wants to add 5 units of monoprop to new EVAs. Instead of fighting KSP here, // we just let it do it's thing and set our amount later in EVA.cs - which seems to work just fine. // don't put that into Cache.VesselInfo because that can be deleted before we get there Cache.SetVesselObjectsCache(data.to.vessel, "eva_prop", evaPropQuantity); // Airlock loss resources.Consume(data.from.vessel, "Nitrogen", Settings.LifeSupportAtmoLoss, ResourceBroker.Generic); // show warning if there is little or no EVA propellant in the suit if (evaPropQuantity <= 0.05 && !Lib.Landed(data.from.vessel)) { Message.Post(Severity.danger, Local.CallBackMsg_EvaNoMP.Format("<b>" + prop_name + "</b>"), Local.CallBackMsg_EvaNoMP2); //Lib.BuildString("There isn't any <<1>> in the EVA suit")"Don't let the ladder go!" } // turn off headlamp light, to avoid stock bug that show them for a split second when going on eva KerbalEVA kerbal = data.to.FindModuleImplementing <KerbalEVA>(); EVA.HeadLamps(kerbal, false); // execute script data.from.vessel.KerbalismData().computer.Execute(data.from.vessel, ScriptType.eva_out); }
void toEVA(GameEvents.FromToAction <Part, Part> data) { // use Hydrazine instead of MonoPropellant if RealFuel is installed string monoprop_name = detected_mods.RealFuels ? "Hydrazine" : "MonoPropellant"; // determine if inside breathable atmosphere // note: the user can force the helmet + oxygen by pressing shift when going on eva bool breathable = Sim.Breathable(data.from.vessel) && !(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)); // get total crew in the origin vessel double tot_crew = (double)data.from.vessel.GetVesselCrew().Count + 1.0; // EVA vessels start with 5 units of eva fuel, remove them data.to.RequestResource("EVA Propellant", 5.0); // determine how much MonoPropellant to get // note: never more that the 'share' of this kerbal double monoprop = Math.Min(ResourceCache.Info(data.from.vessel, monoprop_name).amount / tot_crew, Settings.MonoPropellantOnEVA); // get monoprop from the vessel monoprop = data.from.RequestResource(monoprop_name, monoprop); // transfer monoprop to the EVA kerbal data.to.RequestResource("EVA Propellant", -monoprop); // show warning if there isn't monoprop in the eva suit if (monoprop <= double.Epsilon && !Lib.Landed(data.from.vessel)) { Message.Post(Severity.danger, Lib.BuildString("There isn't any <b>", monoprop_name, "</b> in the EVA suit", "Don't let the ladder go!")); } // manage resources from rules foreach (Rule r in rules) { if (r.resource_name.Length == 0 || r.on_eva <= double.Epsilon) { continue; } // determine amount to take, never more that his own share double amount = Math.Min(ResourceCache.Info(data.from.vessel, r.resource_name).amount / tot_crew, r.on_eva); // deal with breathable modifier if (breathable && r.modifier.Contains("breathable")) { continue; } // remove resource from the vessel amount = data.from.RequestResource(r.resource_name, amount); // create new resource in the eva kerbal Lib.SetupResource(data.to, r.resource_name, amount, r.on_eva); } // 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 EVA.KerbalData(data.to.vessel).has_helmet = !breathable; // execute script on vessel computer if (DB.Ready()) { DB.VesselData(data.from.vessel.id).computer.execute("run", "auto/eva_out", string.Empty, data.from.vessel); } // mute messages for a couple seconds to avoid warning messages from the vessel resource amounts Message.MuteInternal(); base.StartCoroutine(CallbackUtil.DelayedCallback(2.0f, Message.UnmuteInternal)); // if vessel info is open, switch to the eva kerbal // note: for a single tick, the EVA vessel is not valid (sun_dist is zero) // this make IsVessel() return false, that in turn close the vessel info instantly // for this reason, we wait a small amount of time before switching the info window if (Info.IsOpen()) { Info.Open(data.to.vessel); } }
// draw a vessel in the monitor // - return: 1 if vessel wasn't skipped uint render_vessel(Vessel v) { // avoid case when DB isn't ready for whatever reason if (!DB.Ready()) return 0; // skip invalid vessels if (!Lib.IsVessel(v)) return 0; // skip resque missions if (Lib.IsResqueMission(v)) return 0; // skip dead eva kerbals if (EVA.IsDead(v)) return 0; // get vessel info from cache vessel_info vi = Cache.VesselInfo(v); // get vessel data from the db vessel_data vd = DB.VesselData(v.id); // skip filtered vessels if (filtered() && vd.group != filter) return 0; // get vessel crew List<ProtoCrewMember> crew = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew(); // get vessel name string vessel_name = v.isEVA ? crew[0].name : v.vesselName; // get body name string body_name = v.mainBody.name.ToUpper(); // get list of scrubbers List<Scrubber> scrubbers = Scrubber.GetScrubbers(v); // get list of greenhouses List<Greenhouse> greenhouses = Greenhouse.GetGreenhouses(v); // store problems icons & tooltips List<Texture> problem_icons = new List<Texture>(); List<string> problem_tooltips = new List<string>(); // detect problems problem_sunlight(vi, ref problem_icons, ref problem_tooltips); problem_storm(v, ref problem_icons, ref problem_tooltips); if (crew.Count > 0) { problem_kerbals(crew, ref problem_icons, ref problem_tooltips); problem_radiation(vi, ref problem_icons, ref problem_tooltips); problem_scrubbers(v, scrubbers, ref problem_icons, ref problem_tooltips); } problem_greenhouses(v, greenhouses, ref problem_icons, ref problem_tooltips); // choose problem icon const UInt64 problem_icon_time = 3; Texture problem_icon = icon_empty; if (problem_icons.Count > 0) { UInt64 problem_index = (Convert.ToUInt64(Time.realtimeSinceStartup) / problem_icon_time) % (UInt64)(problem_icons.Count); problem_icon = problem_icons[(int)problem_index]; } // generate problem tooltips string problem_tooltip = String.Join("\n", problem_tooltips.ToArray()); // render vessel name & icons GUILayout.BeginHorizontal(row_style); GUILayout.Label(new GUIContent("<b>" + Lib.Epsilon(vessel_name, 20) + "</b>", vessel_name.Length > 20 ? vessel_name : ""), name_style); GUILayout.Label(new GUIContent(Lib.Epsilon(body_name, 8), body_name.Length > 8 ? body_name : ""), body_style); GUILayout.Label(new GUIContent(problem_icon, problem_tooltip), icon_style); GUILayout.Label(indicator_ec(v), icon_style); GUILayout.Label(indicator_supplies(v, scrubbers, greenhouses), icon_style); GUILayout.Label(indicator_reliability(v), icon_style); GUILayout.Label(indicator_signal(v), icon_style); GUILayout.EndHorizontal(); // remember last vessel clicked if (Lib.IsClicked()) last_clicked_id = v.id; // render vessel config if (configured_id == v.id) render_config(v); // spacing between vessels GUILayout.Space(10.0f); // signal that the vessel wasn't skipped for whatever reason return 1; }
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!"); } }
public void Update() { // in flight if (Lib.IsFlight()) { Vessel v = FlightGlobals.ActiveVessel; if (v == null || EVA.IsDead(v)) { return; } // get info from cache Vessel_info vi = Cache.VesselInfo(vessel); // do nothing if vessel is invalid if (!vi.is_valid) { return; } var sampleSize = (exp.scienceCap * exp.dataScale); var recordedPercent = Lib.HumanReadablePerc(dataSampled / sampleSize); var eta = data_rate < double.Epsilon || dataSampled >= sampleSize ? " done" : " T-" + Lib.HumanReadableDuration((sampleSize - dataSampled) / data_rate); // update ui Events["Toggle"].guiName = Lib.StatusToggle(exp.experimentTitle, !recording ? "stopped" : Lib.Color(issue.Length > 0 ? "#ffff00":"", Lib.HumanReadablePerc(dataSampled / sampleSize) + "...")); Events["Toggle"].active = (prepare_cs == null || didPrepare); Events["Prepare"].guiName = Lib.BuildString("Prepare <b>", exp.experimentTitle, "</b>"); Events["Prepare"].active = !didPrepare && prepare_cs != null && string.IsNullOrEmpty(last_subject_id); Events["Reset"].guiName = Lib.BuildString("Reset <b>", exp.experimentTitle, "</b>"); // we need a reset either if we have recorded data or did a setup bool resetActive = sample_mass < float.Epsilon && (reset_cs != null || prepare_cs != null) && !string.IsNullOrEmpty(last_subject_id); Events["Reset"].active = resetActive; Fields["ExperimentStatus"].guiName = exp.experimentTitle; Fields["ExperimentStatus"].guiActive = true; if (issue.Length > 0) { ExperimentStatus = Lib.BuildString("<color=#ffff00>", issue, "</color>"); } else if (dataSampled > 0) { string a = string.Empty; string b = string.Empty; if (sample_mass < float.Epsilon) { a = Lib.HumanReadableDataSize(dataSampled); b = Lib.HumanReadableDataSize(sampleSize); } else { a = Lib.SampleSizeToSlots(dataSampled).ToString(); b = Lib.HumanReadableSampleSize(sampleSize); if (remainingSampleMass > double.Epsilon) { b += " " + Lib.HumanReadableMass(remainingSampleMass) + " left"; } } ExperimentStatus = Lib.BuildString(a, "/", b, eta); } else { var size = sample_mass < double.Epsilon ? Lib.HumanReadableDataSize(sampleSize) : Lib.HumanReadableSampleSize(sampleSize); ExperimentStatus = Lib.BuildString("ready ", size, " in ", Lib.HumanReadableDuration(sampleSize / data_rate)); } } // in the editor else if (Lib.IsEditor()) { // update ui Events["Toggle"].guiName = Lib.StatusToggle(exp.experimentTitle, recording ? "recording" : "stopped"); Events["Reset"].active = false; Events["Prepare"].active = false; } }
// kill a kerbal public static void Kill(Vessel v, ProtoCrewMember c) { // forget kerbal data DB.ForgetKerbal(c.name); // if on pod if (!v.isEVA) { // if vessel is loaded if (v.loaded) { // find part Part part = null; foreach (Part p in v.parts) { if (p.protoModuleCrew.Find(k => k.name == c.name) != null) { part = p; break; } } // remove kerbal and kill it part.RemoveCrewmember(c); c.Die(); } // if vessel is not loaded else { // find proto part ProtoPartSnapshot part = null; foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { if (p.HasCrew(c.name)) { part = p; break; } } // remove from vessel part.RemoveCrew(c.name); // flag as dead c.rosterStatus = ProtoCrewMember.RosterStatus.Dead; // register background death manually for death report notifications Notifications.RegisterDeath(); } } // else it must be an eva death else { // flag as eva death EVA.Kill(v); // rename vessel v.vesselName = c.name + "'s body"; // register eva death manually for death report notifications Notifications.RegisterDeath(); } // remove reputation if (HighLogic.CurrentGame.Mode == Game.Modes.CAREER) { Reputation.Instance.AddReputation(-Settings.DeathReputationPenalty, TransactionReasons.Any); } }
public void FixedUpdate() { // remove control locks in any case clearLocks(); // do nothing else if db isn't ready if (!DB.Ready()) { return; } // do nothing else in the editors and the menus if (!Lib.SceneIsGame()) { return; } // do nothing if paused if (Lib.IsPaused()) { return; } // maintain elapsed_s, converting to double only once // and detect warp blending double fixedDeltaTime = TimeWarp.fixedDeltaTime; if (Math.Abs(fixedDeltaTime - elapsed_s) > double.Epsilon) { warp_blending = 0; } else { ++warp_blending; } elapsed_s = fixedDeltaTime; // for each vessel foreach (Vessel v in FlightGlobals.Vessels) { // skip unloaded vessels if (!v.loaded) { continue; } // get info from cache vessel_info vi = Cache.VesselInfo(v); // set locks for active vessel if (v.isActiveVessel) { setLocks(v, vi); } // maintain eva dead animation and helmet state if (v.isEVA) { EVA.update(v); } // keep track of resque mission kerbals, and gift resources to their vessels on discovery if (vi.is_vessel) { // manage resque mission mechanics manageResqueMission(v); } // update connected spaces using CLS, for QoL and Radiation mechanics if (vi.is_valid) { updateConnectedSpaces(v, vi); } } }
void resourceWarnings() { // for each vessel foreach (Vessel v in FlightGlobals.Vessels) { // skip invalid vessels if (!Lib.IsVessel(v)) { continue; } // skip resque missions if (Lib.IsResqueMission(v)) { continue; } // skip dead eva kerbal if (EVA.IsDead(v)) { continue; } // get vessel data vessel_data vd = DB.VesselData(v.id); // get EC amount and capacity double ec_amount = Lib.GetResourceAmount(v, "ElectricCharge"); double ec_capacity = Lib.GetResourceCapacity(v, "ElectricCharge"); double ec_perc = ec_capacity > 0.0 ? ec_amount / ec_capacity : 0.0; // if it has EC capacity if (ec_capacity > 0.0) { // check EC thresholds and show messages if (ec_perc <= Settings.ResourceDangerThreshold && vd.msg_ec < 2) { if (vd.cfg_ec == 1) { Message.Post(Severity.danger, VesselEvent.ec, v); } vd.msg_ec = 2; } else if (ec_perc <= Settings.ResourceWarningThreshold && vd.msg_ec < 1) { if (vd.cfg_ec == 1) { Message.Post(Severity.warning, VesselEvent.ec, v); } vd.msg_ec = 1; } else if (ec_perc > Settings.ResourceWarningThreshold && vd.msg_ec > 0) { if (vd.cfg_ec == 1) { Message.Post(Severity.relax, VesselEvent.ec, v); } vd.msg_ec = 0; } } // get food amount and capacity double food_amount = Lib.GetResourceAmount(v, "Food"); double food_capacity = Lib.GetResourceCapacity(v, "Food"); double food_perc = food_capacity > 0.0 ? food_amount / food_capacity : 0.0; // if it has food capacity if (food_capacity > 0.0) { // check food thresholds and show messages // note: no warnings at prelaunch if (food_perc <= Settings.ResourceDangerThreshold && vd.msg_food < 2) { if (vd.cfg_supply == 1 && v.situation != Vessel.Situations.PRELAUNCH) { Message.Post(Severity.danger, VesselEvent.food, v); } vd.msg_food = 2; } else if (food_perc <= Settings.ResourceWarningThreshold && vd.msg_food < 1) { if (vd.cfg_supply == 1 && v.situation != Vessel.Situations.PRELAUNCH) { Message.Post(Severity.warning, VesselEvent.food, v); } vd.msg_food = 1; } else if (food_perc > Settings.ResourceWarningThreshold && vd.msg_food > 0) { if (vd.cfg_supply == 1 && v.situation != Vessel.Situations.PRELAUNCH) { Message.Post(Severity.relax, VesselEvent.food, v); } vd.msg_food = 0; } } // get oxygen amount and capacity double oxygen_amount = Lib.GetResourceAmount(v, "Oxygen"); double oxygen_capacity = Lib.GetResourceCapacity(v, "Oxygen"); double oxygen_perc = oxygen_capacity > 0.0 ? oxygen_amount / oxygen_capacity : 0.0; // if it has oxygen capacity if (oxygen_capacity > 0.0) { // check oxygen thresholds and show messages // note: no warnings at prelaunch if (oxygen_perc <= Settings.ResourceDangerThreshold && vd.msg_oxygen < 2) { if (vd.cfg_supply == 1 && v.situation != Vessel.Situations.PRELAUNCH) { Message.Post(Severity.danger, VesselEvent.oxygen, v); } vd.msg_oxygen = 2; } else if (oxygen_perc <= Settings.ResourceWarningThreshold && vd.msg_oxygen < 1) { if (vd.cfg_supply == 1 && v.situation != Vessel.Situations.PRELAUNCH) { Message.Post(Severity.warning, VesselEvent.oxygen, v); } vd.msg_oxygen = 1; } else if (oxygen_perc > Settings.ResourceWarningThreshold && vd.msg_oxygen > 0) { if (vd.cfg_supply == 1 && v.situation != Vessel.Situations.PRELAUNCH) { Message.Post(Severity.relax, VesselEvent.oxygen, v); } vd.msg_oxygen = 0; } } } }