Guest User

KSRe 0.26.2

a guest
Oct 3rd, 2024
18
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 195.16 KB | Gaming | 0 0
  1. using I2.Loc;
  2. using KSP.Game;
  3. using KSP.Game.Science;
  4. using KSP.Iteration.UI.Binding;
  5. using KSP.Sim.impl;
  6. using KSP.Sim.ResourceSystem;
  7. using KSP.UI.Flight;
  8. using Newtonsoft.Json;
  9. using RTG;
  10. using System;
  11. using System.Collections.Generic;
  12. using System.ComponentModel;
  13. using System.IO;
  14. using System.Linq;
  15. using System.Reflection;
  16. using System.Text;
  17. using System.Text.RegularExpressions;
  18. using System.Threading.Tasks;
  19. using UnityEngine;
  20.  
  21. namespace KSRe {
  22.  
  23.     internal class ReUtil {
  24.  
  25.         internal static string GetColorForRange(double min, double max, double value) {
  26.             if (value <= min) return "<color=#b4d455>";
  27.             else if (value >= max) return "<color=#d45455>";
  28.             string[] colorKeys = new string[11] { "b4d4", "c4d4", "d4d4", "d4c4", "d4b4", "d4a4", "d494", "d484", "d474", "d464", "d454" };
  29.             return $"<color=#{colorKeys[(int)Math.Round((value - min) / (max - min) * 10)]}55>";
  30.         }
  31.         internal static string GetUllageStr(double gForce) {
  32.             if (gForce == 0) return $"<color=#d45455>{LocalizationManager.GetTermTranslation($"PartModules/DamageEngine/Low")}</color>";
  33.             else if (gForce > 0.035) return $"<color=#b4d455>{LocalizationManager.GetTermTranslation($"PartModules/DamageEngine/Nominal")}</color>";
  34.             string[] colorKeys = new string[11] { "b4d4", "c4d4", "d4d4", "d4c4", "d4b4", "d4a4", "d494", "d484", "d474", "d464", "d454" };
  35.             string nameKey = "Low";
  36.             if (gForce > 0.0125) nameKey = "Marginal";
  37.             if (gForce > 0.025) nameKey = "Nominal";
  38.             return $"<color=#{colorKeys[(int)Math.Floor((0.035 - gForce) * 300)]}55>{LocalizationManager.GetTermTranslation($"PartModules/DamageEngine/{nameKey}")}</color>";
  39.         }
  40.         internal static string GetDamageStr(double damage) {
  41.             if (damage <= 0) return $"<color=#b4d455>{LocalizationManager.GetTermTranslation($"PartModules/Damage/None")}</color>";
  42.             string[] colorKeys = new string[11] { "b4d4", "c4d4", "d4d4", "d4c4", "d4b4", "d4a4", "d494", "d484", "d474", "d464", "d454" };
  43.             string nameKey = "None";
  44.             if (damage > 85) nameKey = "Heavy";
  45.             else if (damage > 35) nameKey = "Medium";
  46.             else if (damage > 10) nameKey = "Light";
  47.             return $"<color=#{colorKeys[(int)Math.Floor(Math.Min(damage * 0.1, 10))]}55>{LocalizationManager.GetTermTranslation($"PartModules/Damage/{nameKey}")}</color>";
  48.         }
  49.         internal static string GetMachRegStr(double velocity) {
  50.             string key = "Hyper";
  51.             if (velocity < 273) key = "Sub";
  52.             else if (velocity < 409) key = "Trans";
  53.             else if (velocity < 1702) key = "Super";
  54.             return LocalizationManager.GetTranslation($"Diagnostic/{key}sonic");
  55.         }
  56.         internal static string GetGradeStr(float pct) {
  57.             string[] grades = new string[13] { "F", "D-", "D", "D+", "C-", "C", "C+", "B-", "B", "B+", "A-", "A", "A+" };
  58.             return pct < 0.2f ? grades[0] : grades[Mathf.Clamp((int)Math.Round((pct - 0.2f) * 15), 1, 12)];
  59.         }
  60.        
  61.         internal static bool IsGameSandbox() => GameManager.Instance.Game.SessionManager.ActiveGameMode == "SandboxMode";
  62.         internal static bool IsPALActive(VesselComponent vessel = null, int level = 1, bool bypassVessel = false) {
  63.             GameInstance Game = GameManager.Instance.Game;
  64.             if (!bypassVessel) {
  65.                 VesselComponent vsl = vessel ?? GameManager.Instance.Game.ViewController?.GetActiveVehicle()?.GetSimVessel();
  66.                 if (vsl == null) return false;
  67.                 // if (vsl.IsKerbalEVA) return Game.ScienceManager?.IsNodeUnlocked($"tNode_pal5000_13") ?? false;
  68.                 bool hasProbe = vsl.SimulationObject?.PartOwner?.Parts?.Any(p => p.PartData.family == "0010-Probe") ?? false;
  69.                 if (!hasProbe) return false;
  70.             }
  71.             bool partTypeCheck = true;
  72.             if (level == 4) partTypeCheck = RePlugin.UIPart.PartName.EndsWith("reactionwheel");
  73.             else if (level == 6) partTypeCheck = "0100-Methalox|0101-DeepThrottle|0102-Cryogenic|0103-DeepSpace".Contains(RePlugin.UIPart.PartData.family) &&
  74.                 !"engine_0v_monoprop_puff|engine_0v_xenon_dawn".Contains(RePlugin.UIPart.PartName);
  75.             else if (level == 5 || level == 10) partTypeCheck = RePlugin.UIPart.PartData.category == PartCategories.FuelTank && RePlugin.UIPart.PartData.family != "0070-Xenon";
  76.             if (!partTypeCheck) return false;
  77.             return Game.SessionManager.ActiveGameMode == "SandboxMode" || (Game.ScienceManager?.IsNodeUnlocked($"tNode_pal5000_{(level < 10 ? "0" : "")}{level}") ?? true);
  78.         }
  79.        
  80.         internal static Dictionary<string, string[]> careerSpecTechs;
  81.         internal static Dictionary<string, float> dmgSciRates;
  82.         internal static Dictionary<string, string[]> mfParts;
  83.         internal static Dictionary<string, string[]> crewAgencies;
  84.         internal static Dictionary<int, float> sciLvls;
  85.         internal static Dictionary<string, string[]> partKerbStats;
  86.         internal static Dictionary<string, Texture2D> agencyFlags;
  87.  
  88.         internal static string GetPartMfrKey(string partName) => mfParts.FirstOrDefault(m => m.Value.Contains(partName)).Key ?? "";
  89.         internal static ReAgency PlayerReAgency() => RePlugin.saveData.agencies?.FirstOrDefault(a => a.Name == PlayerAgencyName());
  90.         internal static KerbalInfo GetKerbalForAgency(ReAgency agency) {
  91.             KerbalInfo kerbal;
  92.             GameManager.Instance.Game.SessionManager.KerbalRosterManager.TryGetKerbalByName(RePlugin.saveData.kerbals.FirstOrDefault(k => k?.agencyName == agency.Name).Name, out kerbal);
  93.             return kerbal;
  94.         }
  95.         internal static List<KerbalDamage> GetKerbalsForAgency(ReAgency agency) => RePlugin.saveData?.kerbals?.Where(k => k?.agencyName == agency.Name)?.ToList() ?? new List<KerbalDamage>();
  96.         internal static bool IsPlayerAgency(ReAgency agency) => agency?.Name == PlayerAgencyName();
  97.         internal static string RandomTrait() {
  98.             List<string> traits = careerSpecTechs.Keys.Where(s => !s.Contains("_")).ToList();
  99.             List<string> traitsInUse = RePlugin.saveData.kerbals?.Select(k => k?.Trait)?.ToList() ?? new List<string>();
  100.             if (traitsInUse.Count < traits.Count)
  101.                 traits.RemoveAll(t => traitsInUse.Contains(t));
  102.             return traits[UnityEngine.Random.Range(0, traits.Count)];
  103.         }
  104.         internal static string PlayerTrait() => RePlugin.saveData.kerbals.FirstOrDefault(k => k?.agencyName == PlayerAgencyName())?.Trait;
  105.         internal static string PlayerAgencyName() => GameManager.Instance.Game.AgencyManager.FindAgencyEntryFirst().AgencyName;
  106.  
  107.         internal static void TryAddDamage(PartComponent part, double damage, string dmgType) {
  108.             if (part.TryGetModule(out PartComponentModule_DamageEngine moduleDmgEng) && moduleDmgEng != null)
  109.                 moduleDmgEng.AddDamage(damage, dmgType);
  110.             else if (part.TryGetModule(out PartComponentModule_Damage moduleDmg) && moduleDmg != null)
  111.                 moduleDmg.AddDamage(damage, dmgType);
  112.         }
  113.         internal static double TryGetDamage(PartComponent part, bool mod = true) {
  114.             if (part.TryGetModule(out PartComponentModule_DamageEngine moduleDmgEng) && moduleDmgEng != null)
  115.                 return moduleDmgEng.GetDamage(mod);
  116.             else if (part.TryGetModule(out PartComponentModule_Damage moduleDmg) && moduleDmg != null)
  117.                 return moduleDmg.GetDamage(mod);
  118.             return 0;
  119.         }
  120.  
  121.         internal static int GetTotalScience() => !GameManager.Instance.Game.CampaignPlayerManager.TryGetMyCampaignPlayerEntry(out CampaignPlayerEntry player) ? 0 :
  122.             player.AllocatedSciencePoints + player.AvailableSciencePoints;
  123.         internal static void AddScience(int amount, bool pushNotify = true) {
  124.             GameManager.Instance.Game.SessionManager.UpdateMyAgencyAdditionalSciencePoints(GameManager.Instance.Game.SessionManager.GetMyAgencyAdditionalSciencePoints() + amount);
  125.             if (!pushNotify) return;
  126.             NotificationData notify = new NotificationData { Tier = NotificationTier.Alert, Importance = NotificationImportance.Low };
  127.             notify.AlertTitle.LocKey = "Missions/TriumphWindow/AcceptScienceReward";
  128.             notify.FirstLine.LocKey = "+" + amount;
  129.             GameManager.Instance.Game.Notifications.ProcessNotification(notify);
  130.         }
  131.         internal static int GetSciLevel(string sciType) {
  132.             //bool useSub = sciType.Contains("_");
  133.             //List<int> subLvls = new List<int>();
  134.             //if (useSub) {
  135.             //    subLvls = sciLvlBonuses.Keys.Take(sciLvlBonuses.Count - 2).ToList();
  136.             //    List<int> halfLvls = subLvls.Skip(1).Select(i => i / 2).ToList();
  137.             //    subLvls.AddRange(halfLvls);
  138.             //    subLvls = subLvls.Distinct().ToList();
  139.             //    subLvls.Sort();
  140.             //}
  141.             // (!useSub ? sciLvlBonuses.Keys.ToList() : subLvls)
  142.             int sci = PlayerReAgency().rocketScience.Where(s => s.Key.StartsWith(sciType)).Select(s => s.Value).Sum();
  143.             return Mathf.Clamp(sciLvls.Keys.ToList().TakeWhile(l => l <= sci).Count() - 1 +
  144.                 (PlayerTrait() == sciType ? 2 : 0), 0, sciLvls.Keys.ToList().Count - 1);
  145.         }
  146.         internal static float GetSciBonus(string sciType) => sciLvls.ElementAt(GetSciLevel(sciType.Contains("_") ? sciType.Split('_')[0] : sciType)).Value;
  147.         internal static string GetSciLocKey(string sciType) {
  148.             bool isSub = sciType.Contains("_");
  149.             string[] keys = isSub ? sciType.Split('_') : new string[2] { sciType, "Title" };
  150.             string locKey = sciType.StartsWith("Science") && isSub ? $"Science/Experiments/{keys[1]}/{keys[0].Replace("Science", "")}/ReportName" : $"Career/{keys[0]}/{keys[1]}";
  151.             if (sciType.Contains("CrewObservation")) locKey = "Science/Experiments/CrewObservation/Data/ReportName";
  152.             else if (sciType == "Aeronautics_Sample" || sciType == "Aeronautics_Data") locKey = $"Science/Experiments/AtmosphereSurvey/{sciType.Split('_')[1]}/ReportName";
  153.             return locKey;
  154.         }
  155.  
  156.         internal static void PushNotify(string name, string descKey, NotificationImportance color, bool rand = true, string obj = null) {
  157.             UnityEngine.Random.InitState((name + descKey).GetHashCode() + DateTime.Now.Millisecond);
  158.             NotificationData notify = new NotificationData { Tier = NotificationTier.Alert, TimeStamp = GameManager.Instance.Game.UniverseModel.UniverseTime, Importance = color };
  159.             notify.AlertTitle.LocKey = name;
  160.             notify.FirstLine.LocKey = $"{(rand || descKey.StartsWith("Wings") || descKey.StartsWith("Recovery") ? "KerbalLife" : "Career")}/Notifications/{descKey}{(rand ? UnityEngine.Random.Range(0, 3) + "" : "")}";
  161.             if (obj != null) notify.FirstLine.ObjectParams = new object[1] { obj };
  162.             GameManager.Instance.Game.Notifications.ProcessNotification(notify);
  163.         }
  164.  
  165.         internal static string GetRandomKerbalName(int i) {
  166.             string[] prefix = new string[] {
  167.                 "Ad", "Al", "Ald", "An", "Bar", "Bart", "Bil", "Billy-Bob", "Bob", "Bur", "Cal", "Cam", "Chad", "Cor", "Dan", "Der", "Des", "Dil", "Do", "Don", "Dood", "Dud", "Dun",
  168.                 "Ed", "El", "En", "Er", "Fer", "Fred", "Gene", "Geof", "Ger", "Gil", "Greg", "Gus", "Had", "Hal", "Han", "Har", "Hen", "Her", "Hud", "Jed", "Jen", "Jer", "Joe", "John",
  169.                 "Jon", "Jor", "Kel", "Ken", "Ker", "Kir", "Lan", "Lem", "Len", "Lo", "Lod", "Lu", "Lud", "Mac", "Mal", "Mat", "Mel", "Mer", "Mil", "Mit", "Mun", "Ned", "Neil", "Nel",
  170.                 "New", "Ob", "Or", "Pat", "Phil", "Ray", "Rib", "Rich", "Ro", "Rod", "Ron", "Sam", "Sean", "See", "Shel", "Shep", "Sher", "Sid", "Sig", "Son", "Thom", "Thomp", "Tom",
  171.                 "Wehr", "Wil"
  172.             };
  173.             string[] suffix = new string[] {
  174.                 "ald", "bal", "bald", "bart", "bas", "berry", "bert", "bin", "ble", "bles", "bo", "bree", "brett", "bro", "bur", "burry", "bus", "by", "cal", "can", "cas", "cott",
  175.                 "dan", "das", "den", "din", "do", "don", "dorf", "dos", "dous", "dred", "drin", "dun", "ely", "emone", "emy", "eny", "fal", "fel", "fen", "field", "ford", "fred",
  176.                 "frey", "frey", "frid", "frod", "fry", "furt", "gan", "gard", "gas", "gee", "gel", "ger", "gun", "hat", "ing", "ke", "kin", "lan", "las", "ler", "ley", "lie", "lin",
  177.                 "lin", "lo", "lock", "long", "lorf", "ly", "mal", "man", "min", "ming", "mon", "more", "mund", "my", "nand", "nard", "ner", "ney", "nie", "ny", "oly", "ory", "rey",
  178.                 "rick", "rie", "righ", "rim", "rod", "ry", "sby", "sel", "sen", "sey", "ski", "son", "sted", "ster", "sy", "ton", "top", "trey", "van", "vey", "vin", "vis", "well",
  179.                 "wig", "win", "wise", "zer", "zon", "zor"
  180.             };
  181.             UnityEngine.Random.InitState(i);
  182.             return prefix[UnityEngine.Random.Range(0, prefix.Length)] + suffix[UnityEngine.Random.Range(0, suffix.Length)];
  183.         }
  184.  
  185.         internal static string GetGameObjPath(GameObject obj) {
  186.             string path = "/" + obj.name;
  187.             while (obj.transform.parent != null) {
  188.                 obj = obj.transform.parent.gameObject;
  189.                 path = "/" + obj.name + path;
  190.             }
  191.             return path;
  192.         }
  193.     }
  194.  
  195.     internal class ReUtilKerb {
  196.        
  197.         internal static int GetDamageInt(double value) {
  198.             if (value < -50) return 0;
  199.             else if (value < 0) return 1;
  200.             else if (value > 90) return 11;
  201.             return (int)Math.Floor(value / 10) + 2;
  202.         }
  203.         internal static string GetPsychologyStr(float pct) {
  204.             string color = ReUtil.GetColorForRange(0, 1, 1 - pct);
  205.             if (pct <= 0.2) return $"{color}--</color>";
  206.             else if (pct >= 0.8) return $"{color}++</color>";
  207.             return new string[3] { $"{color}-</color>", $"{color}±</color>", $"{color}+</color>" }[(int)Math.Floor((pct - 0.2) * 5)];
  208.         }
  209.  
  210.  
  211.     }
  212.  
  213.  
  214.     public class KerbalDamage {
  215.         // public IGGuid Id { get; private set; }
  216.         public string Name { get; private set; }
  217.         public string Trait { get; private set; }
  218.         public string agencyName;
  219.         public string status;
  220.         public string celName;
  221.         public Dictionary<string, double> damage;
  222.         // public Dictionary<string, double> records;
  223.  
  224.         public KerbalDamage(string name, string trait, string agency) {
  225.             Name = name;
  226.             Trait = trait;
  227.             agencyName = agency;
  228.             damage = new Dictionary<string, double>();
  229.             // records = new Dictionary<string, double>();
  230.         }
  231.  
  232.     }
  233.  
  234.     public class ReAgency {
  235.         public string Name { get; private set; }
  236.         // public string leaderName;
  237.         // public string leaderTrait;
  238.  
  239.         public Dictionary<string, int> rocketScience;
  240.         public Dictionary<string, double> partRecords;
  241.  
  242.         public ReAgency(string name) {
  243.             Name = name;
  244.             rocketScience = new Dictionary<string, int>();
  245.             partRecords = new Dictionary<string, double>();
  246.         }
  247.  
  248.     }
  249.  
  250.     public class Repairer {
  251.  
  252.         public string Name { get; private set; }
  253.         public string Guid { get; private set; }
  254.         public string TargetGuid { get; private set; }
  255.         // public float Estimate { get; private set; }
  256.  
  257.         public Repairer(string name, string guid, string targetGuid) {
  258.             Name = name;
  259.             Guid = guid;
  260.             TargetGuid = targetGuid;
  261.             // Estimate = estimate;
  262.         }
  263.  
  264.     }
  265.  
  266. }
  267.  
  268.  
  269. // ---
  270.  
  271. using BepInEx;
  272. using BepInEx.Logging;
  273. using HarmonyLib;
  274. using I2.Loc;
  275. using KSP;
  276. using KSP.Api.CoreTypes;
  277. using KSP.Game;
  278. using KSP.Game.Science;
  279. using KSP.Iteration.UI.Binding;
  280. using KSP.Messages;
  281. using KSP.Modules;
  282. using KSP.Networking.MP.GameClient;
  283. using KSP.OAB;
  284. using KSP.Rendering.Planets;
  285. using KSP.Sim;
  286. using KSP.Sim.Definitions;
  287. using KSP.Sim.DeltaV;
  288. using KSP.Sim.impl;
  289. using KSP.Sim.ResourceSystem;
  290. using KSP.UI;
  291. using KSP.UI.Binding.Widget;
  292. using KSP.UI.Flight;
  293. using Newtonsoft.Json;
  294. using System;
  295. using System.Collections.Generic;
  296. using System.IO;
  297. using System.Linq;
  298. using System.Reflection;
  299. using System.Reflection.Emit;
  300. using System.Text;
  301. using System.Threading.Tasks;
  302. using TMPro;
  303. using UnityEngine;
  304. using UnityEngine.UI;
  305. using static KSP.Api.UIDataPropertyStrings.View;
  306.  
  307. namespace KSRe {
  308.  
  309.     [HarmonyPatch]
  310.     public class ReHarmony {
  311.  
  312.         [HarmonyPatch(typeof(Curtain), "Awake")]
  313.         [HarmonyPrefix]
  314.         public static void FixLoadingScreen(Curtain __instance) =>
  315.             typeof(Curtain).GetField("_appStartLoadingScreenSprite1", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(__instance, null);
  316.  
  317.         [HarmonyPatch(typeof(PopulationComponent), nameof(PopulationComponent.OnStart))]
  318.         [HarmonyPrefix]
  319.         public static void AddKerbalSpawnDelay(PopulationComponent __instance) =>
  320.             typeof(PopulationComponent).GetField("_refillDelay", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(__instance, 0.8);
  321.  
  322.         [HarmonyPatch(typeof(PopulationComponent), "UpdateCurrentKerbalCount")]
  323.         [HarmonyPostfix]
  324.         public static void FixKerbalSpawnCap(PopulationComponent __instance) {
  325.             typeof(PopulationComponent).GetField("_refillKerbalLimit", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(__instance, ReUtil.IsGameSandbox() ? 10 : GameManager.Instance.Game.UniverseModel.UniverseTime < 10 ? 6 : 0);
  326.             typeof(PopulationComponent).GetField("_refillKerbalEmptySubIdOnly", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(__instance, ReUtil.IsGameSandbox());
  327.         }
  328.  
  329.         [HarmonyPatch(typeof(PartInfoOverlay), "PopulateCoreInfoFromPart")]
  330.         [HarmonyPostfix]
  331.         public static void FixPartStats(IObjectAssemblyAvailablePart IOBAPart, ref List<KeyValuePair<string, string>> __result) {
  332.             __result[0] = new KeyValuePair<string, string>(__result[0].Key, $"{IOBAPart.Mass:F3} {Units.SymbolTonne}");
  333.             if (IOBAPart.CrewCapacity > 0) {
  334.                 __result.Add(new KeyValuePair<string, string>(OABLocalization.GetTranslation("VAB/Tooltip/Crew Capacity"), IOBAPart.CrewCapacity + ""));
  335.                 if (ReUtil.partKerbStats.Any(p => p.Value.Contains(IOBAPart.Name)))
  336.                     __result.Add(new KeyValuePair<string, string>(OABLocalization.GetTranslation("KerbalLife/Title"), ReUtil.partKerbStats.FirstOrDefault(p => p.Value.Contains(IOBAPart.Name)).Key));
  337.             }
  338.         }
  339.  
  340.         [HarmonyPatch(typeof(PartInfoOverlay), "PopulateResourceInfoFromPart")]
  341.         [HarmonyPostfix]
  342.         public static void FixFuelContainerTotal(List<KeyValuePair<string, string>> dict, IObjectAssemblyResource[] resourceArray) {
  343.             if (!resourceArray.Any(r => r.Name == "FuelContainer")) return;
  344.             dict[dict.Count - 1] = new KeyValuePair<string, string>(dict.Last().Key, $"~{resourceArray.First(r => r.Name == "FuelContainer").Capacity} {Units.SymbolTonne}");
  345.         }
  346.        
  347.         [HarmonyPatch(typeof(PartInfoOverlay), "PopulateEngineThrustAndISP")]
  348.         [HarmonyPostfix]
  349.         public static void ReEngineStats(List<KeyValuePair<string, string>> dictToPopulate, IObjectAssemblyAvailablePart IOABPart) {
  350.             if (!(IOABPart is OABPartData) || IOABPart.Category != PartCategories.Engine) return;
  351.             if (!IOABPart.PartData.serializedPartModules.Any(m => m.BehaviourType == typeof(Module_Engine))) return;
  352.             Data_Engine engine = (Data_Engine)IOABPart.PartData.serializedPartModules.First(m => m.BehaviourType == typeof(Module_Engine)).ModuleData.First(d =>
  353.                 d.DataObject.DataType == typeof(Data_Engine)).DataObject;
  354.             Data_Engine.EngineMode mode = engine.engineModes.First();
  355.             dictToPopulate.RemoveAt(dictToPopulate.Count - 4);
  356.             dictToPopulate[dictToPopulate.Count - 3] = new KeyValuePair<string, string>(
  357.                 OABLocalization.GetTranslation($"VAB/Tooltip/Thrust ({(mode.engineType != EngineType.Turbine ? "Vacuum" : "Atmo")})"),
  358.                 dictToPopulate[dictToPopulate.Count - 3].Value);
  359.             string ispStr = $"{dictToPopulate[dictToPopulate.Count - 1].Value}";
  360.             if (mode.engineType != EngineType.Turbine) ispStr = $"{ispStr} - {dictToPopulate[dictToPopulate.Count - 2].Value}";
  361.             dictToPopulate[dictToPopulate.Count - 2] = new KeyValuePair<string, string>(
  362.                 $"{OABLocalization.GetTranslation($"VAB/Tooltip/ISP{(mode.engineType == EngineType.Turbine ? " (Atmo)" : "")}")}" +
  363.                 (mode.engineType != EngineType.Turbine ? $" (0 - 1 < {mode.atmosphereCurve.Curve?.keys?.Last().time:N0} {OABLocalization.GetTranslation("Unit/Symbol/Atmosphere")})" : ""), ispStr);
  364.             dictToPopulate.RemoveAt(dictToPopulate.Count - 1);
  365.             dictToPopulate.Insert(dictToPopulate.Count - 1, new KeyValuePair<string, string>(OABLocalization.GetTranslation("VAB/StagingStack/TWR"),
  366.                 $"{mode.maxThrust / ((mode.engineType != EngineType.SolidBooster ? IOABPart.Mass : IOABPart.TotalMass) * GameManager.Instance.Game.UniverseModel.HomeWorld.gravityASL * 10):N2}"));
  367.             if (mode.engineType != EngineType.SolidBooster) {
  368.                 string throttleStr = Math.Abs(mode.maxThrust - mode.minThrust) > 0.1 ? $"{mode.minThrust / mode.maxThrust * 100:N0}-{1:P0}" : "-";
  369.                 dictToPopulate.Add(new KeyValuePair<string, string>(OABLocalization.GetTranslation("PartModules/Engine/EngineThrottle"), throttleStr));
  370.             }
  371.             string fuelStr = "";
  372.             if (mode.propellant.mixtureName == "Methalox")
  373.                 fuelStr = $"{OABLocalization.GetTranslation("Resource/Abbreviation/Ox")} : {OABLocalization.GetTranslation("Resource/Abbreviation/LF")} " +
  374.                     ((RePlugin.fuelRatiosByVol?.Value ?? false) ? "(2:1)" : "(5:2)");
  375.             else if (mode.propellant.mixtureName == "Hydrolox")
  376.                 fuelStr = $"{OABLocalization.GetTranslation("Resource/Abbreviation/H")} : {OABLocalization.GetTranslation("Resource/Abbreviation/Ox")} " +
  377.                     ((RePlugin.fuelRatiosByVol?.Value ?? false) ? "(5:2)" : "(5:32)");
  378.             else if (mode.propellant.mixtureName == "Hypergolic")
  379.                 fuelStr = $"{OABLocalization.GetTranslation("Resource/Abbreviation/MP")} : {OABLocalization.GetTranslation("Resource/Abbreviation/LF")} " +
  380.                     ((RePlugin.fuelRatiosByVol?.Value ?? false) ? "(5:1)" : "(15:2)");
  381.             else if (mode.propellant.mixtureName == "XenonEC")
  382.                 fuelStr = $"{OABLocalization.GetTranslation("Resource/Abbreviation/Xe")} + {OABLocalization.GetTranslation("Resource/Abbreviation/EC")}";
  383.             else if ("MonoPropellant|Hydrogen|Methane Air".Contains(mode.propellant.mixtureName))
  384.                 fuelStr = OABLocalization.GetTranslation("Resource/DisplayName/" + mode.propellant.mixtureName);
  385.             if (fuelStr.Length > 0) dictToPopulate.Add(new KeyValuePair<string, string>(OABLocalization.GetTranslation("PartModules/Generic/Tooltip/Propellants"), fuelStr));
  386.         }
  387.  
  388.         //[HarmonyPatch(typeof(StagePartDataContext), "GetFuelTotalAmount")]
  389.         //[HarmonyPostfix]
  390.         //public static void ReFuelTotal(int fuelIngredientIndex, ref float __result, StagePartDataContext __instance) {
  391.         //    if (__result == 0.0f) return;
  392.         //    if (!"Methalox|Hydrolox|Hypergolic".Contains(__instance.associatedEngineData.engineModes[0].propellant.mixtureName)) return; // 0 -> 1 for rapier
  393.         //    GameInstance game = GameManager.Instance.Game;
  394.         //    VesselDeltaVComponent deltaComp = game.GlobalGameState.GetGameState().GameState == GameState.VehicleAssemblyBuilder ?
  395.         //        game.OAB.Current.Stats.MainAssembly.VesselDeltaV : game.ViewController.GetActiveVehicle().GetSimVessel().VesselDeltaV;
  396.         //    // deltaComp.GetStage(__instance.associatedPart.InStageIndex).Parts.
  397.         //    if (__instance.associatedEngineData != null && __instance.associatedEngineData.PropellantStates != null) {
  398.         //        ResourceFlowRequestHandle reqHandle = __instance.associatedEngineData.PropellantStates[__instance.associatedEngineData.currentEngineModeIndex].requestHandle;
  399.         //        ResourceFlowRequestManager.ManagedRequestWrapper reqWrap = null;
  400.         //        bool reqValid = false;
  401.         //        if (__instance.associatedPartComponent != null)
  402.         //            reqValid = __instance.associatedPartComponent.PartResourceFlowRequestBroker.TryGetCurrentRequest(reqHandle, out reqWrap);
  403.         //        else if (__instance.associatedPart != null && __instance.associatedPart.ResourceFlowRequestBroker != null)
  404.         //            reqValid = __instance.associatedPart.ResourceFlowRequestBroker.TryGetCurrentRequest(reqHandle, out reqWrap);
  405.         //        if (reqValid && reqWrap.instructions.Count > fuelIngredientIndex) {
  406.         //            FlowInstructionConfig flowConfig = reqWrap.instructions[fuelIngredientIndex];
  407.         //            if (flowConfig.ResourceContainerGroup != null) {
  408.         //                double resTotal = flowConfig.ResourceContainerGroup.GetResourceCapacityUnits(flowConfig.FlowResource);
  409.         //                RePlugin.Logger.LogInfo("flowResTotal: " + resTotal);
  410.         //                foreach (ContainedResourceData resData in flowConfig.ResourceContainerGroup.GetAllResourcesContainedData()) {
  411.         //                    RePlugin.Logger.LogInfo("this stored: " + resData.StoredUnits);
  412.         //                    RePlugin.Logger.LogInfo("this total: " + resData.CapacityUnits);
  413.         //                }
  414.         //                // __result = (float)resTotal;
  415.         //            }
  416.         //        }
  417.         //    }
  418.         //}
  419.  
  420.         [HarmonyPatch(typeof(PartsManagerCore), nameof(PartsManagerCore.Initialize))]
  421.         [HarmonyPostfix]
  422.         public static void AddPartMgrMinBtn(PartsManagerCore __instance) {
  423.             GameObject xBtn = __instance.transform.FindChildRecursive("BTN-Close")?.gameObject;
  424.             if (xBtn == null) return;
  425.             GameObject minBtn = UnityEngine.Object.Instantiate(xBtn, xBtn.transform.parent);
  426.             minBtn.transform.SetSiblingIndex(minBtn.transform.parent.childCount - 2);
  427.             Image imageComp = minBtn.GetChild("Icon")?.GetComponent<Image>();
  428.             if (imageComp != null) {
  429.                 imageComp.sprite = null;
  430.                 // GameManager.Instance.Assets.Load<Sprite>("WB-ICO-Minimize", s => { imageComp.sprite = s; });
  431.             }
  432.             UIAction_Void_Button actionComp = minBtn.GetComponent<UIAction_Void_Button>();
  433.             if (actionComp != null) {
  434.                 actionComp.enabled = false;
  435.                 ButtonExtended btnComp = minBtn.GetComponent<ButtonExtended>();
  436.                 if (btnComp != null) {
  437.                     btnComp.onClick.AddListener(delegate {
  438.                         RectTransform tformComp = __instance.GetComponent<RectTransform>();
  439.                         if (tformComp == null) return;
  440.                         float botEdgePad = 18;
  441.                         float minSizeY = 100;
  442.                         float maxSizeY = Math.Min((Screen.height / 2) + minSizeY - botEdgePad + tformComp.localPosition.y, 900);
  443.                         tformComp.localPosition = new Vector2(tformComp.localPosition.x,
  444.                             tformComp.localPosition.y + (tformComp.sizeDelta.y == minSizeY ? -(maxSizeY - minSizeY) : tformComp.sizeDelta.y - minSizeY));
  445.                         tformComp.sizeDelta = new Vector2 (tformComp.sizeDelta.x,
  446.                             tformComp.sizeDelta.y == minSizeY ? maxSizeY : minSizeY);
  447.                     });
  448.                 }
  449.             }
  450.         }
  451.  
  452.         [HarmonyPatch(typeof(PartBehavior), nameof(PartBehavior.OnCollisionEnter))]
  453.         [HarmonyPostfix]
  454.         public static void AddImpactDamage(PartBehavior __instance, Collision c) {
  455.             if (c == null || (__instance?.IsSimObjectDestroyed() ?? true) || __instance == null) return;
  456.             if (c.relativeVelocity.magnitude > __instance.Model.CrashTolerance || c.relativeVelocity.magnitude < 0.1) return;
  457.             if (__instance.SimObjectComponent.Guid == RePlugin.UIPart?.Guid) {
  458.                 RePlugin.UIPartImpacts = Tuple.Create(c.relativeVelocity.magnitude < RePlugin.UIPartImpacts.Item1 || RePlugin.UIPartImpacts.Item1 == 0 ? c.relativeVelocity.magnitude : RePlugin.UIPartImpacts.Item1,
  459.                     c.relativeVelocity.magnitude > RePlugin.UIPartImpacts.Item2 ? c.relativeVelocity.magnitude : RePlugin.UIPartImpacts.Item2);
  460.             }
  461.             // if (c.relativeVelocity.magnitude < __instance.Model.CrashTolerance * 0.667) return;
  462.             // ((c.relativeVelocity.magnitude - __instance.Model.CrashTolerance * 0.667) / __instance.Model.CrashTolerance) * 200.0;
  463.             double dmg = Math.Pow(1.047, (c.relativeVelocity.magnitude / __instance.Model.CrashTolerance) * 100) - 1;
  464.             if (__instance.SimObjectComponent.PartName == "eva_kerbal") {
  465.                 RePlugin.AddKerbalDamage(__instance.partOwner.SimObjectComponent.DisplayName, "Medicine_Impacts", dmg);
  466.                 if (c.relativeVelocity.magnitude > 3) ReUtil.PushNotify(__instance.partOwner.SimObjectComponent.DisplayName, "Hurt", NotificationImportance.Medium);
  467.             } else ReUtil.TryAddDamage(__instance.SimObjectComponent, dmg, "Flight_Impacts");
  468.         }
  469.  
  470.         [HarmonyPatch(typeof(RDCenterUIController), "InitializeTechTreeEnviroment")]
  471.         [HarmonyPrefix]
  472.         public static void AddTechTiers(RDCenterUIController __instance) {
  473.             typeof(RDCenterUIController).GetField("_tiers", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(__instance, 8);
  474.             //FieldInfo sizeField = typeof(RDCenterUIController).GetField("_treeContainerHorizontalSize", BindingFlags.Instance | BindingFlags.NonPublic);
  475.             //if (sizeField != null) sizeField.SetValue(__instance, (float)sizeField.GetValue(__instance) * 0.5);
  476.         }
  477.  
  478.         [HarmonyPatch(typeof(RDCenterUIController), "UpdateFocusedTechNode")]
  479.         [HarmonyPostfix]
  480.         public static void ReTechPanel(RDCenterUIController __instance) {
  481.             TechNodeView tNode = (TechNodeView)typeof(RDCenterUIController).GetField("_focusedNode", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(__instance);
  482.             FieldInfo textField = typeof(RDCenterUIController).GetField("_techNodeRequiredTechnologiesProperty", BindingFlags.Instance | BindingFlags.NonPublic);
  483.             ScienceManager sciMgr = GameManager.Instance.Game.ScienceManager;
  484.             Property<string> txtProp = (Property<string>)textField?.GetValue(__instance);
  485.             // string path = "GameManager/Default Game Instance(Clone)/UI Manager(Clone)/Main Canvas/RDCenterUI(Clone)/Container/TechNodeInfo/Content/";
  486.             // TextMeshProUGUI txtComp = GameObject.Find(path + "Footer/RequiredTechList")?.GetComponent<TextMeshProUGUI>();
  487.             // if (txtComp == null) return;
  488.             List<string> prqs = tNode?.AssociatedTechNode?.RequiredTechNodeIDs?.ToList();
  489.             if (prqs?.All(t => sciMgr.IsNodeUnlocked(t)) ?? true) {
  490.                 if (tNode.CurrentNodeState == TechNodeView.NodeState.Visible) {
  491.                     string cost = tNode.AssociatedTechNode.RequiredSciencePoints.ToString();
  492.                     bool isSubTier = cost.StartsWith("2") || cost.StartsWith("3") || cost.StartsWith("6") || cost.StartsWith("8");
  493.                     int lvl = tNode.AssociatedTechNode.TierToUnlock * 2 - (isSubTier ? 0 : 1);
  494.                     List<string> sciReqs = ReUtil.careerSpecTechs.Where(p => p.Value.Any(t => tNode.AssociatedTechNode.ID.Contains(t))).Select(p => p.Key).Where(s =>
  495.                         lvl > ReUtil.GetSciLevel(s)).ToList();
  496.                     if (sciReqs?.Count > 0)
  497.                         txtProp?.SetValue($"<color=#5FBFE4>{LocalizationManager.GetTranslation("Career/Level")} {lvl} " +
  498.                             $"{LocalizationManager.GetTranslation(ReUtil.GetSciLocKey(sciReqs.First()))}{(sciReqs.Count == 1 ? "" : $" (+{sciReqs.Count - 1})")}</color>");
  499.                     textField?.SetValue(__instance, txtProp);
  500.                 }
  501.                 return;
  502.             } else if (prqs.Count == 1) return;
  503.             prqs.RemoveAll(t => sciMgr.IsNodeUnlocked(t));
  504.             // txtComp.text = LocalizationManager.GetTranslation(sciMgr.TechNodeDataStore.AvailableData[prqs.First()].NameLocKey) + (prqs.Count == 1 ? "" : $" (+{prqs.Count - 1})");
  505.             txtProp?.SetValue(LocalizationManager.GetTranslation(sciMgr.TechNodeDataStore.AvailableData[prqs.First()].NameLocKey) + (prqs.Count == 1 ? "" : $" (+{prqs.Count - 1})"));
  506.             textField?.SetValue(__instance, txtProp);
  507.         }
  508.  
  509.         [HarmonyPatch(typeof(RDCenterUIController), "GetNodeState")]
  510.         [HarmonyPostfix]
  511.         public static void ReTechNodeState(RDCenterUIController __instance, TechNodeView node, ref TechNodeView.NodeState __result) {
  512.             if (__result != TechNodeView.NodeState.Unlockable) return;
  513.             string cost = node.AssociatedTechNode.RequiredSciencePoints.ToString();
  514.             bool isSubTier = cost.StartsWith("2") || cost.StartsWith("3") || cost.StartsWith("6") || cost.StartsWith("8");
  515.             List<string> sciReqs = ReUtil.careerSpecTechs.Where(p => p.Value.Any(t => node.AssociatedTechNode.ID.Contains(t))).Select(p => p.Key).Where(s =>
  516.                 (node.AssociatedTechNode.TierToUnlock * 2 - (isSubTier ? 0 : 1)) > ReUtil.GetSciLevel(s)).ToList();
  517.             if (sciReqs?.Count > 0)
  518.                 __result = TechNodeView.NodeState.Visible;
  519.         }
  520.  
  521.         [HarmonyPatch(typeof(RDCenterUIController), "OnUnlockTechNode")]
  522.         [HarmonyPostfix]
  523.         public static void UnlockTechEvents(RDCenterUIController __instance) {
  524.             TechNodeView tNode = (TechNodeView)typeof(RDCenterUIController).GetField("_focusedNode", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(__instance);
  525.             if (tNode == null) return;
  526.             List<TechNodeData> tree = GameManager.Instance.Game.ScienceManager.TechNodeDataStore.AvailableData.Select(p => p.Value).ToList();
  527.             int total = tree.Count(t => t.TierToUnlock == tNode.AssociatedTechNode.TierToUnlock);
  528.             int unlocked = tree.Count(t => t.TierToUnlock == tNode.AssociatedTechNode.TierToUnlock && GameManager.Instance.Game.ScienceManager.IsNodeUnlocked(t.ID));
  529.             if (unlocked == 1 || total == unlocked)
  530.                 for (int i = 0; i < tNode.AssociatedTechNode.TierToUnlock; i++)
  531.                     GameManager.Instance.Game.SessionManager.KerbalRosterManager.CreateKerbalByName(ReUtil.GetRandomKerbalName(i + DateTime.Now.Second + DateTime.Now.Millisecond));
  532.             foreach (string partID in tNode.AssociatedTechNode.UnlockedPartsIDs.Where(p => ReUtil.GetPartMfrKey(p)?.Length > 0 && RePlugin.TryGetPartRecord(p) == 0))
  533.                 RePlugin.AddPartRecord(partID, 0);
  534.             bool addAgency = false;
  535.             foreach (TechNodeData tech in tree.Where(t => t.RequiredTechNodeIDs.Contains(tNode.AssociatedTechNode.ID)))
  536.                 foreach (string partID in tech.UnlockedPartsIDs) {
  537.                     string mfKey = ReUtil.GetPartMfrKey(partID);
  538.                     if (mfKey?.Length > 0 && RePlugin.saveData.agencies.All(a => a.Name != mfKey)) {
  539.                         addAgency = true;
  540.                         RePlugin.saveData.agencies.Add(new ReAgency(mfKey));
  541.                         __instance.StartCoroutine(CallbackUtil.DelayedCallback(2.0f, delegate {
  542.                             ReUtil.PushNotify($"Missions/MissionGranter/Name/{mfKey}", "NewAgency", NotificationImportance.Low, false);
  543.                         }));
  544.                     }
  545.                 }
  546.             if (addAgency) return;
  547.             if (tNode.AssociatedTechNode.TierToUnlock > ReUtil.crewAgencies.Keys.Count(ca => RePlugin.saveData.agencies.Any(sa => sa.Name == ca)) - 3) {
  548.                 UnityEngine.Random.InitState((GameManager.Instance.Game.SessionGuidString + tNode.AssociatedTechNode.TierToUnlock).GetHashCode());
  549.                 addAgency = unlocked > UnityEngine.Random.Range(0, total);
  550.                 // addAgOrCrew = Math.Pow(1.047, (unlocked / total) * 100) - 1 > UnityEngine.Random.value * 100;
  551.             }
  552.             if (!addAgency) return;
  553.             int rand = UnityEngine.Random.Range(0, ReUtil.crewAgencies.Keys.Count(ca => RePlugin.saveData.agencies.All(sa => ca != sa.Name)));
  554.             string agency = ReUtil.crewAgencies.Keys.Where(ca => RePlugin.saveData.agencies.All(sa => ca != sa.Name)).ToList()[rand];
  555.             RePlugin.saveData.agencies.Add(new ReAgency(agency));
  556.             __instance.StartCoroutine(CallbackUtil.DelayedCallback(2.0f, delegate {
  557.                 ReUtil.PushNotify($"Missions/MissionGranter/Name/{agency}", "NewAgency", NotificationImportance.Low, false);
  558.                 foreach (string kerb in ReUtil.crewAgencies[agency])
  559.                     GameManager.Instance.Game.SessionManager.KerbalRosterManager.CreateKerbalByName(kerb.Split(' ')[0]);
  560.             }));
  561.            
  562.         }
  563.  
  564.         [HarmonyPatch(typeof(UIWidget_viewport_ivaportrait), nameof(UIWidget_viewport_ivaportrait.SetEmpty))]
  565.         [HarmonyPostfix]
  566.         public static void AddPALPortraitName(UIWidget_viewport_ivaportrait __instance) {
  567.             ButtonExtended btn = (ButtonExtended)typeof(UIWidget_viewport_ivaportrait).GetField("buttonEVA", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(__instance);
  568.             if (!btn?.interactable ?? false) __instance.SetKerbalName(LocalizationManager.GetTranslation("Diagnostic/PAL5000"));
  569.         }
  570.  
  571.         [HarmonyPatch(typeof(IndicatorHeatWarning), "Awake")]
  572.         [HarmonyPostfix]
  573.         public static void HideHeatIndicator(IndicatorHeatWarning __instance) {
  574.             typeof(IndicatorHeatWarning).GetField("_startThreshold", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(__instance, 0.999f);
  575.             typeof(IndicatorHeatWarning).GetField("_criticalThreshold", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(__instance, 0.9999f);
  576.         }
  577.  
  578.         [HarmonyPatch(typeof(IndicatorHeatWarning), nameof(IndicatorHeatWarning.OnUpdate))]
  579.         [HarmonyPostfix]
  580.         public static void TogglePALMini(IndicatorHeatWarning __instance) {
  581.             if (!RePlugin.PALMiniUpdate) return;
  582.             PartHeatIndicatorTracker partTracker = (PartHeatIndicatorTracker)typeof(IndicatorHeatWarning).GetField("_partTracker", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(__instance);
  583.             bool isKerbal = partTracker.PartBehavior.SimObjectComponent.PartData.partName == "eva_kerbal";
  584.             typeof(IndicatorHeatWarning).GetField("_startThreshold", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(__instance, RePlugin.PALMiniActive && !isKerbal ? 0.0f : 0.999f);
  585.             //if (!RePlugin.PALMiniActive) return;
  586.             //__instance.StartCoroutine(CallbackUtil.DelayedCallback(1, delegate {
  587.             //    PartHeatIndicatorTracker partTracker = (PartHeatIndicatorTracker)typeof(IndicatorHeatWarning).GetField("_partTracker", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(__instance);
  588.             //    FieldInfo progress = typeof(IndicatorHeatWarning).GetField("_progress", BindingFlags.Instance | BindingFlags.NonPublic);
  589.             //    Image image = (Image)progress?.GetValue(__instance);
  590.             //    if (partTracker != null && image != null) {
  591.             //        float dmgPct = (float)ReUtil.TryGetDamage(partTracker.PartBehavior.SimObjectComponent) / 100f;
  592.             //        image.rectTransform.sizeDelta = new Vector2(image.rectTransform.sizeDelta.x, dmgPct > 0 ? __instance.GetComponent<RectTransform>().sizeDelta.y * dmgPct : 0);
  593.             //        progress.SetValue(__instance, image);
  594.             //    }
  595.             //}));
  596.         }
  597.        
  598.         [HarmonyPatch(typeof(PartHeatIndicatorTracker), "CalculateTemperatureRatio")]
  599.         [HarmonyPostfix]
  600.         public static void SetPalMiniDamage(PartHeatIndicatorTracker __instance) {
  601.             if (!RePlugin.PALMiniActive) return;
  602.             typeof(PartHeatIndicatorTracker).GetProperty("PartTemperatureRatio")?.SetMethod.Invoke(__instance,
  603.                 new object[] { (float)Math.Max(ReUtil.TryGetDamage(__instance.PartBehavior.SimObjectComponent) / 100, double.Epsilon) });
  604.         }
  605.  
  606.         [HarmonyPatch(typeof(VesselComponent), nameof(VesselComponent.SetModelPhysicsMode))]
  607.         [HarmonyPrefix]
  608.         public static void PersistRotationCheck(VesselComponent __instance, ref PhysicsMode physics) {
  609.             if (physics == __instance.Physics) return;
  610.             if (GameManager.Instance.Game.ViewController?.GetActiveVehicle()?.GetSimVessel().Guid != __instance.Guid) return;
  611.             if (GameManager.Instance.Game.UniverseModel.TimeScale <= 1 || physics != PhysicsMode.Orbital) return;
  612.             if (__instance.AngularVelocity.relativeAngularVelocity.magnitude < 0.015 &&
  613.                 (__instance.SimulationObject?.PartOwner?.Parts?.Any(p => p.PartData.family == "0530-Stabilizer") ?? true))
  614.                 return;
  615.             physics = PhysicsMode.RigidBody;
  616.         }
  617.  
  618.     }
  619.  
  620. }
  621.  
  622.  
  623. // ---
  624.  
  625. using I2.Loc;
  626. using KSP.Game;
  627. using KSP;
  628. using KSP.Modules;
  629. using KSP.Sim;
  630. using KSP.Sim.Definitions;
  631. using KSP.Sim.impl;
  632. using KSP.Sim.ResourceSystem;
  633. using Newtonsoft.Json;
  634. using System;
  635. using System.Collections.Generic;
  636. using System.Linq;
  637. using System.Text;
  638. using System.Threading.Tasks;
  639. using UnityEngine;
  640. using System.Data;
  641.  
  642. namespace KSRe {
  643.  
  644.     [Serializable]
  645.     public class Data_Damage : ModuleData {
  646.  
  647.         [KSPState]
  648.         public double damage;
  649.  
  650.         [KSPState]
  651.         public Dictionary<string, double> records;
  652.  
  653.         public override Type ModuleType => typeof(Module_Damage);
  654.  
  655.     }
  656.  
  657.     [DisallowMultipleComponent]
  658.     public class Module_Damage : PartBehaviourModule {
  659.  
  660.         [SerializeField]
  661.         protected Data_Damage dataDamage;
  662.         protected Module_RCS moduleRCS;
  663.         protected Data_Parachute dataChute;
  664.         protected Data_CompoundPart dataCompound;
  665.         // protected Data_ControlSurface dataCtrlSrf;
  666.         private double initRepairDmg;
  667.         private double lastSlowUpdateUT;
  668.         private float updateDeltaT;
  669.         internal double lastSlowUpdateDmg;
  670.         private List<double> fixedGees;
  671.  
  672.         public override Type PartComponentModuleType => typeof(PartComponentModule_Damage);
  673.         public virtual Data_Damage DataDmg => dataDamage;
  674.         public virtual PartComponentModule_Damage CompModuleDmg => ComponentModule as PartComponentModule_Damage;
  675.  
  676.         protected override void AddDataModules() => DataModules.TryAddUnique(dataDamage, out dataDamage);
  677.         protected override void OnInitialize() {
  678.             fixedGees = new List<double>() { 0, 0 };
  679.             if (PartBackingMode != PartBackingModes.Flight) return;
  680.             lastSlowUpdateUT = Game.UniverseModel.UniverseTime;
  681.             updateDeltaT = (float)CompModuleDmg?.Modifier;
  682.         }
  683.         protected override void OnStart() {
  684.             fixedGees = new List<double>() { 0, 0 };
  685.             TryGetComponent(out moduleRCS);
  686.             if (TryGetComponent(out Module_Parachute moduleChute))
  687.                 moduleChute.DataModules.TryGetByType(out dataChute);
  688.             //if (TryGetComponent(out Module_FuelLine moduleFuelLine))
  689.             //    moduleFuelLine.DataModules.TryGetByType(out dataCompound);
  690.             // moduleRCS.DataModules.TryGetByType(out dataRCS);
  691.         }
  692.         protected override void OnModuleUpdate(float deltaTime) {
  693.             updateDeltaT += deltaTime;
  694.             if (updateDeltaT > 1) {
  695.                 OnModuleSlowUpdate(Game.UniverseModel.UniverseTime - lastSlowUpdateUT);
  696.                 lastSlowUpdateUT = Game.UniverseModel.UniverseTime;
  697.                 updateDeltaT = 0;
  698.             }
  699.         }
  700.         protected override void OnModuleFixedUpdate(float fixedDeltaTime) {
  701.             if (part?.IsSimObjectDestroyed() ?? true) return;
  702.             double heatDmgThreshold = part.SimObjectComponent.PartData.maxTemp * 0.6;
  703.             if (part.SimObjectComponent.ThermalData.Temperature > heatDmgThreshold) {
  704.                 bool ablate = false;
  705.                 if (part.SimObjectComponent.PartData.family == "0450-Heat Shield")
  706.                     if (part.SimObjectComponent.PartData.partName == "heatshield_2v_inflatable") ablate = true;
  707.                     else if (part.SimObjectComponent.PartOwner.ContainerGroup.GetStoredResourcesTotalMass() > 0) ablate = true;
  708.                 // double tempDiff = part.SimObjectComponent.ThermalData.Temperature - part.SimObjectComponent.PartData.maxTemp * 0.6;
  709.                 // CompModuleDmg.AddDamage(fixedDeltaTime * tempDiff * 0.015);
  710.                 double dmg = fixedDeltaTime * (Math.Pow(1.047, ((part.SimObjectComponent.ThermalData.Temperature - heatDmgThreshold) / (part.SimObjectComponent.PartData.maxTemp - heatDmgThreshold)) * 75) - 1);
  711.                 if (ablate) CompModuleDmg.AddRecords(dmg, "Flight_Heat");
  712.                 else CompModuleDmg.AddDamage(dmg, "Flight_Heat");
  713.             }
  714.             if (this is Module_DamageEngine) return;
  715.             if (moduleRCS?.isOperational ?? false)
  716.                 CompModuleDmg.AddDamage(fixedDeltaTime * 0.1, "Propulsion_RCS");
  717.             if (part.SimObjectComponent.PartData.family == "0430-Landing Gear" || part.SimObjectComponent.PartData.family == "0440-Wheel")
  718.                 if (vessel.SimObjectComponent.SurfaceVelocity.magnitude > 0.1 && part.vessel.SimObjectComponent.VesselScienceRegionSituation.ResearchLocation?.ScienceSituation == KSP.Game.Science.ScienceSitutation.Landed) {
  719.                     double dmg = fixedDeltaTime * (Math.Pow(1.047, (vessel.SimObjectComponent.SurfaceVelocity.magnitude / (part.SimObjectComponent.PartData.crashTolerance * 2)) * 25) - 1);
  720.                     CompModuleDmg.AddDamage(dmg, part.SimObjectComponent.PartData.family == "0440-Wheel" ? "Electrical_Wheel" : "Aeronautics_LandingGear");
  721.                 }
  722.             if (dataChute != null) {
  723.                 Data_Parachute.DeploymentStates state = dataChute.deployState.GetValue();
  724.                 if (state == Data_Parachute.DeploymentStates.SEMIDEPLOYED || state == Data_Parachute.DeploymentStates.DEPLOYED)
  725.                     CompModuleDmg.AddDamage(fixedDeltaTime * (state == Data_Parachute.DeploymentStates.DEPLOYED ? 0.4 : 0.2), "Aeronautics_Parachute");
  726.             }
  727.             //RePlugin.Logger.LogInfo(vessel.SimObjectComponent.geeForce + "" + part.SimObjectComponent.PartData.partName + " " + part.SimObjectComponent.Guid);
  728.             //fixedGees.Add(Math.Round(vessel.SimObjectComponent.geeForce, vessel.SimObjectComponent.geeForce < 100 ? 2 : 1));
  729.             //if (fixedGees[1] > part.SimObjectComponent.PartData.crashTolerance)
  730.             //    if (fixedGees.Count == fixedGees.Distinct().Count())
  731.             //        CompModuleDmg.AddDamage(fixedDeltaTime * (Math.Pow(1.047, (fixedGees[1] / (part.SimObjectComponent.PartData.crashTolerance * 2)) * 50) - 1), "Flight_GeeForces");
  732.             // CompModuleDmg.AddDamage(fixedDeltaTime * Math.Min(vessel.SimObjectComponent.geeForce - part.SimObjectComponent.PartData.crashTolerance, 400.0) * 0.5);
  733.             //fixedGees.RemoveAt(0);
  734.             if (vessel.SimObjectComponent.geeForce < part.SimObjectComponent.PartData.crashTolerance) return;
  735.             if (vessel.SimObjectComponent.Physics == PhysicsMode.RigidBody && vessel.flightCtrlState.mainThrottle == 0) return;
  736.             CompModuleDmg.AddDamage(fixedDeltaTime * (Math.Pow(1.047, ((part.SimObjectComponent.PartData.crashTolerance - vessel.SimObjectComponent.geeForce) / (part.SimObjectComponent.PartData.crashTolerance * 2)) * 50) - 1), "Flight_GeeForces");
  737.             // if (dataCtrlSrf?.IsCtrlSurfaceActive ?? false)
  738.             // CompModuleDmg.AddDamage(fixedDeltaTime * 0.075, "Aeronautics_ControlSurface");
  739.         }
  740.  
  741.         protected virtual void OnModuleSlowUpdate(double fixedDeltaT) {
  742.             if (lastSlowUpdateDmg != 0 && lastSlowUpdateDmg < CompModuleDmg.FailPoint && CompModuleDmg.GetDamage() > CompModuleDmg.FailPoint)
  743.                 CompModuleDmg.ApplyFault();
  744.             if (ReUtil.IsPALActive(vessel.SimObjectComponent)) {
  745.                 if (ReUtil.IsPALActive(level: 7, bypassVessel: true) && CompModuleDmg.GetDamage(false) > 85 && lastSlowUpdateDmg <= 85)
  746.                     CompModuleDmg.PushNotify("/Heavy");
  747.                 else if (ReUtil.IsPALActive(level: 8, bypassVessel: true) && CompModuleDmg.GetDamage(false) > 35 && lastSlowUpdateDmg <= 35)
  748.                     CompModuleDmg.PushNotify("/Medium");
  749.             }
  750.             lastSlowUpdateDmg = CompModuleDmg.GetDamage(false);
  751.             if (RePlugin.UIPart?.Guid == part.SimObjectComponent.Guid) {
  752.                 if (RePlugin.inspectTicks == -1)
  753.                     RePlugin.inspectTicks = 7; // (int)Math.Round(part.SimObjectComponent.PartData.PartSizeDiameter * 1.5)
  754.                 RePlugin.UIPartDmg = CompModuleDmg.GetDamage();
  755.             }
  756.             if (RePlugin.repairers.Count == 0) return;
  757.             if (!RePlugin.repairers.Any(r => r.TargetGuid == part.SimObjectComponent.Guid)) return;
  758.             double repairMax = 15.01 + 5 * (vessel.SimObjectComponent.TimeSinceLaunch / 7884000d);
  759.             if (initRepairDmg == 0) {
  760.                 if (CompModuleDmg.GetDamage() < repairMax) {
  761.                     RePlugin.repairers.RemoveAll(r => r.TargetGuid == part.SimObjectComponent.Guid);
  762.                     return;
  763.                 }
  764.                 initRepairDmg = CompModuleDmg.GetDamage();
  765.             }
  766.             CompModuleDmg.AddDamage(-0.02 * fixedDeltaT * RePlugin.repairers.Count(r => r.TargetGuid == part.SimObjectComponent.Guid));
  767.             if (CompModuleDmg.GetDamage() < repairMax) {
  768.                 CompModuleDmg.SetDamage(repairMax);
  769.                 CompModuleDmg.isLeaking = false;
  770.                 //if (RePlugin.repairers.Any(r => r.Guid == RePlugin.activeVessel.Guid))
  771.                 //    RePlugin.lockUI = false;
  772.                 RePlugin.repairers.RemoveAll(r => r.TargetGuid == part.SimObjectComponent.Guid);
  773.                 CompModuleDmg.PushNotify("/Repaired");
  774.                 if (ReUtil.IsGameSandbox()) return;
  775.                 float diffScale;
  776.                 if (!Game.SessionManager.TryGetDifficultyOptionState("ScienceRewards", out diffScale)) diffScale = 1f;
  777.                 int sciGain = Math.Max(1, (int)Math.Round(initRepairDmg * 0.025 * (ReUtil.GetSciBonus("Engineering") + 1) * diffScale));
  778.                 ReUtil.AddScience(sciGain);
  779.                 RePlugin.AddRocketScience("Engineering_Repairs", sciGain);
  780.                 initRepairDmg = 0;
  781.             } else {
  782.                 if (vessel.SimObjectComponent.SimulationObject.Telemetry.CommNetConnectionStatus != ConnectionNodeStatus.Connected)
  783.                     RePlugin.repairers.RemoveAll(r => r.Guid == vessel.SimObjectComponent.Guid);
  784.                 foreach (VesselComponent vessel in Game?.ViewController?.VesselsInRange.Where(v => RePlugin.repairers.Any(r => r.Guid == v.Guid)))
  785.                     if (Position.Distance(vessel.transform.Position, part.SimObjectComponent.transform.Position) > part.SimObjectComponent.PartData.PartSizeDiameter + 1.5)
  786.                         RePlugin.repairers.RemoveAll(r => r.Guid == vessel.Guid);
  787.             }
  788.         }
  789.  
  790.     }
  791.  
  792.     public class PartComponentModule_Damage : PartComponentModule {
  793.  
  794.         private Data_Damage dataDamage;
  795.         private double lastDailyUpdateUT;
  796.         // private double lastSlowUpdateUT;
  797.         internal double Modifier { get; private set; }
  798.         internal double FailPoint { get; private set; }
  799.         internal bool isLeaking;
  800.  
  801.         public override Type PartBehaviourModuleType => typeof(Module_Damage);
  802.         public virtual Data_Damage DataDmg => dataDamage;
  803.  
  804.         public override void OnStart(double universalTime) {
  805.             DataModules.TryGetByType(out dataDamage);
  806.             UnityEngine.Random.InitState(Part.Guid.GetHashCode());
  807.             Modifier = UnityEngine.Random.value;
  808.             lastDailyUpdateUT = universalTime - 21600 * Modifier;
  809.             // lastSlowUpdateUT = universalTime - Modifier * 2;
  810.             FailPoint = ReUtil.mfParts.Values.Any(p => p.Contains(Part.PartData.partName)) && !ReUtil.IsGameSandbox() ?
  811.                 (RePlugin.TryGetPartRecord(Part.PartData.partName, true) - 0.2 + Modifier) * 100 : Modifier * 125;
  812.             if (DataDmg.records == null)
  813.                 DataDmg.records = new Dictionary<string, double> { ["Engineering_Age"] = 0 };
  814.             CheckFailPoint();
  815.         }
  816.  
  817.         public override void OnUpdate(double universalTime, double deltaUniversalTime) {
  818.             if (Part?.IsDestroyedOrBeingDestroyed ?? true) return;
  819.             if (GetDamage() > 100) {
  820.                 // if (RePlugin.destroyableParts?.Value ?? false) {
  821.                 if ((Part.ParentPart?.TryGetModule(out PartComponentModule_DamageEngine moduleDmgEng) ?? false) && moduleDmgEng != null)
  822.                     moduleDmgEng.AddDamage(Part.ExplosionPotential * UnityEngine.Random.Range(15, 35), "Flight_Collateral");
  823.                 else if ((Part.ParentPart?.TryGetModule(out PartComponentModule_Damage moduleDmg) ?? false) && moduleDmg != null)
  824.                     moduleDmg.AddDamage(Part.ExplosionPotential * UnityEngine.Random.Range(15, 35), "Flight_Collateral");
  825.                 Part.DestroyPart(ExplosionType.Default);
  826.                 // } else SetDamage(85.01);
  827.                 return;
  828.             } else if (GetDamage() < 85 && universalTime > (lastDailyUpdateUT + 21600)) {
  829.                 OnDailyUpdate(universalTime - lastDailyUpdateUT);
  830.                 lastDailyUpdateUT = universalTime;
  831.             }
  832.             //if (universalTime > (lastDailyUpdateUT + 2)) {
  833.             //    OnSlowUpdate(universalTime - lastSlowUpdateUT);
  834.             //    lastSlowUpdateUT = universalTime;
  835.             //}
  836.             // if (!Part.SimulationObject.Telemetry.IsInAtmosphere) return;
  837.         }
  838.  
  839.         protected virtual void OnSlowUpdate(double fixedDeltaT) {
  840.        
  841.         }
  842.         protected virtual void OnDailyUpdate(double deltaT) {
  843.             AddDamage(deltaT / 1080000d, "Engineering_Age");
  844.             CheckFailPoint();
  845.             bool isBoiling = "0040-Methalox|0050-Methane|0060-Monopropellant|0065-Nose Cone".Contains(Part.PartData.family);
  846.             if (isBoiling) isBoiling = Part.PartResourceContainer.GetResourceStoredMass(RePlugin.fuelList.Last()) > 0;
  847.             if (isLeaking || isBoiling)
  848.                 DrainResource(deltaT / 21600);
  849.         }
  850.         internal void AddDamage(double damage, string dmgType = null) {
  851.             DataDmg.damage += damage;
  852.             if (dmgType != null) AddRecords(Math.Abs(damage), dmgType);
  853.         }
  854.         internal void SetDamage(double damage) => DataDmg.damage = damage;
  855.         internal double GetDamage(bool mod = true) => DataDmg.damage * (mod ? 0.75 + Modifier * 0.5 : 1);
  856.  
  857.         internal void AddRecords(double amount, string type) {
  858.             if (DataDmg.records.ContainsKey(type))
  859.                 DataDmg.records[type] += amount;
  860.             else DataDmg.records[type] = amount;
  861.         }
  862.  
  863.         private void CheckFailPoint() {
  864.             if (RePlugin.repairers.Any(r => r.TargetGuid == Part.Guid)) return;
  865.             // double failPoint = Modifier * 125;
  866.             if (ReUtil.IsPALActive(Part.SimulationObject.Vessel, 9) && Math.Abs(FailPoint - GetDamage() - 1) < 0.07) {
  867.                 SetDamage(FailPoint - 0.9);
  868.                 Game.UniverseModel.SetTimePaused(true);
  869.                 PushNotify("/FaultDetected");
  870.             } else if (Math.Abs(FailPoint - GetDamage()) < 0.07 || (GetDamage() < 1 && FailPoint < 1)) {
  871.                 ApplyFault();
  872.                 if (GetDamage() > 100) return;
  873.                 if (RePlugin.fuelList.All(r => !Part.PartResourceContainer.IsResourceContained(r))) return;
  874.                 isLeaking = true;
  875.             }
  876.         }
  877.         internal void ApplyFault() {
  878.             UnityEngine.Random.InitState(Part.Guid.GetHashCode() + Game.SessionGuidString.GetHashCode());
  879.             AddDamage(15 + UnityEngine.Random.value * 100 * (1 - RePlugin.TryGetPartRecord(Part.PartData.partName, true) * 0.5), "Engineering_Faults");
  880.         }
  881.  
  882.         private void DrainResource(double deltaT) {
  883.             if (Part.PartResourceContainer.GetStoredResourcesTotalMass() < 0.01) return;
  884.             ResourceDefinitionID resID = RePlugin.fuelList.FirstOrDefault(r => Part.PartResourceContainer.GetResourceStoredUnits(r) > 0);
  885.             double resUnits = Part.PartOwner.ContainerGroup.GetResourceStoredUnits(resID);
  886.             double unitsToRemove = resUnits * (isLeaking ? 0.0005 * GetDamage() : Math.Max(0, Part.ThermalData.Temperature - 50) / 30000) * deltaT;
  887.             Part.PartOwner.ContainerGroup.RemoveResourceUnits(resID, unitsToRemove);
  888.             if (!isLeaking) AddRecords((unitsToRemove / resUnits) * 100, "Flight_Boiloff");
  889.         }
  890.  
  891.         internal void PushNotify(string locKey) {
  892.             NotificationData notify = new NotificationData { Tier = NotificationTier.Alert, TimeStamp = Game.UniverseModel.UniverseTime,
  893.                 Importance = "/Heavy|Engine/IgnitionFailed".Contains(locKey) ? NotificationImportance.High :
  894.                     "/Medium|/FaultDetected".Contains(locKey) ? NotificationImportance.Medium : NotificationImportance.Low
  895.             };
  896.             notify.AlertTitle.LocKey = "PartModules/Damage" + locKey;
  897.             if (locKey == "/FaultDetected") notify.AlertTitle.ObjectParams = new object[1] { Part?.PartOwner?.SimulationObject?.Vessel?.DisplayName };
  898.             bool showName = "/Repaired|Engine/IgnitionFailed".Contains(locKey);
  899.             notify.FirstLine.LocKey = showName ? $"Parts/Title/{Part?.DisplayName}" : "PartModules/Damage/Serial";
  900.             if (!showName) notify.FirstLine.ObjectParams = new object[1] { Part?.Guid.Substring(24) };
  901.             Game.Notifications.ProcessNotification(notify);
  902.         }
  903.  
  904.     }
  905.  
  906.  
  907.     [Serializable]
  908.     public class Data_DamageEngine : Data_Damage {
  909.  
  910.         //[LocalizedField("PartModules/DamageEngine/UnlockThrottle")]
  911.         //[PAMDisplayControl(SortIndex = 4)]
  912.         //[KSPState(CopyToSymmetrySet = true)]
  913.         //[HideInInspector]
  914.         //public ModuleProperty<bool> toggleUnlockThrot = new ModuleProperty<bool>(false);
  915.  
  916.         public override Type ModuleType => typeof(Module_DamageEngine);
  917.  
  918.     }
  919.  
  920.     [DisallowMultipleComponent]
  921.     public class Module_DamageEngine : Module_Damage {
  922.  
  923.         [SerializeField]
  924.         protected Data_DamageEngine dataDamageEngine;
  925.         protected Module_Engine moduleEngine;
  926.         protected Data_Engine dataEngine;
  927.         protected ModuleProperty<bool> toggleOverThrot, toggleUnlockThrot;
  928.         protected float maxThrust;
  929.         protected float minThrust;
  930.         private bool isIgnited;
  931.         private bool reduceDmg;
  932.         private int fuelStarvedTicks;
  933.         private string engTypeKey;
  934.  
  935.         public override Type PartComponentModuleType => typeof(PartComponentModule_DamageEngine);
  936.         public override Data_Damage DataDmg => dataDamageEngine;
  937.         public virtual Data_DamageEngine DataDmgEng => dataDamageEngine;
  938.         public override PartComponentModule_Damage CompModuleDmg => ComponentModule as PartComponentModule_Damage;
  939.  
  940.         protected override void AddDataModules() => DataModules.TryAddUnique(dataDamageEngine, out dataDamageEngine);
  941.         protected override void OnInitialize() {
  942.             base.OnInitialize();
  943.             toggleUnlockThrot = new ModuleProperty<bool>(false);
  944.             string unlockThrotLabel = "PartModules/DamageEngine/UnlockThrottle";
  945.             toggleUnlockThrot.ContextKey = unlockThrotLabel;
  946.             DataDmgEng.AddProperty(unlockThrotLabel, toggleUnlockThrot);
  947.             DataDmgEng.SetSortIndex(toggleUnlockThrot, 4);
  948.             toggleUnlockThrot.OnChangedValue += OnToggleUnlockThrot;
  949.             // DataDmgEng.toggleUnlockThrot.OnChangedValue += OnToggleUnlockThrot;
  950.             toggleOverThrot = new ModuleProperty<bool>(false);
  951.             string overThrotLabel = "PartModules/DamageEngine/Overthrottle";
  952.             toggleOverThrot.ContextKey = overThrotLabel;
  953.             DataDmgEng.AddProperty(overThrotLabel, toggleOverThrot);
  954.             DataDmgEng.SetSortIndex(toggleOverThrot, 4);
  955.             toggleOverThrot.OnChangedValue += OnToggleOverThrot;
  956.             ResetEngSetup();
  957.         }
  958.         protected override void OnStart() {
  959.             base.OnStart();
  960.             TryGetComponent(out moduleEngine);
  961.             moduleEngine.DataModules.TryGetByType(out dataEngine);
  962.             if (PartBackingMode == PartBackingModes.Flight)
  963.                 engTypeKey = $"{(part?.SimObjectComponent?.PartData?.family == "0120-Jet Engine" ? "Aeronautics" : "Propulsion")}_{part?.SimObjectComponent?.PartData?.family?.Substring(5).Replace(" ", "")}";
  964.             ResetEngSetup();
  965.         }
  966.         protected override void OnShutdown() {
  967.             if (toggleUnlockThrot != null)
  968.                 toggleUnlockThrot.OnChangedValue -= OnToggleUnlockThrot;
  969.             //if (DataDmgEng?.toggleUnlockThrot != null)
  970.             //    DataDmgEng.toggleUnlockThrot.OnChangedValue -= OnToggleUnlockThrot;
  971.             if (toggleOverThrot != null)
  972.                 toggleOverThrot.OnChangedValue -= OnToggleOverThrot;
  973.  
  974.         }
  975.         protected override void OnModuleFixedUpdate(float fixedDeltaTime) {
  976.             // RePlugin.Logger.LogInfo(dataEngine.FinalThrustValue);
  977.             if (dataEngine?.CurrentPropellantState?.pendingRequest ?? false) {
  978.                 fuelStarvedTicks = 0;
  979.                 if (!isIgnited) OnIgnition();
  980.                 if (toggleOverThrot.GetValue())
  981.                     CompModuleDmg.AddDamage(fixedDeltaTime * 3, engTypeKey);
  982.                 if (toggleUnlockThrot.GetValue() && !reduceDmg)
  983.                     if (moduleEngine.throttleSetting < MinSafeThrotPct)
  984.                         CompModuleDmg.AddDamage(fixedDeltaTime * 20 * (MinSafeThrotPct - moduleEngine.throttleSetting), engTypeKey);
  985.                 CompModuleDmg.AddDamage(fixedDeltaTime * (CompModuleDmg.GetDamage() > 85 ? 0.7 : 0.1) * (reduceDmg ? 0.25 : 1), engTypeKey);
  986.             } else if (fuelStarvedTicks < 7)
  987.                 fuelStarvedTicks++;
  988.             else isIgnited = false;
  989.             base.OnModuleFixedUpdate(fixedDeltaTime);
  990.         }
  991.  
  992.         public float MinSafeThrotPct => minThrust / maxThrust;
  993.  
  994.         protected override void OnModuleSlowUpdate(double deltaT) {
  995.             if (lastSlowUpdateDmg != 0 && lastSlowUpdateDmg < CompModuleDmg.FailPoint && CompModuleDmg.GetDamage() > CompModuleDmg.FailPoint)
  996.                 CompModuleDmg.ApplyFault();
  997.             base.OnModuleSlowUpdate(deltaT);
  998.             if (dataEngine?.engineModes.Count() > 1)
  999.                 ResetEngSetup();
  1000.             if (RePlugin.UIPart?.Guid == part.SimObjectComponent.Guid)
  1001.                 RePlugin.UIEngInstability = toggleOverThrot.GetValue() ? 0.15 : toggleUnlockThrot.GetValue() ? Mathf.Clamp01(MinSafeThrotPct - moduleEngine.throttleSetting) : 0.0;
  1002.         }
  1003.        
  1004.         private void OnIgnition() {
  1005.             isIgnited = true;
  1006.             bool isSRB = moduleEngine.currentEngineModeData.engineType == EngineType.SolidBooster;
  1007.             if (reduceDmg && !isSRB) return;
  1008.             double gForce = vessel.SimObjectComponent.geeForce;
  1009.             int rand = UnityEngine.Random.Range(0, moduleEngine?.currentEngineModeData?.propellant?.mixtureName == "Hydrolox" ? 38 : 37);
  1010.             if (!isSRB && rand > Math.Min(gForce * 1000, 35) - CompModuleDmg.GetDamage() * 0.3) {
  1011.                 moduleEngine.DeactivateEngine();
  1012.                 CompModuleDmg.PushNotify("Engine/IgnitionFailed");
  1013.             }
  1014.             CompModuleDmg.AddDamage(isSRB ? 20 + rand * 0.5 : Math.Max(10.01, rand * 15 * (0.1 - Math.Min(gForce, 0.035))), engTypeKey);
  1015.             if (gForce < 0.035) CompModuleDmg.AddRecords((0.035 - gForce) * 100, "Flight_Ullage");
  1016.         }
  1017.        
  1018.         protected void OnToggleOverThrot(bool isToggleOn) {
  1019.             if (isToggleOn) {
  1020.                 if (toggleUnlockThrot.GetValue()) {
  1021.                     OnToggleUnlockThrot(false);
  1022.                     toggleUnlockThrot.SetValue(false);
  1023.                 } else TryStoreThrust();
  1024.                 moduleEngine.currentEngineModeData.maxThrust *= 1.1f;
  1025.                 moduleEngine.currentEngineModeData.minThrust = moduleEngine.currentEngineModeData.maxThrust;
  1026.             } else RestoreDefaultThrust();
  1027.             dataEngine.SetConsumptionRate();
  1028.         }
  1029.         protected void OnToggleUnlockThrot(bool isToggleOn) {
  1030.             if (isToggleOn) {
  1031.                 if (toggleOverThrot?.GetValue() ?? false) {
  1032.                     OnToggleOverThrot(false);
  1033.                     toggleOverThrot?.SetValue(false);
  1034.                 } else TryStoreThrust();
  1035.                 moduleEngine.currentEngineModeData.minThrust = 0;
  1036.             } else RestoreDefaultThrust();
  1037.             dataEngine.SetConsumptionRate();
  1038.         }
  1039.  
  1040.         protected void ResetEngSetup() {
  1041.             reduceDmg = !"Methalox|Hydrolox|Hydrogen|SolidFuel".Contains(moduleEngine?.currentEngineModeData?.propellant?.mixtureName ?? "-");
  1042.             DataDmgEng?.SetVisible(toggleUnlockThrot, !"MonoPropellant|SolidFuel|MethaneAir|HydrogenAir".Contains(moduleEngine?.currentEngineModeData?.propellant?.mixtureName ?? "-"));
  1043.             DataDmgEng?.SetVisible(toggleOverThrot, moduleEngine?.currentEngineModeData?.propellant?.mixtureName == "Methalox");
  1044.         }
  1045.         private void TryStoreThrust() {
  1046.             maxThrust = moduleEngine.currentEngineModeData.maxThrust;
  1047.             if (moduleEngine.currentEngineModeData.minThrust > 0)
  1048.                 minThrust = moduleEngine.currentEngineModeData.minThrust;
  1049.         }
  1050.         private void RestoreDefaultThrust() {
  1051.             moduleEngine.currentEngineModeData.maxThrust = maxThrust;
  1052.             moduleEngine.currentEngineModeData.minThrust = minThrust;
  1053.         }
  1054.  
  1055.     }
  1056.  
  1057.     public class PartComponentModule_DamageEngine : PartComponentModule_Damage {
  1058.  
  1059.         private Data_DamageEngine dataDamageEngine;
  1060.  
  1061.         public override Type PartBehaviourModuleType => typeof(Module_DamageEngine);
  1062.         public override Data_Damage DataDmg => dataDamageEngine;
  1063.  
  1064.         public override void OnStart(double universalTime) {
  1065.             DataModules.TryGetByType(out dataDamageEngine);
  1066.             base.OnStart(universalTime);
  1067.         }
  1068.  
  1069.     }
  1070.  
  1071.  
  1072.     [Serializable]
  1073.     public class Data_DamageEVA : ModuleData {
  1074.  
  1075.         public override Type ModuleType => typeof(Module_DamageEVA);
  1076.  
  1077.     }
  1078.  
  1079.     [DisallowMultipleComponent]
  1080.     public class Module_DamageEVA : PartBehaviourModule {
  1081.  
  1082.         [SerializeField]
  1083.         protected Data_DamageEVA dataDamageEVA;
  1084.         private double lastSlowUpdateUT;
  1085.         private float updateDeltaT;
  1086.  
  1087.         public override Type PartComponentModuleType => typeof(PartComponentModule_DamageEVA);
  1088.         public virtual PartComponentModule_DamageEVA CompModuleDmg => ComponentModule as PartComponentModule_DamageEVA;
  1089.  
  1090.         protected override void AddDataModules() => DataModules.TryAddUnique(dataDamageEVA, out dataDamageEVA);
  1091.         protected override void OnInitialize() {
  1092.             lastSlowUpdateUT = Game.UniverseModel.UniverseTime;
  1093.             updateDeltaT = (float)CompModuleDmg?.Modifier;
  1094.            
  1095.         }
  1096.         protected override void OnStart() {
  1097.            
  1098.         }
  1099.         protected override void OnModuleUpdate(float deltaTime) {
  1100.             updateDeltaT += deltaTime;
  1101.             if (updateDeltaT > 1) {
  1102.                 OnModuleSlowUpdate(Game.UniverseModel.UniverseTime - lastSlowUpdateUT);
  1103.                 lastSlowUpdateUT = Game.UniverseModel.UniverseTime;
  1104.                 updateDeltaT = 0;
  1105.             }
  1106.         }
  1107.         protected override void OnModuleFixedUpdate(float fixedDeltaTime) {
  1108.             if (part?.IsSimObjectDestroyed() ?? true) return;
  1109.             //double heatDmgThreshold = part.SimObjectComponent.PartData.maxTemp * 0.6;
  1110.             //if (part.SimObjectComponent.ThermalData.Temperature > heatDmgThreshold) {
  1111.             //    double dmg = fixedDeltaTime * (Math.Pow(1.047, ((part.SimObjectComponent.ThermalData.Temperature - heatDmgThreshold) / (part.SimObjectComponent.PartData.maxTemp - heatDmgThreshold)) * 75) - 1);
  1112.             //    CompModuleDmg.AddDamage(dmg, "Wounded_Heat");
  1113.             //}
  1114.         }
  1115.  
  1116.         protected virtual void OnModuleSlowUpdate(double fixedDeltaT) {
  1117.             if (RePlugin.UIPart?.Guid == part.SimObjectComponent.Guid) {
  1118.                 if (RePlugin.inspectTicks == -1)
  1119.                     RePlugin.inspectTicks = 7;
  1120.                 // RePlugin.UIPartDmg = RePlugin.saveData.kerbals.FirstOrDefault(k => k.Name == part.SimObjectComponent.PartOwner.SimulationObject.Vessel.DisplayName)?.damage.Values.Sum() ?? 0;
  1121.             }
  1122.            
  1123.         }
  1124.  
  1125.     }
  1126.  
  1127.     public class PartComponentModule_DamageEVA : PartComponentModule {
  1128.  
  1129.         private Data_DamageEVA dataDamageEVA;
  1130.         // private KerbalDamage kerbalDamage;
  1131.         internal double Modifier { get; private set; }
  1132.         // internal double FailPoint { get; private set; }
  1133.         private ResourceDefinitionID ecResID;
  1134.  
  1135.         public override Type PartBehaviourModuleType => typeof(Module_DamageEVA);
  1136.  
  1137.         public override void OnStart(double universalTime) {
  1138.             DataModules.TryGetByType(out dataDamageEVA);
  1139.             UnityEngine.Random.InitState(Part.Guid.GetHashCode());
  1140.             Modifier = UnityEngine.Random.value;
  1141.             // FailPoint = Modifier * 125;
  1142.             // kerbalLife = ReUtil.GetKerbalLife(Game.SessionManager.KerbalRosterManager.GetAllKerbalsInSimObject(Part.GlobalId).First().NameKey);
  1143.             ecResID = Game.ResourceDefinitionDatabase.GetAllResourceIDs().FirstOrDefault(r => Game.ResourceDefinitionDatabase.GetDefinitionData(r).name == "ElectricCharge");
  1144.         }
  1145.  
  1146.         public override void OnUpdate(double universalTime, double deltaUniversalTime) {
  1147.             if (Part?.IsDestroyedOrBeingDestroyed ?? true) return;
  1148.             if (!RePlugin.PALMiniActive) return;
  1149.             if (Game.ViewController?.GetActiveVehicle()?.GetSimVessel()?.Guid == null || Part?.PartOwner?.Guid == null) return;
  1150.             if (Game.ViewController.GetActiveVehicle().GetSimVessel().Guid != Part.PartOwner.Guid) return;
  1151.             if (Part.PartOwner.ContainerGroup.RemoveResourceUnits(ecResID, deltaUniversalTime * 0.2) < deltaUniversalTime * 0.19)
  1152.                 RePlugin.UpdatePALMini(false);
  1153.         }
  1154.  
  1155.         protected virtual void OnSlowUpdate(double fixedDeltaT) {
  1156.            
  1157.            
  1158.         }
  1159.  
  1160.     }
  1161.  
  1162.  
  1163.  
  1164. }
  1165.  
  1166. // ---
  1167.  
  1168. using BepInEx;
  1169. using BepInEx.Configuration;
  1170. using BepInEx.Logging;
  1171. using HarmonyLib;
  1172. using I2.Loc;
  1173. using KSP;
  1174. using KSP.Game;
  1175. using KSP.Game.Science;
  1176. using KSP.Messages;
  1177. using KSP.OAB;
  1178. using KSP.Sim;
  1179. using KSP.Sim.impl;
  1180. using KSP.Sim.ResourceSystem;
  1181. using Newtonsoft.Json;
  1182. using SpaceWarp;
  1183. using SpaceWarp.API.Assets;
  1184. using SpaceWarp.API.Mods;
  1185. using SpaceWarp.API.Game.Messages;
  1186. using SpaceWarp.API.SaveGameManager;
  1187. using SpaceWarp.API.UI;
  1188. using SpaceWarp.API.UI.Appbar;
  1189. using System;
  1190. using System.Collections.Generic;
  1191. using System.Linq;
  1192. using System.Reflection;
  1193. using System.Text;
  1194. using System.Threading.Tasks;
  1195. using UnityEngine;
  1196. using TMPro;
  1197. using System.Data;
  1198. using System.IO;
  1199. using KSP.Modules;
  1200. using KSP.UI;
  1201. using SpaceWarp.Modules;
  1202.  
  1203. namespace KSRe {
  1204.  
  1205.     [BepInPlugin(ID, ID, VERSION)]
  1206.     [BepInDependency(SpaceWarpPlugin.ModGuid, SpaceWarpPlugin.ModVer)]
  1207.     public class RePlugin : BaseSpaceWarpPlugin {
  1208.  
  1209.         public const string ID = "KSRe";
  1210.         public const string VERSION = "0.26.2";
  1211.         public static RePlugin Instance { get; private set; }
  1212.        
  1213.         internal static new ManualLogSource Logger;
  1214.  
  1215.         // public ConfigEntry<bool> techDiscovery;
  1216.         public static ConfigEntry<bool> fuelRatiosByVol;
  1217.         // public static ConfigEntry<bool> destroyableParts;
  1218.         public static ConfigEntry<KeyCode> repairKeyCode;
  1219.         public static ConfigEntry<KeyCode> appKeyCode;
  1220.  
  1221.         public static List<KerbalDamage> Kerbals { get; private set; }
  1222.         public static List<ReAgency> Agencies { get; private set; }
  1223.        
  1224.         private ResourceContainerChangedMessage lastResMsg;
  1225.         private ushort fuelContIDVal;
  1226.         internal static List<ResourceDefinitionID> fuelList;
  1227.         internal static ReSaveData saveData;
  1228.         private GUIStyle UIStyle, scrollStyle, barStyle, btnStyle, btnXStyle, btnLStyle, btnRStyle, btnKStyle, btnMStyle, btnFStyle;
  1229.         private Vector2 UIScrollPos;
  1230.         private Color UIBgColor;
  1231.         private Texture2D UIAppTex;
  1232.         private Rect UIRect;
  1233.         private bool inGame, showUI, minUI, diagMode, showApp, lockUI;
  1234.         private float slowUpdateDeltaT;
  1235.         private double lastSlowUpdateUT;
  1236.         private GUIData guiData;
  1237.         internal static bool PALMiniActive, PALMiniUpdate;
  1238.         internal static int inspectTicks;
  1239.         internal static double UIPartDmg, UIEngInstability;
  1240.         internal static Tuple<float, float> UIPartImpacts;
  1241.         private string prevCelTransNames;
  1242.         private VesselComponent activeVessel;
  1243.         internal static PartComponent UIPart;
  1244.         internal static List<Repairer> repairers;
  1245.  
  1246.         public void Awake() {
  1247.             Harmony.CreateAndPatchAll(typeof(RePlugin).Assembly);
  1248.             Logger = base.Logger;
  1249.             // techDiscovery = Config.Bind("Campaign", "Tech Discovery", true, "Whether technologies are discovered gradually in the tree");
  1250.             fuelRatiosByVol = Config.Bind("General", "Show Fuel Ratios by Volume", true, "Whether to display engine fuel ratios by volume (instead of mass)");
  1251.             // destroyableParts = Config.Bind("General", "Ruined Parts are Destroyable", true, "Whether ruined parts may be destroyed from further damage");
  1252.             repairKeyCode = Config.Bind("General", "Repair Key", KeyCode.G, "Key to begin EVA repairs on a damaged part");
  1253.             appKeyCode = Config.Bind("General", "Kerbal Life Key", KeyCode.G, "Key to toggle the Kerbal Life App");
  1254.             // techDiscovery.SettingChanged += delegate { if (Game?.GlobalGameState.GetGameState().GameState == GameState.ResearchAndDevelopment) UpdateTechDiscovery(); };
  1255.             StateChanges.ResearchAndDevelopmentEntered += EnteredRnD;
  1256.             UIBgColor = new Color(0, 0, 0, 1);
  1257.             UIAppTex = new Texture2D(1, 1, TextureFormat.RGBAFloat, false);
  1258.             UIAppTex.SetPixel(0, 0, new Color(1, 1, 1, 0.95f));
  1259.             UIAppTex.Apply();
  1260.             UIRect = new Rect((Screen.width * 0.71f) - (360 / 2), Screen.height * 0.25f, 0, 0); // - 92
  1261.             repairers = new List<Repairer>();
  1262.             diagMode = true;
  1263.             prevCelTransNames = "-";
  1264.             UIPartImpacts = Tuple.Create(0f, 0f);
  1265.             string dataPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "data");
  1266.             StartCoroutine(CallbackUtil.DelayedCallback(1, delegate {
  1267.                 ReUtil.dmgSciRates = JsonConvert.DeserializeObject<Dictionary<string, float>>(File.ReadAllText(Path.Combine(dataPath, "dmg_sci_rates.json"))); }));
  1268.             StartCoroutine(CallbackUtil.DelayedCallback(2, delegate {
  1269.                 ReUtil.careerSpecTechs = JsonConvert.DeserializeObject<Dictionary<string, string[]>>(File.ReadAllText(Path.Combine(dataPath, "sci_types_tech_prereqs.json"))); }));
  1270.             StartCoroutine(CallbackUtil.DelayedCallback(3, delegate {
  1271.                 ReUtil.sciLvls = JsonConvert.DeserializeObject<Dictionary<int, float>>(File.ReadAllText(Path.Combine(dataPath, "sci_lvls_buffs.json"))); }));
  1272.             StartCoroutine(CallbackUtil.DelayedCallback(4, delegate {
  1273.                 ReUtil.mfParts = JsonConvert.DeserializeObject<Dictionary<string, string[]>>(File.ReadAllText(Path.Combine(dataPath, "part_mfr_agencies.json"))); }));
  1274.             StartCoroutine(CallbackUtil.DelayedCallback(5, delegate {
  1275.                 ReUtil.partKerbStats = JsonConvert.DeserializeObject<Dictionary<string, string[]>>(File.ReadAllText(Path.Combine(dataPath, "part_kerb_statuses.json"))); }));
  1276.             StartCoroutine(CallbackUtil.DelayedCallback(6, delegate {
  1277.                 ReUtil.crewAgencies = JsonConvert.DeserializeObject<Dictionary<string, string[]>>(File.ReadAllText(Path.Combine(dataPath, "crew_agencies.json")));
  1278.             }));
  1279.         }
  1280.  
  1281.         public override void OnInitialized() {
  1282.             Instance = this;
  1283.             Game.Messages.PersistentSubscribe<GameStateChangedMessage>(ChangedGameState);
  1284.             Game.Messages.PersistentSubscribe<UISliderReleasedMessage>(ReleasedUISlider);
  1285.             Game.Messages.PersistentSubscribe<ResourceContainerChangedMessage>(ChangedResCont);
  1286.             // game.Messages.PersistentSubscribe<PartPlacedMessage>(PlacedPart);
  1287.             Game.Messages.PersistentSubscribe<PartSeparatedMessage>(SeparatedPart);
  1288.             Game.Messages.PersistentSubscribe<PartBehaviourInitializedMessage>(PartBehaviourInitialized);
  1289.             // Game.Messages.PersistentSubscribe<VABSymmetryModeChangedMessage>(ChangedSymmetryMode);
  1290.             Game.Messages.PersistentSubscribe<VesselChangedMessage>(ChangedVessel);
  1291.             Game.Messages.PersistentSubscribe<PartUnderMouseChanged>(ChangedMousePart);
  1292.             Game.Messages.PersistentSubscribe<VesselCreatedMessage>(CreatedVessel);
  1293.             Game.Messages.PersistentSubscribe<SOIEnteredMessage>(EnteredSOI);
  1294.             Game.Messages.PersistentSubscribe<ResearchReportScoredMessage>(ScoredResearchReport);
  1295.             Game.Messages.PersistentSubscribe<VesselRecoveredMessage>(RecoveredVessel);
  1296.             // Game.Messages.PersistentSubscribe<MissionCompleteMessage>(CompletedMission);
  1297.             Game.Messages.PersistentSubscribe<SciencePointCapacityUpdateMessage>(UpdatedSciencePoints);
  1298.             Game.Messages.PersistentSubscribe<KerbalAddedToRoster>(AddedNewKerbal);
  1299.             Game.Messages.PersistentSubscribe<KerbalRemovedFromRoster>(RemovedKerbal);
  1300.             Game.Messages.PersistentSubscribe<KerbalLocationChanged>(RelocatedKerbal);
  1301.             Appbar.RegisterAppButton(LocalizationManager.GetTranslation("Diagnostic/Title"), "BTN-ReDiagnostic", AssetManager.GetAsset<Texture2D>($"KSRe/images/icon2_w.png"), ToggleGUI);
  1302.             //Appbar.RegisterKSCAppButton(LocalizationManager.GetTranslation("Diagnostic/Title"), "BTN-ReDiagnostic", AssetManager.GetAsset<Texture2D>($"KSRe/images/icon2.png"), ToggleGUI);
  1303.             //Appbar.RegisterOABAppButton(LocalizationManager.GetTranslation("Diagnostic/Title"), "BTN-ReDiagnostic", AssetManager.GetAsset<Texture2D>($"KSRe/images/icon2_w.png"), ToggleGUI);
  1304.             Appbar.RegisterAppButton(LocalizationManager.GetTranslation("KerbalLife/Title"), "BTN-KerbalLife", AssetManager.GetAsset<Texture2D>($"KSRe/images/icon_w.png"), ToggleApp);
  1305.             Appbar.RegisterKSCAppButton(LocalizationManager.GetTranslation("KerbalLife/Title"), "BTN-KerbalLife", AssetManager.GetAsset<Texture2D>($"KSRe/images/icon.png"), ToggleApp);
  1306.             Appbar.RegisterOABAppButton(LocalizationManager.GetTranslation("KerbalLife/Title"), "BTN-KerbalLife", AssetManager.GetAsset<Texture2D>($"KSRe/images/icon_w.png"), ToggleApp);
  1307.             UIStyle = new GUIStyle(Skins.ConsoleSkin.window) { padding = new RectOffset(8, 8, 8, 8), contentOffset = new Vector2(0, -5), fixedWidth = 360 };
  1308.             UIStyle.normal.background = UIAppTex;
  1309.             // barStyle = new GUIStyle(Skins.ConsoleSkin.box) { padding = new RectOffset(4,4,10,0) };
  1310.             scrollStyle = new GUIStyle(Skins.ConsoleSkin.scrollView);
  1311.             scrollStyle.normal.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/btnEmpty.png");
  1312.             btnStyle = new GUIStyle(Skins.ConsoleSkin.button);
  1313.             btnXStyle = new GUIStyle(Skins.ConsoleSkin.button);
  1314.             btnLStyle = new GUIStyle(Skins.ConsoleSkin.button);
  1315.             btnRStyle = new GUIStyle(Skins.ConsoleSkin.button);
  1316.             btnKStyle = new GUIStyle(Skins.ConsoleSkin.button);
  1317.             btnMStyle = new GUIStyle(Skins.ConsoleSkin.button);
  1318.             btnFStyle = new GUIStyle(Skins.ConsoleSkin.button);
  1319.             btnStyle.normal.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/btnNorm.png");
  1320.             btnStyle.hover.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/btnHover.png");
  1321.             btnStyle.active.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/btnActive.png");
  1322.             btnStyle.onNormal.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/btnActive.png");
  1323.             btnStyle.onActive.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/btnActive.png");
  1324.             btnStyle.onHover.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/btnHover.png");
  1325.             btnXStyle.normal.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/btnNorm.png");
  1326.             btnXStyle.hover.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/btnXHover.png");
  1327.             btnXStyle.active.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/btnXActive.png");
  1328.             btnLStyle.normal.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/btnLNorm.png");
  1329.             btnLStyle.hover.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/btnLHover.png");
  1330.             btnLStyle.active.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/btnLActive.png");
  1331.             btnRStyle.normal.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/btnRNorm.png");
  1332.             btnRStyle.hover.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/btnRHover.png");
  1333.             btnRStyle.active.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/btnRActive.png");
  1334.             btnKStyle.normal.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/icon.png");
  1335.             btnKStyle.hover.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/icon_h.png");
  1336.             btnKStyle.active.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/icon_h.png");
  1337.             btnMStyle.normal.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/icon3.png");
  1338.             btnMStyle.hover.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/icon3_h.png");
  1339.             btnMStyle.active.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/icon3_h.png");
  1340.             btnFStyle.normal.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/icon5.png");
  1341.             btnFStyle.hover.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/icon5_h.png");
  1342.             btnFStyle.active.background = AssetManager.GetAsset<Texture2D>($"KSRe/images/icon5_h.png");
  1343.             // ModSaves.RegisterSaveLoadGameData<ReSaveData>(ID, OnSaveData, OnLoadData);
  1344.             saveData = ModSaves.RegisterSaveLoadGameData(ID, null, null, saveData);
  1345.             ReUtil.agencyFlags = new Dictionary<string, Texture2D>();
  1346.             Dictionary<string, string> flags = new Dictionary<string, string>() {
  1347.                 ["KSC"] = "flag_AGY_Default",
  1348.                 ["BobsGirders"] = "flag_MFG_Bobs.png",
  1349.                 ["1Proton"] = "flag_MFG_1Proton.png",
  1350.                 ["HyperShovel"] = "flag_MFG_Hyper.png",
  1351.                 ["JoelsLenses"] = "flag_MFG_Joel.png",
  1352.                 ["Kitbashi"] = "flag_MFG_Kitbashi.png",
  1353.                 ["MajorComms"] = "flag_MFG_Major.png",
  1354.                 ["C7"] = "flag_MFG_C7.png",
  1355.                 ["Ionic"] = "flag_MFG_Ionic.png",
  1356.                 ["JebsJunkyard"] = "flag_MFG_Jebediah.png",
  1357.                 ["Kerbodyne"] = "flag_MFG_Kerbodyne_v2.png",
  1358.                 ["Lightyear"] = "flag_MFG_LightYear.png",
  1359.                 ["Periapsis"] = "flag_MFG_Periapsis.png",
  1360.                 ["ReactionSystems"] = "flag_MFG_Reaction.png",
  1361.                 ["Rockomax"] = "flag_MFG_Rockomax.png",
  1362.                 ["STEADLER"] = "flag_MFG_STEADLER_v2.png",
  1363.             };
  1364.             Dictionary<string, string> flagsExt = new Dictionary<string, string>() {
  1365.                 ["KerbalMotion"] = "flag_KerbalMotion.png",
  1366.             };
  1367.             foreach (var pair in flags)
  1368.                 GameManager.Instance.Assets.Load<Texture2D>(pair.Value, t => { ReUtil.agencyFlags[pair.Key] = t; });
  1369.             foreach (var pair in flagsExt)
  1370.                 ReUtil.agencyFlags[pair.Key] = AssetManager.GetAsset<Texture2D>($"KSRe/images/{pair.Value}");
  1371.             // GameManager.Instance.Assets.Load<Texture2D>("flag_MFG_Reaction.png", tex => { testAsset = tex; });
  1372.             // Kerbals = new KerbalOverseer();
  1373.         }
  1374.  
  1375.         private void OnGUI() {
  1376.             if (!showUI && !showApp) return;
  1377.             GUI.skin = Skins.ConsoleSkin;
  1378.             if (showUI) GUI.backgroundColor = UIBgColor;
  1379.             UIRect = showUI ? GUILayout.Window(GUIUtility.GetControlID(FocusType.Passive), UIRect, GUIContent, guiData.WindowTitle, UIStyle, GUILayout.Height(0)) :
  1380.                 GUILayout.Window(GUIUtility.GetControlID(FocusType.Passive), UIRect, AppContent, guiData.AppTitle, UIStyle, GUILayout.Height(0));
  1381.         }
  1382.         public void Update() {
  1383.             if (!inGame) return;
  1384.             slowUpdateDeltaT += Time.deltaTime;
  1385.             if (slowUpdateDeltaT > 1) SlowUpdate();
  1386.             GameState? gameState = Game?.GlobalGameState?.GetGameState()?.GameState;
  1387.             if (gameState != GameState.FlightView && Input.GetKeyDown(appKeyCode.Value)) ToggleApp();
  1388.             if (gameState != GameState.FlightView) return;
  1389.             if (showUI && (activeVessel?.IsKerbalEVA ?? false) && diagMode && Input.GetKeyDown(repairKeyCode.Value)) {
  1390.                 if (UIPart != null && inspectTicks == 0) {
  1391.                     if (UIPart.PartOwner.SimulationObject.Vessel.IsKerbalEVA) {
  1392.                         lockUI = !lockUI;
  1393.                         ReUtil.PushNotify(UIPart.PartOwner.SimulationObject.Vessel.DisplayName, lockUI ? "Hello" : "Bye", NotificationImportance.Low);
  1394.                     } else if (!repairers.Any(r => r.Guid == activeVessel.Guid) &&
  1395.                         UIPart.PartOwner.SimulationObject.Vessel.SimulationObject.Telemetry.CommNetConnectionStatus == ConnectionNodeStatus.Connected) {
  1396.                         // lockUI = true;
  1397.                         repairers.Add(new Repairer(activeVessel.DisplayName, activeVessel.Guid, UIPart.Guid));
  1398.                         guiData.UpdateEVARepairers();
  1399.                     }
  1400.                 } else if (UIPart == null && ReUtil.IsGameSandbox() || (Game.ScienceManager?.IsNodeUnlocked("tNode_pal5000_13") ?? false)) {
  1401.                     if (PALMiniActive)
  1402.                         UpdatePALMini(false);
  1403.                     else if (activeVessel.SimulationObject.PartOwner.ContainerGroup.GetResourceStoredUnits(
  1404.                         Game.ResourceDefinitionDatabase.GetAllResourceIDs().FirstOrDefault(r => Game.ResourceDefinitionDatabase.GetDefinitionData(r).name == "ElectricCharge")) > 0)
  1405.                         UpdatePALMini(true);
  1406.                 }
  1407.                    
  1408.             }
  1409.             if (!Game?.ResourceManager?.IsVisible ?? false) return;
  1410.             foreach (PartComponent part in Game?.ViewController?.GetActiveVehicle()?.GetSimVessel()?.SimulationObject.PartOwner.Parts) {
  1411.                 if (part == null) continue;
  1412.                 ResourceContainer resCont = part.PartResourceContainer;
  1413.                 if (resCont == null) continue;
  1414.                 if (resCont.GetResourcesContainedCount() == 0 || part.PartResourceContainer.GetStoredResourcesTotalMass() == 0) continue;
  1415.                 if (!fuelList.All(f => resCont.IsResourceContained(f))) continue;
  1416.                 if (fuelList.Where(r => resCont.GetResourceStoredMass(r) > 0).Count() > 1)
  1417.                     resCont.DumpAllResources();
  1418.             }
  1419.         }
  1420.         //public void FixedUpdate() {
  1421.         //    if (!inGame || Game.UniverseModel?.TimeScale <= 1) return;
  1422.         //}
  1423.         private void SlowUpdate() {
  1424.             slowUpdateDeltaT = 0;
  1425.             // if (saveData.modVersion != VERSION) InitOrUpdateSave();
  1426.             if (lastSlowUpdateUT == 0)
  1427.                 lastSlowUpdateUT = Game.UniverseModel.UniverseTime;
  1428.             else {
  1429.                 UpdateKerbalStatus(Game.UniverseModel.UniverseTime - lastSlowUpdateUT);
  1430.                 lastSlowUpdateUT = Game.UniverseModel.UniverseTime;
  1431.             }
  1432.             if (showApp && !ReUtil.IsGameSandbox() && ReUtil.IsPlayerAgency(guiData.Agency)) {
  1433.                 guiData.UpdateDiscovery();
  1434.                 guiData.UpdateSciLevel();
  1435.             }
  1436.             if (Game.UniverseModel.UniverseTime > saveData.lastDailyUpdateUT + 21600) {
  1437.                 DailySlowUpdate(Game.UniverseModel.UniverseTime - saveData.lastDailyUpdateUT);
  1438.                 saveData.lastDailyUpdateUT = Math.Round(Game.UniverseModel.UniverseTime);
  1439.             }
  1440.             if (!showUI) return;
  1441.             if (activeVessel == null || (UIPart?.IsDestroyedOrBeingDestroyed ?? false))
  1442.                 UpdateGUIVessel();
  1443.             if (!diagMode) guiData.UpdateListMode();
  1444.             else if (UIPart != null) {
  1445.                 if (activeVessel?.IsKerbalEVA ?? false) {
  1446.                     if (Position.Distance(activeVessel.transform.Position, UIPart.transform.Position) > UIPart.PartData.PartSizeDiameter + 1.5) {
  1447.                         UIPart = null;
  1448.                         inspectTicks = -1;
  1449.                         if (lockUI) {
  1450.                             lockUI = false;
  1451.                             // ReUtil.PushNotify(UIPart.PartOwner.SimulationObject.Vessel.DisplayName, "Bye", NotificationImportance.Low);
  1452.                         }
  1453.                     } else {
  1454.                         guiData.UpdateEVADamage();
  1455.                         if (inspectTicks > 0) inspectTicks--;
  1456.                     }
  1457.                     guiData.UpdateEVARepairers();
  1458.                 } else if (!(activeVessel?.IsKerbalEVA ?? true))
  1459.                     guiData.UpdateForVessel(ReUtil.IsPALActive(UIPart?.PartOwner?.SimulationObject?.Vessel));
  1460.             }
  1461.         }
  1462.         private void DailySlowUpdate(double deltaT) {
  1463.             double oneDayRate = deltaT / 21600;
  1464.             foreach (KerbalDamage kerbd in saveData.kerbals) {
  1465.                 if (kerbd.status.StartsWith("KSC")) {
  1466.                     // UnityEngine.Random.InitState((kerbd.Name + kerbd.Trait + "Psychology_Therapy").GetHashCode());
  1467.                     // kerbd.status = "KSC STANDBY";
  1468.                     double impactDmg = TryGetKerbalDamage(kerbd, "Medicine_Impacts");
  1469.                     UnityEngine.Random.InitState((kerbd.Name + kerbd.Trait + "Medicine_Impacts").GetHashCode());
  1470.                     if (impactDmg > UnityEngine.Random.value * 5) {
  1471.                         AddKerbalDamage(kerbd, "Medicine_Impacts", -oneDayRate * 0.25);
  1472.                         if (TryGetKerbalDamage(kerbd, "Medicine_Atrophy") < 25)
  1473.                             AddKerbalDamage(kerbd, "Medicine_Atrophy", oneDayRate * 0.25);
  1474.                         kerbd.status = "KSC>MED BAY";
  1475.                         continue;
  1476.                     } else if (kerbd.status == "KSC>MED BAY") {
  1477.                         KerbalLifeRewards("Medicine_Impacts", force: true);
  1478.                         kerbd.status = "KSC>STANDBY";
  1479.                         ReUtil.PushNotify(kerbd.Name, "Recovery/Impacts", NotificationImportance.Low, false);
  1480.                         continue;
  1481.                     }
  1482.                     double atrophyDmg = TryGetKerbalDamage(kerbd, "Medicine_Atrophy");
  1483.                     if (atrophyDmg > 0) {
  1484.                         AddKerbalDamage(kerbd, "Medicine_Atrophy", -oneDayRate * 0.5);
  1485.                         kerbd.status = "KSC>RECOVERY";
  1486.                         continue;
  1487.                     } else if (kerbd.status == "KSC>RECOVERY") {
  1488.                         KerbalLifeRewards("Medicine_Atrophy", force: true);
  1489.                         kerbd.status = "KSC>STANDBY";
  1490.                         ReUtil.PushNotify(kerbd.Name, "Recovery/Atrophy", NotificationImportance.Low, false);
  1491.                         continue;
  1492.                     }
  1493.                     double socialDmg = TryGetKerbalDamage(kerbd, "Psychology_Social");
  1494.                     if (socialDmg > 25 && Math.Abs(TryGetKerbalDamage(kerbd, "Psychology_Therapy")) < socialDmg) {
  1495.                         AddKerbalDamage(kerbd, "Psychology_Therapy", -oneDayRate);
  1496.                         kerbd.status = "KSC>THERAPY";
  1497.                         continue;
  1498.                     } else if (kerbd.status == "KSC>THERAPY") {
  1499.                         KerbalLifeRewards("Psychology_Therapy", force:true);
  1500.                         kerbd.status = "KSC>STANDBY";
  1501.                         ReUtil.PushNotify(kerbd.Name, "Recovery/Therapy", NotificationImportance.Low, false);
  1502.                         continue;
  1503.                     }
  1504.                     UnityEngine.Random.InitState((kerbd.Name + kerbd.Trait + "Medicine_Atrophy").GetHashCode());
  1505.                     if (atrophyDmg > UnityEngine.Random.value * -10) {
  1506.                         AddKerbalDamage(kerbd, "Medicine_Atrophy", -oneDayRate * 0.5);
  1507.                         kerbd.status = "KSC>FITNESS";
  1508.                         continue;
  1509.                     } else if (kerbd.status == "KSC>FITNESS") {
  1510.                         kerbd.status = "KSC>STANDBY";
  1511.                         continue;
  1512.                     }
  1513.                     if (kerbd.status == "KSC>STANDBY")
  1514.                         kerbd.status = "KSC>READY";
  1515.                 } else {
  1516.                     Game.SessionManager.KerbalRosterManager.TryGetKerbalByName(kerbd.Name, out KerbalInfo kerbi);
  1517.                     SimulationObjectModel simObj = Game.ViewController.Universe.FindSimObject(kerbi.Location.SimObjectId);
  1518.                     double geeForce = simObj?.Part?.PartOwner?.SimulationObject?.Vessel?.geeForce ?? 0;
  1519.                     if (TryGetKerbalDamage(kerbd, "Medicine_Atrophy") < (1 - Mathf.Clamp01((float)geeForce)) * 50)
  1520.                         AddKerbalDamage(kerbd, "Medicine_Atrophy", oneDayRate * (0.25 - geeForce));
  1521.                 }
  1522.             }
  1523.         }
  1524.  
  1525.         private void OnLoadData(ReSaveData data) {
  1526.             Logger.LogInfo("Loading...");
  1527.         }
  1528.        
  1529.         private void OnSaveData(ReSaveData data) {
  1530.             Logger.LogInfo("Saving...");
  1531.             //data.lastDailyUpdateUT = saveData.lastDailyUpdateUT;
  1532.             //data.modVersion = saveData.modVersion;
  1533.             //data.agencies = saveData.agencies;
  1534.             //data.kerbals = saveData.kerbals;
  1535.         }
  1536.  
  1537.  
  1538.         private void ChangedGameState(MessageCenterMessage message) {
  1539.             GameStateChangedMessage msg = (GameStateChangedMessage)message;
  1540.             UnityEngine.Random.InitState(int.Parse($"{DateTime.Now.Minute}{DateTime.Now.Second}{DateTime.Now.Millisecond}"));
  1541.             inGame = msg?.CurrentState != GameState.MainMenu;
  1542.             if (!((msg?.CurrentState == GameState.FlightView && msg?.PreviousState == GameState.Map3DView) ||
  1543.                 (msg?.CurrentState == GameState.Map3DView && msg?.PreviousState == GameState.FlightView))) {
  1544.                 UIPart = null;
  1545.                 showUI = false;
  1546.             } else if (showUI && msg.CurrentState == GameState.Map3DView)
  1547.                 minUI = true;
  1548.             showApp = false;
  1549.             repairers.Clear();
  1550.             if (guiData == null) guiData = new GUIData();
  1551.             if (!inGame) {
  1552.                 saveData.agencies = null;
  1553.                 saveData.kerbals = null;
  1554.                 saveData.modVersion = null;
  1555.                 saveData.lastDailyUpdateUT = 0;
  1556.                 guiData.Reset();
  1557.             } else if (saveData.modVersion != VERSION) InitOrUpdateSave();
  1558.             // if (msg?.CurrentState == GameState.KerbalSpaceCenter || msg?.CurrentState == GameState.MissionControl || msg?.CurrentState == GameState.ResearchAndDevelopment)
  1559.             //     TryUpdateRocketScienceHeaders();
  1560.             if (fuelContIDVal != 0) return;
  1561.             if (Game.ResourceDefinitionDatabase.GetAllResourceIDs().Count() == 0) return;
  1562.             // stock bug (v0.2.0): game.ResourceDefinitionDatabase.GetResourceIDFromName()
  1563.             ResourceDefinitionID recipeID = Game.ResourceDefinitionDatabase.GetAllResourceIDs().FirstOrDefault(r => Game.ResourceDefinitionDatabase.GetDefinitionData(r).name == "FuelContainer");
  1564.             if (recipeID.Value == 0) return;
  1565.             fuelContIDVal = recipeID.Value;
  1566.             fuelList = Game.ResourceDefinitionDatabase.GetDefinitionData(recipeID).recipeProperties.IngredientsResourceIDs.ToList();
  1567.             // cryoResIDVal = Game.ResourceDefinitionDatabase.GetAllResourceIDs().FirstOrDefault(r => Game.ResourceDefinitionDatabase.GetDefinitionData(r).name == "Hydrogen").Value;
  1568.         }
  1569.  
  1570.         private void PartBehaviourInitialized(MessageCenterMessage message) {
  1571.             PartBehaviourInitializedMessage msg = (PartBehaviourInitializedMessage)message;
  1572.             // Logger.LogInfo("init: " + msg.Part.SimObjectComponent.PartData.partName);
  1573.             if (msg.Part.SimObjectComponent.PartData.partName != "booster_1v_solid_flea") return;
  1574.             msg.Part.gameObject.transform.localScale = new Vector3(0.5f, 1, 0.5f);
  1575.         }
  1576.         private void PlacedPart(MessageCenterMessage message) {
  1577.             PartPlacedMessage msg = (PartPlacedMessage)message;
  1578.             if (msg.messagePart.PartName != "booster_1v_solid_flea") return;
  1579.             msg.messagePart.PartTransform.localScale = new Vector3(0.5f, 1, 0.5f);
  1580.         }
  1581.         private void SeparatedPart(MessageCenterMessage message) {
  1582.             PartSeparatedMessage msg = (PartSeparatedMessage)message;
  1583.             // Logger.LogInfo("sep: " + msg.messagePart.PartName);
  1584.             if (msg?.messagePart?.PartName != "booster_1v_solid_flea") return;
  1585.             msg.messagePart.PartTransform.localScale = new Vector3(0.5f, 1, 0.5f);
  1586.         }
  1587.         private void ChangedSymmetryMode(MessageCenterMessage message) {
  1588.             IObjectAssemblyPart part = Game?.OAB?.Current.Stats?.LastPartGrabbed;
  1589.             // Logger.LogInfo("sym: " + part?.PartName);
  1590.             // if (part?.PartName != "booster_1v_solid_flea") return;
  1591.             VABSymmetryModeChangedMessage msg = (VABSymmetryModeChangedMessage)message;
  1592.             // Logger.LogInfo("mode: " + (int)Game?.OAB?.Current?.Stats?.SymmetryMode.GetValue());
  1593.             if ((int)Game?.OAB?.Current?.Stats?.SymmetryMode.GetValue() < 2) return;
  1594.             Logger.LogInfo("symcount: " + part.SymmetrySet.Parts.Count);
  1595.             Logger.LogInfo("symfleacount: " + part.SymmetrySet.Parts.Count(p => p.PartName == "booster_1v_solid_flea"));
  1596.             // Logger.LogInfo("symparts: " + part.SymmetrySet.Visualizer.SingleCopyMap.Keys.Where(p => p.PartName == "booster_1v_solid_flea").ToList().Count);
  1597.             // part.SymmetrySet.Visualizer.SingleCopyMap.Keys.Where(p => p.PartName == "booster_1v_solid_flea").ToList().ForEach(p => p.PartTransform.localScale = new Vector3(0.5f, 1, 0.5f));
  1598.         }
  1599.  
  1600.         private void EnteredRnD(GameStateEnteredMessage message) {
  1601.             string rdScrollBarPath = "GameManager/Default Game Instance(Clone)/UI Manager(Clone)/Main Canvas/RDCenterUI(Clone)/Container/TechTree/ELE-Scrollbar-Horizontal";
  1602.             GameObject.Find(rdScrollBarPath)?.SetActive(false);
  1603.             string tierContPath = "GameManager/Default Game Instance(Clone)/UI Manager(Clone)/Main Canvas/RDCenterUI(Clone)/Container/TechTree/TierScroll/TierSelectionContainer";
  1604.             RectTransform tierContTrans = GameObject.Find(tierContPath)?.GetComponent<RectTransform>();
  1605.             if (transform != null) tierContTrans.sizeDelta = new Vector2(0, 28);
  1606.             for (int i = 0; i < tierContTrans.childCount; i++) {
  1607.                 RectTransform t1 = tierContTrans.GetChild(i)?.FindChildRecursive("Text (TMP)")?.GetComponent<RectTransform>();
  1608.                 RectTransform t2 = tierContTrans.GetChild(i)?.FindChildRecursive("Text On")?.GetComponent<RectTransform>();
  1609.                 if (t1 == null || t2 == null) continue;
  1610.                 t1.anchoredPosition = new Vector2(0, 8);
  1611.                 t2.anchoredPosition = new Vector2(0, 8);
  1612.             }
  1613.             //for (int i = 0; i < 4; i++)
  1614.             //    Instantiate(tierContTrans.GetChild(i).gameObject, tierContTrans.GetChild(i).parent);
  1615.             StartCoroutine(CallbackUtil.DelayedCallback(1, delegate {
  1616.                 string treePath = "GameManager/Default Game Instance(Clone)/UI Manager(Clone)/Main Canvas/RDCenterUI(Clone)/Container/TechTree/TreeContainer/TreeContent";
  1617.                 Transform tree = GameObject.Find(treePath)?.transform;
  1618.                 if (tree == null) return;
  1619.                 for (int i = 1; i < tree.childCount; i++) {
  1620.                     Transform t = tree.GetChild(i);
  1621.                     if (t == null) continue;
  1622.                     RectTransform t1 = t.GetComponent<RectTransform>();
  1623.                     if (t1 != null) t1.sizeDelta = new Vector2(164, 76); // 82
  1624.                     RectTransform t2 = t.FindChildRecursive("ToggleOn")?.GetComponent<RectTransform>();
  1625.                     if (t2 != null) t2.sizeDelta = new Vector2(164, 76); // 82
  1626.                     RectTransform t3 = t.FindChildRecursive("Icon")?.GetComponent<RectTransform>();
  1627.                     if (t3 != null) { t3.anchoredPosition = new Vector2(15, -7); t3.sizeDelta = new Vector2(20, 20); }
  1628.                     RectTransform t4 = t.FindChildRecursive("ToggleOnIcon")?.GetComponent<RectTransform>();
  1629.                     if (t4 != null) { t4.anchoredPosition = new Vector2(65, -17); /* 27, -19 */ t4.sizeDelta = new Vector2(20, 20); }
  1630.                     RectTransform t5 = t.FindChildRecursive("Text (TMP)")?.GetComponent<RectTransform>();
  1631.                     if (t5 != null) t5.anchoredPosition = new Vector2(3, -18); // -20
  1632.                     RectTransform t6 = t.FindChildRecursive("ToggleOnText")?.GetComponent<RectTransform>();
  1633.                     if (t6 != null) t6.anchoredPosition = new Vector2(3, -18); // -20
  1634.                     RectTransform t7 = t.FindChildRecursive("CostContainer")?.GetComponent<RectTransform>();
  1635.                     if (t7 != null) { t7.sizeDelta = new Vector2(75, 16); t7.anchoredPosition = new Vector2(-11, -10); }
  1636.                     RectTransform t8 = t.FindChildRecursive("Cost")?.GetComponent<RectTransform>(); // textmesh component inside "cost"
  1637.                     if (t8 != null) { t8.sizeDelta = new Vector2(45, 16); t8.anchoredPosition = new Vector2(45, -8); }
  1638.                 }
  1639.             }));
  1640.             StartCoroutine(CallbackUtil.DelayedCallback(2, delegate {
  1641.                 List<TechNodeData> techNodes = new List<TechNodeData>();
  1642.                 if (Game?.ScienceManager?.TechNodeDataStore?.TryGetTechNodeDataCollection(out IReadOnlyCollection<TechNodeData> tNodes) ?? false)
  1643.                     techNodes = tNodes?.ToList();
  1644.                 string treePath = "GameManager/Default Game Instance(Clone)/UI Manager(Clone)/Main Canvas/RDCenterUI(Clone)/Container/TechTree/TreeContainer/TreeContent";
  1645.                 Transform tree = GameObject.Find(treePath)?.transform;
  1646.                 if (tree == null) return;
  1647.                 int totalSci = ReUtil.GetTotalScience();
  1648.                 for (int i = 1; i < tree.childCount; i++) {
  1649.                     GameObject tNodeObj = tree.GetChild(i)?.gameObject;
  1650.                     if (tNodeObj == null) continue;
  1651.                     TechNodeData tNode = techNodes.FirstOrDefault(n => n.ID == tNodeObj.name);
  1652.                     if (tNode == null) continue;
  1653.                     bool isDiscovered = tNode.RequiredSciencePoints < 10 || totalSci > tNode.RequiredSciencePoints * 2.5;
  1654.                     if (!isDiscovered && totalSci > tNode.RequiredSciencePoints * 1.5) {
  1655.                         UnityEngine.Random.InitState((Game.SessionGuidString + tNode.RequiredSciencePoints).GetHashCode());
  1656.                         isDiscovered = totalSci >= tNode.RequiredSciencePoints * (1.5 + UnityEngine.Random.value);
  1657.                     }
  1658.                     tNodeObj.SetActive(isDiscovered);
  1659.                 }
  1660.                 for (int i = 0; i < tree.GetChild(0).childCount; i++) {
  1661.                     Transform t = tree.GetChild(0).GetChild(i);
  1662.                     if (t == null) continue;
  1663.                     string[] name = t.name.Split(new string[] { "_to_" }, StringSplitOptions.None);
  1664.                     bool reqsShown = tree.gameObject.GetChild(name[0]).activeSelf && tree.gameObject.GetChild(name[1]).activeSelf;
  1665.                     t.gameObject.SetActive(reqsShown/* || !techDiscovery.Value*/);
  1666.                 }
  1667.             }));
  1668.         }
  1669.  
  1670.         private void ChangedResCont(MessageCenterMessage message) {
  1671.             if (Game.GlobalGameState.GetGameState().GameState != GameState.VehicleAssemblyBuilder) return;
  1672.             ResourceContainerChangedMessage resMsg = (ResourceContainerChangedMessage)message;
  1673.             if (resMsg == null) return;
  1674.             lastResMsg = resMsg;
  1675.         }
  1676.         private void ReleasedUISlider(MessageCenterMessage message) {
  1677.             if (Game.GlobalGameState.GetGameState().GameState != GameState.VehicleAssemblyBuilder) return;
  1678.             if (lastResMsg == null) return;
  1679.             if (!Game.ResourceDefinitionDatabase.GetRecipesForIngredient(lastResMsg.ResourceId, out List<ResourceDefinitionID> recipeList)) return;
  1680.             if (!recipeList.Any(r => r.Value == fuelContIDVal)) return;
  1681.             fuelList.Where(r => r != lastResMsg.ResourceId).ToList().ForEach(r => lastResMsg.Container.DumpResource(r));
  1682.             lastResMsg = null;
  1683.         }
  1684.  
  1685.         private void ChangedVessel(MessageCenterMessage message) => UpdateGUIVessel();
  1686.         private void ChangedMousePart(MessageCenterMessage message) {
  1687.             if (!showUI || !(activeVessel?.IsKerbalEVA ?? false) || lockUI) return;
  1688.             inspectTicks = -1;
  1689.             PartUnderMouseChanged msg = (PartUnderMouseChanged)message;
  1690.             if (msg?.newPartUnderMouse == null) { UIPart = null; return; }
  1691.             PartComponent part = msg.newPartUnderMouse.SimObjectComponent;
  1692.             // if (part.PartName == "eva_kerbal") { UIPart = null; return; }
  1693.             UIPart = Position.Distance(activeVessel.transform.Position, part.transform.Position) > part.PartData.PartSizeDiameter + 1 ? null : part;
  1694.             if (UIPart != null) guiData.UpdateEVAPart();
  1695.         }
  1696.  
  1697.         private void CreatedVessel(MessageCenterMessage message) {
  1698.             if (ReUtil.IsGameSandbox()) return;
  1699.             VesselCreatedMessage msg = (VesselCreatedMessage)message;
  1700.             float diffScale;
  1701.             if (!Game.SessionManager.TryGetDifficultyOptionState("ScienceRewards", out diffScale)) diffScale = 1f;
  1702.             int sciGain = (int)Math.Round(Math.Ceiling(Math.Min((msg?.SerializedVessel?.parts?.Count ?? 0) * 0.015, 3)) * (ReUtil.GetSciBonus("Engineering") + 1) * diffScale);
  1703.             if (sciGain < 1) return;
  1704.             ReUtil.AddScience(sciGain);
  1705.             AddRocketScience("Engineering_Assembly", sciGain);
  1706.         }
  1707.         private void EnteredSOI(MessageCenterMessage message) {
  1708.             if (ReUtil.IsGameSandbox()) return;
  1709.             SOIEnteredMessage msg = (SOIEnteredMessage)message;
  1710.             if (msg?.bodyExited == null) return;
  1711.             float diffScale;
  1712.             if (!Game.SessionManager.TryGetDifficultyOptionState("ScienceRewards", out diffScale)) diffScale = 1f;
  1713.             int sciGain = (int)Math.Round(Math.Ceiling((msg.vessel?.VesselScienceRegionSituation.SituationScalar ?? 0) *
  1714.                 (prevCelTransNames.Contains(msg.bodyExited.bodyName) && prevCelTransNames.Contains(msg.bodyEntered.bodyName) ? 0.5 : 2.0)) * (ReUtil.GetSciBonus("Control") + 1) * diffScale);
  1715.             if (sciGain < 1) return;
  1716.             prevCelTransNames = $"{msg.bodyExited.bodyName}-{msg.bodyEntered.bodyName}";
  1717.             ReUtil.AddScience(sciGain);
  1718.             AddRocketScience("Control_SOI", sciGain);
  1719.         }
  1720.         private void ScoredResearchReport(MessageCenterMessage message) {
  1721.             ResearchReportScoredMessage msg = (ResearchReportScoredMessage)message;
  1722.             CompletedResearchReport report = Game.ScienceManager.GetSubmittedResearchReports().FirstOrDefault(r => r.ResearchReportKey == msg?.ResearchReportKey);
  1723.             string key = $"{(report.ResearchReportKey.Contains("CrewObservation") ? "Psychology" : $"Science{report.ResearchReportType.ToString().Replace("Type", "")}")}_{report.ResearchReportKey.Split('_')[0]}";
  1724.             if (report.ResearchReportKey.Contains("AtmosphereSurvey"))
  1725.                 key = $"Aeronautics_{report.ResearchReportType.ToString().Replace("Type", "")}";
  1726.             string sciType = key.Split('_')[0];
  1727.             int sciBoost = (int)Math.Round(report.FinalScienceValue * ReUtil.GetSciBonus(sciType));
  1728.             if (sciBoost > 0) ReUtil.AddScience(sciBoost);
  1729.             AddRocketScience(key, (int)Math.Round(report.FinalScienceValue * (ReUtil.GetSciBonus(sciType) + 1)));
  1730.         }
  1731.         private void CompletedMission(MessageCenterMessage message) {
  1732.             MissionCompleteMessage msg = (MissionCompleteMessage)message;
  1733.  
  1734.         }
  1735.         private void RecoveredVessel(MessageCenterMessage message) {
  1736.             if (ReUtil.IsGameSandbox()) return;
  1737.             VesselRecoveredMessage msg = (VesselRecoveredMessage)message;
  1738.             VesselComponent vessel = Game.UniverseModel.FindVesselComponent(msg.VesselID);
  1739.             if (vessel?.SimulationObject?.IsKerbal ?? true) return;
  1740.             Dictionary<string, double> recSci = new Dictionary<string, double> { ["Engineering_Age"] = 0 };
  1741.             foreach (PartComponent part in vessel.SimulationObject.PartOwner.Parts)
  1742.                 if (part.TryGetModule(out PartComponentModule_DamageEngine moduleDmgEng) && moduleDmgEng != null) {
  1743.                     if (moduleDmgEng.DataDmg.records.Any(r => r.Key.StartsWith("Aeronautics") || r.Key.StartsWith("Propulsion")))
  1744.                         AddPartRecord(part.PartData.partName, moduleDmgEng.DataDmg.records.FirstOrDefault(r => r.Key.StartsWith("Aeronautics") || r.Key.StartsWith("Propulsion")).Value);
  1745.                     foreach (var pair in moduleDmgEng.DataDmg.records) {
  1746.                         double val = pair.Value * (ReUtil.dmgSciRates.ContainsKey(pair.Key) ? ReUtil.dmgSciRates[pair.Key] : 0.05);
  1747.                         if (recSci.ContainsKey(pair.Key))
  1748.                             recSci[pair.Key] += val;
  1749.                         else recSci[pair.Key] = val;
  1750.                         //int sci = (int)Math.Ceiling(pair.Value * (ReUtil.dmgSciRates.ContainsKey(pair.Key) ? ReUtil.dmgSciRates[pair.Key] : 0.05));
  1751.                         //AddRocketScience(pair.Key, sci);
  1752.                         //sciGain += sci;
  1753.                     }
  1754.                 } else if (part.TryGetModule(out PartComponentModule_Damage moduleDmg) && moduleDmg != null) {
  1755.                     foreach (string sciType in new string[] { "Propulsion_RCS", "Electrical_Wheel", "Aeronautics_LandingGear", "Aeronautics_Parachute" })
  1756.                         if (moduleDmg.DataDmg.records.TryGetValue(sciType, out double rec))
  1757.                             AddPartRecord(part.PartData.partName, rec);
  1758.                     foreach (var pair in moduleDmg.DataDmg.records) {
  1759.                         double val = pair.Value * (ReUtil.dmgSciRates.ContainsKey(pair.Key) ? ReUtil.dmgSciRates[pair.Key] : 0.05);
  1760.                         if (recSci.ContainsKey(pair.Key))
  1761.                             recSci[pair.Key] += val;
  1762.                         else recSci[pair.Key] = val;
  1763.                     }
  1764.                 }
  1765.             int sciGain = 0;
  1766.             //if (recSci["Engineering_Age"] == 0)
  1767.             //    recSci.Remove("Engineering_Age");
  1768.             float diffScale;
  1769.             if (!Game.SessionManager.TryGetDifficultyOptionState("ScienceRewards", out diffScale)) diffScale = 1f;
  1770.             foreach (var pair in recSci) {
  1771.                 int val = Math.Max(1, (int)Math.Round(pair.Value * (ReUtil.GetSciBonus(pair.Key.Split('_')[0]) + 1) * diffScale));
  1772.                 AddRocketScience(pair.Key, val);
  1773.                 sciGain += val;
  1774.             }
  1775.             if (sciGain == 0) return;
  1776.             ReUtil.AddScience(sciGain);
  1777.         }
  1778.         private void UpdatedSciencePoints(MessageCenterMessage message) =>
  1779.             StartCoroutine(CallbackUtil.DelayedCallback(1, delegate {
  1780.                 // SciencePointCapacityUpdateMessage msg = (SciencePointCapacityUpdateMessage)message;
  1781.                 ReAgency playerAgency = ReUtil.PlayerReAgency();
  1782.                 if (playerAgency == null) return;
  1783.                 int missionSci = Math.Max(0, ReUtil.GetTotalScience() - playerAgency.rocketScience.Where(s => s.Key != "Command_Missions").Select(s => s.Value).Sum()); // - Game.SessionManager.GetMyAgencyAdditionalSciencePoints()
  1784.                 if (!playerAgency.rocketScience.ContainsKey("Command_Missions"))
  1785.                     playerAgency.rocketScience["Command_Missions"] = 0;
  1786.                 if (playerAgency.rocketScience["Command_Missions"] != missionSci) {
  1787.                     int sciBoost = Math.Max(0, (int)Math.Round((missionSci - playerAgency.rocketScience["Command_Missions"]) * ReUtil.GetSciBonus("Command")));
  1788.                     if (sciBoost > 0) ReUtil.AddScience(sciBoost);
  1789.                     playerAgency.rocketScience["Command_Missions"] = missionSci + sciBoost;
  1790.                 }
  1791.             }));
  1792.  
  1793.         private void AddedNewKerbal(MessageCenterMessage message) {
  1794.             KerbalAddedToRoster msg = (KerbalAddedToRoster)message;
  1795.             if (msg == null) return;
  1796.             ReUtil.PushNotify(msg.Kerbal.NameKey, "Hello", NotificationImportance.Low);
  1797.             SetupKerbalDamage(msg.Kerbal);
  1798.         }
  1799.         private void RemovedKerbal(MessageCenterMessage message) {
  1800.             KerbalRemovedFromRoster msg = (KerbalRemovedFromRoster)message;
  1801.             if (msg == null) return;
  1802.             ReUtil.PushNotify(msg.Kerbal.NameKey, "Quit", NotificationImportance.High);
  1803.             saveData.kerbals.RemoveAll(k => k.Name == msg.Kerbal.NameKey);
  1804.             saveData.kerbals.ForEach(k => AddKerbalDamage(k, "Psychology_Social_KerbonautLost", 50));
  1805.             // EmptyAgencyCheck();
  1806.         }
  1807.         private void RelocatedKerbal(MessageCenterMessage message) {
  1808.             KerbalLocationChanged msg = (KerbalLocationChanged)message;
  1809.             if (msg == null) return;
  1810.             SimulationObjectModel simObj = Game.ViewController.Universe.FindSimObject(msg.Kerbal.Location.SimObjectId);
  1811.             SimulationObjectModel oldSimObj = Game.ViewController.Universe.FindSimObject(msg.OldLocation.SimObjectId);
  1812.             if (simObj == null) return;
  1813.             if (oldSimObj != null && msg.OldLocation.SimObjectId != Game.SessionManager.KerbalRosterManager.KSCGuid && msg.Kerbal.Location.SimObjectId != Game.SessionManager.KerbalRosterManager.KSCGuid) {
  1814.                 ResourceDefinitionID resID = Game.ResourceDefinitionDatabase.GetAllResourceIDs().FirstOrDefault(r =>
  1815.                     Game.ResourceDefinitionDatabase.GetDefinitionData(r).name == "MonoPropellant");
  1816.                 if (simObj.Part?.PartName == "eva_kerbal")
  1817.                     simObj.Part.PartOwner.ContainerGroup.AddResourceUnits(resID,
  1818.                         oldSimObj.Part.PartOwner.ContainerGroup.RemoveResourceUnits(resID, simObj.Part.PartOwner.ContainerGroup.GetResourceCapacityUnits(resID)));
  1819.                  else simObj.Part.PartOwner.ContainerGroup.AddResourceUnits(resID, oldSimObj.Part.PartOwner.ContainerGroup.GetResourceStoredUnits(resID));
  1820.             }
  1821.             KerbalDamage kerbd = saveData.kerbals.FirstOrDefault(k => k.Name == msg.Kerbal.NameKey);
  1822.             if (kerbd == null) return;
  1823.             if (HasKerbalDamage(kerbd, "Psychology_Role_Wings") || simObj.Part?.PartName != "eva_kerbal") return;
  1824.             StartCoroutine(CallbackUtil.DelayedCallback(1, delegate {
  1825.                 ScienceSitutation? sit = simObj.Part?.PartOwner?.SimulationObject?.Vessel?.VesselScienceRegionSituation.ResearchLocation?.ScienceSituation;
  1826.                 if (sit != ScienceSitutation.LowOrbit && sit != ScienceSitutation.HighOrbit) return;
  1827.                 AddKerbalDamage(kerbd, "Psychology_Role_WingsPending", -5, true);
  1828.                 ReUtil.PushNotify(kerbd.Name, "WingsPending", NotificationImportance.Low);
  1829.             }));
  1830.             // Game.SessionManager.KerbalRosterManager.GetAllKerbals().ForEach(k => Logger.LogInfo(k.Location.SimObjectId));
  1831.         }
  1832.  
  1833.  
  1834.         private void ToggleGUI() {
  1835.             GameState state = Game.GlobalGameState.GetGameState().GameState;
  1836.             if (state == GameState.Map3DView) return;
  1837.             else if (state == GameState.KerbalSpaceCenter || state == GameState.VehicleAssemblyBuilder) diagMode = false;
  1838.             UpdateGUIVessel();
  1839.             showUI = !showUI;
  1840.             // GameObject.Find("BTN-ReDiagnostic")?.GetComponent<UIValue_WriteBool_Toggle>()?.SetValue(showUI);
  1841.             if (showApp && showUI) {
  1842.                 showApp = false;
  1843.                 // GameObject.Find("BTN-ReKerbalLife")?.GetComponent<UIValue_WriteBool_Toggle>()?.SetValue(showApp);
  1844.             }
  1845.         }
  1846.         private void ToggleGUI(bool show) => ToggleGUI();
  1847.         private void GUIContent(int windowID) {
  1848.             GameState state = Game.GlobalGameState.GetGameState().GameState;
  1849.             bool modeBtn = false;
  1850.             bool minBtn = false;
  1851.             GUILayout.BeginHorizontal();
  1852.             if (!minUI)
  1853.                 modeBtn = GUILayout.Button(guiData.ModeBtn, btnStyle, GUILayout.Width(28));
  1854.             GUILayout.FlexibleSpace();
  1855.             if (state != GameState.Map3DView)
  1856.                 minBtn = GUILayout.Button(minUI ? guiData.MaxBtn : guiData.MinBtn, btnStyle, GUILayout.Width(28));
  1857.             bool closeBtn = GUILayout.Button(guiData.CloseBtn, btnXStyle, GUILayout.Width(28));
  1858.             GUILayout.EndHorizontal();
  1859.             if (modeBtn) {
  1860.                 diagMode = !diagMode;
  1861.                 if (!diagMode) guiData.UpdateListMode();
  1862.                 else if (activeVessel.IsKerbalEVA) guiData.UpdateEVAPart();
  1863.                 else guiData.UpdateVesselPart(ReUtil.IsPALActive(UIPart.PartOwner.SimulationObject.Vessel));
  1864.             } else if (minBtn) minUI = !minUI;
  1865.             else if (closeBtn) showUI = false;
  1866.             if (!showUI || minUI || modeBtn || (activeVessel == null && state != GameState.KerbalSpaceCenter && state != GameState.VehicleAssemblyBuilder)) {
  1867.                 lockUI = false;
  1868.                 inspectTicks = -1;
  1869.                 GUI.DragWindow(new Rect(0, 0, 10000, 500));
  1870.                 return;
  1871.             }
  1872.             if ((activeVessel?.IsKerbalEVA ?? false) && diagMode && UIPart == null) {
  1873.                 GUILayout.Label(guiData.InspectHint);
  1874.                 if (ReUtil.IsGameSandbox() || (Game.ScienceManager?.IsNodeUnlocked("tNode_pal5000_13") ?? false))
  1875.                     GUILayout.Label(guiData.PALMiniHint);
  1876.                 GUI.DragWindow(new Rect(0, 0, 10000, 500)); return;
  1877.             }
  1878.             GUILayout.BeginHorizontal(); GUILayout.BeginVertical();
  1879.             if (diagMode) {
  1880.                 if (activeVessel?.IsKerbalEVA ?? false) GUIContentEVA();
  1881.                 else {
  1882.                     if (ReUtil.IsPALActive(activeVessel)) // UIPart?.PartOwner?.SimulationObject?.Vessel
  1883.                         GUIContentPAL();
  1884.                     GUIContentVessel();
  1885.                 }
  1886.             } else GUIContentListMode();
  1887.             GUILayout.EndVertical(); GUILayout.EndHorizontal();
  1888.             GUI.DragWindow(new Rect(0, 0, 10000, 500));
  1889.         }
  1890.         private void GUIContentEVA() {
  1891.             GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUILayout.Label(guiData.PartTitle); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal();
  1892.             GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUILayout.Label(guiData.Subtitle); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal();
  1893.             bool isIdleEVA = !repairers.Any(r => r.Guid == activeVessel.Guid);
  1894.             bool isBeingRepaired = repairers.Count(r => r.TargetGuid == UIPart.Guid) > 0;
  1895.             bool isInspected = inspectTicks == 0;
  1896.             bool isKerbal = UIPart.PartName == "eva_kerbal";
  1897.             if (isBeingRepaired || isInspected)
  1898.                 GUILayout.Label(guiData.Damage);
  1899.             if (isIdleEVA && (isInspected || isBeingRepaired))
  1900.                 GUILayout.Label(isKerbal || UIPart.PartOwner.SimulationObject.Vessel.SimulationObject.Telemetry.CommNetConnectionStatus == ConnectionNodeStatus.Connected ?
  1901.                     guiData.RepairHint : guiData.CommNetReq);
  1902.             // int sel = 0;
  1903.             if (lockUI) {
  1904.                 KerbalDamage currentKerb = saveData.kerbals.FirstOrDefault(k => k.Name == UIPart.PartOwner.SimulationObject.Vessel.DisplayName);
  1905.                 //GUILayout.BeginHorizontal(); GUILayout.Space(120); GUILayout.Label("Assess"); GUILayout.Label("Analyze"); GUILayout.Label("Discuss"); GUILayout.EndHorizontal();
  1906.                 //GUILayout.BeginHorizontal();
  1907.                 //GUILayout.BeginVertical(); GUILayout.Label("Veteran"); GUILayout.Label("Wings"); GUILayout.Label("Kerbonaut"); GUILayout.EndVertical();
  1908.                 //sel = GUILayout.SelectionGrid(sel, new string[] { ">¤<", ">¤<", ">¤<", ">¤<", ">¤<", ">¤<", ">¤<", ">¤<", ">¤<" }, 3, GUILayout.Width(250));
  1909.                 //GUILayout.EndHorizontal();
  1910.                 GUILayout.BeginVertical();
  1911.                 List<string> dmgTypes = new List<string>();
  1912.                 foreach (string subTitle in new string[] { "Medicine/Negative", "Psychology/Positive", "Psychology/Negative" }) {
  1913.                     if (subTitle == "Psychology/Negative" && !HasKerbalDamage(currentKerb, "Psychology_Social")) continue;
  1914.                     GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace();
  1915.                     GUILayout.Label($"{(subTitle.Contains("Positive") ? "" : "")} {LocalizationManager.GetTranslation($"KerbalLife/{subTitle}").ToUpper()} {(subTitle.Contains("Positive") ? "" : "")}");
  1916.                     GUILayout.FlexibleSpace(); GUILayout.EndHorizontal();
  1917.                     if (subTitle.Contains("Medicine"))
  1918.                         dmgTypes = new List<string> { "Medicine_Atrophy", "Medicine_Impacts" };
  1919.                     else if (subTitle.Contains("Positive"))
  1920.                         dmgTypes = new List<string> { "Psychology_Role_Kerbonaut_Chief", "Psychology_Role_Kerbonaut_Grad", "Psychology_Role_Veteran", "Psychology_Role_Wings",
  1921.                             "Psychology_Therapy" }.Where(s => HasKerbalDamage(currentKerb, s)).ToList();
  1922.                     else dmgTypes = new string[] { "Psychology_Social_Harassed", "Psychology_Social_Fired_Self", "Psychology_Social_Fired_Other", "Psychology_Social_KerbonautLost" }.Where(s => HasKerbalDamage(currentKerb, s)).ToList();
  1923.                     foreach (string type in dmgTypes) {
  1924.                         bool btn2 = false;
  1925.                         GUILayout.BeginHorizontal();
  1926.                         GUILayout.Label(LocalizationManager.GetTranslation($"KerbalLife/{type.Replace('_', '/')}"));
  1927.                         GUILayout.FlexibleSpace();
  1928.                         bool btn1 = GUILayout.Button(subTitle.Contains("Negative") ? "Tolerance" : "Attitude");
  1929.                         GUILayout.Space(20);
  1930.                         if (!type.StartsWith("Psychology_Role"))
  1931.                             btn2 = GUILayout.Button(type.Contains("Therapy") ? "Progress" : "Current");
  1932.                         GUILayout.EndHorizontal();
  1933.                         if (btn1) UIPartDmg = subTitle.Contains("Negative") ? (100 - KerbalDamageModifier(currentKerb, type) * 100) : KerbalDamageModifier(currentKerb, type) * 100;
  1934.                         else if (btn2) UIPartDmg = type.Contains("Therapy") ? Math.Abs(TryGetKerbalDamage(currentKerb, type) * 100) : (100 - TryGetKerbalDamage(currentKerb, type) * 100);
  1935.                     }
  1936.                 }
  1937.                 GUILayout.EndVertical();
  1938.             }
  1939.             if (isBeingRepaired) {
  1940.                 GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUILayout.Label(guiData.Repairing); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal();
  1941.                 foreach (string repName in guiData.Repairers)
  1942.                     GUILayout.Label(repName);
  1943.             } else if (isIdleEVA && !isInspected) {
  1944.                 GUILayout.BeginHorizontal();
  1945.                 GUILayout.Label($"{guiData.Inspecting}{(inspectTicks % 3 == 0 ? "." : inspectTicks % 3 == 2 ? ".." : "...")}");
  1946.                 GUILayout.HorizontalSlider(inspectTicks, 7, 0, GUILayout.Width(180));
  1947.                 GUILayout.EndHorizontal();
  1948.             }
  1949.         }
  1950.         private void GUIContentPAL() {
  1951.             GUILayout.BeginHorizontal();
  1952.             GUILayout.Space(10);
  1953.             GUILayout.Label(guiData.Wireframe);
  1954.             GUILayout.Space(10);
  1955.             GUILayout.BeginVertical();
  1956.             GUILayout.Space(8);
  1957.             GUILayout.Label(guiData.Impacts);
  1958.             if (ReUtil.IsPALActive(level: 3, bypassVessel: true))
  1959.                 GUILayout.Label(guiData.Temperature);
  1960.             if (UIPart.PartName != "eva_kerbal") {
  1961.                 if (ReUtil.IsPALActive(level: 2, bypassVessel: true))
  1962.                     GUILayout.Label(guiData.GeeForces);
  1963.                 if (ReUtil.IsPALActive(level: 4, bypassVessel: true))
  1964.                     GUILayout.Label(guiData.AngularVel);
  1965.                 if (ReUtil.IsPALActive(level: 6, bypassVessel: true))
  1966.                     GUILayout.Label(guiData.Instability);
  1967.                 if (ReUtil.IsPALActive(level: 5, bypassVessel: true))
  1968.                     GUILayout.Label(guiData.Ullage);
  1969.                 if (ReUtil.IsPALActive(level: 10, bypassVessel: true))
  1970.                     GUILayout.Label(guiData.Boiloff);
  1971.             }
  1972.             GUILayout.EndVertical();
  1973.             GUILayout.EndHorizontal();
  1974.         }
  1975.         private void GUIContentVessel() {
  1976.             bool navSerL = false;
  1977.             bool navSerR = false;
  1978.             bool navCatL = false;
  1979.             bool navCatR = false;
  1980.             bool isPart = UIPart != null && UIPart?.PartName != "eva_kerbal";
  1981.             if (isPart) GUILayout.Label(guiData.Age);
  1982.             GUILayout.Space(4);
  1983.             GUILayout.BeginHorizontal();
  1984.             GUILayout.Label(guiData.SerialTitle);
  1985.             if (isPart) navSerL = GUILayout.Button(guiData.NavLBtn, btnStyle, GUILayout.Width(28));
  1986.             GUILayout.FlexibleSpace();
  1987.             GUILayout.Label(guiData.SerialDesc);
  1988.             GUILayout.FlexibleSpace();
  1989.             if (isPart) navSerR = GUILayout.Button(guiData.NavRBtn, btnStyle, GUILayout.Width(28));
  1990.             GUILayout.EndHorizontal();
  1991.             GUILayout.Space(4);
  1992.             GUILayout.BeginHorizontal();
  1993.             GUILayout.Label(guiData.CategoryTitle);
  1994.             if (isPart) navCatL = GUILayout.Button(guiData.NavLBtn, btnStyle, GUILayout.Width(28));
  1995.             GUILayout.FlexibleSpace();
  1996.             GUILayout.Label(guiData.CategoryDesc);
  1997.             GUILayout.FlexibleSpace();
  1998.             if (isPart) navCatR = GUILayout.Button(guiData.NavRBtn, btnStyle, GUILayout.Width(28));
  1999.             GUILayout.EndHorizontal();
  2000.             if (navSerL || navSerR || navCatL || navCatR) {
  2001.                 List<PartComponent> parts = UIPart.PartOwner.SimulationObject.Vessel.SimulationObject.PartOwner.Parts.ToList();
  2002.                 if (navSerL || navSerR) {
  2003.                     parts = parts.Where(p => p.PartData.category == UIPart.PartData.category).ToList();
  2004.                     int i = parts.FindIndex(p => p.Guid == UIPart.Guid);
  2005.                     if (navSerL) UIPart = parts[i - 1 > -1 ? i - 1 : parts.Count - 1];
  2006.                     else UIPart = parts[i + 1 < parts.Count ? i + 1 : 0];
  2007.                 } else {
  2008.                     parts = parts.GroupBy(p => p.PartData.category).Select(g => g.First()).ToList();
  2009.                     int i = parts.FindIndex(p => p.PartData.category == UIPart.PartData.category);
  2010.                     if (navCatL) UIPart = parts[i - 1 > -1 ? i - 1 : parts.Count - 1];
  2011.                     else UIPart = parts[i + 1 < parts.Count ? i + 1 : 0];
  2012.                 }
  2013.                 guiData.UpdateVesselPart(ReUtil.IsPALActive(UIPart.PartOwner.SimulationObject.Vessel));
  2014.             }
  2015.         }
  2016.         private void GUIContentListMode() {
  2017.             GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUILayout.Label(guiData.VesselsNearby); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal();
  2018.             foreach (VesselComponent vessel in guiData.VesselsList) {
  2019.                 GUILayout.BeginHorizontal();
  2020.                 GUILayout.Space(10);
  2021.                 GUILayout.Label(vessel.DisplayName);
  2022.                 GUILayout.FlexibleSpace();
  2023.                 if (vessel.Guid == activeVessel.Guid && UIPart?.PartOwner?.SimulationObject?.Vessel?.Guid == activeVessel.Guid)
  2024.                     GUILayout.Label(guiData.Current);
  2025.                 else if (vessel.Guid == UIPart?.PartOwner?.SimulationObject?.Vessel?.Guid)
  2026.                     GUILayout.Label(guiData.Connected);
  2027.                 else if (!activeVessel.IsKerbalEVA) {
  2028.                     bool vslSwitch = GUILayout.Button(guiData.Contact);
  2029.                     if (vslSwitch) {
  2030.                         UIPart = vessel.SimulationObject?.PartOwner?.Parts?.First();
  2031.                         guiData.UpdateVesselPart(ReUtil.IsPALActive(activeVessel));
  2032.                     }
  2033.                 }
  2034.                 GUILayout.Space(10);
  2035.                 GUILayout.EndHorizontal();
  2036.             }
  2037.             GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUILayout.Label(guiData.KerbalsOnEVA); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal();
  2038.             // foreach (Tuple<string, string> evaData in guiData.EVAsList) {
  2039.             foreach (VesselComponent evaVessel in guiData.EVAsList) {
  2040.                 bool evaSwitch = false;
  2041.                 GUILayout.BeginHorizontal();
  2042.                 GUILayout.Space(10);
  2043.                 GUILayout.Label(evaVessel.DisplayName);
  2044.                 GUILayout.FlexibleSpace();
  2045.                 GUILayout.Label(repairers.Any(r => r.Guid == evaVessel.Guid) ? guiData.Repairing : guiData.Idle);
  2046.                 GUILayout.Space(10);
  2047.                 if (!activeVessel.IsKerbalEVA)
  2048.                     if (evaVessel.Guid == UIPart?.PartOwner?.SimulationObject?.Vessel?.Guid)
  2049.                         GUILayout.Label(guiData.ModeBtn + " ");
  2050.                     else evaSwitch = GUILayout.Button(guiData.ModeBtn, btnStyle, GUILayout.Width(28));
  2051.                 GUILayout.EndHorizontal();
  2052.                 if (evaSwitch) {
  2053.                     UIPart = evaVessel.SimulationObject?.PartOwner?.Parts?.First();
  2054.                     guiData.UpdateVesselPart(ReUtil.IsPALActive(activeVessel));
  2055.                 }
  2056.             }
  2057.         }
  2058.         private void UpdateGUIVessel() {
  2059.             activeVessel = Game.ViewController?.GetActiveVehicle()?.GetSimVessel();
  2060.             lockUI = false;
  2061.             if (PALMiniActive) UpdatePALMini(false);
  2062.             // lockUI = (activeVessel?.IsKerbalEVA ?? false) && repairers.Any(r => r.Guid == activeVessel.Guid);
  2063.             if (activeVessel?.IsKerbalEVA ?? true) { UIPart = null; guiData.UpdateWindowTitle(); return; }
  2064.             UIPart = activeVessel?.SimulationObject?.PartOwner?.Parts?.First();
  2065.             bool hasPAL = ReUtil.IsPALActive(activeVessel);
  2066.             guiData.UpdateWindowTitle(hasPAL);
  2067.             guiData.UpdateVesselPart(hasPAL);
  2068.         }
  2069.         internal static void UpdatePALMini(bool setActive) {
  2070.             PALMiniActive = setActive;
  2071.             PALMiniUpdate = true;
  2072.             Instance.StartCoroutine(CallbackUtil.DelayedCallback(1, delegate { PALMiniUpdate = false; }));
  2073.         }
  2074.        
  2075.         private void ToggleApp() {
  2076.             KerbalDamageCheckIn();
  2077.             if (guiData.Agency == null) {
  2078.                 guiData.Agency = ReUtil.PlayerReAgency();
  2079.                 guiData.Kerbal = ReUtil.GetKerbalForAgency(guiData.Agency);
  2080.             }
  2081.             if (!ReUtil.IsGameSandbox() && ReUtil.IsPlayerAgency(guiData.Agency)) {
  2082.                 guiData.UpdateDiscovery();
  2083.                 guiData.UpdateSciLevel();
  2084.             }
  2085.             showApp = !showApp;
  2086.             // GameObject.Find("BTN-ReKerbalLife")?.GetComponent<UIValue_WriteBool_Toggle>()?.SetValue(showApp);
  2087.             if (showUI && showApp) {
  2088.                 showUI = false;
  2089.                 // GameObject.Find("BTN-ReDiagnostic")?.GetComponent<UIValue_WriteBool_Toggle>()?.SetValue(showUI);
  2090.             }
  2091.         }
  2092.         private void ToggleApp(bool show) => ToggleApp();
  2093.         private void AppContent(int windowID) {
  2094.             bool modeBtn = false;
  2095.             GUILayout.BeginHorizontal();
  2096.             GUIStyle[] btns = new GUIStyle[] { btnStyle, btnKStyle, btnMStyle, btnFStyle };
  2097.             if (!minUI && !ReUtil.IsGameSandbox())
  2098.                 modeBtn = GUILayout.Button(guiData.ModeIndex == 0 ? "<color=#0D0D0D>∞</color>" : "", btns[guiData.ModeIndex], GUILayout.Width(28), GUILayout.Height(28));
  2099.             else if (ReUtil.IsGameSandbox())
  2100.                 GUILayout.Label(guiData.Icon);
  2101.             GUILayout.FlexibleSpace();
  2102.             bool minBtn = GUILayout.Button($"<color=#0D0D0D>{(minUI ? "" : " - ")}</color>", btnStyle, GUILayout.Width(28));
  2103.             bool closeBtn = GUILayout.Button("<color=#0D0D0D>x</color>", btnXStyle, GUILayout.Width(28));
  2104.             GUILayout.EndHorizontal();
  2105.             if (modeBtn) guiData.ModeIndex = guiData.ModeIndex == 3 ? 0 : guiData.ModeIndex + 1;
  2106.             else if (minBtn) minUI = !minUI;
  2107.             else if (closeBtn) showApp = false;
  2108.             if (!showApp || minUI || modeBtn) { GUI.DragWindow(new Rect(0, 0, 10000, 500)); return; }
  2109.             AppContentHeader();
  2110.             AppContentAgency();
  2111.             GUI.DragWindow(new Rect(0, 0, 10000, 500));
  2112.         }
  2113.         private void AppContentHeader() {
  2114.             bool isPlayer = ReUtil.IsPlayerAgency(guiData.Agency);
  2115.             Texture2D tex = guiData.Kerbal == null ? isPlayer ? GameManager.Instance.Game.AgencyManager.FindAgencyEntryFirst().AgencyFlag.texture :
  2116.                 ReUtil.agencyFlags[guiData.Agency?.Name] : guiData.Kerbal?.Portrait.texture;
  2117.             // if (tex == null) GUILayout.Label("", GUILayout.Height(294));
  2118.             string aName = isPlayer ? (guiData.Agency?.Name ?? "") : LocalizationManager.GetTranslation($"Missions/MissionGranter/Name/{guiData.Agency.Name}");
  2119.             List<KerbalDamage> agencyKerbals = ReUtil.GetKerbalsForAgency(guiData.Agency);
  2120.             bool isChief = guiData.Kerbal?.NameKey == agencyKerbals?.FirstOrDefault()?.Name;
  2121.             KerbalDamage currentKerb = guiData.Kerbal == null ? null : agencyKerbals?.FirstOrDefault(k => k?.Name == guiData.Kerbal?.NameKey);
  2122.             // AddKerbalDamage(currentKerb, "Psychology_Social_Stalk", 0.0005);
  2123.             // GUILayout.Label(guiData.Dots, GUILayout.Height(6), GUILayout.Width(344));
  2124.             if (guiData.Kerbal == null) GUILayout.Space(28);
  2125.             GUILayout.Label(tex, GUILayout.Height(guiData.Kerbal == null ? 225 : 281)); // , GUILayout.Width(344)
  2126.             if (guiData.Kerbal == null) GUILayout.Space(28);
  2127.             // GUILayout.Label(guiData.Dots, GUILayout.Height(6));
  2128.             GUILayout.Label(guiData.Dots, GUILayout.Height(8));
  2129.             GUILayout.BeginHorizontal();
  2130.             GUILayout.BeginVertical(); GUILayout.Space(17);
  2131.             bool navL = GUILayout.Button("", btnLStyle, GUILayout.Width(28));
  2132.             GUILayout.Space(17); GUILayout.EndVertical();
  2133.             GUILayout.FlexibleSpace();
  2134.             // if (aName.Length > 26) aName = aName.Substring(0, 26) + "...";
  2135.             GUILayout.BeginVertical(); GUILayout.FlexibleSpace();
  2136.             GUILayout.Label($"<color=#0D0D0D><size=20>{aName}</size></color>");
  2137.             GUILayout.FlexibleSpace(); GUILayout.EndVertical();
  2138.             GUILayout.FlexibleSpace();
  2139.             GUILayout.BeginVertical(); GUILayout.Space(17);
  2140.             bool navR = GUILayout.Button("", btnRStyle, GUILayout.Width(28));
  2141.             GUILayout.Space(17); GUILayout.EndVertical();
  2142.             GUILayout.EndHorizontal();
  2143.             if (!ReUtil.IsGameSandbox()) {
  2144.                 List<Tuple<Texture2D, string>> agencyTypes = new List<Tuple<Texture2D, string>> { Tuple.Create(guiData.Icon, "KerbalLife/Kerbonaut"),
  2145.                 Tuple.Create(AssetManager.GetAsset<Texture2D>($"KSRe/images/icon3.png"), "KerbalLife/Manufacturer"),
  2146.                 Tuple.Create(AssetManager.GetAsset<Texture2D>($"KSRe/images/icon4.png"), "Menu/Escape/Science"),
  2147.                 Tuple.Create(AssetManager.GetAsset<Texture2D>($"KSRe/images/icon5.png"), "Flight/FlightReport/Crew") };
  2148.                 if (!isPlayer && guiData.Agency.Name != "KSC") agencyTypes.RemoveAt(3);
  2149.                 if (!isPlayer) agencyTypes.RemoveAt(2);
  2150.                 if (!isPlayer && !ReUtil.crewAgencies.ContainsKey(guiData.Agency.Name)) agencyTypes.RemoveAt(0);
  2151.                 if (!ReUtil.mfParts.ContainsKey(guiData.Agency.Name)) agencyTypes.RemoveAt(agencyTypes.Count > 1 ? 1 : 0);
  2152.                 GUILayout.BeginHorizontal();
  2153.                 foreach (var agencyType in agencyTypes) {
  2154.                     GUILayout.FlexibleSpace();
  2155.                     GUILayout.Label(agencyType.Item1);
  2156.                     GUILayout.Label($"<color=#0D0D0D><size=13>{LocalizationManager.GetTranslation(agencyType.Item2).ToUpper()}</size></color>");
  2157.                 }
  2158.                 GUILayout.FlexibleSpace(); GUILayout.EndHorizontal();
  2159.             }
  2160.             GUILayout.Label(guiData.Dots, GUILayout.Height(8));
  2161.             if (navL || navR) {
  2162.                 List<ReAgency> navList = guiData.ModeIndex == 0 ? saveData.agencies : guiData.ModeIndex == 1 ?
  2163.                     saveData.agencies.Where(a => ReUtil.crewAgencies.ContainsKey(a.Name) || a.Name == guiData.Agency.Name || ReUtil.IsPlayerAgency(a)).ToList() : guiData.ModeIndex == 2 ?
  2164.                     saveData.agencies.Where(a => ReUtil.mfParts.ContainsKey(a.Name) || a.Name == guiData.Agency.Name || ReUtil.IsPlayerAgency(a)).ToList() :
  2165.                     saveData.agencies.Where(a => a.Name == "KSC" || ReUtil.IsPlayerAgency(a) || a.Name == guiData.Agency.Name).ToList();
  2166.                 int i = navList.FindIndex(a => a.Name == guiData.Agency.Name);
  2167.                 if (navL) guiData.Agency = navList[i - 1 > -1 ? i - 1 : navList.Count - 1];
  2168.                 else guiData.Agency = navList[i + 1 < navList.Count ? i + 1 : 0];
  2169.                 if (guiData.AgencyIndex == 3 && !ReUtil.IsPlayerAgency(guiData.Agency) && guiData.Agency.Name != "KSC")
  2170.                     guiData.AgencyIndex = 0;
  2171.                 if (guiData.AgencyIndex == 1 && !ReUtil.IsPlayerAgency(guiData.Agency) && !ReUtil.mfParts.ContainsKey(guiData.Agency.Name))
  2172.                     guiData.AgencyIndex = 0;
  2173.                 if (guiData.AgencyIndex == 0 && !ReUtil.IsPlayerAgency(guiData.Agency) && !ReUtil.crewAgencies.ContainsKey(guiData.Agency.Name))
  2174.                     guiData.AgencyIndex = 1;
  2175.                 guiData.Kerbal = guiData.AgencyIndex != 0 ? null : ReUtil.GetKerbalForAgency(guiData.Agency);
  2176.             }
  2177.         }
  2178.         private void AppContentAgency() {
  2179.             bool isPlayer = ReUtil.IsPlayerAgency(guiData.Agency);
  2180.             string[] titles = new string[] { isPlayer ? "Flight/FlightReport/Crew" : "KerbalLife/Roster", "Career/PartGrade", "Menu/VAB/Description", "KerbalLife/CrewStatus", "Career/Title", "Flight/FlightReport/Flight Status" };
  2181.             GUILayout.BeginHorizontal();
  2182.             bool navL = GUILayout.Button("", btnLStyle, GUILayout.Width(28));
  2183.             GUILayout.FlexibleSpace();
  2184.             GUILayout.Label($"<color=#0D0D0D><size=19>{LocalizationManager.GetTranslation(titles[(isPlayer && guiData.AgencyIndex == 1 ? 4 : isPlayer && guiData.AgencyIndex == 3 ? 5 : guiData.AgencyIndex)]).ToUpper()}</size></color>");
  2185.             GUILayout.FlexibleSpace();
  2186.             bool navR = GUILayout.Button("", btnRStyle, GUILayout.Width(28));
  2187.             GUILayout.EndHorizontal();
  2188.             GUILayout.Label(guiData.Dots, GUILayout.Height(8));
  2189.             if (guiData.AgencyIndex == 0) AppContentRoster();
  2190.             else if (guiData.AgencyIndex == 3) AppContentCrew();
  2191.             else if (guiData.AgencyIndex == 2) {
  2192.                 string desc = isPlayer ? LocalizationManager.GetTranslation("Menu/NewCampaign/Agency Name Menu Description").Split(new string[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries)[0] :
  2193.                     LocalizationManager.GetTranslation($"Missions/MissionGranter/Description/{guiData.Agency.Name}");
  2194.                 GUILayout.Label($"<color=#0D0D0D><size=14>{desc}</size></color>"); // -------------------------------------------\n<i></i>\n-------------------------------------------
  2195.             } else if (isPlayer) AppContentScience();
  2196.             else if (guiData.Agency.partRecords.Count == 0)
  2197.                 GUILayout.Label($"<color=#0D0D0D><size=14>{LocalizationManager.GetTranslation("Career/PartGradeHint", new object[] { LocalizationManager.GetTranslation($"Missions/MissionGranter/Name/{guiData.Agency.Name}") })}</size></color>");
  2198.             else foreach (var pair in guiData.Agency.partRecords) {
  2199.                     float rec = (float)TryGetPartRecord(pair.Key, true);
  2200.                     GUILayout.BeginHorizontal();
  2201.                     GUILayout.Label($"<color=#0D0D0D>{LocalizationManager.GetTranslation($"Parts/Title/{pair.Key}")}</color>"); // : <b>{ReUtil.GetPartGradeStr(rec)}</b>
  2202.                     GUILayout.HorizontalSlider(rec, 0f, 1f, GUILayout.Width(150));
  2203.                     GUILayout.Box(AssetManager.GetAsset<Texture2D>($"KSRe/images/grade_{ReUtil.GetGradeStr(rec)}.png"), GUILayout.Width(28), GUILayout.Height(28));
  2204.                     GUILayout.EndHorizontal();
  2205.                 }
  2206.             if (navL || navR) {
  2207.                 int cap = isPlayer || guiData.Agency.Name == "KSC" ? 3 : 2;
  2208.                 if (navL) guiData.AgencyIndex = guiData.AgencyIndex - 1 > -1 ? guiData.AgencyIndex - 1 : cap;
  2209.                 else guiData.AgencyIndex = guiData.AgencyIndex + 1 < cap + 1 ? guiData.AgencyIndex + 1 : 0;
  2210.                 if (guiData.AgencyIndex == 1 && (ReUtil.IsGameSandbox() || (!isPlayer && !ReUtil.mfParts.ContainsKey(guiData.Agency.Name))))
  2211.                     guiData.AgencyIndex = navL ? 0 : 2;
  2212.                 if (guiData.AgencyIndex == 0 && !isPlayer && !ReUtil.crewAgencies.ContainsKey(guiData.Agency.Name))
  2213.                     guiData.AgencyIndex = navL ? 2 : 1;
  2214.                 guiData.Kerbal = guiData.AgencyIndex != 0 ? null : ReUtil.GetKerbalForAgency(guiData.Agency);
  2215.                 //if (guiData.AgencyIndex == 0)
  2216.                 //    ReUtil.PushNotify(guiData.Kerbal.NameKey, "Hello", NotificationImportance.Low);
  2217.             }
  2218.         }
  2219.         private void AppContentScience() {
  2220.             int count = ReUtil.PlayerReAgency().rocketScience.Select(s => s.Key.Split('_')[0]).Distinct().Count();
  2221.             bool navL = false;
  2222.             bool navR = false;
  2223.             GUILayout.BeginHorizontal();
  2224.             GUILayout.Label($"<color=#0D0D0D><size=13>{LocalizationManager.GetTranslation("Missions/TriumphWindow/AcceptScienceReward").ToUpper()}:</size></color>");
  2225.             GUILayout.FlexibleSpace();
  2226.             GUILayout.Label(AssetManager.GetAsset<Texture2D>($"KSRe/images/rocket_sci.png"));
  2227.             GUILayout.Label($"<color=#0D0D0D><size=17>{ReUtil.GetTotalScience()}</size></color>");
  2228.             GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal();
  2229.             GUILayout.Label(guiData.Discovery);
  2230.             int progress = (int)Math.Round(guiData.DiscoveryValue * 200);
  2231.             GUILayout.Box("", GUILayout.Width(progress), GUILayout.Height(10));
  2232.             GUILayout.Space(200 - progress);
  2233.             // GUILayout.HorizontalSlider(guiData.DiscoveryValue, 0f, 1f, GUILayout.Width(200));
  2234.             GUILayout.EndHorizontal();
  2235.             GUILayout.Label(guiData.Dots, GUILayout.Height(8));
  2236.             GUILayout.BeginHorizontal();
  2237.             if (count > 1) navL = GUILayout.Button("", btnLStyle, GUILayout.Width(28));
  2238.             GUILayout.FlexibleSpace(); GUILayout.Label(guiData.SciTitle); GUILayout.FlexibleSpace();
  2239.             if (count > 1) navR = GUILayout.Button("", btnRStyle, GUILayout.Width(28));
  2240.             GUILayout.EndHorizontal();
  2241.             GUILayout.BeginHorizontal();
  2242.             GUILayout.Label(guiData.Level);
  2243.             GUILayout.Box(AssetManager.GetAsset<Texture2D>($"KSRe/images/{guiData.SciLvl}.png"), GUILayout.Width(28), GUILayout.Height(28));
  2244.             progress = (int)Math.Round(guiData.SciLvlProgress * 160);
  2245.             GUILayout.Box("", GUILayout.Width(progress), GUILayout.Height(10));
  2246.             GUILayout.Space(160 - progress);
  2247.             // GUILayout.HorizontalSlider(guiData.SciLvlProgress, 0f, 1f, GUILayout.Width(160));
  2248.             GUILayout.EndHorizontal();
  2249.             GUILayout.BeginVertical();
  2250.             foreach (var pair in guiData.SciSubtypes) {
  2251.                 GUILayout.BeginHorizontal();
  2252.                 GUILayout.Label(pair.Item1);
  2253.                 GUILayout.Box(AssetManager.GetAsset<Texture2D>($"KSRe/images/{Math.Truncate(pair.Item2)}.png"), GUILayout.Width(28), GUILayout.Height(28));
  2254.                 progress = (int)Math.Round((pair.Item2 - (float)Math.Truncate(pair.Item2)) * 100);
  2255.                 GUILayout.Box("", GUILayout.Width(progress), GUILayout.Height(10));
  2256.                 GUILayout.Space(100 - progress);
  2257.                 // GUILayout.HorizontalSlider(pair.Item2 - (float)Math.Truncate(pair.Item2), 0f, 1f, GUILayout.Width(100));
  2258.                 GUILayout.EndHorizontal();
  2259.             }
  2260.             GUILayout.EndVertical();
  2261.             if (navL || navR) { // ReUtil.careerSpecTechs.Where(s => !s.Key.Contains("_")).Count();
  2262.                 if (navL) guiData.SciIndex = guiData.SciIndex - 1 > -1 ? guiData.SciIndex - 1 : count - 1;
  2263.                 else guiData.SciIndex = guiData.SciIndex + 1 < count ? guiData.SciIndex + 1 : 0;
  2264.                 guiData.UpdateSciLevel();
  2265.             }
  2266.         }
  2267.         private void AppContentCrew() {
  2268.             if (guiData.Agency.Name == "KSC") {
  2269.                 int count = saveData.kerbals.Count(k => k.status.StartsWith("KSC>"));
  2270.                 if (count == 0)
  2271.                     GUILayout.Label($"<color=#0D0D0D><size=14>{LocalizationManager.GetTranslation($"KerbalLife/NoCrewKSCHint")}</size></color>");
  2272.                 // foreach (string status in new string[] { "READY", "STANDBY", "FITNESS", "THERAPY", "RECOVERY", "MED BAY" }) {
  2273.                 //if (saveData.kerbals.Count(k => k.status == $"KSC>{status}") > 0) {
  2274.                 //    GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace();
  2275.                 //    GUILayout.Label($"<color=#0D0D0D><size=14>{status}</size></color>");
  2276.                 //    GUILayout.FlexibleSpace(); GUILayout.EndHorizontal();
  2277.                 //}
  2278.                 else if (count > 8) UIScrollPos = GUILayout.BeginScrollView(UIScrollPos, scrollStyle, GUILayout.Height(260));
  2279.                 foreach (KerbalDamage kerbal in saveData.kerbals.Where(k => k.status?.StartsWith("KSC>") ?? false)) {
  2280.                     GUILayout.BeginHorizontal();
  2281.                     GUILayout.Label(AssetManager.GetAsset<Texture2D>($"KSRe/images/icon_d{ReUtilKerb.GetDamageInt(kerbal.damage.Values.Sum())}{(kerbal.damage.ContainsKey("Psychology_Role_Wings") ? "w" : "")}.png"), GUILayout.Width(28), GUILayout.Height(28));
  2282.                     // GUILayout.FlexibleSpace();
  2283.                     GUILayout.Label($"<color=#0D0D0D>{kerbal.Name}</color>");
  2284.                     GUILayout.FlexibleSpace();
  2285.                     GUILayout.Label($"<color=#0D0D0D><size=14>{kerbal.status?.Split('>').Last() ?? ""}</size></color>");
  2286.                     GUILayout.Space(4);
  2287.                     GUILayout.EndHorizontal();
  2288.                 }
  2289.                 if (count > 8) GUILayout.EndScrollView();
  2290.                 // }
  2291.             } else {
  2292.                 int count = saveData.kerbals.Count(k => !k.status.StartsWith("KSC>"));
  2293.                 if (count == 0)
  2294.                     GUILayout.Label($"<color=#0D0D0D><size=14>{LocalizationManager.GetTranslation($"KerbalLife/NoCrewFlightsHint")}</size></color>");
  2295.                 else if (count > 6) UIScrollPos = GUILayout.BeginScrollView(UIScrollPos, scrollStyle, GUILayout.Height(260));
  2296.                 foreach (string status in saveData.kerbals.Select(k => k.status.Split('>').First()).Where(s => s != "KSC" && s != "EVA").Distinct()) {
  2297.                     GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace();
  2298.                     GUILayout.Label($"<color=#0D0D0D><size=14>{status}</size></color>");
  2299.                     GUILayout.FlexibleSpace();
  2300.                     GUILayout.Label(AssetManager.GetAsset<Texture2D>($"KSRe/images/cel_{saveData.kerbals.FirstOrDefault(k => k.status.StartsWith(status))?.celName ?? "Kerbin"}.png"), GUILayout.Width(28), GUILayout.Height(28));
  2301.                     GUILayout.EndHorizontal();
  2302.                     foreach (KerbalDamage kerbal in saveData.kerbals.Where(k => k.status.StartsWith(status))) {
  2303.                         GUILayout.BeginHorizontal();
  2304.                         GUILayout.Label(AssetManager.GetAsset<Texture2D>($"KSRe/images/icon_d{ReUtilKerb.GetDamageInt(kerbal.damage.Values.Sum())}{(kerbal.damage.ContainsKey("Psychology_Role_Wings") ? "w" : "")}.png"), GUILayout.Width(28), GUILayout.Height(28));
  2305.                         GUILayout.Label($"<color=#0D0D0D>{kerbal.Name}</color>");
  2306.                         GUILayout.FlexibleSpace();
  2307.                         GUILayout.Label($"<color=#0D0D0D><size=14>{kerbal.status?.Split('>').Last() ?? ""}</size></color>");
  2308.                         GUILayout.Space(4);
  2309.                         GUILayout.EndHorizontal();
  2310.                     }
  2311.                 }
  2312.                 if (saveData.kerbals.Count(k => k.status.StartsWith("EVA>")) > 0) {
  2313.                     GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace();
  2314.                     GUILayout.Label($"<color=#0D0D0D><size=14>{LocalizationManager.GetTranslation("Diagnostic/KerbalsOnEVA")}</size></color>");
  2315.                     GUILayout.FlexibleSpace(); GUILayout.EndHorizontal();
  2316.                 }
  2317.                 foreach (KerbalDamage kerbal in saveData.kerbals.Where(k => k.status.StartsWith("EVA>"))) {
  2318.                     GUILayout.BeginHorizontal();
  2319.                     GUILayout.Label(AssetManager.GetAsset<Texture2D>($"KSRe/images/icon_d{ReUtilKerb.GetDamageInt(kerbal.damage.Values.Sum())}{(kerbal.damage.ContainsKey("Psychology_Role_Wings") ? "w" : "")}.png"), GUILayout.Width(28), GUILayout.Height(28));
  2320.                     GUILayout.Label($"<color=#0D0D0D>{kerbal.Name}</color>");
  2321.                     GUILayout.FlexibleSpace();
  2322.                     GUILayout.Label($"<color=#0D0D0D><size=14>{kerbal.status?.Replace("EVA>", "") ?? ""}</size></color>");
  2323.                     GUILayout.Space(20);
  2324.                     GUILayout.Label(AssetManager.GetAsset<Texture2D>($"KSRe/images/cel_{kerbal.celName ?? "Kerbin"}.png"), GUILayout.Width(28), GUILayout.Height(28));
  2325.                     GUILayout.EndHorizontal();
  2326.                 }
  2327.                 if (count > 6) GUILayout.EndScrollView();
  2328.             }
  2329.         }
  2330.         private void AppContentRoster() {
  2331.             bool navL = false;
  2332.             bool navR = false;
  2333.             bool swapBtn = false;
  2334.             bool isSandbox = ReUtil.IsGameSandbox();
  2335.             List<KerbalDamage> agencyKerbals = ReUtil.GetKerbalsForAgency(guiData.Agency);
  2336.             bool isPlayer = ReUtil.IsPlayerAgency(guiData.Agency);
  2337.             bool isChief = guiData.Kerbal?.NameKey == agencyKerbals?.FirstOrDefault()?.Name;
  2338.             KerbalDamage currentKerb = guiData.Kerbal == null ? null : agencyKerbals?.FirstOrDefault(k => k?.Name == guiData.Kerbal?.NameKey);
  2339.             int count = saveData.kerbals.Count(k => k.agencyName == guiData.Agency.Name);
  2340.             if (count == 0)
  2341.                 GUILayout.Label($"<color=#0D0D0D><size=14>{LocalizationManager.GetTranslation($"KerbalLife/NoKerbonautsHint", new object[] { isPlayer ? ReUtil.PlayerAgencyName() : LocalizationManager.GetTranslation($"Missions/MissionGranter/Name/{guiData.Agency.Name}") })}</size></color>");
  2342.             else {
  2343.                 if (guiData.Kerbal != null) {
  2344.                     GUILayout.BeginHorizontal(); GUILayout.BeginVertical();
  2345.                     GUILayout.Label($"<color=#0D0D0D><size=13>{LocalizationManager.GetTranslation($"KerbalLife/{(isChief ? "ChiefKerbonaut" : "Kerbonaut")}").ToUpper()}:</size></color>");
  2346.                     GUILayout.Label(guiData.Training);
  2347.                     //if (!saveData.playerConfirmed && leader != null)
  2348.                     //    guiData.newKerbName = GUILayout.TextField(guiData.newKerbName ?? "", 21, GUILayout.Width(180));
  2349.                     //else if (!saveData.playerConfirmed && leader != null) GUILayout.Label(guiData.Surname);
  2350.                     GUILayout.EndVertical(); GUILayout.FlexibleSpace(); GUILayout.BeginVertical(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace();
  2351.                     GUILayout.Label($"<color=#0D0D0D><size=18>{guiData.Kerbal?.NameKey}</size></color>");
  2352.                     GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace();
  2353.                     GUILayout.Label($"<color=#0D0D0D>{LocalizationManager.GetTranslation($"Career/{currentKerb?.Trait}/Crew")}</color>");
  2354.                     GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.FlexibleSpace();
  2355.                     if (currentKerb?.status == "KSC>READY" && !(isPlayer && guiData.CrewIndex > 0)) { // && (isPlayer || !isChief || isSandbox)
  2356.                         GUILayout.BeginVertical(); GUILayout.FlexibleSpace();
  2357.                         swapBtn = GUILayout.Button($"<size=13><b>{(isPlayer ? "F" : "H")}IRE</b></size>");
  2358.                         GUILayout.FlexibleSpace(); GUILayout.EndVertical();
  2359.                     }
  2360.                     GUILayout.EndHorizontal(); GUILayout.Label(guiData.Dots, GUILayout.Height(8));
  2361.                 }
  2362.                 if (isPlayer) {
  2363.                     string[] titles = new string[] { "KerbalLife/Roster", "Career/Medicine/Title", "Career/Psychology/Title" };
  2364.                     GUILayout.BeginHorizontal();
  2365.                     navL = GUILayout.Button("", btnLStyle, GUILayout.Width(28));
  2366.                     GUILayout.FlexibleSpace();
  2367.                     GUILayout.Label($"<color=#0D0D0D><size=19>{LocalizationManager.GetTranslation(titles[guiData.CrewIndex]).ToUpper()}</size></color>");
  2368.                     GUILayout.FlexibleSpace();
  2369.                     navR = GUILayout.Button("", btnRStyle, GUILayout.Width(28));
  2370.                     GUILayout.EndHorizontal();
  2371.                 }
  2372.                 if (!isPlayer || guiData.CrewIndex == 0) {
  2373.                     if (count > 5) UIScrollPos = GUILayout.BeginScrollView(UIScrollPos, scrollStyle, GUILayout.Height(160));
  2374.                     foreach (KerbalDamage kerbal in saveData.kerbals.Where(k => k.agencyName == guiData.Agency.Name)) {
  2375.                         bool kerbBtn = false;
  2376.                         GUILayout.BeginHorizontal();
  2377.                         if (guiData.Kerbal?.NameKey != kerbal.Name)
  2378.                             kerbBtn = GUILayout.Button("<color=#0D0D0D>>¤<</color>", btnStyle, GUILayout.Width(28), GUILayout.Height(28));
  2379.                         else GUILayout.Label(AssetManager.GetAsset<Texture2D>($"KSRe/images/icon{(kerbal.damage.ContainsKey("Psychology_Role_Wings") ? "_ww" : "")}.png"), GUILayout.Width(28), GUILayout.Height(28));
  2380.                         GUILayout.FlexibleSpace(); GUILayout.Label($"<color=#0D0D0D>{kerbal.Name}</color>"); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal();
  2381.                         if (kerbBtn) {
  2382.                             Game.SessionManager.KerbalRosterManager.TryGetKerbalByName(kerbal.Name, out guiData.Kerbal);
  2383.                             // ReUtil.PushNotify(guiData.Kerbal.NameKey, "Hello", NotificationImportance.Low);
  2384.                         }
  2385.                     }
  2386.                     if (count > 5) GUILayout.EndScrollView();
  2387.                 } else if (currentKerb != null) {
  2388.                     GUILayout.BeginVertical();
  2389.                     Dictionary<string, float> dmgTypes = new Dictionary<string, float>();
  2390.                     foreach (string subTitle in new string[] { "Medicine/Negative", "Psychology/Positive", "Psychology/Negative" }) {
  2391.                         if ((guiData.CrewIndex == 1 && subTitle.Contains("Psychology")) || (guiData.CrewIndex == 2 && !subTitle.Contains("Psychology"))) continue;
  2392.                         if (subTitle == "Psychology/Negative" && !HasKerbalDamage(currentKerb, "Psychology_Social")) continue;
  2393.                         GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace();
  2394.                         GUILayout.Label($"<color=#0D0D0D><size=14>{(subTitle.Contains("Positive") ? "" : "")} {LocalizationManager.GetTranslation($"KerbalLife/{subTitle}").ToUpper()} {(subTitle.Contains("Positive") ? "" : "")}</size></color>");
  2395.                         GUILayout.FlexibleSpace(); GUILayout.EndHorizontal();
  2396.                         if (guiData.CrewIndex == 1)
  2397.                             dmgTypes = new string[] { "Medicine_Atrophy", "Medicine_Impacts" }.ToDictionary(s => s, s => 1 - KerbalDamageModifier(currentKerb, s));
  2398.                         else if (subTitle.Contains("Positive"))
  2399.                             dmgTypes = new string[] { "Psychology_Role_Kerbonaut_Chief", "Psychology_Role_Kerbonaut_Grad", "Psychology_Role_Veteran", "Psychology_Role_Wings", "Psychology_Therapy"
  2400.                         }.Where(s => HasKerbalDamage(currentKerb, s)).ToDictionary(s => s, s => KerbalDamageModifier(currentKerb, s));
  2401.                         else if (subTitle.Contains("Negative"))
  2402.                             dmgTypes = new string[] { "Psychology_Social_Harassed", "Psychology_Social_Fired_Self", "Psychology_Social_Fired_Other", "Psychology_Social_KerbonautLost"
  2403.                         }.Where(s => HasKerbalDamage(currentKerb, s)).ToDictionary(s => s, s => 1 - KerbalDamageModifier(currentKerb, s));
  2404.                         foreach (var pair in dmgTypes) {
  2405.                             GUILayout.BeginHorizontal();
  2406.                             GUILayout.Label($"<color=#0D0D0D><size=13>:: {LocalizationManager.GetTranslation($"KerbalLife/{pair.Key.Replace('_', '/')}")}</size></color>");
  2407.                             GUILayout.FlexibleSpace();
  2408.                             GUILayout.HorizontalSlider(pair.Value, 0f, 1f, GUILayout.Width(100));
  2409.                             GUILayout.Box($"<color=white>{ReUtilKerb.GetPsychologyStr(pair.Value)}</color>", GUILayout.Width(28), GUILayout.Height(28));
  2410.                             GUILayout.EndHorizontal();
  2411.                         }
  2412.                     }
  2413.                     GUILayout.EndVertical();
  2414.                 }
  2415.             }
  2416.             if (navL || navR) {
  2417.                 guiData.CrewIndex = guiData.CrewIndex == 2 ? 0 : 2;
  2418.                 //if (navL) guiData.CrewIndex = guiData.CrewIndex > 0 ? guiData.CrewIndex - 1 : 2;
  2419.                 //else guiData.CrewIndex = guiData.CrewIndex < 2 ? guiData.CrewIndex + 1 : 0;
  2420.             } else if (swapBtn) {
  2421.                 bool update = false;
  2422.                 double socialDmg = TryGetKerbalDamage(currentKerb, "Psychology_Social");
  2423.                 if (isPlayer) {
  2424.                     ReUtil.PushNotify(currentKerb.Name, "Sad", NotificationImportance.Medium);
  2425.                     double dmg = (currentKerb.damage.Where(p => p.Key.StartsWith("Psychology_Role")).Sum(p => Math.Abs(p.Value)) + TryGetKerbalDamage(currentKerb, "Psychology_Social_Fired")) * 3;
  2426.                     saveData.kerbals.Where(k => k.Name != currentKerb.Name).ToList().ForEach(k => AddKerbalDamage(k, "Psychology_Social_Fired_Other", dmg / 10));
  2427.                     AddKerbalDamage(currentKerb, "Psychology_Social_Fired_Self", dmg);
  2428.                     update = true;
  2429.                 } else if (isChief) {
  2430.                     ReUtil.PushNotify(currentKerb.Name, "No", NotificationImportance.High);
  2431.                     AddKerbalDamage(currentKerb, "Psychology_Social_Harassed", socialDmg + 5);
  2432.                 } else if (socialDmg > 50) {
  2433.                     ReUtil.PushNotify(currentKerb.Name, "Bully", NotificationImportance.High);
  2434.                     saveData.kerbals.Where(k => k.Name != currentKerb.Name).ToList().ForEach(k => AddKerbalDamage(k, "Psychology_Social_Harassed", 10));
  2435.                     //} else if (isChief && saveData.kerbals.FindIndex(k => k.Name == guiData.Kerbal.NameKey) > saveData.kerbals.FindIndex(k => k.Name == ReUtil.GetKerbalsForAgency(ReUtil.PlayerReAgency()).FirstOrDefault()?.Name)) {
  2436.                     //    ReUtil.PushNotify(currentKerb.Name, "No", NotificationImportance.High);
  2437.                 } else if (socialDmg > 25) {
  2438.                     ReUtil.PushNotify(currentKerb.Name, "No", NotificationImportance.High);
  2439.                     AddKerbalDamage(currentKerb, "Psychology_Social_Harassed", socialDmg + 5);
  2440.                 } else {
  2441.                     ReUtil.PushNotify(currentKerb.Name, "Yes", NotificationImportance.Low);
  2442.                     update = true;
  2443.                 }
  2444.                 if (update) {
  2445.                     ReAgency[] agencies = saveData.agencies.Where(a => a.Name != "KSC" && ReUtil.crewAgencies.ContainsKey(a.Name)).ToArray();
  2446.                     string otherAgencyName = agencies[UnityEngine.Random.Range(0, agencies.Length - 1)].Name;
  2447.                     //if (agencyKerbals.Count == 1)
  2448.                     //    saveData.kerbals.LastOrDefault(k => k.agencyName == otherAgencyName).agencyName = guiData.Agency.Name;
  2449.                     if (currentKerb != null)
  2450.                         currentKerb.agencyName = !isPlayer ? ReUtil.PlayerAgencyName() : otherAgencyName;
  2451.                     // EmptyAgencyCheck();
  2452.                     guiData.Kerbal = null; // ReUtil.GetKerbalForAgency(guiData.Agency)
  2453.                 }
  2454.             }
  2455.         }
  2456.        
  2457.  
  2458.         private void KerbalDamageCheckIn() {
  2459.             List<KerbalInfo> kerbis = Game.SessionManager.KerbalRosterManager.GetAllKerbals();
  2460.             saveData.kerbals.RemoveAll(kd => kerbis.All(ki => ki.NameKey != kd.Name));
  2461.             //if (saveData.kerbals.Count < kerbis.Count)
  2462.             kerbis?.ForEach(k => SetupKerbalDamage(k));
  2463.             // EmptyAgencyCheck();
  2464.         }
  2465.         private void SetupKerbalDamage(KerbalInfo kerbi) {
  2466.             string[] vets = new string[] { "Valentina Kerman", "Jebediah Kerman", "Bill Kerman", "Bob Kerman" };
  2467.             KerbalDamage kerbd = saveData.kerbals.FirstOrDefault(k => k.Name == kerbi.NameKey);
  2468.             if (kerbd != null) {
  2469.                 if (vets.Contains(kerbd.Name) && !kerbd.damage.ContainsKey("Psychology_Role_Veteran"))
  2470.                     AddKerbalDamage(kerbd, "Psychology_Role_Veteran", -10, true);
  2471.                 if (TryGetKerbalDamage(kerbd, "Medicine_Atrophy") == 0)
  2472.                     AddKerbalDamage(kerbd, "Medicine_Atrophy", -10, true);
  2473.                 return;
  2474.             }
  2475.             List<ReAgency> crewAgencies = saveData.agencies.Where(a => ReUtil.crewAgencies.ContainsKey(a.Name)).ToList();
  2476.             ReAgency agency = null;
  2477.             string key = ReUtil.crewAgencies.Any(p => p.Value.Contains(kerbi.NameKey)) ? ReUtil.crewAgencies.FirstOrDefault(p => p.Value.Contains(kerbi.NameKey)).Key : null;
  2478.             if (key != null)
  2479.                 agency = saveData.agencies.FirstOrDefault(a => a.Name == key);
  2480.             //if (vets.Contains(kerbi.NameKey))
  2481.             //    agency = saveData.agencies.FirstOrDefault(a => a.Name == "KSC");
  2482.             //else if (kerbi.NameKey == "Tim C. Kerman")
  2483.             //    agency = saveData.agencies.FirstOrDefault(a => a.Name == "KSC");
  2484.             if (agency == null && ReUtil.GetKerbalsForAgency(ReUtil.PlayerReAgency())?.Count == 0)
  2485.                 agency = ReUtil.PlayerReAgency();
  2486.             if (agency == null)
  2487.                 agency = crewAgencies.Where(a => a.Name != "KSC").LastOrDefault(a => saveData.kerbals.All(k => k.agencyName != a.Name));
  2488.             if (agency == null) {
  2489.                 UnityEngine.Random.InitState(kerbi.NameKey.GetHashCode());
  2490.                 agency = crewAgencies.Where(a => a.Name != "KSC").ElementAt(UnityEngine.Random.Range(0, crewAgencies.Count - 1));
  2491.             }
  2492.             kerbd = new KerbalDamage(kerbi.NameKey, ReUtil.RandomTrait(), agency.Name) { status = "KSC>READY", celName = "Kerbin" };
  2493.             if (vets.Contains(kerbi.NameKey))
  2494.                 AddKerbalDamage(kerbd, "Psychology_Role_Veteran", -10, true);
  2495.             AddKerbalDamage(kerbd, "Medicine_Atrophy", -10, true);
  2496.             saveData.kerbals.Add(kerbd);
  2497.             if (!showApp && ReUtil.IsPlayerAgency(agency) && ReUtil.GetKerbalsForAgency(agency)?.Count == 1)
  2498.                 ToggleApp();
  2499.         }
  2500.         //private static void EmptyAgencyCheck() {
  2501.         //    if (saveData.kerbals.Count == 0) return;
  2502.         //    for (int i = 0; i < (saveData.agencies.Count - saveData.kerbals.Select(k => k.agencyName).Distinct().Count()); i++) {
  2503.         //        string fullAgName = saveData.agencies.LastOrDefault(a => saveData.kerbals.Count(k => k.agencyName == a.Name) > 1).Name;
  2504.         //        string emptyAgName = saveData.agencies.FirstOrDefault(a => saveData.kerbals.Count(k => k.agencyName == a.Name) == 0).Name;
  2505.         //        saveData.kerbals.LastOrDefault(k => k.agencyName == fullAgName).agencyName = emptyAgName;
  2506.         //    }
  2507.         //}
  2508.        
  2509.         private void UpdateKerbalStatus(double fixedDeltaT) {
  2510.             foreach (KerbalDamage kerbd in saveData.kerbals) {
  2511.                 if (kerbd.damage.Keys.All(k => !k.StartsWith("Psychology_Role_Kerbonaut"))) {
  2512.                     if (kerbd.Name == saveData.kerbals.Where(k => k.agencyName == kerbd.agencyName)?.FirstOrDefault()?.Name) {
  2513.                         AddKerbalDamage(kerbd, "Psychology_Role_Kerbonaut_Chief", -10, true);
  2514.                     } else AddKerbalDamage(kerbd, "Psychology_Role_Kerbonaut_Grad", -5, true);
  2515.                 }
  2516.                 Game.SessionManager.KerbalRosterManager.TryGetKerbalByName(kerbd.Name, out KerbalInfo kerbi);
  2517.                 if (kerbi == null) continue;
  2518.                 if (kerbi.Location.SimObjectId.Guid == Game.SessionManager.KerbalRosterManager.KSCGuid.Guid) {
  2519.                     if (!kerbd.status.StartsWith("KSC>"))
  2520.                         kerbd.status = "KSC>STANDBY";
  2521.                     kerbd.celName = "Kerbin";
  2522.                     if (kerbd.damage.ContainsKey("Psychology_Role_WingsPending")) {
  2523.                         kerbd.damage.Remove("Psychology_Role_WingsPending");
  2524.                         AddKerbalDamage(kerbd, "Psychology_Role_Wings", -10, true);
  2525.                         ReUtil.PushNotify(kerbd.Name, "Wings", NotificationImportance.Low, false);
  2526.                     }
  2527.                     continue;
  2528.                 }
  2529.                 SimulationObjectModel simObj = Game.ViewController.Universe.FindSimObject(kerbi.Location.SimObjectId);
  2530.                 kerbd.celName = simObj?.Part?.PartCelestialBody?.Name;
  2531.                 if (repairers.Any(r => r.Name == kerbd.Name))
  2532.                     kerbd.status = "EVA>REPAIRS";
  2533.                 else {
  2534.                     string partName = simObj?.Part?.PartName;
  2535.                     if (partName == null || ReUtil.partKerbStats.All(s => !s.Value.Contains(partName ?? "-")))
  2536.                         kerbd.status = "UNKNOWN";
  2537.                     else {
  2538.                         kerbd.status = $"{(partName == "eva_kerbal" ? "EVA" : simObj?.Part?.PartOwner?.SimulationObject?.Vessel?.DisplayName)}>";
  2539.                         //kerbd.status += simObj?.Part?.PartOwner?.SimulationObject?.Vessel?.SimulationObject?.Telemetry.CommNetConnectionStatus == ConnectionNodeStatus.Connected ||
  2540.                         //    partName == "eva_kerbal" ? ReUtil.partKerbStats.FirstOrDefault(s => s.Value.Contains(partName)).Key : "COMMS BLACKOUT";
  2541.                         kerbd.status += ReUtil.partKerbStats.FirstOrDefault(s => s.Value.Contains(partName)).Key;
  2542.                     }
  2543.                 }
  2544.                 if (kerbd.status.Contains("PILOT")) {
  2545.                     PartComponentModule_Command moduleCmd = null;
  2546.                     Data_Command dataCmd = null;
  2547.                     simObj?.Part?.TryGetModule(out moduleCmd);
  2548.                     if (moduleCmd == null) continue;
  2549.                     moduleCmd?.DataModules.TryGetByType(out dataCmd);
  2550.                     if (dataCmd == null) continue;
  2551.                     if (!dataCmd.isCommandEnabled.GetValue() || simObj?.Part?.PartOwner?.SimulationObject?.Vessel?.GetControlOwner()?.Guid != simObj?.Part?.Guid)
  2552.                         kerbd.status = kerbd.status.Replace("PILOT", "PASSENGER");
  2553.                     else if (dataCmd.IsHibernating)
  2554.                         kerbd.status = kerbd.status.Replace("PILOT", "STANDBY PILOT");
  2555.                     else {
  2556.                         List<int> occSeats = Game.SessionManager.KerbalRosterManager.GetAllKerbalsInSimObject(kerbi.Location.SimObjectId).Select(k => k.Location.PositionIndex).ToList();
  2557.                         if (occSeats.Count == 1 || kerbi.Location.PositionIndex == occSeats.Min())
  2558.                             kerbd.status = kerbd.status.Replace("PILOT", "CAPTAIN");
  2559.                         else kerbd.status = kerbd.status.Replace("PILOT", "CO-PILOT");
  2560.                     }
  2561.                 }
  2562.                 //double geeForce = simObj?.Part?.PartOwner?.SimulationObject?.Vessel?.geeForce ?? 0;
  2563.                 //if (kerbd.status == "EXERCISE" && TryGetKerbalDamage(kerbd, "Medicine_Atrophy") < (1 - Mathf.Clamp01((float)geeForce)) * 25)
  2564.                 //    AddKerbalDamage(kerbd, "Medicine_Atrophy", (fixedDeltaT / 21600) * (0.25 - geeForce));
  2565.             }
  2566.         }
  2567.        
  2568.         private void InitOrUpdateSave() {
  2569.             Version.TryParse(saveData.modVersion ?? VERSION, out Version version);
  2570.             if (saveData.modVersion != null && version?.Minor == 24 && version?.Build <= 13) {
  2571.                 ReAgency player = ReUtil.PlayerReAgency();
  2572.                 Dictionary<string, string> fixSciType = new Dictionary<string, string>() {
  2573.                     ["Engineering_Wheel"] = "Electrical_Wheel", ["Flight_SOI"] = "Control_SOI",
  2574.                     ["Command_CrewObservation"] = "Psychology_CrewObservation"
  2575.                 };
  2576.                 foreach (var pair in fixSciType.Where(p => player?.rocketScience?.ContainsKey(p.Key) ?? false)) {
  2577.                     if (player.rocketScience.ContainsKey(pair.Value))
  2578.                         player.rocketScience[pair.Value] += player.rocketScience[pair.Key];
  2579.                     else player.rocketScience[pair.Value] = player.rocketScience[pair.Key];
  2580.                     player.rocketScience.Remove(pair.Key);
  2581.                 }
  2582.                 saveData.modVersion = VERSION;
  2583.             }
  2584.             if (saveData.modVersion != null && version?.Minor == 24 && version?.Build >= 16) {
  2585.                 ReAgency player = ReUtil.PlayerReAgency();
  2586.                 Dictionary<string, string> fixSciType = new Dictionary<string, string>() {
  2587.                     ["Electrical Wheel"] = "Electrical_Wheel", ["ScienceData_AtmosphereSurvey"] = "Aeronautics_Data",
  2588.                     ["ScienceSample_AtmosphereSurvey"] = "Aeronautics_Sample"
  2589.                 };
  2590.                 foreach (var pair in fixSciType.Where(p => player?.rocketScience?.ContainsKey(p.Key) ?? false)) {
  2591.                     if (player.rocketScience.ContainsKey(pair.Value))
  2592.                         player.rocketScience[pair.Value] += player.rocketScience[pair.Key];
  2593.                     else player.rocketScience[pair.Value] = player.rocketScience[pair.Key];
  2594.                     player.rocketScience.Remove(pair.Key);
  2595.                 }
  2596.                 saveData.modVersion = VERSION;
  2597.                 return;
  2598.             }
  2599.             saveData.modVersion = VERSION;
  2600.             saveData.lastDailyUpdateUT = Math.Round(Game.UniverseModel.UniverseTime);
  2601.             ReAgency playerAgency = new ReAgency(Game.AgencyManager.FindAgencyEntryFirst().AgencyName);
  2602.             List<ReAgency> agencies = (ReUtil.IsGameSandbox() ? ReUtil.crewAgencies.Keys : ReUtil.crewAgencies.Keys.Take(3)).Select(a => new ReAgency(a)).ToList();
  2603.             if (!ReUtil.IsGameSandbox()) {
  2604.                 agencies.Add(new ReAgency("ReactionSystems"));
  2605.                 agencies.Add(new ReAgency("Lightyear"));
  2606.             }
  2607.             if (saveData.agencies == null) {
  2608.                 if (!ReUtil.IsGameSandbox()) {
  2609.                     playerAgency.rocketScience = new Dictionary<string, int>() { ["Engineering_Assembly"] = 0 };
  2610.                     ReUtil.PushNotify($"Missions/MissionGranter/Name/ReactionSystems", "NewAgency", NotificationImportance.Low, false);
  2611.                     ReUtil.PushNotify($"Missions/MissionGranter/Name/Lightyear", "NewAgency", NotificationImportance.Low, false);
  2612.                 }
  2613.                 saveData.agencies = new List<ReAgency>() { playerAgency };
  2614.             } else {
  2615.                 ReAgency oldPlayerAgency = saveData.agencies.FirstOrDefault(a => a.rocketScience.Count != 0);
  2616.                 playerAgency.rocketScience = oldPlayerAgency.rocketScience;
  2617.                 saveData.agencies.Remove(oldPlayerAgency);
  2618.                 saveData.agencies.RemoveAll(a => a.partRecords.Count == 0);
  2619.                 saveData.agencies.Add(playerAgency);
  2620.                 agencies.RemoveAll(a => saveData.agencies.Any(sa => sa.Name == a.Name));
  2621.             }
  2622.             saveData.agencies.AddRange(agencies);
  2623.             if (saveData.kerbals == null)
  2624.                 saveData.kerbals = new List<KerbalDamage>();
  2625.             // if (saveData.pals == null) saveData.pals = new Dictionary<string, double>();
  2626.         }
  2627.  
  2628.         internal static void AddKerbalDamage(string name, string type, double amount) {
  2629.             KerbalDamage kerb = saveData.kerbals.FirstOrDefault(k => k.Name == name);
  2630.             if (kerb == null) return;
  2631.             AddKerbalDamage(kerb, type, amount);
  2632.         }
  2633.         internal static void AddKerbalDamage(KerbalDamage kerb, string type, double amount, bool set = false) {
  2634.             if (kerb == null) return;
  2635.             amount *= KerbalDamageModifier(kerb, type) * 2;
  2636.             if (kerb.damage.ContainsKey(type) && !set)
  2637.                 kerb.damage[type] += amount;
  2638.             else kerb.damage[type] = amount;
  2639.             if (kerb.damage.Values.Sum() > 100) {
  2640.                 Game.SessionManager.KerbalRosterManager.TryGetKerbalByName(kerb.Name, out KerbalInfo kerbal);
  2641.                 if (kerbal == null) return;
  2642.                 Game.SessionManager.KerbalRosterManager.DestroyKerbal(kerbal.Id);
  2643.                 return;
  2644.             }
  2645.             if (set || amount > 0 || !new string[] { "Medicine_Impacts", "Medicine_Atrophy", "Psychology_Therapy" }.Contains(type)) return;
  2646.             KerbalLifeRewards(type, kerb.damage[type] - amount, kerb.damage[type]);
  2647.         }
  2648.         internal static void KerbalLifeRewards(string type, double prevDmg = 0, double newDmg = 0, bool force = false) {
  2649.             bool check = false;
  2650.             if (!force)
  2651.                 for (int i = -10; i < 10; i++)
  2652.                     if (prevDmg > i * 10 && newDmg < i * 10) {
  2653.                         check = true; break;
  2654.                     }
  2655.             if (!check && !force) return;
  2656.             float diffScale;
  2657.             if (!Game.SessionManager.TryGetDifficultyOptionState("ScienceRewards", out diffScale)) diffScale = 1f;
  2658.             int sciGain = Math.Max(2, (int)Math.Round((ReUtil.GetSciBonus(type) + 1) * diffScale * (force ? 2 : 1)));
  2659.             ReUtil.AddScience(sciGain);
  2660.             AddRocketScience(type, sciGain);
  2661.         }
  2662.         internal static bool HasKerbalDamage(KerbalDamage kerb, string type) => TryGetKerbalDamage(kerb, type) != 0;
  2663.         internal static double TryGetKerbalDamage(KerbalDamage kerb, string type) {
  2664.             if (kerb.damage.All(p => !p.Key.StartsWith(type))) return 0;
  2665.             return kerb.damage.Where(p => p.Key.StartsWith(type)).Sum(p => p.Value);
  2666.         }
  2667.         internal static float KerbalDamageModifier(KerbalDamage kerb, string type) {
  2668.             UnityEngine.Random.InitState((kerb.Name + kerb.Trait + type).GetHashCode());
  2669.             return UnityEngine.Random.value;
  2670.         }
  2671.         internal static void AddPartRecord(string partName, double amount) {
  2672.             if (ReUtil.IsGameSandbox()) return;
  2673.             string mfKey = ReUtil.GetPartMfrKey(partName);
  2674.             if (mfKey == null || mfKey == "") return;
  2675.             ReAgency mfAgency = saveData.agencies?.FirstOrDefault(a => a.Name == mfKey);
  2676.             if (mfAgency == null) {
  2677.                 mfAgency = new ReAgency(mfKey);
  2678.                 saveData.agencies.Add(mfAgency);
  2679.                 ReUtil.PushNotify($"Missions/MissionGranter/Name/{mfKey}", "NewAgency", NotificationImportance.Low, false);
  2680.             }
  2681.             if (mfAgency.partRecords == null)
  2682.                 mfAgency.partRecords = new Dictionary<string, double>();
  2683.             if (!mfAgency.partRecords.ContainsKey(partName)) {
  2684.                 mfAgency.partRecords[partName] = amount;
  2685.                 ReUtil.PushNotify($"Missions/MissionGranter/Name/{mfKey}", "NewPartGrade", NotificationImportance.Low, false, LocalizationManager.GetTranslation($"Parts/Title/{partName}"));
  2686.             } else mfAgency.partRecords[partName] += amount;
  2687.         }
  2688.         internal static void AddRocketScience(string sciType, int amount) {
  2689.             if (ReUtil.IsGameSandbox()) return;
  2690.             ReAgency playerAgency = ReUtil.PlayerReAgency();
  2691.             if (playerAgency == null) return;
  2692.             if (playerAgency.rocketScience.ContainsKey(sciType))
  2693.                 playerAgency.rocketScience[sciType] += amount;
  2694.             else playerAgency.rocketScience[sciType] = amount;
  2695.         }
  2696.         internal static double TryGetPartRecord(string partName, bool asPct = false) {
  2697.             double record = 0;
  2698.             if (saveData.agencies?.Any(a => a.partRecords.ContainsKey(partName)) ?? false)
  2699.                 record = saveData.agencies.FirstOrDefault(a => a.partRecords.ContainsKey(partName)).partRecords[partName];
  2700.             if (asPct && record > 0) record = Math.Min(Math.Sqrt(record / 10) / 10, 1);
  2701.             return record;
  2702.         }
  2703.        
  2704.         public class GUIData {
  2705.  
  2706.             public int SciIndex, AgencyIndex, ModeIndex, CrewIndex;
  2707.             public KerbalInfo Kerbal;
  2708.             public ReAgency Agency;
  2709.             public string WindowTitle { get; private set; }
  2710.             public string AppTitle { get; private set; }
  2711.             public string ModeBtn { get; private set; }
  2712.             public string MinBtn { get; private set; }
  2713.             public string MaxBtn { get; private set; }
  2714.             public string CloseBtn { get; private set; }
  2715.             public string NavLBtn { get; private set; }
  2716.             public string NavRBtn { get; private set; }
  2717.             public string OkBtn { get; private set; }
  2718.             public Texture2D Icon { get; private set; }
  2719.             public Texture2D Dots { get; private set; }
  2720.             public string Name { get; private set; }
  2721.             public string Surname { get; private set; }
  2722.             public string Training { get; private set; }
  2723.             public string Discovery { get; private set; }
  2724.             public string InspectHint { get; private set; }
  2725.             public string PALMiniHint { get; private set; }
  2726.             public string PartTitle { get; private set; }
  2727.             public string Subtitle { get; private set; }
  2728.             public string Damage { get; private set; }
  2729.             public string RepairHint { get; private set; }
  2730.             public string CommNetReq { get; private set; }
  2731.             public string RepairStatus { get; private set; }
  2732.             public string RepairTitle { get; private set; }
  2733.             public List<string> Repairers { get; private set; }
  2734.             public string Inspecting { get; private set; }
  2735.             public Texture2D Wireframe { get; private set; }
  2736.             public string Impacts { get; private set; }
  2737.             public string Temperature { get; private set; }
  2738.             public string GeeForces { get; private set; }
  2739.             public string AngularVel { get; private set; }
  2740.             public string Instability { get; private set; }
  2741.             public string Ullage { get; private set; }
  2742.             public string Boiloff { get; private set; }
  2743.             public string Age { get; private set; }
  2744.             public string SerialTitle { get; private set; }
  2745.             public string SerialDesc { get; private set; }
  2746.             public string CategoryTitle { get; private set; }
  2747.             public string CategoryDesc { get; private set; }
  2748.             public string VesselsNearby { get; private set; }
  2749.             public List<VesselComponent> VesselsList { get; private set; }
  2750.             public List<VesselComponent> EVAsList { get; private set; }
  2751.  
  2752.             public string Current { get; private set; }
  2753.             public string Connected { get; private set; }
  2754.             public string Contact { get; private set; }
  2755.             public string KerbalsOnEVA { get; private set; }
  2756.             // public List<Tuple<string, string>> EVAsList { get; private set; }
  2757.             public string Repairing { get; private set; }
  2758.             public string Idle { get; private set; }
  2759.             public float DiscoveryValue { get; private set; }
  2760.             public string Level { get; private set; }
  2761.             public string SciTitle { get; private set; }
  2762.             public int SciLvl { get; private set; }
  2763.             public float SciLvlProgress { get; private set; }
  2764.             public List<Tuple<string, float>> SciSubtypes { get; private set; }
  2765.  
  2766.             public GUIData() {
  2767.                 Repairers = new List<string>();
  2768.                 VesselsList = new List<VesselComponent>();
  2769.                 EVAsList = new List<VesselComponent>();
  2770.                 // EVAsList = new List<Tuple<string, string>>();
  2771.                 UpdateWindowTitle();
  2772.                 AppTitle = $"<color=#0D0D0D><size=13> {LocalizationManager.GetTranslation("KerbalLife/Title").ToUpper()} --------------------/      </size></color>";
  2773.                 ModeBtn = "<color=#D6E0FF>>¤<</color>";
  2774.                 MinBtn = "<color=#D6E0FF>-</color>";
  2775.                 MaxBtn = "<color=#D6E0FF>□</color>";
  2776.                 CloseBtn = "<color=#D6E0FF>x</color>";
  2777.                 NavLBtn = "<color=#D6E0FF><</color>";
  2778.                 NavRBtn = "<color=#D6E0FF>></color>";
  2779.                 OkBtn = LocalizationManager.GetTranslation("Application/OK");
  2780.                 Icon = AssetManager.GetAsset<Texture2D>($"KSRe/images/icon.png");
  2781.                 Dots = AssetManager.GetAsset<Texture2D>($"KSRe/images/dots.png");
  2782.                 Name = $"<color=#0D0D0D>{LocalizationManager.GetTranslation("Menu/SaveLoad/Name")}:</color> ";
  2783.                 Surname = $"<color=#0D0D0D>{LocalizationManager.GetTranslation("Career/Surname")}</color> ";
  2784.                 Training = $"<color=#0D0D0D><size=13>{LocalizationManager.GetTranslation("Career/Training").ToUpper()}:</size></color> ";
  2785.                 Discovery = $"<color=#0D0D0D><size=13>{LocalizationManager.GetTranslation("Career/Discovery").ToUpper()}:</size></color> ";
  2786.                 InspectHint = $"<color=yellow>{LocalizationManager.GetTranslation("Diagnostic/InspectHint")}</color>";
  2787.                 PALMiniHint = $"<color=yellow>{LocalizationManager.GetTranslation("Diagnostic/PALMiniHint", new object[1] { repairKeyCode.Value.Description() })}</color>";
  2788.                 RepairStatus = $"<color=#D6E0FF>- {LocalizationManager.GetTranslation("PartModules/Damage/Repairing")} -</color>";
  2789.                 RepairHint = $"<color=yellow>{LocalizationManager.GetTranslation("Diagnostic/RepairHint", new object[1] { repairKeyCode.Value.Description() })}</color>";
  2790.                 CommNetReq = $"<color=#D45455>{LocalizationManager.GetTranslation("Diagnostic/CommNetRequired")}</color>";
  2791.                 Inspecting = LocalizationManager.GetTranslation("PartModules/Damage/Inspecting");
  2792.                 SerialTitle = $"{LocalizationManager.GetTranslation("PartModules/Damage/Serial", new object[1] { "" })}  ";
  2793.                 CategoryTitle = $"{LocalizationManager.GetTranslation("VAB/PartsPicker/type")}:      ";
  2794.                 VesselsNearby = $"<color=#D6E0FF>- {LocalizationManager.GetTranslation("Diagnostic/VesselsNearby")} -</color>";
  2795.                 Current = LocalizationManager.GetTranslation("Diagnostic/Current");
  2796.                 Connected = LocalizationManager.GetTranslation("Diagnostic/Connected");
  2797.                 Contact = $"<color=#D6E0FF>{LocalizationManager.GetTranslation("Diagnostic/Contact")}</color>";
  2798.                 KerbalsOnEVA = $"<color=#D6E0FF>- {LocalizationManager.GetTranslation("Diagnostic/KerbalsOnEVA")} -</color>";
  2799.                 Repairing = LocalizationManager.GetTranslation("PartModules/Damage/Repairing");
  2800.                 Idle = LocalizationManager.GetTranslation("Diagnostic/Idle");
  2801.             }
  2802.  
  2803.             public void Reset() {
  2804.                 Agency = null;
  2805.                 Kerbal = null;
  2806.                 ModeIndex = 0;
  2807.                 AgencyIndex = 0;
  2808.             }
  2809.            
  2810.             public void UpdateWindowTitle(bool hasPAL = false) => WindowTitle = $"<color=#D6E0FF><size=13> {LocalizationManager.GetTranslation(hasPAL ? "Diagnostic/PAL5000" : "Diagnostic/Title").ToUpper()} --------------------/      </size></color>";
  2811.  
  2812.             public void UpdateEVAPart() {
  2813.                 bool isKerbal = UIPart.PartName == "eva_kerbal";
  2814.                 PartTitle = $"<b>{(isKerbal ? Game.SessionManager.KerbalRosterManager.GetAllKerbalsInSimObject(UIPart.SimulationObject.GlobalId).First().NameKey : LocalizationManager.GetTranslation($"Parts/Title/{UIPart.PartName}"))}</b>";
  2815.                 Subtitle = $"<color=#5B5FDB>#</color> <color=#C6CEDC><size=13>{LocalizationManager.GetTranslation($"Parts/Subtitle/{UIPart.PartName}").ToUpper()}</size></color>";
  2816.                 Inspecting = LocalizationManager.GetTranslation(isKerbal ? "KerbalLife/CheckIn" : "PartModules/Damage/Inspecting");
  2817.                 RepairHint = $"<color=yellow>{LocalizationManager.GetTranslation(isKerbal ? "KerbalLife/ChatHint" : "Diagnostic/RepairHint", new object[1] { repairKeyCode.Value.Description() })}</color>";
  2818.                 Repairing = LocalizationManager.GetTranslation(isKerbal ? "KerbalLife/Chatting" : "PartModules/Damage/Repairing");
  2819.                 if (!isKerbal) return;
  2820.                 UIPartDmg = 100 - saveData.kerbals.FirstOrDefault(k => k.Name == UIPart.PartOwner.SimulationObject.Vessel.DisplayName)?.damage.Values.Sum() ?? 0;
  2821.             }
  2822.             public void UpdateEVADamage() =>
  2823.                 Damage = $"{LocalizationManager.GetTranslation("PartModules/Damage/Name")}: {(UIPart.PartName == "eva_kerbal" ? ReUtilKerb.GetPsychologyStr((float)UIPartDmg * 0.01f) : ReUtil.GetDamageStr(UIPartDmg))}"; // <color=#b4d455>Happy :)</color>
  2824.             public void UpdateEVARepairers() => Repairers = repairers.Where(r => r.TargetGuid == UIPart.Guid).Select(r => r.Name).ToList();
  2825.  
  2826.             public void UpdateForPAL() {
  2827.                 bool isKerbal = UIPart.PartName == "eva_kerbal";
  2828.                 Wireframe = AssetManager.GetAsset<Texture2D>($"KSRe/images/wf_{(isKerbal ? "eva" : UIPart.PartData.category + "")}.png");
  2829.                 Impacts = $"{LocalizationManager.GetTranslation("Diagnostic/Impacts")}: " +
  2830.                     (UIPartImpacts.Item1 != UIPartImpacts.Item2 ? $"{ReUtil.GetColorForRange(UIPart.CrashTolerance * 0.667, UIPart.CrashTolerance, UIPartImpacts.Item1)}{UIPartImpacts.Item1:N1}</color>-" : "") +
  2831.                     $"{ReUtil.GetColorForRange(UIPart.CrashTolerance * 0.667, UIPart.CrashTolerance, UIPartImpacts.Item2)}{UIPartImpacts.Item2:N1}</color> {LocalizationManager.GetTranslation("Unit/Symbol/MetersPerSecond")}";
  2832.                 Temperature = $"{LocalizationManager.GetTranslation("Diagnostic/Temperature")}: {ReUtil.GetColorForRange(UIPart.MaxTemp * 0.6, UIPart.MaxTemp * 0.8, UIPart.Temperature)}{UIPart.Temperature:N0}{LocalizationManager.GetTranslation("Unit/Symbol/Kelvin")}</color>";
  2833.                 double gees = UIPart.PartOwner.SimulationObject.Vessel.geeForce;
  2834.                 GeeForces = $"{LocalizationManager.GetTranslation("Diagnostic/GeeForces")}: {ReUtil.GetColorForRange(UIPart.CrashTolerance, UIPart.CrashTolerance * 1.25, gees)}{gees:N2}</color>";
  2835.                 double rot = UIPart.PartOwner.SimulationObject.Vessel.AngularVelocity.relativeAngularVelocity.magnitude;
  2836.                 AngularVel = $"{LocalizationManager.GetTranslation("Diagnostic/Rotation")}: <color=#{(rot < 0.015 ? "b4d4" : "d4a4")}55>{rot:N3}</color>";
  2837.                 Instability = $"{LocalizationManager.GetTranslation("Diagnostic/Instability")}: {ReUtil.GetColorForRange(0, 1, UIEngInstability)}{UIEngInstability:P0}</color>";
  2838.                 Ullage = $"{LocalizationManager.GetTranslation("Diagnostic/Ullage")}: {ReUtil.GetUllageStr(UIPart.PartOwner.SimulationObject.Vessel.geeForce)}";
  2839.                 bool isBoiling = "0040-Methalox|0050-Methane|0060-Monopropellant|0065-Nose Cone".Contains(UIPart.PartData.family) && UIPart.PartResourceContainer.GetResourceStoredMass(fuelList.Last()) > 0;
  2840.                 Boiloff = $"{LocalizationManager.GetTranslation("Diagnostic/Boiloff")}: {ReUtil.GetColorForRange(50, 650, isBoiling ? UIPart.Temperature : 0)}{(isBoiling ? Math.Max(0, UIPart.ThermalData.Temperature - 50) / 300 : 0):N1}% / {LocalizationManager.GetTranslation("Units/Symbol/Days")}</color>";
  2841.             }
  2842.             public void UpdateForVessel(bool hasPAL) {
  2843.                 Age = $"{LocalizationManager.GetTranslation("Diagnostic/Age")}: {ReUtil.GetColorForRange(0, 118000000, UIPart.PartOwner.SimulationObject.Vessel.TimeSinceLaunch) + Units.FormatTimeString(UIPart.PartOwner.SimulationObject.Vessel.TimeSinceLaunch)}</color>";
  2844.                 if (hasPAL) UpdateForPAL();
  2845.             }
  2846.             public void UpdateVesselPart(bool hasPAL) {
  2847.                 bool isKerbal = UIPart.PartName == "eva_kerbal";
  2848.                 SerialTitle = $"{LocalizationManager.GetTranslation("PartModules/Damage/Serial", new object[1] { "" })}  "; // isKerbal ? LocalizationManager.GetTranslation("VAB/SaveAndLoad/Name") :
  2849.                 SerialDesc = UIPart.Guid.Substring(24); // isKerbal ? UIPart.PartOwner.SimulationObject.Vessel.DisplayName :
  2850.                 CategoryDesc = LocalizationManager.GetTranslation(isKerbal ? "Diagnostic/EVASuit" : $"PartsManager/{UIPart.PartData.category}");
  2851.                 UIPartImpacts = Tuple.Create(0f, 0f);
  2852.                 UIEngInstability = 0.0;
  2853.                 UpdateForVessel(hasPAL);
  2854.             }
  2855.  
  2856.             public void UpdateListMode() {
  2857.                 VesselsList = Game.ViewController.VesselsInRange.Where(v => !v.IsKerbalEVA).ToList();
  2858.                 EVAsList = Game.ViewController.VesselsInRange.Where(v => v.IsKerbalEVA).ToList();
  2859.                 //EVAsList = Game.ViewController.VesselsInRange.Where(v => v.IsKerbalEVA).Select(v => Tuple.Create(v.DisplayName,
  2860.                 //    repairers.Any(r => r.Guid == v.Guid) ? Repairing : Idle)).ToList();
  2861.             }
  2862.  
  2863.             public void UpdateDiscovery() {
  2864.                 List<TechNodeData> techNodes = new List<TechNodeData>();
  2865.                 if (Game.ScienceManager?.TechNodeDataStore?.TryGetTechNodeDataCollection(out IReadOnlyCollection<TechNodeData> tNodes) ?? false)
  2866.                     techNodes = tNodes?.ToList();
  2867.                 if (techNodes.Count == 0) { DiscoveryValue = 0f; return; }
  2868.                 List<int> costs = techNodes.Select(t => t.RequiredSciencePoints).Distinct().ToList();
  2869.                 for (int i = 0; i < costs.Count; i++) {
  2870.                     UnityEngine.Random.InitState((Game.SessionGuidString + costs[i]).GetHashCode());
  2871.                     costs[i] = (int)Math.Round(costs[i] * (1.5 + UnityEngine.Random.value));
  2872.                 }
  2873.                 int sci = ReUtil.GetTotalScience();
  2874.                 int prev = costs.TakeWhile(c => c < sci).LastOrDefault();
  2875.                 int next = Math.Max(techNodes.Select(t => t.RequiredSciencePoints * 2).Distinct().SkipWhile(c => c <= sci).FirstOrDefault(), 10);
  2876.                 DiscoveryValue = Mathf.Clamp01((float)(sci - prev) / (next - prev));
  2877.             }
  2878.             public void UpdateSciLevel() {
  2879.                 ReAgency player = ReUtil.PlayerReAgency();
  2880.                 string sciName = ReUtil.PlayerReAgency().rocketScience.Select(s => s.Key.Split('_')[0]).Distinct().ElementAt(SciIndex);
  2881.                 SciTitle = $"<color=#0D0D0D><size=19>{LocalizationManager.GetTranslation($"Career/{sciName}/Title").ToUpper()}</size></color>";
  2882.                 int sci = player.rocketScience.Where(s => s.Key.StartsWith(sciName)).Select(s => s.Value).Sum();
  2883.                 SciLvl = Mathf.Clamp(ReUtil.sciLvls.Keys.TakeWhile(l => l <= sci).Count() - 1, 0, ReUtil.sciLvls.Count - 3);
  2884.                 int prev = ReUtil.sciLvls.Keys.ElementAt(SciLvl);
  2885.                 int next = SciLvl == ReUtil.sciLvls.Count - 3 ? 0 : ReUtil.sciLvls.Keys.ElementAt(SciLvl + 1); //ReUtil.sciLvlBonuses.Keys.Skip(SciLvl + 1).FirstOrDefault()
  2886.                 SciLvlProgress = Mathf.Clamp01((float)(sci - prev) / (next - prev));
  2887.                 if (ReUtil.PlayerTrait() == sciName) SciLvl += 2;
  2888.                 // Level = $"<color=#0D0D0D>{LocalizationManager.GetTranslation("Career/Level")}: {SciLvl} (+{ReUtil.sciLvls.ElementAt(SciLvl).Value:P0})</color>";
  2889.                 Level = $"<color=#0D0D0D>[ +{ReUtil.sciLvls.ElementAt(SciLvl).Value:P0} ]  <size=14>{LocalizationManager.GetTranslation("Career/Level").ToUpper()}:</size></color>";
  2890.                 //List<int> subLvls = ReUtil.sciLvlBonuses.Keys.Take(ReUtil.sciLvlBonuses.Count - 2).ToList();
  2891.                 //List<int> halfLvls = subLvls.Skip(1).Select(i => i / 2).ToList();
  2892.                 //subLvls.AddRange(halfLvls);
  2893.                 //subLvls = subLvls.Distinct().ToList();
  2894.                 //subLvls.Sort();
  2895.                 SciSubtypes = new List<Tuple<string, float>>();
  2896.                 foreach (var pair in player.rocketScience.Where(s => s.Key.StartsWith(sciName))) {
  2897.                     int subLvl = Mathf.Clamp(ReUtil.sciLvls.Keys.TakeWhile(l => l <= pair.Value).Count() - 1, 0, ReUtil.sciLvls.Keys.Count - 3);
  2898.                     int prevSub = ReUtil.sciLvls.Keys.ElementAt(subLvl);
  2899.                     int nextSub = subLvl == ReUtil.sciLvls.Keys.Count - 3 ? 0 : ReUtil.sciLvls.Keys.ElementAt(subLvl + 1);
  2900.                     SciSubtypes.Add(Tuple.Create($"<color=#0D0D0D><size=13>:: {LocalizationManager.GetTranslation(ReUtil.GetSciLocKey(pair.Key))}</size></color>", // {subLvl}
  2901.                         subLvl + Mathf.Clamp01((float)(pair.Value - prevSub) / (nextSub - prevSub))));
  2902.                 }
  2903.             }
  2904.        
  2905.         }
  2906.  
  2907.         public class ReSaveData {
  2908.             public string modVersion;
  2909.             public double lastDailyUpdateUT;
  2910.             public List<ReAgency> agencies;
  2911.             public List<KerbalDamage> kerbals;
  2912.             // public Dictionary<string, double> pals;
  2913.             // public Dictionary<string, double> partRecords;
  2914.         }
  2915.  
  2916.         public class KerbalOverseer {
  2917.  
  2918.             public List<KerbalDamage> Manifest { get; private set; }
  2919.  
  2920.             public KerbalOverseer() {
  2921.                 Manifest = new List<KerbalDamage>();
  2922.  
  2923.                 //Game.Messages.PersistentSubscribe<KerbalAddedToRoster>(AddedKerbal);
  2924.                 //Game.Messages.PersistentSubscribe<KerbalRemovedFromRoster>(RemovedKerbal);
  2925.                 //Game.Messages.PersistentSubscribe<KerbalLocationChanged>(MovedKerbal);
  2926.  
  2927.             }
  2928.  
  2929.         }
  2930.  
  2931.         public class KerbalSaveData {
  2932.             public string Name { get; private set; }
  2933.             public string Trait { get; private set; }
  2934.             public string agency;
  2935.             public Dictionary<string, double> damage;
  2936.  
  2937.             public KerbalSaveData(string name, string trait, string agency) {
  2938.                 Name = name;
  2939.                 Trait = trait;
  2940.                 this.agency = agency;
  2941.                 damage = new Dictionary<string, double>();
  2942.             }
  2943.  
  2944.         }
  2945.  
  2946.         public class ReAgencyData {
  2947.             public string Name { get; private set; }
  2948.             public Dictionary<string, int> rocketScience;
  2949.             public Dictionary<string, double> partRecords;
  2950.  
  2951.             public ReAgencyData(string name) {
  2952.                 Name = name;
  2953.                 rocketScience = new Dictionary<string, int>();
  2954.                 partRecords = new Dictionary<string, double>();
  2955.             }
  2956.  
  2957.         }
  2958.  
  2959.  
  2960.     }
  2961.  
  2962. }
  2963.  
Add Comment
Please, Sign In to add comment