From 948e2b991160b8598c20fce7d5bee176bab7321f Mon Sep 17 00:00:00 2001 From: formicant Date: Sun, 21 Aug 2016 01:28:18 +0700 Subject: [PATCH] CriticalTemperatureGauge 1.1.3.3 Initial commit. --- .gitignore | 4 + .../Plugins/CriticalTemperatureGauge.dll | Bin 0 -> 23040 bytes .../Textures/GaugeFrame.dds | Bin 0 -> 4128 bytes .../Textures/GaugeScale.dds | Bin 0 -> 1256 bytes .../Textures/ToolbarButton.png | Bin 0 -> 604 bytes src/CriticalTemperatureGauge.sln | 22 +++ src/CriticalTemperatureGauge/ConfigWindow.cs | 90 +++++++++++ .../CriticalTemperatureGauge.csproj | 75 +++++++++ src/CriticalTemperatureGauge/Flight.cs | 143 ++++++++++++++++++ src/CriticalTemperatureGauge/GaugeWindow.cs | 132 ++++++++++++++++ src/CriticalTemperatureGauge/PartState.cs | 63 ++++++++ .../Properties/AssemblyInfo.cs | 36 +++++ src/CriticalTemperatureGauge/Settings.cs | 124 +++++++++++++++ src/CriticalTemperatureGauge/Static.cs | 25 +++ src/CriticalTemperatureGauge/Toolbar.cs | 91 +++++++++++ src/CriticalTemperatureGauge/Window.cs | 69 +++++++++ 16 files changed, 874 insertions(+) create mode 100644 .gitignore create mode 100644 GameData/CriticalTemperatureGauge/Plugins/CriticalTemperatureGauge.dll create mode 100644 GameData/CriticalTemperatureGauge/Textures/GaugeFrame.dds create mode 100644 GameData/CriticalTemperatureGauge/Textures/GaugeScale.dds create mode 100644 GameData/CriticalTemperatureGauge/Textures/ToolbarButton.png create mode 100644 src/CriticalTemperatureGauge.sln create mode 100644 src/CriticalTemperatureGauge/ConfigWindow.cs create mode 100644 src/CriticalTemperatureGauge/CriticalTemperatureGauge.csproj create mode 100644 src/CriticalTemperatureGauge/Flight.cs create mode 100644 src/CriticalTemperatureGauge/GaugeWindow.cs create mode 100644 src/CriticalTemperatureGauge/PartState.cs create mode 100644 src/CriticalTemperatureGauge/Properties/AssemblyInfo.cs create mode 100644 src/CriticalTemperatureGauge/Settings.cs create mode 100644 src/CriticalTemperatureGauge/Static.cs create mode 100644 src/CriticalTemperatureGauge/Toolbar.cs create mode 100644 src/CriticalTemperatureGauge/Window.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f2e7846 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/Releases +/src/.vs +/src/CriticalTemperatureGauge/obj +/src/CriticalTemperatureGauge/bin diff --git a/GameData/CriticalTemperatureGauge/Plugins/CriticalTemperatureGauge.dll b/GameData/CriticalTemperatureGauge/Plugins/CriticalTemperatureGauge.dll new file mode 100644 index 0000000000000000000000000000000000000000..0f74c7c2d3030223096d6d73b66b9f2693445ac7 GIT binary patch literal 23040 zcmeHvd3@W|mG8Mf?TVyW@{)xtN`e!`8_oh*Ac-A22?m8Mb`r7_N4Aw%MApxdNuLx_lmV85*WEyze>pCrg%{ zw9I>dy!V;;B|5*e-E;3f_uO;u{plyK-*Fq+h{%r5+iw#+iaTFT0?!X}5C>*{H9%kT zJU8o66@6}2M{gn%?oXS0)3LsAcPy1Mv*E6II6aUGCsN_o4ejARvnSr*_j@n2OmAF6 z6je5Q|0f^amapwJ%??jcjYONlacb@r{kVtm$>Bp(Cb*&7%?xh8Tqgm+=gUS-?_*W| zU$|Q&vv8d;i8e5jBYKe?QFKV8))0sB|z^lbG^Wq7b4(I9W%q*bQk=cOe90nIM zJ%(G&+$rW<+@ zR@_vi9=OO0O8aI%_uc4<*^3rsR(XPUORoWXFnhdtM>28|aI~7Q#Z5cSMv&?2M(SAr z@^7j~*U0Mcu*S$jlra|p6t#RY$n=+aTy@6~h+gxO(K>L3H~r%houyz}We~W!lj6G~ z1Zm0g$d%Ae?>mQ?nzi||#@w37UKB!r@_c*{v1Q|V zhGgLNYNchL028*?gSG6#zzt`_1I`3j-2_(zS=h~u-xx`??OjQV<4U5(2`xLZx) znb7Fd1IA%f=d-lqP^l>@Sy$((n^9bngLUzwLdp6GmNxeqO-I{pl8S+n?GWO^Yn*jX zfG~shc_T6<@!qtA(*~ij$NUL)!iNFuF5qP*(d}E{S zY#dhxbJAHI*$Pc_5~IeUZHyyd0gnW5;|#@oWOA>G^u#!ddrhP!#&AYoX+kOUF5nRdnp$;M)l90HZB#}4V)24T zJMD%&$byFW#q5Nxw{|JAECk*OE@`}N(KOeg7+dLL&wA^cycQC~(>~k|B>{Zy2u8$PXSjT)9CHE4T|-<4QG_Npkpqi_nqfrXC2tLn|85rH z((Zv@ir251vVI+gAM9p7M7(gJhySp8)au2B3Of5Vx*trv$muohnxZu={WNCSB)y{j zwe>6wJ33a~bhkpP;CY7dbT0cSD8Xd1?8~Rc47Ua|%`WYTWSK+=+b4unWT2QhP)uA0 z!ntEa@gT^|AS+-TWQ8IgS#kpAR*59?Ziuzl5cuA@cH=Onj(zG(U-}tzPqz#$ck1Qk zDqtpp)LZY8?$=I9zcQjWe?$%Q-Wxf@0K0{vG+nn|o1X≠6rO3mr@XeJymQ%Wc$O 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 O128|hDOy|cGXMZ33RCU? literal 0 HcmV?d00001 diff --git a/GameData/CriticalTemperatureGauge/Textures/GaugeScale.dds b/GameData/CriticalTemperatureGauge/Textures/GaugeScale.dds new file mode 100644 index 0000000000000000000000000000000000000000..adcd36d76950d8907fd7c8c3d936fe105a5bb3eb GIT binary patch literal 1256 zcmeIwv1?ON6vy$`BqpLA`Ui9fB^JbrxOcHl?Bt{cCuyXw@gFFNh#*0`b?jKEng+gN zoh%MgleVT!79klN5@?X5>5|ti5trvV_mD{^2Pc1!9NxL-Uf#QW(&@X&r=I5}CN6rH zq<)g$qnP~tMUt{dx2EskyZOiM6Tf%c2`>^#Mm0I!j((QhnoN#gmpq@zPQH`u&u-0V zjby&hYJYxl>WIa=4oJwZ>i^tiF|`);lk5NHQC#b)EA!GaHvBg&lwMy`-{R z*!dti-K*^Cdi>~gPuCZ|#gBA7cQXA|&spo`PISF^zSg@cIUW|zb?z)IhHc4lxG?-F z$%TpGGf8i-JbhKaSEVs13+)1@` zKGr&HYX41hzM<#ko6&~OM;ixq?Z2+i)y^aj>r*wIkJR?7+J9Bex|*$ymG!)_a=E1a zt@`|I2#^yLYgA2lEc*9n3qJcQEf@-odPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02*{fSaefwW^{L9 za%BKeVQFr3E>1;MAa*k@H7+qRNAp5A0005YNklofr$KoL=}GAS&gAcz4?FP0Xu6tS>W3tvGCIsZS-z%ia7duH!WyoH~+ znY+FDGP`rIDFi`K$dX1daR%m9p;IV4=vQOD?~5f45av{%2Lq!n;}usZU8T4{ZOQ%t zdjKtprO(k$8mK3UhvCGC4JF=VTtL@h!)G|OV15|@Q zYc!J@kjp-r){>2_P!5DL=M{?F2B?g-efBB@xOoAjwQYZe*ptzg*S1Hp9r^aqTA+Qz z&W+Ym14^as$5ki?l0Ea|9?C29=PCr?Yv{#jJGHh4$R2cNv^rHFyu1#*%Eyo&!l+Hn z`*rV|8cE8~v%tK;HlQQ%Y3Vc0!RN}8Lg88WV-O}aB^k{!bP@7-NuO~YP2WHR$#es& zM>@obALDundK3U! z`iyh%xrSFDIUe^zRk_9W3dFNLEqxvRNnzx@?rkpmdd)kUrGc>po&Pi0o@rA q`ivJ{V7^rdFUZWlXqf@gqI?2s)L#j(nn_y#0000 + /// Represents the configuration window. + /// + public class ConfigWindow : Window + { + /// Slider resolution.. + const int ThresholdSteps = 20; + + // Layout parameters + const int SeparatorHeight = 8; + const int IndentWidth = 20; + + protected override Rect InitialWindowRectangle => + new Rect( + Static.Settings.ConfigWindowPosition != Vector2.zero + ? Static.Settings.ConfigWindowPosition + : new Vector2(Screen.width - 285, (Screen.height - 410) / 2), + Vector2.zero); + + /// Creates the configuration window. + public ConfigWindow() + : base(Static.Settings.BaseWindowId + 1, "Critical Temperature Gauge") { } + + /// Writes current window position into settings. + protected override void OnWindowRectUpdated() + { + Static.Settings.ConfigWindowPosition = WindowRectangle.position; + } + + /// Draws the window contents. + /// Id of the window. + protected override void WindowGUI(int windowId) + { + // Defining styles + var windowStyle = new GUIStyle(GUI.skin.window) { padding = new RectOffset(8, 8, 8, 8) }; + var textEditStyle = new GUIStyle(GUI.skin.textField); + var labelStyle = new GUIStyle(GUI.skin.label) + { + normal = new GUIStyleState { textColor = Color.white }, + focused = new GUIStyleState { textColor = Color.white }, + alignment = TextAnchor.MiddleLeft, + }; + var sliderStyle = new GUIStyle(GUI.skin.horizontalSlider) { fixedWidth = 222 }; + var sliderThumbStyle = new GUIStyle(GUI.skin.horizontalSliderThumb); + + // Drawing layout + GUILayout.Space(SeparatorHeight); + + // Drawing gauge visibility settings controls + GUILayout.Label($"Gauge showing threshold: {Static.Settings.GaugeShowingThreshold:G2}", labelStyle); + Static.Settings.GaugeShowingThreshold = + Math.Round(GUILayout.HorizontalSlider((float)Static.Settings.GaugeShowingThreshold, 1F / ThresholdSteps, 1F - 1F / ThresholdSteps, sliderStyle, sliderThumbStyle) * ThresholdSteps) / ThresholdSteps; + GUILayout.Label($"Gauge hiding threshold: {Static.Settings.GaugeHidingThreshold:G2}", labelStyle); + Static.Settings.GaugeHidingThreshold = Math.Min(Static.Settings.GaugeShowingThreshold, + Math.Round(GUILayout.HorizontalSlider((float)Static.Settings.GaugeHidingThreshold, 1F / ThresholdSteps, 1F - 1F / ThresholdSteps, sliderStyle, sliderThumbStyle) * ThresholdSteps) / ThresholdSteps); + Static.Settings.ForceShowGauge = GUILayout.Toggle(Static.Settings.ForceShowGauge, "Force show gauge"); + + // Drawing additional information settings controls + Static.Settings.ShowTemperature = GUILayout.Toggle(Static.Settings.ShowTemperature, "Show temperature"); + GUILayout.BeginHorizontal(); + GUILayout.Space(IndentWidth); + Static.Settings.ShowTemperatureLimit = GUILayout.Toggle(Static.Settings.ShowTemperatureLimit, "Show temperature limit"); + GUILayout.EndHorizontal(); + Static.Settings.ShowTemperatureRate = GUILayout.Toggle(Static.Settings.ShowTemperatureRate, "Show temperature rate"); + Static.Settings.ShowCriticalPart = GUILayout.Toggle(Static.Settings.ShowCriticalPart, "Show critical part"); + // (Part highlighting does not work for some reason) + //Static.Settings.HighlightCriticalPart = GUILayout.Toggle(Static.Settings.HighlightCriticalPart, "Highlight critical part"); + + // Drawing exclusion list settings controls + GUILayout.Space(SeparatorHeight); + Static.Settings.UseExclusionList = GUILayout.Toggle(Static.Settings.UseExclusionList, "Ignore parts with following\nmodules (comma-separated):"); + GUILayout.Space(SeparatorHeight * 2); + Static.Settings.ExclusionList = GUILayout.TextField(Static.Settings.ExclusionList, 512, textEditStyle); + + // Drawing interface settings controls + GUILayout.Space(SeparatorHeight); + Static.Settings.LockGaugeWindow = GUILayout.Toggle(Static.Settings.LockGaugeWindow, "Lock gauge position"); + + GUI.DragWindow(); + } + } +} diff --git a/src/CriticalTemperatureGauge/CriticalTemperatureGauge.csproj b/src/CriticalTemperatureGauge/CriticalTemperatureGauge.csproj new file mode 100644 index 0000000..0a6d26d --- /dev/null +++ b/src/CriticalTemperatureGauge/CriticalTemperatureGauge.csproj @@ -0,0 +1,75 @@ + + + + + Debug + AnyCPU + {8FB4471E-FBBB-4538-9776-D22AD22B9B18} + Library + Properties + CriticalTemperatureGauge + CriticalTemperatureGauge + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + true + + + + ..\..\..\_Lib\1.1.3 x64\Assembly-CSharp.dll + + + ..\..\..\_Lib\1.1.3 x64\Assembly-CSharp-firstpass.dll + + + ..\..\..\_Lib\1.1.3 x64\KSPUtil.dll + + + + + ..\..\..\_Lib\1.1.3 x64\UnityEngine.dll + + + ..\..\..\_Lib\1.1.3 x64\UnityEngine.UI.dll + + + + + + + + + + + + + + + + copy /Y "$(TargetDir)$(ProjectName).dll" "$(SolutionDir)..\GameData\$(ProjectName)\Plugins\$(ProjectName).dll" + + + \ No newline at end of file diff --git a/src/CriticalTemperatureGauge/Flight.cs b/src/CriticalTemperatureGauge/Flight.cs new file mode 100644 index 0000000..0f4ebfb --- /dev/null +++ b/src/CriticalTemperatureGauge/Flight.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace CriticalTemperatureGauge +{ + /// + /// Main add-on class. + /// + [KSPAddon(KSPAddon.Startup.Flight, false)] + public class Flight : MonoBehaviour + { + Window _gaugeWindow; + + // KSP events: + + public void Start() + { + Debug.Log($"CriticalTemperatureGauge: Entering scene {HighLogic.LoadedScene}."); + _gaugeWindow = new GaugeWindow(); + } + + public void OnDestroy() + { + _gaugeWindow?.Hide(); + Static.CriticalPartState = null; + Static.Settings.Save(); + Debug.Log($"CriticalTemperatureGauge: Exiting scene {HighLogic.LoadedScene}."); + } + + public void OnGUI() + { + _gaugeWindow?.DrawGUI(); + } + + public void Update() + { + var vessel = FlightGlobals.ActiveVessel; + + if(_gaugeWindow != null && vessel != null) + { + // Updating critical part state + var criticalPartState = GetCriticalPartState(vessel); + criticalPartState?.UpdateTemperatureRates(Static.CriticalPartState); + Static.CriticalPartState = criticalPartState; + + // Determining if the gauge should be shown or hidden + if(!_gaugeWindow.IsVisible && + criticalPartState != null && + (criticalPartState.Index > Static.Settings.GaugeShowingThreshold || + Static.Settings.ForceShowGauge)) + { + _gaugeWindow.Show(); + // (Part highlighting does not work for some reason) + //if(Static.Settings.HighlightCriticalPart) + // HighlightPart(vessel, criticalPartState.Id); + } + else if(_gaugeWindow.IsVisible && + (criticalPartState == null || + criticalPartState.Index < Static.Settings.GaugeHidingThreshold && + !Static.Settings.ForceShowGauge)) + { + _gaugeWindow.Hide(); + //HighlightNone(vessel); + } + } + } + + /// Finds the part with the greatest Temp/TempLimit ratio. + /// Current vessel. + /// Critical part state. + PartState GetCriticalPartState(Vessel vessel) => + vessel.parts + .Where(IsPartNotIgnored) + .Select(GetPartState) + .OrderByDescending(partState => partState.Index) + .FirstOrDefault(); + + /// Gets parameters of a part. + /// A vessel part. + /// Part state. + static PartState GetPartState(Part part) => + new PartState + { + Time = Planetarium.GetUniversalTime(), + Id = part.flightID, + Name = GetPartTitle(part), + CoreTemperatureLimit = part.maxTemp, + SkinTemperatureLimit = part.skinMaxTemp, + CoreTemperature = part.temperature, + SkinTemperature = part.skinTemperature, + }; + + /// Determines if the part has a module containing in the exclusion list. + /// A vessel part. + /// false if the part is ignored; true otherwise. + bool IsPartNotIgnored(Part part) => + !(Static.Settings.UseExclusionList && Static.Settings.ExclusionListItems.Any(moduleName => part.Modules.Contains(moduleName))); + + // Currently, not used + static void HighlightNone(Vessel craft) => HighlightPart(craft, 0); + + // Currently, not used + static void HighlightPart(Vessel craft, uint partId) + { + foreach(var part in craft.parts) + { + if(part.flightID == partId) + part.highlighter.FlashingOn(Color.red, Color.yellow, 0.5F); + else + part.highlighter.FlashingOff(); + } + } + + /// Gets the title (long name) of a part. + /// A vessel part. + /// Part title. + static string GetPartTitle(Part part) + { + // Removing vessel name from part name + int indexOfWhitespace = part.name.IndexOf(' '); + var name = indexOfWhitespace >= 0 + ? part.name.Substring(0, indexOfWhitespace) + : part.name; + + // Trying to get part title; using part name if failed + string title; + try + { + title = PartLoader.getPartInfoByName(name).title; + if(string.IsNullOrEmpty(title)) + title = name; + } + catch + { + title = name; + } + return title; + } + } +} diff --git a/src/CriticalTemperatureGauge/GaugeWindow.cs b/src/CriticalTemperatureGauge/GaugeWindow.cs new file mode 100644 index 0000000..bdb4bf6 --- /dev/null +++ b/src/CriticalTemperatureGauge/GaugeWindow.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace CriticalTemperatureGauge +{ + /// + /// Represents the temperature gauge window. + /// + public class GaugeWindow : Window + { + // Resources and layout + static readonly Texture2D GaugeFrameTexture = GameDatabase.Instance.GetTexture(Static.TexturePath + "GaugeFrame", false); + static readonly Texture2D GaugeScaleTexture = GameDatabase.Instance.GetTexture(Static.TexturePath + "GaugeScale", false); + static readonly Color TextColor = Color.white; + static readonly Color[] TextShadowColors = + { + new Color(0, 0, 0, 0.8F), + new Color(0, 0, 0, 0.3F), + new Color(0, 0, 0, 0.2F), + }; + static readonly Rect GaugeFrameRectangle = new Rect(0, 0, GaugeFrameTexture.width, GaugeFrameTexture.height); + static readonly Rect GaugeScaleRectangle = new Rect(6, 4, GaugeScaleTexture.width, 12); + static readonly Rect InnerLabelRectangle = new Rect(0, 2, GaugeFrameTexture.width, GaugeFrameTexture.height); + static readonly Rect OuterLabelRectangle = new Rect(0, GaugeFrameTexture.height - 2, GaugeFrameTexture.width, GaugeFrameTexture.height); + const int FontSize = 12; + + protected override Rect InitialWindowRectangle => + new Rect( + Static.Settings.GaugeWindowPosition != Vector2.zero + ? Static.Settings.GaugeWindowPosition + : new Vector2((Screen.width - GaugeFrameTexture.width) / 2, 83), + new Vector2(GaugeFrameTexture.width, GaugeFrameTexture.height)); + + /// Creates the temperature gauge window. + public GaugeWindow() + : base(Static.Settings.BaseWindowId + 0, hasClearBackground: true) { } + + /// Writes current window position into settings. + protected override void OnWindowRectUpdated() + { + Static.Settings.GaugeWindowPosition = WindowRectangle.position; + } + + /// Draws the window contents. + /// Id of the window. + protected override void WindowGUI(int windowId) + { + if(Static.CriticalPartState != null) + { + float gaugeScaleValue = (float)Static.CriticalPartState.Index; + + GUILayout.BeginVertical(); + + // Drawing gauge + GUI.DrawTexture(GaugeFrameRectangle, GaugeFrameTexture); + GUI.DrawTextureWithTexCoords( + new Rect(GaugeScaleRectangle.x, GaugeScaleRectangle.y, GaugeScaleRectangle.width * gaugeScaleValue, GaugeScaleRectangle.height), + GaugeScaleTexture, new Rect(0, 0, gaugeScaleValue, 1)); + + // Drawing temperature and temperature limit values + if(Static.Settings.ShowTemperature) + DrawContrastLabel(InnerLabelRectangle, TextAnchor.MiddleCenter, FontSize, + $@"{Static.CriticalPartState.CriticalTemperature:F0}{ + (Static.Settings.ShowTemperatureLimit ? $" / {Static.CriticalPartState.CriticalTemperatureLimit:F0}" : "")} K"); + + // Drawing temperature rate value + if(Static.Settings.ShowTemperatureRate) + DrawContrastLabel(InnerLabelRectangle, TextAnchor.MiddleLeft, FontSize, + $@" {(Static.CriticalPartState.CriticalTemperatureRate < 0 ? "−" : "+")}{ + Math.Abs(Static.CriticalPartState.CriticalTemperatureRate):F0} K/s"); + + // Drawing critical part name + if(Static.Settings.ShowCriticalPart) + DrawContrastLabel(OuterLabelRectangle, TextAnchor.MiddleLeft, FontSize, + $@" {Static.CriticalPartState.Name} ({(Static.CriticalPartState.IsSkinCritical ? "skin" : "core")})"); + + GUILayout.EndVertical(); + + if(!Static.Settings.LockGaugeWindow) + GUI.DragWindow(); + } + } + + /// Draws white text with a black shadow. + /// Label rectangle + /// Text alignment. + /// Font size. + /// Text to draw. + void DrawContrastLabel(Rect rectangle, TextAnchor alignment, int fontSize, string text) + { + var style = new GUIStyle(GUI.skin.label) + { + alignment = alignment, + fontSize = fontSize, + wordWrap = false, + }; + + // Drawing text shadow + style.normal.textColor = TextShadowColors[0]; + rectangle.x--; + GUI.Label(rectangle, text, style); + rectangle.x++; rectangle.y--; + GUI.Label(rectangle, text, style); + rectangle.x++; rectangle.y++; + GUI.Label(rectangle, text, style); + rectangle.x--; rectangle.y++; + GUI.Label(rectangle, text, style); + style.normal.textColor = TextShadowColors[1]; + rectangle.x--; + GUI.Label(rectangle, text, style); + rectangle.y -= 2; + GUI.Label(rectangle, text, style); + rectangle.x += 2; + GUI.Label(rectangle, text, style); + rectangle.y += 2; + GUI.Label(rectangle, text, style); + style.normal.textColor = TextShadowColors[2]; + rectangle.y--; rectangle.x++; + GUI.Label(rectangle, text, style); + rectangle.x -= 4; + GUI.Label(rectangle, text, style); + rectangle.x += 2; + + // Drawing text + style.normal.textColor = TextColor; + GUI.Label(rectangle, text, style); + } + } +} diff --git a/src/CriticalTemperatureGauge/PartState.cs b/src/CriticalTemperatureGauge/PartState.cs new file mode 100644 index 0000000..d1c267c --- /dev/null +++ b/src/CriticalTemperatureGauge/PartState.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace CriticalTemperatureGauge +{ + /// + /// Parameters of a part. + /// + public class PartState + { + /// Moment of time the parameters were measured at. + public double Time { get; set; } + + /// Vessel part Id. + public uint Id { get; set; } + + /// Part short name. + public string Name { get; set; } + + // Thermal parameters + public double CoreTemperatureLimit { get; set; } + public double SkinTemperatureLimit { get; set; } + public double CoreTemperature { get; set; } + public double SkinTemperature { get; set; } + public double CoreTemperatureRate { get; set; } + public double SkinTemperatureRate { get; set; } + + // Critical thermal parameters + public bool IsSkinCritical => CoreTemperature / CoreTemperatureLimit < SkinTemperature / SkinTemperatureLimit; + public double CriticalTemperatureLimit => IsSkinCritical ? SkinTemperatureLimit : CoreTemperatureLimit; + public double CriticalTemperature => IsSkinCritical ? SkinTemperature : CoreTemperature; + public double CriticalTemperatureRate => IsSkinCritical ? SkinTemperatureRate : CoreTemperatureRate; + + double? _index; + /// A number between 0 and 1 showing how critical the part is. + public double Index => _index ?? (_index = + Math.Max(CoreTemperature / CoreTemperatureLimit, SkinTemperature / SkinTemperatureLimit)).Value; + + /// Calculates temperature rates. + /// The part's state in the previous moment of time. + public void UpdateTemperatureRates(PartState previousState) + { + if(previousState != null && previousState.Id == Id) + { + double timeSpan = 2 * (Time - previousState.Time); + CoreTemperatureRate = timeSpan > 0 + ? (1D - CoreTemperatureRateSmoothing) * previousState.CoreTemperatureRate + + CoreTemperatureRateSmoothing * (CoreTemperature - previousState.CoreTemperature) / timeSpan + : previousState.CoreTemperatureRate; + SkinTemperatureRate = timeSpan > 0 + ? (1D - SkinTemperatureRateSmoothing) * previousState.SkinTemperatureRate + + SkinTemperatureRateSmoothing * (SkinTemperature - previousState.SkinTemperature) / timeSpan + : previousState.SkinTemperatureRate; + } + } + + // Temperature rate value temporal smoothing. + const double CoreTemperatureRateSmoothing = 0.5; + const double SkinTemperatureRateSmoothing = 0.1; + } +} diff --git a/src/CriticalTemperatureGauge/Properties/AssemblyInfo.cs b/src/CriticalTemperatureGauge/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..affed35 --- /dev/null +++ b/src/CriticalTemperatureGauge/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CriticalTemperatureGauge")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CriticalTemperatureGauge")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("8fb4471e-fbbb-4538-9776-d22ad22b9b18")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.1.3.3")] +[assembly: AssemblyFileVersion("1.1.3.3")] diff --git a/src/CriticalTemperatureGauge/Settings.cs b/src/CriticalTemperatureGauge/Settings.cs new file mode 100644 index 0000000..85f4932 --- /dev/null +++ b/src/CriticalTemperatureGauge/Settings.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using KSP.IO; +using UnityEngine; + +namespace CriticalTemperatureGauge +{ + /// + /// Represents a set of add-on settings. + /// + /// A type attributed with KSPAddonAttribute. + public class Settings + { + // Window settings + public Vector2 ConfigWindowPosition { get; set; } + public Vector2 GaugeWindowPosition { get; set; } + public bool LockGaugeWindow { get; set; } + + // Additional information settings + public bool ShowTemperature { get; set; } + public bool ShowTemperatureLimit { get; set; } + public bool ShowTemperatureRate { get; set; } + public bool ShowCriticalPart { get; set; } + + // (Part highlighting does not work for some reason) + //public bool HighlightCriticalPart { get; set; } + + // Gauge visibility settings + + public bool ForceShowGauge { get; set; } + + const double DefaultGaugeShowingThreshold = 0.5; + double _gaugeShowingThreshold; + public double GaugeShowingThreshold + { + get { return _gaugeShowingThreshold; } + set { _gaugeShowingThreshold = value > 0 && value < 1 ? value : DefaultGaugeShowingThreshold; } + } + + const double DefaultGaugeHidingThreshold = 0.4; + double _gaugeHidingThreshold; + public double GaugeHidingThreshold + { + get { return _gaugeHidingThreshold; } + set { _gaugeHidingThreshold = Math.Min(GaugeShowingThreshold, value > 0 && value < 1 ? value : DefaultGaugeHidingThreshold); } + } + + const int DefaultBaseWindowId = -130716; + int _baseWindowId; + public int BaseWindowId + { + get { return _baseWindowId; } + set { _baseWindowId = value != 0 ? value : DefaultBaseWindowId; } + } + + // Exclusion list settings + + public bool UseExclusionList { get; set; } + + string _exclusionList; + public string ExclusionList + { + get { return _exclusionList; } + set + { + _exclusionList = value; + ExclusionListItems = _exclusionList.Split(',') + .Select(item => item.Trim()) + .Where(item => !string.IsNullOrEmpty(item)) + .ToList(); + } + } + public IEnumerable ExclusionListItems { get; private set; } + + /// Saves the settings to an XML file inside PluginData directory. + public void Save() + { + var config = PluginConfiguration.CreateForType(); + + config.SetValue(nameof(ConfigWindowPosition), ConfigWindowPosition); + config.SetValue(nameof(GaugeWindowPosition), GaugeWindowPosition); + config.SetValue(nameof(LockGaugeWindow), LockGaugeWindow); + config.SetValue(nameof(ForceShowGauge), ForceShowGauge); + config.SetValue(nameof(GaugeShowingThreshold), GaugeShowingThreshold); + config.SetValue(nameof(GaugeHidingThreshold), GaugeHidingThreshold); + config.SetValue(nameof(ShowTemperature), ShowTemperature); + config.SetValue(nameof(ShowTemperatureLimit), ShowTemperatureLimit); + config.SetValue(nameof(ShowTemperatureRate), ShowTemperatureRate); + config.SetValue(nameof(ShowCriticalPart), ShowCriticalPart); + //config.SetValue(nameof(HighlightCriticalPart), HighlightCriticalPart); + config.SetValue(nameof(UseExclusionList), UseExclusionList); + config.SetValue(nameof(ExclusionList), ExclusionList); + + config.save(); + } + + /// Loads settings from an XML file inside PluginData directory. + /// Loaded settings. + public static Settings Load() + { + var config = PluginConfiguration.CreateForType(); + config.load(); + + return new Settings + { + ConfigWindowPosition = config.GetValue(nameof(ConfigWindowPosition), Vector2.zero), + GaugeWindowPosition = config.GetValue(nameof(GaugeWindowPosition), Vector2.zero), + LockGaugeWindow = config.GetValue(nameof(LockGaugeWindow), true), + ForceShowGauge = config.GetValue(nameof(ForceShowGauge), false), + GaugeShowingThreshold = config.GetValue(nameof(GaugeShowingThreshold), DefaultGaugeShowingThreshold), + GaugeHidingThreshold = config.GetValue(nameof(GaugeHidingThreshold), DefaultGaugeHidingThreshold), + ShowTemperature = config.GetValue(nameof(ShowTemperature), true), + ShowTemperatureLimit = config.GetValue(nameof(ShowTemperatureLimit), true), + ShowTemperatureRate = config.GetValue(nameof(ShowTemperatureRate), true), + ShowCriticalPart = config.GetValue(nameof(ShowCriticalPart), true), + //HighlightCriticalPart = config.GetValue(nameof(HighlightCriticalPart), false), + UseExclusionList = config.GetValue(nameof(UseExclusionList), false), + ExclusionList = config.GetValue(nameof(ExclusionList), ""), + }; + } + } +} diff --git a/src/CriticalTemperatureGauge/Static.cs b/src/CriticalTemperatureGauge/Static.cs new file mode 100644 index 0000000..425f770 --- /dev/null +++ b/src/CriticalTemperatureGauge/Static.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace CriticalTemperatureGauge +{ + /// + /// Static class containing miscellaneous static objects. + /// + public static class Static + { + /// Path to the Textures folder inside the add-on folder. + public const string TexturePath = nameof(CriticalTemperatureGauge) + "/Textures/"; + + static Settings _settings; + /// Add-on settings. + public static Settings Settings => _settings ?? (_settings = Settings.Load()); + + /// Current critical part state. + /// null if there is no current critical part. + public static PartState CriticalPartState { get; set; } + } +} diff --git a/src/CriticalTemperatureGauge/Toolbar.cs b/src/CriticalTemperatureGauge/Toolbar.cs new file mode 100644 index 0000000..d334f44 --- /dev/null +++ b/src/CriticalTemperatureGauge/Toolbar.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using KSP.UI.Screens; +using UnityEngine; + +namespace CriticalTemperatureGauge +{ + /// + /// Class that adds the toolbar button. + /// + [KSPAddon(KSPAddon.Startup.Flight, false)] + public class Toolbar : MonoBehaviour + { + // Resources + static readonly Texture2D ToolbarButtonTexture = GameDatabase.Instance.GetTexture(Static.TexturePath + "ToolbarButton", false); + + readonly ConfigWindow _configWindow = new ConfigWindow(); + ApplicationLauncherButton _toolbarButton; + + // KSP events: + + public void Start() + { + OnGUIApplicationLauncherReady(); + } + + public void Awake() + { + GameEvents.onGUIApplicationLauncherReady.Add(OnGUIApplicationLauncherReady); + GameEvents.onGameSceneLoadRequested.Add(OnSceneChangeRequest); + } + + internal void OnDestroy() + { + GameEvents.onGUIApplicationLauncherReady.Remove(OnGUIApplicationLauncherReady); + GameEvents.onGameSceneLoadRequested.Remove(OnSceneChangeRequest); + RemoveToolbarButton(); + } + + public void OnGUI() + { + _configWindow?.DrawGUI(); + } + + void OnGUIApplicationLauncherReady() + { + AddToolbarButton(); + } + + void OnSceneChangeRequest(GameScenes scene) + { + RemoveToolbarButton(); + } + + /// Shows the configuration window when the button is pressed. + void OnButtonOn() + { + _configWindow?.Show(); + } + + /// Hides the configuration window when the button is pressed again. + void OnButtonOff() + { + _configWindow?.Hide(); + } + + /// Adds the button to the toolbar. + void AddToolbarButton() + { + if(_toolbarButton == null) + _toolbarButton = ApplicationLauncher.Instance.AddModApplication( + onTrue: OnButtonOn, + onFalse: OnButtonOff, + onHover: null, + onHoverOut: null, + onEnable: null, + onDisable: null, + visibleInScenes: ApplicationLauncher.AppScenes.FLIGHT | ApplicationLauncher.AppScenes.MAPVIEW, + texture: ToolbarButtonTexture); + } + + /// Removes the button from the toolbar. + void RemoveToolbarButton() + { + if(_toolbarButton != null) + ApplicationLauncher.Instance.RemoveModApplication(_toolbarButton); + } + } +} diff --git a/src/CriticalTemperatureGauge/Window.cs b/src/CriticalTemperatureGauge/Window.cs new file mode 100644 index 0000000..be033e8 --- /dev/null +++ b/src/CriticalTemperatureGauge/Window.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace CriticalTemperatureGauge +{ + /// + /// Represents a GUI window. + /// + public abstract class Window + { + public int WindowId { get; } + public string Title { get; } + public bool HasClearBackground { get; } + public bool IsVisible { get; private set; } + + Rect? _windowRectangle; + public Rect WindowRectangle + { + get + { + return _windowRectangle ?? (_windowRectangle = InitialWindowRectangle).Value; + } + private set + { + _windowRectangle = value; + OnWindowRectUpdated(); + } + } + + protected Window(int windowId, string title = "", bool hasClearBackground = false) + { + WindowId = windowId; + Title = title; + HasClearBackground = hasClearBackground; + } + + public void Show() + { + IsVisible = true; + } + + public void Hide() + { + IsVisible = false; + } + + public void DrawGUI() + { + if(IsVisible) + { + GUI.skin = HighLogic.Skin; + var backgroundColor = GUI.backgroundColor; + if(HasClearBackground) + GUI.backgroundColor = Color.clear; + WindowRectangle = GUILayout.Window(WindowId, WindowRectangle, WindowGUI, Title); + GUI.backgroundColor = backgroundColor; + } + } + + protected abstract Rect InitialWindowRectangle { get; } + + protected abstract void OnWindowRectUpdated(); + + protected abstract void WindowGUI(int windowId); + } +}