/// <summary> /// soft filter: run on ALL vessels that pass the hard filter (CouldBeCandidate), /// determine if the vessel currently meets the condition (i.e. currently over location or not) /// </summary> /// <param name="label">label to add to this vessel in the status display</param> private bool VesselMeetsCondition(Vessel vessel, bool doUpdateLabel, out string label) { label = string.Empty; bool result = true; if (!allowUnpowered) { var powered = API.ResourceAmount(vessel, "ElectricCharge") > 0.01; if (!powered) { if (doUpdateLabel) { label = Lib.Color("No Electricity", Lib.Kolor.Red); } return(false); } } foreach (var sp in subRequirementParameters) { if (sp.subRequirement == null) { continue; } var state = sp.subRequirement.VesselMeetsCondition(vessel, context); result &= state.requirementMet; if (doUpdateLabel) { var vesselLabel = sp.subRequirement.GetLabel(vessel, context, state); if (!string.IsNullOrEmpty(vesselLabel)) { if (string.IsNullOrEmpty(label)) { label = " - " + vesselLabel; } else { label += "\n - " + vesselLabel; } } } } return(result); }
internal override bool VesselsMeetCondition(List <Vessel> vessels, EvaluationContext context, out string statusLabel) { if (minSurface == 0 || vessels.Count == 0) { statusLabel = string.Empty; return(vessels.Count > 0); } Vector3d bodyPosition = context.BodyPosition(context.targetBody); double visible = 100.0 * BodySurfaceObservation.VisibleSurface(vessels, context, bodyPosition, minElevation, Surfaces()); string observedPercStr = Lib.HumanReadablePerc(visible / 100.0) + " / " + Lib.HumanReadablePerc(minSurface / 100.0); observedPercStr = Lib.Color(observedPercStr, visible > minSurface ? Lib.Kolor.Green : Lib.Kolor.Red); statusLabel = Localizer.Format("#KerCon_XofSurfaceObserved", observedPercStr); return(visible > minSurface); }
internal override bool VesselsMeetCondition(List <Vessel> vessels, EvaluationContext context, out string statusLabel) { if (minSurface == 0 || vessels.Count == 0) { statusLabel = string.Empty; return(vessels.Count > 0); } double visible; double R = context.targetBody.Radius; if (vessels.Count == 1) { Vessel vessel = vessels[0]; double a = context.Altitude(vessel, context.targetBody); visible = 100.0 * a / (2 * (a + R)); } else { ResetVisibleSurfaces(); Vector3d bodyPosition = context.BodyPosition(context.targetBody); foreach (Vessel v in vessels) { Vector3d vesselPosition = context.VesselPosition(v); Vector3d viewDirection = (vesselPosition - bodyPosition); double a = R; double b = R + context.Altitude(v, context.targetBody); double c = Math.Sqrt(a * a + b * b); double α = Math.Asin(b / c) * 180.0 / Math.PI; MarkVisibleSurfaces(viewDirection.normalized, α); } visible = 100.0 * visibleSurfaces / (double)surfaces.Count; } string observedPercStr = Lib.HumanReadablePerc(visible / 100.0) + " / " + Lib.HumanReadablePerc(minSurface / 100.0); observedPercStr = Lib.Color(observedPercStr, visible > minSurface ? Lib.Kolor.Green : Lib.Kolor.Red); statusLabel = Localizer.Format("<<1>> of surface observed", observedPercStr); return(visible > minSurface); }
private static void ShowMessage(CelestialBody body, bool wasVisible, bool visible, RadiationFieldType field) { if (visible && !wasVisible) { StringBuilder sb = new StringBuilder(256); string message = Localizer.Format("#KerCon_FieldXofYresearched", // <<1>>: <<2>> researched Lib.Bold(body.bodyName), Lib.Color(RadiationField.Name(field), Lib.Kolor.Science)); sb.Append(message); sb.Append("\n\n"); sb.Append(Localizer.Format("#KerCon_FieldResearchedMessage")); var bd = Instance.BodyData(body); API.Message(sb.ToString()); MessageSystem.Message m = new MessageSystem.Message("#KerCon_FieldResearched", sb.ToString(), MessageSystemButton.MessageButtonColor.GREEN, MessageSystemButton.ButtonIcons.ACHIEVE); MessageSystem.Instance.AddMessage(m); } }
///<summary> Add habitat sub-panel, including tooltips </summary> private static void AddSubPanelHabitat(Panel p) { SimulatedResource atmo_res = resource_sim.Resource("Atmosphere"); SimulatedResource waste_res = resource_sim.Resource("WasteAtmosphere"); // generate tooltips string atmo_tooltip = atmo_res.Tooltip(); string waste_tooltip = waste_res.Tooltip(true); // generate status string for scrubbing string waste_status = !Features.Poisoning //< feature disabled ? "n/a" : waste_res.produced <= double.Epsilon //< unnecessary ? Local.Planner_scrubbingunnecessary //"not required" : waste_res.consumed <= double.Epsilon //< no scrubbing ? Lib.Color(Local.Planner_noscrubbing, Lib.Kolor.Orange) //"none" : waste_res.produced > waste_res.consumed * 1.001 //< insufficient scrubbing ? Lib.Color(Local.Planner_insufficientscrubbing, Lib.Kolor.Yellow) //"inadequate" : Lib.Color(Local.Planner_sufficientscrubbing, Lib.Kolor.Green); //"good" //< sufficient scrubbing // generate status string for pressurization string atmo_status = !Features.Pressure //< feature disabled ? "n/a" : atmo_res.consumed <= double.Epsilon //< unnecessary ? Local.Planner_pressurizationunnecessary //"not required" : atmo_res.produced <= double.Epsilon //< no pressure control ? Lib.Color(Local.Planner_nopressurecontrol, Lib.Kolor.Orange) //"none" : atmo_res.consumed > atmo_res.produced * 1.001 //< insufficient pressure control ? Lib.Color(Local.Planner_insufficientpressurecontrol, Lib.Kolor.Yellow) //"inadequate" : Lib.Color(Local.Planner_sufficientpressurecontrol, Lib.Kolor.Green); //"good" //< sufficient pressure control p.AddSection(Local.Planner_HABITAT, string.Empty, //"HABITAT" () => { p.Prev(ref environment_index, panel_environment.Count); enforceUpdate = true; }, () => { p.Next(ref environment_index, panel_environment.Count); enforceUpdate = true; }); p.AddContent(Local.Planner_volume, Lib.HumanReadableVolume(vessel_analyzer.volume), Local.Planner_volume_tip); //"volume""volume of enabled habitats" p.AddContent(Local.Planner_habitatssurface, Lib.HumanReadableSurface(vessel_analyzer.surface), Local.Planner_habitatssurface_tip); //"surface""surface of enabled habitats" p.AddContent(Local.Planner_scrubbing, waste_status, waste_tooltip); //"scrubbing" p.AddContent(Local.Planner_pressurization, atmo_status, atmo_tooltip); //"pressurization" }
private static void RunningUpdate(Vessel v, VesselData vd, EquipmentData ed, ModuleKsmContractEquipment prefab, double elapsed_s) { double connectionRate = API.VesselConnectionRate(v); ed.state = GetState(v, vd, ed, prefab, connectionRate); bool running = ed.state == EquipmentState.nominal; if (running) { vd.ResHandler.ElectricCharge.Consume(prefab.RequiredEC * elapsed_s, EquipmentBroker); } KerbalismContracts.EquipmentStates.Update(v, prefab.id, ed.state); if (ed.loadedModule != null) { ed.loadedModule.UIState = Lib.BuildString("EC: ", Lib.HumanReadableRate(prefab.RequiredEC)); if (prefab.RequiredBandwidth > 0) { var color = Lib.Kolor.Green; var rate = connectionRate / prefab.RequiredBandwidth; if (rate <= 1.0) { color = Lib.Kolor.Red; } else if (rate <= 1.2) { color = Lib.Kolor.Orange; } ed.loadedModule.UIState += Lib.BuildString(", ", Localizer.Format("#KerCon_DataRate", Lib.HumanReadableRate(prefab.RequiredBandwidth), Lib.Color(Lib.HumanReadableRate(connectionRate), color))); } } }
protected override string GetTitle() { string result = ""; double now = Planetarium.GetUniversalTime(); string remainingStr = durationType == DurationType.countdown ? DurationUtil.StringValue(Math.Max(0, doneAfter - now)) : DurationUtil.StringValue(Math.Max(0, duration - accumulatedDuration)); switch (durationState) { case DurationState.off: result = Localizer.Format("Duration: <<1>>", DurationUtil.StringValue(duration)); if (waitDuration > 0) { result += "\n\t - " + Localizer.Format("Time starts <<1>> after accepting the contract", Lib.Color(DurationUtil.StringValue(waitDuration), Lib.Kolor.Yellow)); } if (allowedDowntime > 0) { result += "\n\t - " + Localizer.Format("Allows interruptions up to <<1>>", DurationUtil.StringValue(allowedDowntime)); } else { result += "\n\t - " + "Does not allow interruptions"; } if (!allowReset) { result += "\n\t - " + Lib.Color("Will fail if interrupted beyond allowance", Lib.Kolor.Orange); } break; case DurationState.preRun: result = Localizer.Format("Duration: <<1>>", DurationUtil.StringValue(duration)); result += "\n\t - " + Localizer.Format("Time starts in <<1>>", Lib.Color(DurationUtil.StringValue(Math.Max(0, startAfter - now)), Lib.Kolor.Yellow)); if (allowedDowntime > 0) { result += "\n\t - " + Localizer.Format("Allows interruptions up to <<1>>", DurationUtil.StringValue(allowedDowntime)); } break; case DurationState.running: result = Localizer.Format("Remaining: <<1>>", Lib.Color(remainingStr, Lib.Kolor.Green)); if (allowedDowntime > 0) { result += "\n\t - " + Localizer.Format("Allows interruptions up to <<1>>", DurationUtil.StringValue(allowedDowntime)); } break; case DurationState.preReset: result = Localizer.Format("Remaining: <<1>> (stop in: <<2>>)", Lib.Color(remainingStr, Lib.Kolor.Green), Lib.Color(DurationUtil.StringValue(Math.Max(0, failAfter - now)), allowReset ? Lib.Kolor.Yellow : Lib.Kolor.Red)); break; case DurationState.done: result = "Done!"; break; case DurationState.failed: result = "Time is up!"; break; } titleTracker.Add(result); if (lastTitle != result && Root != null && (Root.ContractState == Contract.State.Active || Root.ContractState == Contract.State.Failed)) { titleTracker.UpdateContractWindow(result); lastTitle = result; } return(result); }
///<summary> Add reliability sub-panel, including tooltips </summary> private static void AddSubPanelReliability(Panel p) { // evaluate redundancy metric // - 0: no redundancy // - 0.5: all groups have 2 elements // - 1.0: all groups have 3 or more elements double redundancy_metric = 0.0; foreach (KeyValuePair <string, int> pair in vessel_analyzer.redundancy) { switch (pair.Value) { case 1: break; case 2: redundancy_metric += 0.5 / vessel_analyzer.redundancy.Count; break; default: redundancy_metric += 1.0 / vessel_analyzer.redundancy.Count; break; } } // traduce the redundancy metric to string string redundancy_str = string.Empty; if (redundancy_metric <= 0.1) { redundancy_str = Local.Planner_none; //"none" } else if (redundancy_metric <= 0.33) { redundancy_str = Local.Planner_poor; //"poor" } else if (redundancy_metric <= 0.66) { redundancy_str = Local.Planner_okay; //"okay" } else { redundancy_str = Local.Planner_great; //"great" } // generate redundancy tooltip string redundancy_tooltip = string.Empty; if (vessel_analyzer.redundancy.Count > 0) { StringBuilder sb = new StringBuilder(); foreach (KeyValuePair <string, int> pair in vessel_analyzer.redundancy) { if (sb.Length > 0) { sb.Append("\n"); } sb.Append(Lib.Color(pair.Value.ToString(), pair.Value == 1 ? Lib.Kolor.Red : pair.Value == 2 ? Lib.Kolor.Yellow : Lib.Kolor.Green, true)); sb.Append("\t"); sb.Append(pair.Key); } redundancy_tooltip = Lib.BuildString("<align=left />", sb.ToString()); } // generate repair string and tooltip string repair_str = Local.Planner_none; //"none" string repair_tooltip = string.Empty; if (vessel_analyzer.crew_engineer) { repair_str = "engineer"; repair_tooltip = Local.Planner_engineer_tip; //"The engineer on board should\nbe able to handle all repairs" } else if (vessel_analyzer.crew_capacity == 0) { repair_str = "safemode"; repair_tooltip = Local.Planner_safemode_tip; //"We have a chance of repairing\nsome of the malfunctions remotely" } // render panel p.AddSection(Local.Planner_RELIABILITY, string.Empty, //"RELIABILITY" () => { p.Prev(ref special_index, panel_special.Count); enforceUpdate = true; }, () => { p.Next(ref special_index, panel_special.Count); enforceUpdate = true; }); p.AddContent(Local.Planner_malfunctions, Lib.HumanReadableAmount(vessel_analyzer.failure_year, "/y"), Local.Planner_malfunctions_tip); //"malfunctions""average case estimate\nfor the whole vessel" p.AddContent(Local.Planner_highquality, Lib.HumanReadablePerc(vessel_analyzer.high_quality), Local.Planner_highquality_tip); //"high quality""percentage of high quality components" p.AddContent(Local.Planner_redundancy, redundancy_str, redundancy_tooltip); //"redundancy" p.AddContent(Local.Planner_repair, repair_str, repair_tooltip); //"repair" }
/// <summary> /// Shows the Network status, ControlPath, Signal strength /// </summary> public static void ConnMan(this Panel p, Vessel v) { // avoid corner-case when this is called in a lambda after scene changes v = FlightGlobals.FindVessel(v.id); // if vessel doesn't exist anymore, leave the panel empty if (v == null) { return; } VesselData vd = v.KerbalismData(); // if not a valid vessel, leave the panel empty if (!vd.IsSimulated) { return; } // set metadata p.Title(Lib.BuildString(Lib.Ellipsis(v.vesselName, Styles.ScaleStringLength(40)), " ", Lib.Color("CONNECTION MANAGER", Lib.Kolor.LightGrey))); p.Width(Styles.ScaleWidthFloat(365.0f)); p.paneltype = Panel.PanelType.connection; // time-out simulation if (!Lib.IsControlUnit(v) && p.Timeout(vd)) { return; } // draw ControlPath section p.AddSection("CONTROL PATH"); if (vd.Connection.linked) { if (vd.Connection.control_path != null) { foreach (string[] hop in vd.Connection.control_path) { if (hop == null || hop.Length < 1) { continue; } string name = hop[0]; string value = hop.Length >= 2 ? hop[1] : ""; string tooltip = hop.Length >= 3 ? ("\n" + hop[2]) : ""; p.AddContent(name, value, tooltip); } } } else { p.AddContent("<i>no connection</i>", string.Empty); } }
protected override string GetTitle() { InitStrings(); string result = ""; double now = Planetarium.GetUniversalTime(); string remainingStr = durationType == DurationType.countdown ? DurationUtil.StringValue(Math.Max(0, doneAfter - now)) : DurationUtil.StringValue(Math.Max(0, duration - accumulatedDuration)); switch (durationState) { case DurationState.off: result = KerCon_Duration_X; // Duration: <<1>> if (waitDuration > 0) { result += "\n\t - " + KerCon_TimeStartsAfterAccepting; } if (allowedDowntime > 0) { result += "\n\t - " + KerCon_AllowsInterruptionsUpTo; } else if (durationType == DurationType.accumulating) { result += "\n\t - " + KerCon_AccumulatingDuration; } else { result += "\n\t - " + KerCon_DoesNotAllowInterruptions; // Does not allow interruptions } if (!allowReset) { result += "\n\t - " + KerCon_WillFailIfInterrupted; // Will fail if interrupted beyond allowance } break; case DurationState.preRun: result = KerCon_Duration_X; // Duration: <<1>> result += "\n\t - " + Localizer.Format("#KerCon_TimeStartsIn", // Time starts in <<1>> Lib.Color(DurationUtil.StringValue(Math.Max(0, startAfter - now)), Lib.Kolor.Yellow)); if (allowedDowntime > 0) { result += "\n\t - " + KerCon_AllowsInterruptionsUpTo; } break; case DurationState.running: result = Localizer.Format("#KerCon_Reamining_X", Lib.Color(remainingStr, Lib.Kolor.Green)); // Remaining: <<1>> //if (allowedDowntime > 0) // result += "\n\t - " + Localizer.Format("#KerCon_AllowsInterruptionsUpTo", // Allows interruptions up to <<1>> // DurationUtil.StringValue(allowedDowntime)); break; case DurationState.preReset: result = Localizer.Format("#KerCon_Reamining_X_stopIn_Y", Lib.Color(remainingStr, Lib.Kolor.Green), // Remaining: <<1>> (stop in: <<2>>) Lib.Color(DurationUtil.StringValue(Math.Max(0, failAfter - now)), allowReset ? Lib.Kolor.Yellow : Lib.Kolor.Red)); break; case DurationState.done: result = "#autoLOC_135144"; // Success!! break; case DurationState.failed: result = "#KerCon_TimeIsUp"; // Time is up! break; } if (result.StartsWith("#")) { result = Localizer.Format(result); } titleTracker.Add(result); if (lastTitle != result && Root != null && (Root.ContractState == Contract.State.Active || Root.ContractState == Contract.State.Failed)) { titleTracker.UpdateContractWindow(result); lastTitle = result; } return(result); }
internal override string GetLabel(Vessel vessel, EvaluationContext context, SubRequirementState state) { ExperimentRunningState runningState = (ExperimentRunningState)state; string label = runningState.experimentState == ExperimentState.running ? Lib.Color(Local.Generic_RUNNING, Lib.Kolor.Green) : Lib.Color(Local.Generic_STOPPED, Lib.Kolor.Red); if (!string.IsNullOrEmpty(shortDescription)) { label = shortDescription + ": " + label; } return(label); }
protected override void OnUpdate() { base.OnUpdate(); if (ParameterCount == 0) { return; } if (state != ParameterState.Incomplete) { return; } if (!KerbalismContractsMain.KerbalismInitialized) { return; } if (lastUpdate == 0) { lastUpdate = Planetarium.GetUniversalTime(); return; } bool childParameterChanged = false; if (subRequirementParameters.Count == 0) { childParameterChanged = true; CreateSubParameters(); } var lastUpdateAge = Planetarium.GetUniversalTime() - lastUpdate; if (lastUpdateAge < 1.0) { return; } lastUpdate = Planetarium.GetUniversalTime(); vessels.Clear(); context = CreateContext(lastUpdateAge); foreach (var sp in subRequirementParameters) { sp.ResetContext(context); } foreach (Vessel vessel in FlightGlobals.Vessels) { if (!Utils.IsVessel(vessel)) { continue; } if (!CouldBeCandidate(vessel)) { continue; } if (arguments.requireElectricity && !API.IsPowered(vessel)) { if (!hideChildren) { childParameterChanged |= UpdateVesselStatus(vessel, Lib.Color("#KerCon_NoEC", Lib.Kolor.Red), false); // No EC } continue; } if (arguments.requireCommunication && !API.VesselConnectionLinked(vessel)) { if (!hideChildren) { childParameterChanged |= UpdateVesselStatus(vessel, Lib.Color("#KerCon_NoComms", Lib.Kolor.Red), false); // No Comms } continue; } vessels.Add(vessel); } if (!hideChildren) { foreach (var vsp in vesselStatusParameters) { vsp.obsolete = true; } } List <Vessel> vesselsMeetingCondition = new List <Vessel>(); int stepCount = context.steps.Count; for (int i = 0; i < stepCount; i++) { var now = context.steps[i]; context.SetTime(now); vesselsMeetingCondition.Clear(); bool doLabelUpdate = !hideChildren && i + 1 == stepCount; foreach (Vessel vessel in vessels) { // Note considering early termination for performance gains: // If we already know that we have enough vessels to satisfy // our requirement, and if we don't have to update labels, // then we don't need to test all vessels. However, this // doesn't work when we have complex requirements that need // to consider multiple vessels at once (like body surface // observation percentage). We could change the implementation // to continuously integrate one vessel at a time into the // multi-vessel test and abort as soon as that one is satisfied, // but if that also calculates a number visible to the user // (like percentage of surface observed), that number would // be wrong. So we need to test all vessels, all the time. // if (!doLabelUpdate && vesselsMeetingCondition.Count >= minVessels) // break; string statusLabel; bool conditionMet = VesselMeetsCondition(vessel, doLabelUpdate, out statusLabel); if (conditionMet) { vesselsMeetingCondition.Add(vessel); } if (doLabelUpdate) { childParameterChanged |= UpdateVesselStatus(vessel, statusLabel, conditionMet); } } bool allConditionsMet = vesselsMeetingCondition.Count >= arguments.minVessels; allConditionsMet &= VesselsMeetCondition(vesselsMeetingCondition); if (durationParameter == null) { SetState(allConditionsMet ? ParameterState.Complete : ParameterState.Incomplete); } else { durationParameter.Update(allConditionsMet, now); SetState(durationParameter.State); } if (state == ParameterState.Complete) { break; } } childParameterChanged |= RemoveObsoleteVesselStatusParameters(); if (childParameterChanged) { ContractConfigurator.ContractConfigurator.OnParameterChange.Fire(this.Root, this); } }