// trigger malfunction for unloaded module public static void Break(Vessel v, ProtoPartModuleSnapshot m) { // get data uint malfunctions = Lib.GetProtoValue<uint>(m, "malfunctions"); double lifetime = Lib.GetProtoValue<double>(m, "lifetime"); double age = Lib.GetProtoValue<double>(m, "age"); string malfunction_msg = m.moduleValues.GetValue("malfunction_msg"); // limit number of malfunctions per-component if (malfunctions >= 2u) return; // increase malfunction ++malfunctions; // reset age and lifetime age = 0.0; lifetime = 0.0; // show message if (DB.Ready() && DB.VesselData(v.id).cfg_malfunction == 1) { Message.Post(Severity.warning, PrepareMsg(malfunction_msg, v, malfunctions)); } // record first malfunction if (DB.Ready()) DB.NotificationData().first_malfunction = 1; // save data Lib.SetProtoValue<uint>(m, "malfunctions", malfunctions); Lib.SetProtoValue<double>(m, "lifetime", lifetime); Lib.SetProtoValue<double>(m, "age", age); }
public static void ShowMessage(Vessel v, string type) { if (DB.Ready() && DB.VesselData(v.id).cfg_malfunction == 1) { Message.Post(Severity.warning, Lib.BuildString("<b>", type, "</b> malfunctioned on <b>", v.vesselName, "</b>")); } }
// return true if the vessel is a resque mission public static bool IsResqueMission(Vessel v) { // if db isn't ready, assume a resque mission if (!DB.Ready()) { return(true); } // avoid re-creating dead eva kerbals in the db // note: no extra cost if vessel is not eva if (EVA.IsDead(v)) { return(true); } // if at least one of the crew is flagged as resque, consider it a resque mission var crew_members = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew(); foreach (var c in crew_members) { kerbal_data kd = DB.KerbalData(c.name); if (kd.resque == 1) { return(true); } } // not a resque mission return(false); }
// called every frame twice, first to compute ui layout and then to draw the ui void OnGUI() { // re-enable camera mouse scrolling, as some of the following functions can // disable it on mouse-hover, but can't re-enable it again consistently // (eg: you mouse-hover and then close the window with the cursor still inside it) // note: we are ignoring user preference on mouse wheel GameSettings.AXIS_MOUSEWHEEL.primary.scale = 1.0f; // always render the launcher launcher.on_gui(); // always render the messages message.on_gui(); // do nothing else if DB isn't ready or if we are in the editor if (!DB.Ready() || !Lib.SceneIsGame()) return; // do nothing if GUI should be hidden if (launcher.must_hide_ui) return; // render subsystems info.on_gui(); body_info.on_gui(); console.on_gui(); editor.on_gui(); }
// return time left until CME is over public static double TimeLeftCME(CelestialBody body) { if (body.flightGlobalsIndex == 0) return 0.0; if (!DB.Ready()) return 0.0; body_data bd = DB.BodyData(Lib.PlanetarySystem(body).name); return Math.Max(0.0, bd.storm_time - bd.storm_age); }
public float height() { // note: this function is abused to determine if the filter must be shown // guess vessel count uint count = 0; show_filter = false; 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 kerbals if (EVA.IsDead(v)) continue; // avoid problems if the DB isn't ready if (DB.Ready()) { // get vessel data vessel_data vd = DB.VesselData(v.id); // determine if filter must be shown show_filter |= vd.group.Length > 0 && vd.group != "NONE"; // if the panel is filtered, skip filtered vessels if (filtered() && vd.group != filter) continue; } // the vessel will be rendered ++count; } // deal with no vessels case count = Math.Max(1u, count); // calculate height float vessels_height = 10.0f + (float)count * (16.0f + 10.0f); uint config_entries = 0; if (configured_id != Guid.Empty) { config_entries = 3u; // group, info & notes if (Kerbalism.ec_rule != null) ++config_entries; if (Kerbalism.supply_rules.Count > 0) ++config_entries; if (Kerbalism.features.signal) config_entries += 2u; if (Kerbalism.features.malfunction) config_entries += 2u; if (Settings.StormDuration > double.Epsilon) ++config_entries; if (filtered()) ++config_entries; } float config_height = (float)config_entries * 16.0f; float filter_height = show_filter ? 16.0f + 10.0f : 0.0f; return Math.Min(vessels_height + config_height + filter_height, Screen.height * 0.5f); }
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; } // if there is an active vessel Vessel v = FlightGlobals.ActiveVessel; if (v != null && Lib.IsVessel(v)) { // set control locks if necessary setLocks(v); // update thermometer readings updateThermometers(v); // manage resque mission mechanics manageResqueMission(v); } // show vessel resource warnings resourceWarnings(); // decay debris orbits atmosphereDecay(); // FIXME: forcing warp rate here essentially stop the 'slow warp change' done in Lib.StopWarp(), temporarely disabled // [disabled] detect if there are any serious warnings active // note: we consider serious only the ones that have a time-to-death shorther than 1 day //bool warnings = false; //foreach(var p in DB.Kerbals()) //{ // kerbal_data kd = p.Value; // warnings |= kd.msg_freezing > 0; // warnings |= kd.msg_burning > 0; // warnings |= kd.msg_deprived > 0; //} // if there is a warning active, do not allow the user to warp at high speed // rationale: at high warp speed, even stopping warping don't give the user time to react, kerbals just die //if (warnings && TimeWarp.CurrentRateIndex > 4) TimeWarp.SetRate(4, true); }
public override bool MeetRequirements() { // stop checking when requirements are met if (!meet_requirements) { meet_requirements = Kerbalism.rad_rule != null // a radiation rule is present && ProgressTracking.Instance != null && ProgressTracking.Instance.reachSpace.IsComplete // first suborbit flight completed && DB.Ready() && DB.Landmarks().belt_crossing == 0; // belt never crossed before } return meet_requirements; }
public override bool MeetRequirements() { // stop checking when requirements are met if (!meet_requirements) { meet_requirements = PartLoader.getPartInfoByName("Greenhouse") != null // greenhouse part is present && Lib.TechReady() && Lib.HasTech("scienceTech") // greenhouse unlocked && DB.Ready() && DB.Landmarks().space_harvest == 0; // greenhouse never harvested in space before } return meet_requirements; }
public override bool MeetRequirements() { // stop checking when requirements are met if (!meet_requirements) { meet_requirements = (Kerbalism.ec_rule != null || Kerbalism.supply_rules.Count > 0) // some resource-related life support rule is present && ProgressTracking.Instance != null && ProgressTracking.Instance.celestialBodyHome.orbit.IsComplete // first orbit completed && DB.Ready() && DB.Landmarks().manned_orbit== 0; // contract never completed } return meet_requirements; }
// hook: DisableKerbal() public static void hook_DisableKerbal(string k_name, bool disabled) { if (!DB.Ready()) { return; } if (!DB.Kerbals().ContainsKey(k_name)) { return; } DB.KerbalData(k_name).disabled = disabled ? 1u : 0; }
// hook: LivingSpace() public static double hook_LivingSpace(string k_name) { if (!DB.Ready()) { return(1.0); } if (!DB.Kerbals().ContainsKey(k_name)) { return(1.0); } return(DB.KerbalData(k_name).living_space); }
// hook: Entertainment() public static double hook_Entertainment(string k_name) { if (!DB.Ready()) { return(1.0); } if (!DB.Kerbals().ContainsKey(k_name)) { return(1.0); } return(DB.KerbalData(k_name).entertainment); }
// hook: Shielding() public static double hook_Shielding(string k_name) { if (!DB.Ready()) { return(0.0); } if (!DB.Kerbals().ContainsKey(k_name)) { return(0.0); } return(DB.KerbalData(k_name).shielding); }
public void BuildLinks() { // clear links container links.Clear(); // clear active relays container active_relays.Clear(); // iterate over all vessels foreach (Vessel v in FlightGlobals.Vessels) { // skip invalid vessels if (!Lib.IsVessel(v)) { continue; } // generate and store link status link_data ld = ComputeLink(v, new HashSet <Guid>()); links.Add(v.id, ld); // maintain and send messages // - do nothing if db isn't ready // - do not send messages for vessels without an antenna if (DB.Ready() && ld.status != link_status.no_antenna) { vessel_data vd = DB.VesselData(v.id); if (vd.msg_signal < 1 && !ld.linked) { vd.msg_signal = 1; if (DB.Ready()) { DB.NotificationData().first_signal_loss = 1; //< record first signal loss } if (vd.cfg_signal == 1 && !Blackout(v)) //< do not send message during storms { bool is_probe = (v.loaded ? v.GetCrewCount() == 0 : v.protoVessel.GetVesselCrew().Count == 0); Message.Post(Severity.warning, "Signal lost with <b>" + v.vesselName + "</b>", is_probe ? "Remote control disabled" : "Data transmission disabled"); } } else if (vd.msg_signal > 0 && ld.linked) { vd.msg_signal = 0; if (vd.cfg_signal == 1 && !Storm.JustEnded(v.mainBody, TimeWarp.deltaTime)) //< do not send messages after a storm { Message.Post(Severity.relax, "<b>" + v.vesselName + "</b> signal is back", ld.path.Count == 0 ? "We got a direct link with the space center" : "Relayed by <b>" + ld.path[ld.path.Count - 1] + "</b>"); } } } } }
// called every frame public void OnGUI() { // avoid case when DB isn't ready for whatever reason if (!DB.Ready()) return; // check only when at the space center if (HighLogic.LoadedScene != GameScenes.SPACECENTER) return; // check once in a while float time = Time.realtimeSinceStartup; if (last_check + check_interval > time) return; last_check = time; // get notification data notification_data nd = DB.NotificationData(); // if there are tutorials left to show if (nd.next_tutorial < tutorials.Length) { // check tutorial condition if (tutorial_condition(nd.next_tutorial)) { // check if the relative feature is enabled if (tutorial_feature(nd.next_tutorial)) { // show notification Entry e = tutorials[nd.next_tutorial]; Notification(e.title, e.msg, "INFO"); } // move to next tutorial ++nd.next_tutorial; } } // if there is one or more new deaths if (nd.death_counter > nd.last_death_counter) { // show notification Entry e = death_report[nd.next_death_report]; Notification(e.title, e.msg, "ALERT"); // move to next tutorial, cycle throwgh all of them repetatedly // note: done this way because modulo didn't work... ++nd.next_death_report; if (nd.next_death_report >= death_report.Length) nd.next_death_report -= (uint)death_report.Length; } // remember number of death kerbals nd.last_death_counter = nd.death_counter; }
// hook: InjectRadiation() public static void hook_InjectRadiation(string k_name, double amount) { if (!DB.Ready()) { return; } if (!DB.Kerbals().ContainsKey(k_name)) { return; } kerbal_data kd = DB.KerbalData(k_name); kd.radiation = Math.Max(kd.radiation + amount, 0.0); }
void fromEVA(GameEvents.FromToAction <Part, Part> data) { // use Hydrazine instead of MonoPropellant if RealFuel is installed string monoprop_name = detected_mods.RealFuels ? "Hydrazine" : "MonoPropellant"; // get any leftover EVA Fuel from EVA vessel double monoprop = data.from.Resources.list[0].amount; // add the leftover monoprop back to the pod data.to.RequestResource(monoprop_name, -monoprop); // manage resources in rules foreach (Rule r in rules) { if (r.resource_name.Length == 0 || r.on_eva <= double.Epsilon) { continue; } double leftover = ResourceCache.Info(data.from.vessel, r.resource_name).amount; data.to.RequestResource(r.resource_name, -leftover); } // merge EVA computer files into vessel Computer a = DB.VesselData(data.from.vessel.id).computer; Computer b = DB.VesselData(data.to.vessel.id).computer; b.merge(a); // forget vessel data DB.ForgetVessel(data.from.vessel.id); // purge vessel from resource cache ResourceCache.Purge(data.from.vessel.id); // execute script on vessel computer if (DB.Ready()) { DB.VesselData(data.to.vessel.id).computer.execute("run", "auto/eva_in", string.Empty, data.to.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 vessel if (Info.IsOpen()) { Info.Open(data.to.vessel); } }
// return true if a storm just ended // used to avoid sending 'signal is back' messages en-masse after the storm is over // - delta_time: time between calls to this function public static bool JustEnded(Vessel v, double delta_time) { if (!DB.Ready()) return false; // if in interplanetary space if (v.mainBody.flightGlobalsIndex == 0) { return DB.VesselData(v.id).storm_age < delta_time * 2.0; } // if inside a planetary system else { return DB.BodyData(Lib.PlanetarySystem(v.mainBody).name).storm_age < delta_time * 2.0; } }
// return true if a storm is in progress public static bool InProgress(Vessel v) { if (!DB.Ready()) return false; // if in interplanetary space if (v.mainBody.flightGlobalsIndex == 0) { return DB.VesselData(v.id).storm_state == 2; } // if inside a planetary system else { return DB.BodyData(Lib.PlanetarySystem(v.mainBody).name).storm_state == 2; } }
void OnPostRender() { // do nothing if DB isn't ready for whatever reason if (!DB.Ready()) return; // do nothing when not in map view if (!MapView.MapIsEnabled) return; // commit all geometry Signal.render(); Radiation.render(); // render all committed geometry LineRenderer.render(); ParticleRenderer.render(); }
void vesselDock(GameEvents.FromToAction <Part, Part> e) { // this need to happen when db is in a valid state if (DB.Ready()) { // merge computer data from vessel A to vessel B on docking Computer a = DB.VesselData(e.from.vessel.id).computer; Computer b = DB.VesselData(e.to.vessel.id).computer; b.merge(a); // forget vessel being docked DB.ForgetVessel(e.from.vessel.id); } // purge vessel from resource cache ResourceCache.Purge(e.from.vessel.id); }
// return time left until CME is over public static double TimeLeftCME(Vessel v) { if (!DB.Ready()) return 0.0; // if in interplanetary space if (v.mainBody.flightGlobalsIndex == 0) { vessel_data vd = DB.VesselData(v.id); return TimeLeftCME(vd.storm_time, vd.storm_age); } // if inside a planetary system else { body_data bd = DB.BodyData(Lib.PlanetarySystem(v.mainBody).name); return TimeLeftCME(bd.storm_time, bd.storm_age); } }
protected override void OnUpdate() { foreach(Vessel v in FlightGlobals.Vessels) { vessel_info vi = Cache.VesselInfo(v); if (!vi.is_valid) continue; bool manned = v.loaded ? v.GetCrewCount() > 0 : v.protoVessel.GetVesselCrew().Count > 0; bool in_orbit = Sim.Apoapsis(v) > v.mainBody.atmosphereDepth && Sim.Periapsis(v) > v.mainBody.atmosphereDepth; bool for_30days = v.missionTime > 60.0 * 60.0 * Lib.HoursInDay() * 30.0; if (manned && in_orbit && for_30days && DB.Ready()) { base.SetComplete(); DB.Landmarks().manned_orbit = 1; //< remember that contract was completed break; } } }
public void EmergencyHarvest() { // calculate reduced harvest size double reduced_harvest = harvest_size * growth * 0.5; // produce reduced quantity of food, proportional to current growth ResourceCache.Produce(vessel, resource_name, reduced_harvest); // reset growth growth = 0.0; // show message Message.Post(Lib.BuildString("On <color=FFFFFF>", vessel.vesselName, "</color> an emergency harved produced <color=FFFFFF>", reduced_harvest.ToString("F0"), " ", resource_name, "</color>")); // record first harvest if (!Lib.Landed(vessel) && DB.Ready()) DB.Landmarks().space_harvest = 1; }
public float height() { // note: this function is abused to determine if the filter must be shown // guess vessel count uint count = 0; show_filter = false; 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 kerbals if (EVA.IsDead(v)) continue; // avoid problems if the DB isn't ready if (DB.Ready()) { // get vessel data vessel_data vd = DB.VesselData(v.id); // determine if filter must be shown show_filter |= vd.group.Length > 0 && vd.group != "NONE"; // if the panel is filtered, skip filtered vessels if (filtered() && vd.group != filter) continue; } // the vessel will be rendered ++count; } // deal with no vessels case count = Math.Max(1u, count); // calculate height float vessels_height = 10.0f + (float)count * (16.0f + 10.0f); float config_height = configured_id == Guid.Empty ? 0.0f : filtered() ? 80.0f : 96.0f; float filter_height = show_filter ? 16.0f + 10.0f : 0.0f; return Math.Min(vessels_height + config_height + filter_height, Screen.height * 0.5f); }
// set highlighting public static void SetHighlight(Part p) { // get max malfunction among all reliability components in the part uint max_malfunctions = 0; foreach (Reliability m in p.FindModulesImplementing <Reliability>()) { max_malfunctions = Math.Max(max_malfunctions, m.malfunctions); } // note: when solar panels break (the stock mechanic), this throw exceptions inside KSP try { if (DB.Ready() && DB.VesselData(p.vessel.id).cfg_highlights > 0) { switch (max_malfunctions) { case 0: p.SetHighlightDefault(); break; case 1: p.SetHighlightType(Part.HighlightType.AlwaysOn); p.SetHighlightColor(Color.yellow); p.SetHighlight(true, false); break; default: p.SetHighlightType(Part.HighlightType.AlwaysOn); p.SetHighlightColor(Color.red); p.SetHighlight(true, false); break; } } else { p.SetHighlightDefault(); } } catch {} }
// hook: InjectRadiation() public static void hook_InjectRadiation(string k_name, double amount) { if (!DB.Ready()) { return; } if (!DB.Kerbals().ContainsKey(k_name)) { return; } kerbal_data kd = DB.KerbalData(k_name); foreach (Rule r in Kerbalism.rules) { if (r.modifier.Contains("radiation")) { var kmon = DB.KmonData(k_name, r.name); kmon.problem = Math.Max(kmon.problem + amount, 0.0); } } }
// hook: Breakdown public static void hook_Breakdown(Vessel v, ProtoCrewMember c) { if (!Cache.VesselInfo(v).is_valid) { return; } if (!DB.Ready()) { return; } if (!DB.Vessels().ContainsKey(v.id)) { return; } if (!DB.Kerbals().ContainsKey(c.name)) { return; } Breakdown(v, c); }
// trigger malfunction public void Break() { // reset age and lifetime age = 0.0; lifetime = 0.0; // apply malfunction penalty immediately Apply(0.5); // increase malfunction ++malfunctions; // show message if (DB.Ready() && DB.VesselData(vessel.id).cfg_malfunction == 1) { Message.Post(Severity.warning, PrepareMsg(malfunction_msg, vessel, malfunctions)); } // record first malfunction if (DB.Ready()) DB.NotificationData().first_malfunction = 1; }