// calculate a cache entry for the vessel static vessel_info Compute(Vessel v) { vessel_info info = new vessel_info(); info.position = Lib.VesselPosition(v); info.sunlight = Sim.RaytraceBody(v, Sim.Sun(), out info.sun_dir, out info.sun_dist); info.temperature = Sim.Temperature(v, info.sunlight); info.cosmic_radiation = Radiation.CosmicRadiation(v); info.belt_radiation = Radiation.BeltRadiation(v); info.storm_radiation = Radiation.StormRadiation(v, info.sunlight); info.env_radiation = info.cosmic_radiation + info.belt_radiation + info.storm_radiation; info.breathable = Sim.Breathable(v); foreach(var p in Kerbalism.rules) { Rule r = p.Value; if (r.resource_name.Length > 0) { var vmon = new vmon_cache(); vmon.depletion = r.EstimateLifetime(v); double amount = Lib.GetResourceAmount(v, r.resource_name); double capacity = Lib.GetResourceCapacity(v, r.resource_name); vmon.level = capacity > double.Epsilon ? amount / capacity : 1.0; //< level is 1 with no capacity info.vmon.Add(p.Value.name, vmon); } } return info; }
// hook: InsideMagnetosphere() public static bool hook_InsideMagnetosphere(Vessel v) { // note: this doesn't consider the sun heliopause vessel_info vi = Cache.VesselInfo(v); return(vi.is_valid && vi.inside_pause); }
static void render_habitat(Panel p, Vessel v, vessel_info vi) { // if habitat feature is disabled, do not show the panel if (!Features.Habitat) return; // if vessel is unmanned, do not show the panel if (vi.crew_count == 0) return; // determine some content, with colors string pressure_str = Lib.Color(Lib.HumanReadablePressure(vi.pressure * Sim.PressureAtSeaLevel()), vi.pressure < Settings.PressureThreshold, "yellow"); string poisoning_str = Lib.Color(Lib.HumanReadablePerc(vi.poisoning, "F2"), vi.poisoning > Settings.PoisoningThreshold * 0.5, "yellow"); // render panel, add some content based on enabled features if (!v.isEVA) { p.section("HABITAT"); if (Features.Pressure) p.content("pressure", pressure_str); if (Features.Poisoning) p.content("co2 level", poisoning_str); if (Features.Shielding) p.content("shielding", Habitat.shielding_to_string(vi.shielding)); if (Features.LivingSpace) p.content("living space", Habitat.living_space_to_string(vi.living_space)); if (Features.Comfort) p.content("comfort", vi.comforts.summary(), vi.comforts.tooltip()); } else { p.section("HABITAT"); if (Features.Poisoning) p.content("co2 level", poisoning_str); } }
static void render_supplies(Panel p, Vessel v, vessel_info vi, vessel_resources resources) { // for each supply int supplies = 0; foreach(Supply supply in Profile.supplies) { // get resource info resource_info res = resources.Info(v, supply.resource); // only show estimate if the resource is present if (res.amount <= double.Epsilon) continue; // render panel title, if not done already if (supplies == 0) p.section("SUPPLIES"); // rate tooltip string rate_tooltip = Math.Abs(res.rate) >= 1e-10 ? Lib.BuildString ( res.rate > 0.0 ? "<color=#00ff00><b>" : "<color=#ff0000><b>", Lib.HumanReadableRate(Math.Abs(res.rate)), "</b></color>" ) : string.Empty; // determine label string label = supply.resource == "ElectricCharge" ? "battery" : Lib.SpacesOnCaps(supply.resource).ToLower(); // finally, render resource supply p.content(label, Lib.HumanReadableDuration(res.Depletion(vi.crew_count)), rate_tooltip); ++supplies; } }
void indicator_problems(Panel p, Vessel v, vessel_info vi, List<ProtoCrewMember> crew) { // 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); if (Features.SpaceWeather) problem_storm(v, ref problem_icons, ref problem_tooltips); if (crew.Count > 0 && Profile.rules.Count > 0) problem_kerbals(crew, ref problem_icons, ref problem_tooltips); if (crew.Count > 0 && Features.Radiation) problem_radiation(vi, ref problem_icons, ref problem_tooltips); problem_greenhouses(v, vi.greenhouses, ref problem_icons, ref problem_tooltips); if (Features.Poisoning) problem_poisoning(vi, ref problem_icons, ref problem_tooltips); // choose problem icon const UInt64 problem_icon_time = 3; Texture problem_icon = Icons.empty; if (problem_icons.Count > 0) { UInt64 problem_index = ((UInt64)Time.realtimeSinceStartup / problem_icon_time) % (UInt64)(problem_icons.Count); problem_icon = problem_icons[(int)problem_index]; } // generate problem icon p.icon(problem_icon, String.Join("\n", problem_tooltips.ToArray())); }
void render_crew(Vessel v, vessel_info vi, List<ProtoCrewMember> crew) { // get degenerative rules List<Rule> degen_rules = Kerbalism.rules.FindAll(k => k.degeneration > 0.0); // do nothing if there are no degenerative rules if (degen_rules.Count == 0) return; // do nothing if there isn't a crew if (crew.Count == 0) return; // select a kerbal ProtoCrewMember kerbal = crew[crew_index % crew.Count]; // render it kerbal_data kd = DB.KerbalData(kerbal.name); render_title(Lib.Epsilon(kerbal.name.ToUpper(), 20), ref crew_index, crew.Count); foreach(Rule r in degen_rules) { var kmon = DB.KmonData(kerbal.name, r.name); var bar = Lib.ProgressBar(20, kmon.problem, r.warning_threshold, r.danger_threshold, r.fatal_threshold, kd.disabled > 0 ? "cyan" : ""); render_content(r.name.AddSpacesOnCaps().ToLower(), bar); } render_content("specialization", kerbal.trait); if (Kerbalism.detected_mods.DeepFreeze) render_content("hibernated", kd.disabled > 0 ? "yes" : "no"); if (Kerbalism.detected_mods.CLS) render_content("inside", v.isEVA ? "EVA" : Lib.Epsilon(kd.space_name.Length == 0 ? v.vesselName : kd.space_name, 24)); render_space(); }
GUIContent indicator_supplies(Vessel v, vessel_info vi) { GUIContent state = new GUIContent(); List<string> tooltips = new List<string>(); uint max_severity = 0; if (vi.crew_count > 0) { foreach(Rule r in Kerbalism.supply_rules) { resource_info res = ResourceCache.Info(v, r.resource_name); if (res.capacity > double.Epsilon) { double depletion = r.Depletion(v, res); string deplete_str = depletion <= double.Epsilon ? ", depleted" : double.IsNaN(depletion) ? "" : Lib.BuildString(", deplete in <b>", Lib.HumanReadableDuration(depletion), "</b>"); tooltips.Add(Lib.BuildString(r.resource_name, ": <b>", Lib.HumanReadablePerc(res.level), "</b>", deplete_str)); uint severity = res.level <= 0.005 ? 2u : res.level <= r.low_threshold ? 1u : 0; max_severity = Math.Max(max_severity, severity); } } } switch(max_severity) { case 0: state.image = icon_supplies_nominal; break; case 1: state.image = icon_supplies_warning; break; case 2: state.image = icon_supplies_danger; break; } state.tooltip = string.Join("\n", tooltips.ToArray()); return state; }
void render_environment(Vessel v, vessel_info vi) { // determine atmosphere string atmo_desc = "none"; if (Sim.Underwater(v)) atmo_desc = "ocean"; else if (vi.atmo_factor < 1.0) //< inside an atmosphere { atmo_desc = vi.breathable ? "breathable" : "not breathable"; } render_title("ENVIRONMENT"); render_content("temperature", Lib.HumanReadableTemp(vi.temperature)); render_content("radiation", Lib.HumanReadableRadiationRate(vi.radiation)); render_content("atmosphere", atmo_desc); if (Settings.ShowFlux) { render_content("solar flux", Lib.HumanReadableFlux(vi.solar_flux)); render_content("albedo flux", Lib.HumanReadableFlux(vi.albedo_flux)); render_content("body flux", Lib.HumanReadableFlux(vi.body_flux)); } if (Settings.RelativisticTime) { render_content("time dilation", Lib.HumanReadablePerc(1.0 / vi.time_dilation)); } render_space(); }
GUIContent indicator_supplies(Vessel v, vessel_info vi) { GUIContent state = new GUIContent(); List<string> tooltips = new List<string>(); uint max_severity = 0; if (Lib.CrewCount(v) > 0) { foreach(Rule r in Kerbalism.supply_rules) { var vmon = vi.vmon[r.name]; string deplete_str = vmon.depletion <= double.Epsilon ? ", depleted" : double.IsNaN(vmon.depletion) ? "" : ", deplete in <b>" + Lib.HumanReadableDuration(vmon.depletion) + "</b>"; tooltips.Add(r.resource_name + ": <b>" + (vmon.level * 100.0).ToString("F0") + "%</b>" + deplete_str); uint severity = vmon.level <= double.Epsilon ? 2u : vmon.level <= r.low_threshold ? 1u : 0; max_severity = Math.Max(max_severity, severity); } } switch(max_severity) { case 0: state.image = icon_supplies_nominal; break; case 1: state.image = icon_supplies_warning; break; case 2: state.image = icon_supplies_danger; break; } state.tooltip = string.Join("\n", tooltips.ToArray()); return state; }
public void Update() { // in flight if (Lib.IsFlight()) { // get info from cache vessel_info vi = Cache.VesselInfo(vessel); // do nothing if vessel is invalid if (!vi.is_valid) { return; } // update ui bool has_operator = operator_cs.check(vessel); Events["Toggle"].guiName = Lib.StatusToggle(exp_name, !recording ? "stopped" : issue.Length == 0 ? "recording" : Lib.BuildString("<color=#ffff00>", issue, "</color>")); } // in the editor else if (Lib.IsEditor()) { // update ui Events["Toggle"].guiName = Lib.StatusToggle(exp_name, recording ? "recording" : "stopped"); } }
// called every frame public void Update() { if (HighLogic.LoadedSceneIsEditor) return; // get info from cache vessel_info vi = Cache.VesselInfo(this.vessel); // do nothing if vessel is invalid if (!vi.is_valid) return; // set reading switch(type) { case "temperature": Status = Lib.HumanReadableTemp(vi.temperature); break; case "radiation": Status = vi.radiation > double.Epsilon ? Lib.HumanReadableRadiationRate(vi.radiation) : "nominal"; break; case "solar_flux": Status = Lib.HumanReadableFlux(vi.solar_flux); break; case "albedo_flux": Status = Lib.HumanReadableFlux(vi.albedo_flux); break; case "body_flux": Status = Lib.HumanReadableFlux(vi.body_flux); break; } // manage pin animation on geiger counter if (pinanim != null && type == "radiation") { pinanim["pinanim"].normalizedTime = Lib.Clamp(pinfc.Evaluate((float)(vi.radiation * 3600.0)), 0.0f, 1.0f); pinanim.Play(); } }
// used to influence aging speed using radiation public static double RadiationInfluence(Vessel v) { vessel_info vi = Cache.VesselInfo(v); return vi.radiation > Settings.StormRadiation * 0.9 ? (Lib.CrewCapacity(v) > 0 ? 3.0 : 5.0) : 1.0; }
// return true if body is relevant to the player // - body: reference body of the planetary system static bool body_is_relevant(CelestialBody body) { // [disabled] // special case: home system is always relevant // note: we deal with the case of a planet mod setting homebody as a moon //if (body == Lib.PlanetarySystem(FlightGlobals.GetHomeBody())) return true; // for each vessel foreach(Vessel v in FlightGlobals.Vessels) { // if inside the system if (Lib.PlanetarySystem(v.mainBody) == body) { // get info from the cache vessel_info vi = Cache.VesselInfo(v); // skip invalid vessels if (!vi.is_valid) continue; // obey message config if (!DB.Vessel(v).cfg_storm) continue; // body is relevant return true; } } return false; }
// show warning message when a vessel cross a radiation belt public static void beltWarnings(Vessel v, vessel_info vi, vessel_data vd) { // if there is a radiation rule if (Kerbalism.rad_rule != null) { // we only show the warning for manned vessels, or for all vessels the first time its crossed bool must_warn = vi.crew_count > 0 || DB.Landmarks().belt_crossing == 0; // show the message if (vi.inside_belt && vd.msg_belt < 1 && must_warn) { Message.Post(Lib.BuildString("<b>", v.vesselName, "</b> is crossing <i>", v.mainBody.bodyName, " radiation belt</i>"), "Exposed to extreme radiation"); vd.msg_belt = 1; } else if (!vi.inside_belt && vd.msg_belt > 0) { // no message after crossing the belt vd.msg_belt = 0; } } // record first belt crossing if (vi.inside_belt) { DB.Landmarks().belt_crossing = 1; } }
public void Update() { // in flight if (Lib.IsFlight()) { // get info from cache vessel_info vi = Cache.VesselInfo(vessel); // do nothing if vessel is invalid if (!vi.is_valid) { return; } // update status Status = telemetry_content(vessel, vi, type); // if there is a pin animation if (pin.Length > 0) { // still-play pin animation pin_anim.still(telemetry_pin(vessel, vi, type)); } } }
void indicator_ec(Panel p, Vessel v, vessel_info vi) { resource_info ec = ResourceCache.Info(v, "ElectricCharge"); Supply supply = Profile.supplies.Find(k => k.resource == "ElectricCharge"); double low_threshold = supply != null ? supply.low_threshold : 0.15; double depletion = ec.Depletion(vi.crew_count); string tooltip = Lib.BuildString ( "<align=left /><b>name\tlevel\tduration</b>\n", ec.level <= 0.005 ? "<color=#ff0000>" : ec.level <= low_threshold ? "<color=#ffff00>" : "<color=#cccccc>", "EC\t", Lib.HumanReadablePerc(ec.level), "\t", depletion <= double.Epsilon ? "depleted" : Lib.HumanReadableDuration(depletion), "</color>" ); Texture image = ec.level <= 0.005 ? Icons.battery_red : ec.level <= low_threshold ? Icons.battery_yellow : Icons.battery_white; p.icon(image, tooltip); }
static void render_environment(Panel p, Vessel v, vessel_info vi) { // don't show env panel in eva kerbals if (v.isEVA) return; // get all sensor readings HashSet<string> readings = new HashSet<string>(); if (v.loaded) { foreach(var s in Lib.FindModules<Sensor>(v)) { readings.Add(s.type); } } else { foreach(ProtoPartModuleSnapshot m in Lib.FindModules(v.protoVessel, "Sensor")) { readings.Add(Lib.Proto.GetString(m, "type")); } } readings.Remove(string.Empty); p.section("ENVIRONMENT"); foreach(string type in readings) { p.content(type, Sensor.telemetry_content(v, vi, type), Sensor.telemetry_tooltip(v, vi, type)); } if (readings.Count == 0) p.content("<i>no sensors installed</i>"); }
void problem_sunlight(vessel_info info, ref List<Texture> icons, ref List<string> tooltips) { if (!info.sunlight) { icons.Add(icon_sun_shadow); tooltips.Add("In shadow"); } }
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"); } }
void problem_sunlight(vessel_info info, ref List<Texture> icons, ref List<string> tooltips) { if (info.sunlight <= double.Epsilon) { icons.Add(Icons.sun_black); tooltips.Add("In shadow"); } }
public static double evaluate(Vessel v, vessel_info vi, vessel_resources resources, List<string> modifiers) { double k = 1.0; foreach (string mod in modifiers) { switch (mod) { case "breathable": k *= vi.breathable ? 0.0 : 1.0; break; case "temperature": k *= vi.temp_diff; break; case "radiation": k *= vi.radiation; break; case "shielding": k *= 1.0 - vi.shielding; break; case "volume": k *= vi.volume; break; case "surface": k *= vi.surface; break; case "living_space": k /= vi.living_space; break; case "comfort": k /= vi.comforts.factor; break; case "pressure": k *= vi.pressure > Settings.PressureThreshold ? 1.0 : Settings.PressureFactor; break; case "poisoning": k *= vi.poisoning > Settings.PoisoningThreshold ? 1.0 : Settings.PoisoningFactor; break; case "per_capita": k /= (double)Math.Max(vi.crew_count, 1); break; default: k *= resources.Info(v, mod).amount; break; } } return k; }
bool render_vessel(Panel p, Vessel v) { // get vessel info vessel_info vi = Cache.VesselInfo(v); // skip invalid vessels if (!vi.is_valid) return false; // get data from db VesselData vd = DB.Vessel(v); // determine if filter must be shown show_filter |= vd.group.Length > 0 && vd.group != "NONE"; // skip filtered vessels if (filtered() && vd.group != filter) return false; // get resource handler vessel_resources resources = ResourceCache.Get(v); // get vessel crew List<ProtoCrewMember> crew = Lib.CrewList(v); // get vessel name string vessel_name = v.isEVA ? crew[0].name : v.vesselName; // get body name string body_name = v.mainBody.name.ToUpper(); // render entry p.header ( Lib.BuildString("<b>", Lib.Ellipsis(vessel_name, Styles.ScaleStringLength(((page == MonitorPage.data || page == MonitorPage.log || selected_id == Guid.Empty) && !Lib.IsFlight()) ? 50 : 30)), "</b> <size=", Styles.ScaleInteger(9).ToString(), "><color=#cccccc>", Lib.Ellipsis(body_name, Styles.ScaleStringLength(8)), "</color></size>"), string.Empty, () => { selected_id = selected_id != v.id ? v.id : Guid.Empty; } ); // problem indicator indicator_problems(p, v, vi, crew); // battery indicator indicator_ec(p, v, vi); // supply indicator if (Features.Supplies) indicator_supplies(p, v, vi); // reliability indicator if (Features.Reliability) indicator_reliability(p, v, vi); // signal indicator if (RemoteTech.Enabled() || HighLogic.fetch.currentGame.Parameters.Difficulty.EnableCommNet) indicator_signal(p, v, vi); // done return true; }
public static void update(Vessel v, vessel_info vi, VesselData vd, double elapsed_s) { if (!Enabled()) { return; } SetCommsBlackout(v.id, vi.blackout, "kerbalism_cme"); }
public static bool timeout(this Panel p, vessel_info vi) { if (!vi.connection.linked && vi.crew_count == 0) { p.header(msg[((int)Time.realtimeSinceStartup) % msg.Length]); return(true); } return(false); }
// --- RADIATION ------------------------------------------------------------ // return amount of environment radiation at the position of the specified vessel public static double Radiation(Vessel v) { if (!Features.Radiation) { return(0.0); } vessel_info vi = Cache.VesselInfo(v); return(vi.radiation); }
// implement scrubber mechanics public void FixedUpdate() { // do nothing in the editor if (HighLogic.LoadedSceneIsEditor) return; // do nothing until tech tree is ready if (!Lib.TechReady()) return; // deduce quality from technological level if necessary if (efficiency <= double.Epsilon) efficiency = DeduceEfficiency(); // get vessel info from the cache vessel_info vi = Cache.VesselInfo(vessel); // do nothing if vessel is invalid if (!vi.is_valid) return; // get resource cache vessel_resources resources = ResourceCache.Get(vessel); // get elapsed time double elapsed_s = Kerbalism.elapsed_s * vi.time_dilation; // if inside breathable atmosphere if (vi.breathable) { // produce oxygen from the intake resources.Produce(vessel, resource_name, intake_rate * elapsed_s); // set status Status = "Intake"; } // if outside breathable atmosphere and enabled else if (is_enabled) { // transform waste + ec into resource resource_recipe recipe = new resource_recipe(resource_recipe.scrubber_priority); recipe.Input(waste_name, co2_rate * elapsed_s); recipe.Input("ElectricCharge", ec_rate * elapsed_s); recipe.Output(resource_name, co2_rate * co2_ratio * efficiency * elapsed_s); resources.Transform(recipe); // set status Status = "Running"; } // if outside breathable atmosphere and disabled else { // set status Status = "Off"; } // add efficiency to status Status += Lib.BuildString(" (Efficiency: ", (efficiency * 100.0).ToString("F0"), "%)"); }
// execute a command on the computer public void execute(string cmd, string arg0, string arg1, Vessel environment, bool remote = false) { // do nothing if cmd is empty if (cmd.Length == 0) return; // check if there is EC on the vessel if (ResourceCache.Info(environment, "ElectricCharge").amount <= double.Epsilon) { error("no electric charge"); return; } // check if there is signal in case the command is remote vessel_info vi = Cache.VesselInfo(environment); if (remote && !vi.link.linked && vi.crew_count == 0) { error("no signal"); return; } // add 'device files' from all the drivers if (environment != null) { boot_crew(environment); boot_network(environment); boot_devices(environment); } // execute the command switch(cmd) { case "list": list(arg0); break; case "info": info(arg0); break; case "ctrl": ctrl(arg0, arg1); break; case "run": /* do nothing */ break; case "send": send(arg0, arg1); break; case "copy": copy(arg0, arg1); break; case "move": move(arg0, arg1); break; case "del": del(arg0); break; case "switch": switch_(arg0); break; case "edit": edit(arg0); break; case "log": log(arg0); break; case "msg": msg(arg0, arg1); break; case "help": help(arg0); break; case "clear": clear(); break; case "exit": exit(); break; default: error("unknown command"); break; } // remove all device files cleanup(); // execute script in a clear environment if (cmd == "run") run(arg0, environment); }
bool render_vessel(Panel p, Vessel v) { // get vessel info vessel_info vi = Cache.VesselInfo(v); // skip invalid vessels if (!vi.is_valid) return false; // get data from db VesselData vd = DB.Vessel(v); // determine if filter must be shown show_filter |= vd.group.Length > 0 && vd.group != "NONE"; // skip filtered vessels if (filtered() && vd.group != filter) return false; // get resource handler vessel_resources resources = ResourceCache.Get(v); // get vessel crew List<ProtoCrewMember> crew = Lib.CrewList(v); // get vessel name string vessel_name = v.isEVA ? crew[0].name : v.vesselName; // get body name string body_name = v.mainBody.name.ToUpper(); // render entry p.header ( Lib.BuildString("<b>", Lib.Ellipsis(vessel_name, 20), "</b> <size=9><color=#cccccc>", Lib.Ellipsis(body_name, 8), "</color></size>"), string.Empty, () => { selected_id = selected_id != v.id ? v.id : Guid.Empty; } ); // problem indicator indicator_problems(p, v, vi, crew); // battery indicator indicator_ec(p, v, vi); // supply indicator if (Features.Supplies) indicator_supplies(p, v, vi); // reliability indicator if (Features.Reliability) indicator_reliability(p, v, vi); // signal indicator if (Features.Signal) indicator_signal(p, v, vi); // done return true; }
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 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 = ""; } } }