// update every frame // note: we don't do it every simulation step because: // - performance // - during scene changes the vessel list change asynchronously, but is synched every frame, apparently public void Update() { // get existing antennas BuildAntennas(); // build visibility cache BuildVisibility(); // build the links cache BuildLinks(); // if there is an active vessel, and is valid Vessel v = FlightGlobals.ActiveVessel; if (v != null && Lib.IsVessel(v)) { // get link state link_data ld = links[v.id]; // for each antenna foreach(Antenna m in v.FindPartModulesImplementing<Antenna>()) { // remove incomplete data toggle m.Events["TransmitIncompleteToggle"].active = false; // enable/disable science transmission m.can_transmit = ld.linked; // store transmission distance in the antenna m.transmission_distance = ld.distance; } } }
public void update(Vessel v, vessel_info vi, vessel_data vd, vessel_resources resources, double elapsed_s) { // do nothing if signal mechanic is disabled if (!Kerbalism.features.signal) { return; } // get link data link_data link = vi.link; // consume relay ec // note: this is the only way to do it with new signal and resource systems if (vi.antenna.relay_range > 0.0) { foreach (Vessel w in FlightGlobals.Vessels) { vessel_info wi = Cache.VesselInfo(w); if (wi.is_valid) { if (wi.link.path.Contains(v)) { resources.Consume(v, "ElectricCharge", vi.antenna.relay_cost * elapsed_s); break; } } } } // maintain and send messages // - do nothing if db isn't ready // - do not send messages for vessels without an antenna if (link.status != link_status.no_antenna) { if (vd.msg_signal < 1 && !link.linked) { vd.msg_signal = 1; if (vd.cfg_signal == 1 && !vi.blackout) //< do not send message during storms { Message.Post(Severity.warning, Lib.BuildString("Signal lost with <b>", v.vesselName, "</b>"), vi.crew_count == 0 && Settings.RemoteControlLink ? "Remote control disabled" : "Data transmission disabled"); } } else if (vd.msg_signal > 0 && link.linked) { vd.msg_signal = 0; if (vd.cfg_signal == 1 && !Storm.JustEnded(v, elapsed_s)) //< do not send messages after a storm { var path = link.path; Message.Post(Severity.relax, Lib.BuildString("<b>", v.vesselName, "</b> signal is back"), path.Count == 0 ? "We got a direct link with the space center" : Lib.BuildString("Relayed by <b>", path[path.Count - 1].vesselName, "</b>")); } } } }
public link_data(link_data other) { this.linked = other.linked; this.status = other.status; this.distance = other.distance; this.path = new List <Vessel>(); foreach (Vessel v in other.path) { this.path.Add(v); } }
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>"); } } } } }
public void Update() { // do nothing if tech tree is not ready if (!Lib.TechReady()) return; // get range double range = Signal.Range(scope, Signal.ECC()); // update rmb ui status RangeStatus = Lib.HumanReadableRange(range); RelayStatus = relay ? "Active" : "Disabled"; // when in flight if (HighLogic.LoadedSceneIsFlight) { // remove incomplete data toggle Events["TransmitIncompleteToggle"].active = false; // get vessel info from the cache vessel_info info = Cache.VesselInfo(vessel); // proper antenna mechanics for valid vessels if (info.is_valid) { // shortcut link_data link = info.link; // enable/disable science transmission can_transmit = link.linked; // determine currect packet cost // note: we set it to max float if out of range, to indirectly specify antenna score if (link.distance <= range) { this.packetResourceCost = (float)(min_transmission_cost + (max_transmission_cost - min_transmission_cost) * link.distance / range); CostStatus = Lib.BuildString(this.packetResourceCost.ToString("F2"), " EC/Mbit"); } else { this.packetResourceCost = float.MaxValue; CostStatus = ""; } } // sane defaults for invalid vessels else { can_transmit = false; this.packetResourceCost = float.MaxValue; CostStatus = ""; } } }
GUIContent indicator_signal(Vessel v) { GUIContent state = new GUIContent(); link_data ld = Signal.Link(v); switch(ld.status) { case link_status.direct_link: state.image = icon_signal_direct; state.tooltip = "Direct link"; break; case link_status.indirect_link: state.image = icon_signal_relay; if (ld.path.Count == 1) { state.tooltip = "Signal relayed by <b>" + ld.path[ld.path.Count - 1] + "</b>"; } else { state.tooltip = "Signal relayed by:"; for(int i=ld.path.Count-1; i>0; --i) state.tooltip += "\n<b>" + ld.path[i] + "</b>"; } break; case link_status.no_link: state.image = icon_signal_none; state.tooltip = !Signal.Blackout(v) ? "No signal" : "Blackout"; break; case link_status.no_antenna: state.image = icon_signal_none; state.tooltip = "No antenna"; break; } return state; }
GUIContent indicator_signal(Vessel v, vessel_info vi) { GUIContent state = new GUIContent(); link_data ld = vi.link; switch(ld.status) { case link_status.direct_link: state.image = icon_signal_direct; state.tooltip = "Direct link"; break; case link_status.indirect_link: state.image = icon_signal_relay; if (ld.path.Count == 1) { state.tooltip = Lib.BuildString("Signal relayed by <b>", ld.path[ld.path.Count - 1].vesselName, "</b>"); } else { state.tooltip = "Signal relayed by:"; for(int i=ld.path.Count-1; i>0; --i) state.tooltip += Lib.BuildString("\n<b>", ld.path[i].vesselName, "</b>"); } break; case link_status.no_link: state.image = icon_signal_none; state.tooltip = !vi.blackout ? "No signal" : "Blackout"; break; case link_status.no_antenna: state.image = icon_signal_none; state.tooltip = "No antenna"; break; } return state; }
// called at every frame public void Update() { // FIXME: can't double-click on vessel to switch active one, if line is rendered // hide all lines foreach (var l in lines) { l.Value.Show(false); } // do nothing if db isn't ready if (!DB.Ready()) { return; } // do nothing if signal is disabled if (!Kerbalism.features.signal) { return; } // do nothing if not in map view or tracking station if (!MapView.MapIsEnabled) { return; } // get homebody position Vector3d home = FlightGlobals.GetHomeBody().position; // iterate all vessels foreach (Vessel v in FlightGlobals.Vessels) { // skip invalid vessels if (!Lib.IsVessel(v)) { continue; } // skip resque missions if (Lib.IsResqueMission(v)) { continue; } // skip EVA kerbals if (v.isEVA) { continue; } // get vessel data vessel_data vd = DB.VesselData(v.id); // do nothing if showlink is disabled if (vd.cfg_showlink == 0) { continue; } // get link status link_data ld = Signal.Link(v); // if there is an antenna if (ld.status != link_status.no_antenna) { // get line renderer from the cache Line line = getLine(v.id); // start of the line Vector3d a = Lib.VesselPosition(v); // determine end of line and color Vector3d b; Color clr; if (ld.status == link_status.direct_link) { b = home; clr = Color.green; } else if (ld.status == link_status.indirect_link) { Vessel relay = FlightGlobals.Vessels.Find(k => k.id == ld.path_id[ld.path.Count - 1]); if (relay == null) { line.Show(false); continue; } //< skip if it doesn't exist anymore b = Lib.VesselPosition(relay); clr = Color.yellow; } else // no link { b = home; clr = Color.red; } // setup the line and show it line.UpdatePoints(a, b, clr); line.Show(true); } } }
public link_data ComputeLink(Vessel v, HashSet <Guid> avoid_inf_recursion) { // if it has no antenna if (antennas[v.id].range <= double.Epsilon) { return(new link_data(false, link_status.no_antenna, 0.0)); } // if there is a storm and the vessel is inside a magnetosphere if (Blackout(v)) { return(new link_data(false, link_status.no_link, 0.0)); } // check for direct link // note: we also get distance from the cache and store it in the link double direct_visible_dist = direct_visibility_cache[v.id]; bool direct_visible = direct_visible_dist > 0.0; if (direct_visible) { return(new link_data(true, link_status.direct_link, direct_visible_dist)); } // avoid infinite recursion avoid_inf_recursion.Add(v.id); // get antenna data antenna_data v_ad = antennas[v.id]; // check for indirect link foreach (Vessel w in FlightGlobals.Vessels) { // skip invalid vessels if (!Lib.IsVessel(w)) { continue; } // avoid infinite recursion if (avoid_inf_recursion.Contains(w.id)) { continue; } // avoid testing against itself if (v == w) { continue; } // get antenna data antenna_data w_ad = antennas[w.id]; // check for indirect link to home body // note: we also get distance from the cache // note: we check the relay range, that is asymmetric double indirect_visible_dist = indirect_visibility_cache[Lib.CombineGuid(v.id, w.id)]; bool indirect_visible = indirect_visible_dist > 0.0 && indirect_visible_dist < Math.Min(v_ad.range, w_ad.relay_range); if (indirect_visible) { // check link to home body, recursively link_data next_link = ComputeLink(w, avoid_inf_recursion); // if indirectly linked if (next_link.linked) { // flag the relay for ec consumption, but only once if (!active_relays.ContainsKey(w.id)) { active_relays.Add(w.id, w_ad.relay_cost); } // update the link data and return it next_link.status = link_status.indirect_link; next_link.distance = indirect_visible_dist; //< store distance of last link next_link.path.Add(w.vesselName); next_link.path_id.Add(w.id); return(next_link); } } } // no link return(new link_data(false, link_status.no_link, 0.0)); }
public static void render() { // get home body position Vector3 home = ScaledSpace.LocalToScaledSpace(FlightGlobals.GetHomeBody().position); // for each vessel foreach (Vessel v in FlightGlobals.Vessels) { // get info from the cache vessel_info vi = Cache.VesselInfo(v); // skip invalid vessels if (!vi.is_valid) { continue; } // get data from db vessel_data vd = DB.VesselData(v.id); // skip vessels with showlink disabled if (vd.cfg_showlink == 0) { continue; } // get link data link_data link = vi.link; // skip vessels with no antenna if (link.status == link_status.no_antenna) { continue; } // start of the line Vector3 a = ScaledSpace.LocalToScaledSpace(v.GetWorldPos3D()); // determine end of line and color Vector3 b; Color color; if (link.status == link_status.no_link) { b = home; color = Color.red; } else if (link.status == link_status.direct_link) { b = home; color = Color.green; } else //< indirect link { // get link path var path = link.path; // use relay position b = ScaledSpace.LocalToScaledSpace(path[path.Count - 1].GetWorldPos3D()); color = Color.yellow; } // commit the line LineRenderer.commit(a, b, color); } }
public static link_data Link(Vessel v, Vector3d position, antenna_data antenna, bool blackout, HashSet <Guid> avoid_inf_recursion) { // assume linked if signal mechanic is disabled if (!Kerbalism.features.signal) { return(new link_data(true, link_status.direct_link, double.MaxValue)); } // if it has no antenna if (antenna.range <= double.Epsilon) { return(new link_data(false, link_status.no_antenna, 0.0)); } // if there is a storm and the vessel is inside a magnetosphere if (blackout) { return(new link_data(false, link_status.no_link, 0.0)); } // store raytracing data Vector3d dir; double dist; bool visible; // raytrace home body visible = Sim.RaytraceBody(v, position, FlightGlobals.GetHomeBody(), out dir, out dist); dist = visible && antenna.range > dist ? dist : double.MaxValue; // if directly linked if (antenna.range > dist) { return(new link_data(true, link_status.direct_link, dist)); } // for each other vessel foreach (Vessel w in FlightGlobals.Vessels) { // do not test with itself if (v == w) { continue; } // skip vessels already in this chain if (avoid_inf_recursion.Contains(w.id)) { continue; } // get vessel from the cache // note: safe because we are avoiding infinite recursion vessel_info wi = Cache.VesselInfo(w); // skip invalid vessels if (!wi.is_valid) { continue; } // skip non-relays and non-linked relays if (wi.antenna.relay_range <= double.Epsilon || !wi.link.linked) { continue; } // raytrace the other vessel visible = Sim.RaytraceVessel(v, w, position, wi.position, out dir, out dist); dist = visible && antenna.range > dist ? dist : double.MaxValue; // if indirectly linked // note: relays with no EC have zero relay_range // note: avoid relay loops if (antenna.range > dist && wi.antenna.relay_range > dist && !wi.link.path.Contains(v)) { // create indirect link data link_data link = new link_data(wi.link); // update the link data and return it link.status = link_status.indirect_link; link.distance = dist; //< store distance of last link link.path.Add(w); return(link); } } // no link return(new link_data(false, link_status.no_link, 0.0)); }