// 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 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; }
// tutorial conditions private static bool tutorial_condition(uint i) { if (ProgressTracking.Instance == null) return false; switch(i) { case 0: return ProgressTracking.Instance.reachSpace.IsComplete; // 'food & oxygen' case 1: return ProgressTracking.Instance.celestialBodyHome.orbit.IsComplete; // 'electric charge' case 2: return DB.NotificationData().first_belt_crossing > 0; // 'radiation' case 3: // 'quality of life' foreach(var b in ProgressTracking.Instance.celestialBodyNodes) { if (b != ProgressTracking.Instance.celestialBodyHome && b.flyBy.IsComplete) return true; } return false; case 4: return DB.NotificationData().first_signal_loss > 0; // 'signals' case 5: return DB.NotificationData().first_malfunction > 0; // 'malfunctions' } return false; }
// 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; }
// 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 } } }
// called manually to register a death (used for eva death) public static void RegisterDeath() { ++DB.NotificationData().death_counter; }
// called by the engine when a kerbal die public void RegisterDeathEvent(EventReport e) { ++DB.NotificationData().death_counter; }