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)); }
// build the visibility caches void BuildVisibility() { // get home body CelestialBody home = FlightGlobals.GetHomeBody(); // build direct visibility cache direct_visibility_cache.Clear(); foreach (Vessel v in FlightGlobals.Vessels) { // skip invalid vessels if (!Lib.IsVessel(v)) { continue; } // get antenna data antenna_data ad = antennas[v.id]; // raytrace home body Vector3d dir; double dist = 0.0; bool visible = Sim.RaytraceBody(v, home, out dir, out dist); dist = Math.Abs(dist); //< avoid problem below water level // store in visibility cache // note: we store distance & visibility flag at the same time direct_visibility_cache.Add(v.id, visible && dist < ad.range ? dist : 0.0); } // build indirect visibility cache indirect_visibility_cache.Clear(); foreach (Vessel v in FlightGlobals.Vessels) { // skip invalid vessels if (!Lib.IsVessel(v)) { continue; } // get antenna data antenna_data v_ad = antennas[v.id]; // for each vessel foreach (Vessel w in FlightGlobals.Vessels) { // skip invalid vessels if (!Lib.IsVessel(w)) { continue; } // do not test with itself if (v == w) { continue; } // do not compute visibility when both vessels have a direct link // rationale: optimization, the indirect visibility it never used in this case if (direct_visibility_cache[v.id] > double.Epsilon && direct_visibility_cache[w.id] > double.Epsilon) { continue; } // generate merged guid Guid id = Lib.CombineGuid(v.id, w.id); // avoid raycasting the same pair twice if (indirect_visibility_cache.ContainsKey(id)) { continue; } // get antenna data antenna_data w_ad = antennas[w.id]; // raytrace the vessel Vector3d dir; double dist = 0.0; bool visible = Sim.RaytraceVessel(v, w, out dir, out dist); // store visibility in cache // note: we store distance & visibility flag at the same time // note: relay visibility is asymmetric, done at link build time indirect_visibility_cache.Add(id, visible && dist < Math.Min(v_ad.range, w_ad.range) ? dist : 0.0); } } }