From 87a06fa7c2a4fd1191557af4fa6db598fcc7b646 Mon Sep 17 00:00:00 2001 From: periodically-makes-puns Date: Tue, 21 Jul 2026 08:55:47 -0400 Subject: [PATCH 1/7] lock fixes --- Source/Tanks/RealFuelsWindow.cs | 38 ++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/Source/Tanks/RealFuelsWindow.cs b/Source/Tanks/RealFuelsWindow.cs index b0f213b4..e9ec326d 100644 --- a/Source/Tanks/RealFuelsWindow.cs +++ b/Source/Tanks/RealFuelsWindow.cs @@ -468,18 +468,26 @@ private void Update() private void ReassertLocks() { double capacity = _module.volume; - bool anyReasserted = false; // ── Collect all valid locked entries ───────────────────────────── var entries = new List<(FuelTank tank, string key, double lockedAmt)>(); foreach (var kv in _lockedAmounts) { FuelTank t; - if (_module.tanksDict.TryGetValue(kv.Key, out t)) + if (_module.tanksDict.TryGetValue(kv.Key, out t) && t.maxAmount != kv.Value) // only consider if the value is actually different entries.Add((t, kv.Key, kv.Value)); } if (entries.Count == 0) return; + double unlockedVolume = 0; + foreach (var kv in _module.tanksDict) + { + if (!_lockedAmounts.ContainsKey(kv.Key)) + { + unlockedVolume += kv.Value.Volume; + } + } + // ── Total locked physical volume ────────────────────────────────── // If the tank shrank so much that even the locked resources alone // don't fit, scale them all proportionally (their ratio is preserved). @@ -492,6 +500,9 @@ private void ReassertLocks() ? capacity / totalLockedLitres : 1d; + // Re-scale unlocked resources so total volume remains unchanged + double unlockedScale = Math.Max(0, (capacity - totalLockedLitres) / unlockedVolume); + // ── Re-assert each locked resource ──────────────────────────────── foreach (var (tank, key, lockedAmt) in entries) { @@ -512,7 +523,6 @@ private void ReassertLocks() { tank.maxAmount = targetAmt; tank.amount = tank.fillable ? tank.maxAmount : 0d; - anyReasserted = true; } // Always keep edit and pct buffers current for locked resources. @@ -522,9 +532,22 @@ private void ReassertLocks() double pct = capacity > 0d ? (targetLitres / capacity) * 100d : 0d; _pctBuf[key] = pct.ToString("F2"); } + foreach (var kv in _module.tanksDict) + { + if (!_lockedAmounts.ContainsKey(kv.Key)) + { + var tank = kv.Value; + tank.maxAmount *= unlockedScale; + tank.amount = tank.fillable ? tank.maxAmount : 0d; + if (unlockedScale == 0) + RemoveTank(tank); + _editBuf[kv.Key] = tank.maxAmount.ToString("F4"); + double pct = capacity > 0d ? (tank.maxAmount / capacity) * 100d : 0d; + _pctBuf[kv.Key] = pct.ToString("F2"); + } + } - if (anyReasserted) - _pendingNotify = true; // tell the editor the part changed + _pendingNotify = true; // The part always changes if we reach this part. } // ── Main OnGUI ─────────────────────────────────────────────────────── @@ -1929,7 +1952,10 @@ private void SetVolumeAndScaleOthers(FuelTank tank, double newLitres) { double scale = Math.Max(0d, remaining) / totalOther; foreach (var o in others) + { o.maxAmount = o.Volume * scale * o.utilization; + _lockedAmounts[o.name] = o.maxAmount; // reset locked amount + } } } else if (unlockedSum > 0d) @@ -1945,6 +1971,8 @@ private void SetVolumeAndScaleOthers(FuelTank tank, double newLitres) // Write the target tank last (RF setter may trigger events). tank.maxAmount = newLitres * tank.utilization; tank.amount = tank.fillable ? tank.maxAmount : 0d; + if (_lockedAmounts.ContainsKey(tank.name)) + _lockedAmounts[tank.name] = tank.maxAmount; SyncEditBuffers(); _module.MarkWindowDirty(); From 2cd0191d525361d8313d4462742b07079d9e8ac6 Mon Sep 17 00:00:00 2001 From: periodically-makes-puns Date: Thu, 23 Jul 2026 14:58:39 -0400 Subject: [PATCH 2/7] symmetry fixes --- Source/Tanks/ModuleFuelTanks.cs | 8 +++++++- Source/Tanks/RealFuelsWindow.cs | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Source/Tanks/ModuleFuelTanks.cs b/Source/Tanks/ModuleFuelTanks.cs index 05a72d87..cc3f5295 100644 --- a/Source/Tanks/ModuleFuelTanks.cs +++ b/Source/Tanks/ModuleFuelTanks.cs @@ -351,6 +351,9 @@ private void OnPartActionGuiDismiss(Part p) if (p == part) { showUI = false; + foreach (var sym in p.symmetryCounterparts) + foreach (var mft in sym.FindModulesImplementing()) + mft.showUI = false; if (tankDefinitionSelectionGUI != null) Destroy(tankDefinitionSelectionGUI); } @@ -365,7 +368,10 @@ public void Update() // Only show the fuel tank window in the Parts tab, not in Action Groups. if (showUI && EditorLogic.fetch?.editorScreen == EditorScreen.Parts) - RealFuelsWindow.ShowGUI(this); + { + if (part.PartActionWindow != null) + RealFuelsWindow.ShowGUI(this); + } else RealFuelsWindow.HideGUIForModule(this); } diff --git a/Source/Tanks/RealFuelsWindow.cs b/Source/Tanks/RealFuelsWindow.cs index e9ec326d..df6c38c5 100644 --- a/Source/Tanks/RealFuelsWindow.cs +++ b/Source/Tanks/RealFuelsWindow.cs @@ -72,6 +72,9 @@ public class RealFuelsWindow : MonoBehaviour public static void ShowGUI(ModuleFuelTanks module) { if (_instance == null) return; + if (_instance._module?.part?.symmetryCounterparts?.Contains(module.part) == true) + return; + // prevent thrashing if we attempt to swap between symmetry counterparts // Switching to a different module: discard stale edit buffers and cancel // any deferred notification that belongs to the old module. Firing From 11558727b8f5723a634eecc2024c0ed9a037ae9c Mon Sep 17 00:00:00 2001 From: periodically-makes-puns Date: Thu, 23 Jul 2026 15:26:20 -0400 Subject: [PATCH 3/7] volume authoritative --- Source/Tanks/RealFuelsWindow.cs | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/Source/Tanks/RealFuelsWindow.cs b/Source/Tanks/RealFuelsWindow.cs index df6c38c5..c6f2cdfe 100644 --- a/Source/Tanks/RealFuelsWindow.cs +++ b/Source/Tanks/RealFuelsWindow.cs @@ -1416,20 +1416,17 @@ private void DrawAvailRow(FuelTank tank) // so we don't clobber text the player is actively editing. else if (GUI.GetNameOfFocusedControl() != "availAmt_" + tank.name) { - double pct; - double.TryParse(_availFillPctBuf[tank.name], out pct); - _availAmountBuf[tank.name] = - (maxRfUnits * Math.Max(0d, Math.Min(100d, pct)) / 100d).ToString("F1"); + double.TryParse(_availAmountBuf[tank.name], out double amt); + _availFillPctBuf[tank.name] = + Math.Min(100d, amt / maxRfUnits * 100d).ToString("F1"); } - // fillFrac is always derived from the pct buffer (source of truth) - double fillPct; - double.TryParse(_availFillPctBuf[tank.name], out fillPct); - double fillFrac = Math.Max(0d, Math.Min(100d, fillPct)) / 100d; + // fillFrac is always derived from the volume buffer (source of truth) + double.TryParse(_availAmountBuf[tank.name], out double fillAmt); // ── +ADD / FULL ─────────────────────────────────────────────────── if (canAdd && GUI.Button(new Rect(rx, aby, btnW, abh), "+ADD", _sBtnAdd)) - AddTank(tank, fillFrac); + AddTank(tank, fillAmt); else if (!canAdd) GUI.Label(new Rect(rx, aby, btnW, abh), "FULL", _sAvailFull); if (canAdd) @@ -1994,18 +1991,14 @@ private void RemoveTank(FuelTank tank) } /// - /// Activate a previously-empty tank, filling of the - /// remaining available volume (1.0 = 100%). Defaults to full fill. + /// Activate a previously-empty tank, filling L /// - private void AddTank(FuelTank tank, double fillFrac = 1.0) + private void AddTank(FuelTank tank, double amount) { double avail = Math.Max(0d, _module.AvailableVolume - _reserveVolume); if (avail < 0.001d) return; - // fillFrac is relative to total tank capacity (same basis as the pct field in - // DrawAvailRow), so two resources set to 5% each always contribute 5% of the - // total regardless of add order. Cap at physAvail to prevent overfill. - double fill = Math.Min(_module.volume * Math.Max(0d, Math.Min(1d, fillFrac)), avail); - tank.maxAmount = fill * tank.utilization; + if (avail * tank.utilization < amount) amount = avail * tank.utilization; + tank.maxAmount = amount; tank.amount = tank.fillable ? tank.maxAmount : 0d; // Store RF units in the edit buffer — consistent with DrawCurrentRow display. _editBuf[tank.name] = tank.maxAmount.ToString("F4"); From 4cd5c61b6eb82bd283458e3bec33297a66c1612a Mon Sep 17 00:00:00 2001 From: periodically-makes-puns Date: Sun, 26 Jul 2026 15:12:36 -0400 Subject: [PATCH 4/7] fixes, revert passive sync change, F2 not F1 --- Source/Tanks/FuelTank.cs | 4 +- Source/Tanks/RealFuelsWindow.cs | 104 +++++++++++++++++++------------- 2 files changed, 63 insertions(+), 45 deletions(-) diff --git a/Source/Tanks/FuelTank.cs b/Source/Tanks/FuelTank.cs index 3450b31e..64b4a4a5 100644 --- a/Source/Tanks/FuelTank.cs +++ b/Source/Tanks/FuelTank.cs @@ -210,7 +210,7 @@ private void UpdateAmount(PartResource partResource, double newAmount) RaiseResourceInitialChanged(partResource.part, partResource, newAmount); } if (partResource.part.PartActionWindow?.ListItems.FirstOrDefault(r => r is UIPartActionResourceEditor e && partResource == e.Resource) is UIPartActionResourceEditor resItem && resItem != null) - resItem.resourceAmnt.text = KSPUtil.LocalizeNumber(newAmount, "F1"); + resItem.resourceAmnt.text = KSPUtil.LocalizeNumber(newAmount, "F2"); } private void UpdateMaxAmount(PartResource partResource, double newAmount) @@ -218,7 +218,7 @@ private void UpdateMaxAmount(PartResource partResource, double newAmount) partResource.maxAmount = newAmount; RaiseResourceMaxChanged(partResource.part, partResource, newAmount); if (partResource.part.PartActionWindow?.ListItems.FirstOrDefault(r => r is UIPartActionResourceEditor e && partResource == e.Resource) is UIPartActionResourceEditor resItem && resItem != null) - resItem.resourceMax.text = KSPUtil.LocalizeNumber(newAmount, "F1"); + resItem.resourceMax.text = KSPUtil.LocalizeNumber(newAmount, "F2"); } void AddTank(double value) diff --git a/Source/Tanks/RealFuelsWindow.cs b/Source/Tanks/RealFuelsWindow.cs index c6f2cdfe..961d20c0 100644 --- a/Source/Tanks/RealFuelsWindow.cs +++ b/Source/Tanks/RealFuelsWindow.cs @@ -48,7 +48,7 @@ public QuickFillPreset(FuelInfo fi) // Combined label embeds each propellant's volume percentage inline. CombinedLabel = string.Join(" / ", props.Select(kv => kv.Key.displayName + " (" + - (kv.Key.ratio * kv.Value / fi.efficiency * 100d).ToString("F1") + "%)")); + (kv.Key.ratio * kv.Value / fi.efficiency * 100d).ToString("F2") + "%)")); } } @@ -474,22 +474,20 @@ private void ReassertLocks() // ── Collect all valid locked entries ───────────────────────────── var entries = new List<(FuelTank tank, string key, double lockedAmt)>(); + bool anyChanged = false; + double scaledLockedVolume = 0; foreach (var kv in _lockedAmounts) { FuelTank t; - if (_module.tanksDict.TryGetValue(kv.Key, out t) && t.maxAmount != kv.Value) // only consider if the value is actually different - entries.Add((t, kv.Key, kv.Value)); - } - if (entries.Count == 0) return; - - double unlockedVolume = 0; - foreach (var kv in _module.tanksDict) - { - if (!_lockedAmounts.ContainsKey(kv.Key)) + if (_module.tanksDict.TryGetValue(kv.Key, out t)) // only consider if the value is actually different { - unlockedVolume += kv.Value.Volume; + entries.Add((t, kv.Key, kv.Value)); + scaledLockedVolume += t.Volume; + if (Math.Abs(t.maxAmount - kv.Value) > 0.0001d) + anyChanged = true; } } + if (!anyChanged) return; // ── Total locked physical volume ────────────────────────────────── // If the tank shrank so much that even the locked resources alone @@ -504,7 +502,7 @@ private void ReassertLocks() : 1d; // Re-scale unlocked resources so total volume remains unchanged - double unlockedScale = Math.Max(0, (capacity - totalLockedLitres) / unlockedVolume); + double unlockedScale = Math.Max(0, (capacity - totalLockedLitres) / (capacity - scaledLockedVolume)); // ── Re-assert each locked resource ──────────────────────────────── foreach (var (tank, key, lockedAmt) in entries) @@ -535,21 +533,24 @@ private void ReassertLocks() double pct = capacity > 0d ? (targetLitres / capacity) * 100d : 0d; _pctBuf[key] = pct.ToString("F2"); } - foreach (var kv in _module.tanksDict) + + if (scaledLockedVolume != capacity) { - if (!_lockedAmounts.ContainsKey(kv.Key)) + foreach (var kv in _module.tanksDict) { - var tank = kv.Value; - tank.maxAmount *= unlockedScale; - tank.amount = tank.fillable ? tank.maxAmount : 0d; - if (unlockedScale == 0) - RemoveTank(tank); - _editBuf[kv.Key] = tank.maxAmount.ToString("F4"); - double pct = capacity > 0d ? (tank.maxAmount / capacity) * 100d : 0d; - _pctBuf[kv.Key] = pct.ToString("F2"); + if (!_lockedAmounts.ContainsKey(kv.Key) && kv.Value.maxAmount != 0) + { + var tank = kv.Value; + tank.maxAmount *= unlockedScale; + tank.amount = tank.fillable ? tank.maxAmount : 0d; + if (unlockedScale == 0) + RemoveTank(tank); + _editBuf[kv.Key] = tank.maxAmount.ToString("F4"); + double pct = capacity > 0d ? (tank.maxAmount / capacity) * 100d : 0d; + _pctBuf[kv.Key] = pct.ToString("F2"); + } } } - _pendingNotify = true; // The part always changes if we reach this part. } @@ -1359,7 +1360,7 @@ private void DrawAvailRow(FuelTank tank) double initPct; double.TryParse(_availFillPctBuf[tank.name], out initPct); _availAmountBuf[tank.name] = - (maxRfUnits * Math.Max(0d, Math.Min(100d, initPct)) / 100d).ToString("F1"); + (maxRfUnits * Math.Max(0d, Math.Min(100d, initPct)) / 100d).ToString("F2"); } string oldPctText = _availFillPctBuf[tank.name]; @@ -1400,29 +1401,34 @@ private void DrawAvailRow(FuelTank tank) double amt; if (double.TryParse(newAmt, out amt) && maxRfUnits > 0.001d) _availFillPctBuf[tank.name] = - Math.Min(100d, amt / maxRfUnits * 100d).ToString("F1"); + Math.Min(100d, amt / maxRfUnits * 100d).ToString("F2"); } // Percentage field changed — recompute amount else if (newPct != oldPctText) { _availFillPctBuf[tank.name] = newPct; - double pct; - double.TryParse(newPct, out pct); + double.TryParse(newPct, out double fillPct); _availAmountBuf[tank.name] = - (maxRfUnits * Math.Max(0d, Math.Min(100d, pct)) / 100d).ToString("F1"); + (maxRfUnits * Math.Max(0d, Math.Min(100d, fillPct)) / 100d).ToString("F2"); + } + // Neither changed. If percentage is still default, sync amount to match. Otherwise, respect the user-entered value + double.TryParse(_availFillPctBuf[tank.name], out double pct); + double fillAmt; + if (pct >= 100d) + { + fillAmt = maxRfUnits; + if (GUI.GetNameOfFocusedControl() != "availAmt_" + tank.name) + _availAmountBuf[tank.name] = + (maxRfUnits * Math.Max(0d, Math.Min(100d, pct)) / 100d).ToString("F2"); } - // Neither changed — passive sync so amount reflects current physAvail - // (e.g. after another resource is added). Skip if amount field is focused - // so we don't clobber text the player is actively editing. - else if (GUI.GetNameOfFocusedControl() != "availAmt_" + tank.name) + else if (GUI.GetNameOfFocusedControl() != "availPct_" + tank.name) { - double.TryParse(_availAmountBuf[tank.name], out double amt); - _availFillPctBuf[tank.name] = - Math.Min(100d, amt / maxRfUnits * 100d).ToString("F1"); + double.TryParse(_availAmountBuf[tank.name], out fillAmt); + _availFillPctBuf[tank.name] = + Math.Min(100d, fillAmt / maxRfUnits * 100d).ToString("F2"); } - // fillFrac is always derived from the volume buffer (source of truth) - double.TryParse(_availAmountBuf[tank.name], out double fillAmt); + // fillFrac is always derived from the (updated) volume buffer (source of truth) // ── +ADD / FULL ─────────────────────────────────────────────────── if (canAdd && GUI.Button(new Rect(rx, aby, btnW, abh), "+ADD", _sBtnAdd)) @@ -1431,7 +1437,7 @@ private void DrawAvailRow(FuelTank tank) GUI.Label(new Rect(rx, aby, btnW, abh), "FULL", _sAvailFull); if (canAdd) SetTooltip(new Rect(rx, aby, btnW, abh), - "Add this resource at the specified % of total tank capacity.\nCapped at currently available space."); + "Add the specified amount of this resource.\nCapped at currently available space."); } // ── Quick Fill ratio status ────────────────────────────────────────── @@ -1497,7 +1503,7 @@ private RatioResult GetRatioStatus(QuickFillPreset p) double actual = (loaded[i].Volume / mixTotal) * 100d; double target = p.PropTargets[i].targetPct; maxDev = Math.Max(maxDev, Math.Abs(actual - target)); - parts.Add(actual.ToString("F1") + "%"); + parts.Add(actual.ToString("F2") + "%"); } string label = string.Join(" / ", parts); @@ -1596,7 +1602,7 @@ private void DrawQuickFillRow(Rect r, QuickFillPreset p, double remaining) double.TryParse(_qfFillPctBuf[p.Key], out fillPct); double fillLitres = remaining * Math.Max(0d, Math.Min(100d, fillPct)) / 100d; GUI.Label(new Rect(rx, r.y, litW, topH), - fillLitres.ToString("F1") + " L", _sQfRatio); + fillLitres.ToString("F2") + " L", _sQfRatio); // ── Status line (bottom) ──────────────────────────────────────── var rs = GetRatioStatus(p); @@ -1954,7 +1960,7 @@ private void SetVolumeAndScaleOthers(FuelTank tank, double newLitres) foreach (var o in others) { o.maxAmount = o.Volume * scale * o.utilization; - _lockedAmounts[o.name] = o.maxAmount; // reset locked amount + SyncLockedTank(o); } } } @@ -1971,8 +1977,7 @@ private void SetVolumeAndScaleOthers(FuelTank tank, double newLitres) // Write the target tank last (RF setter may trigger events). tank.maxAmount = newLitres * tank.utilization; tank.amount = tank.fillable ? tank.maxAmount : 0d; - if (_lockedAmounts.ContainsKey(tank.name)) - _lockedAmounts[tank.name] = tank.maxAmount; + SyncLockedTank(tank); SyncEditBuffers(); _module.MarkWindowDirty(); @@ -2005,6 +2010,19 @@ private void AddTank(FuelTank tank, double amount) _module.MarkWindowDirty(); NotifyEditor(); } + /// + /// Updates the contents of _lockedAmounts to match the current fill level of the tank, if it was locked before. + /// + /// + private void SyncLockedTank(FuelTank o) + { + if (_lockedAmounts.ContainsKey(o.name)) + { + _lockedAmounts[o.name] = o.maxAmount; + if (o.maxAmount == 0) + _lockedAmounts.Remove(o.name); + } + } /// /// Fills the remaining tank volume with this engine's propellant ratios. From de079cc2c1d670aaddba1b697393b03e1c2ff5f4 Mon Sep 17 00:00:00 2001 From: periodically-makes-puns Date: Sun, 26 Jul 2026 18:19:31 -0400 Subject: [PATCH 5/7] i may be stupid --- Source/Tanks/RealFuelsWindow.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Tanks/RealFuelsWindow.cs b/Source/Tanks/RealFuelsWindow.cs index 961d20c0..0b3ae584 100644 --- a/Source/Tanks/RealFuelsWindow.cs +++ b/Source/Tanks/RealFuelsWindow.cs @@ -1413,7 +1413,7 @@ private void DrawAvailRow(FuelTank tank) } // Neither changed. If percentage is still default, sync amount to match. Otherwise, respect the user-entered value double.TryParse(_availFillPctBuf[tank.name], out double pct); - double fillAmt; + double.TryParse(_availAmountBuf[tank.name], out double fillAmt); if (pct >= 100d) { fillAmt = maxRfUnits; @@ -1423,7 +1423,6 @@ private void DrawAvailRow(FuelTank tank) } else if (GUI.GetNameOfFocusedControl() != "availPct_" + tank.name) { - double.TryParse(_availAmountBuf[tank.name], out fillAmt); _availFillPctBuf[tank.name] = Math.Min(100d, fillAmt / maxRfUnits * 100d).ToString("F2"); } From 24c7ddfb471d97f046a3de1dbf14588679cfc4a3 Mon Sep 17 00:00:00 2001 From: periodically-makes-puns Date: Mon, 27 Jul 2026 10:10:35 -0400 Subject: [PATCH 6/7] moar fixes --- Source/Tanks/FuelTank.cs | 4 ++-- Source/Tanks/ModuleFuelTanks.cs | 1 + Source/Tanks/RealFuelsWindow.cs | 30 +++++++++++++++++++----------- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/Source/Tanks/FuelTank.cs b/Source/Tanks/FuelTank.cs index 64b4a4a5..3450b31e 100644 --- a/Source/Tanks/FuelTank.cs +++ b/Source/Tanks/FuelTank.cs @@ -210,7 +210,7 @@ private void UpdateAmount(PartResource partResource, double newAmount) RaiseResourceInitialChanged(partResource.part, partResource, newAmount); } if (partResource.part.PartActionWindow?.ListItems.FirstOrDefault(r => r is UIPartActionResourceEditor e && partResource == e.Resource) is UIPartActionResourceEditor resItem && resItem != null) - resItem.resourceAmnt.text = KSPUtil.LocalizeNumber(newAmount, "F2"); + resItem.resourceAmnt.text = KSPUtil.LocalizeNumber(newAmount, "F1"); } private void UpdateMaxAmount(PartResource partResource, double newAmount) @@ -218,7 +218,7 @@ private void UpdateMaxAmount(PartResource partResource, double newAmount) partResource.maxAmount = newAmount; RaiseResourceMaxChanged(partResource.part, partResource, newAmount); if (partResource.part.PartActionWindow?.ListItems.FirstOrDefault(r => r is UIPartActionResourceEditor e && partResource == e.Resource) is UIPartActionResourceEditor resItem && resItem != null) - resItem.resourceMax.text = KSPUtil.LocalizeNumber(newAmount, "F2"); + resItem.resourceMax.text = KSPUtil.LocalizeNumber(newAmount, "F1"); } void AddTank(double value) diff --git a/Source/Tanks/ModuleFuelTanks.cs b/Source/Tanks/ModuleFuelTanks.cs index cc3f5295..c834f05c 100644 --- a/Source/Tanks/ModuleFuelTanks.cs +++ b/Source/Tanks/ModuleFuelTanks.cs @@ -369,6 +369,7 @@ public void Update() // Only show the fuel tank window in the Parts tab, not in Action Groups. if (showUI && EditorLogic.fetch?.editorScreen == EditorScreen.Parts) { + // intentionally no-op if the PAW is not open. covers the symmetry case where a tank in symmetry can have showUI = true bc a symmetry counterpart had Show GUI clicked if (part.PartActionWindow != null) RealFuelsWindow.ShowGUI(this); } diff --git a/Source/Tanks/RealFuelsWindow.cs b/Source/Tanks/RealFuelsWindow.cs index 0b3ae584..074baf20 100644 --- a/Source/Tanks/RealFuelsWindow.cs +++ b/Source/Tanks/RealFuelsWindow.cs @@ -72,9 +72,13 @@ public class RealFuelsWindow : MonoBehaviour public static void ShowGUI(ModuleFuelTanks module) { if (_instance == null) return; - if (_instance._module?.part?.symmetryCounterparts?.Contains(module.part) == true) - return; // prevent thrashing if we attempt to swap between symmetry counterparts + // null-conditional is forbidden so you get this stack! + if (_instance._module != null && + _instance._module.part != null && + _instance._module.part.symmetryCounterparts != null && + _instance._module.part.symmetryCounterparts.Contains(module.part)) + return; // Switching to a different module: discard stale edit buffers and cancel // any deferred notification that belongs to the old module. Firing @@ -545,9 +549,12 @@ private void ReassertLocks() tank.amount = tank.fillable ? tank.maxAmount : 0d; if (unlockedScale == 0) RemoveTank(tank); - _editBuf[kv.Key] = tank.maxAmount.ToString("F4"); - double pct = capacity > 0d ? (tank.maxAmount / capacity) * 100d : 0d; - _pctBuf[kv.Key] = pct.ToString("F2"); + else + { + _editBuf[kv.Key] = tank.maxAmount.ToString("F4"); + double pct = capacity > 0d ? (tank.maxAmount / capacity) * 100d : 0d; + _pctBuf[kv.Key] = pct.ToString("F2"); + } } } } @@ -1421,7 +1428,7 @@ private void DrawAvailRow(FuelTank tank) _availAmountBuf[tank.name] = (maxRfUnits * Math.Max(0d, Math.Min(100d, pct)) / 100d).ToString("F2"); } - else if (GUI.GetNameOfFocusedControl() != "availPct_" + tank.name) + else if (GUI.GetNameOfFocusedControl() != "availPct_" + tank.name && maxRfUnits >= 0.0001) { _availFillPctBuf[tank.name] = Math.Min(100d, fillAmt / maxRfUnits * 100d).ToString("F2"); @@ -2009,17 +2016,18 @@ private void AddTank(FuelTank tank, double amount) _module.MarkWindowDirty(); NotifyEditor(); } + /// /// Updates the contents of _lockedAmounts to match the current fill level of the tank, if it was locked before. /// /// - private void SyncLockedTank(FuelTank o) + private void SyncLockedTank(FuelTank tank) { - if (_lockedAmounts.ContainsKey(o.name)) + if (_lockedAmounts.ContainsKey(tank.name)) { - _lockedAmounts[o.name] = o.maxAmount; - if (o.maxAmount == 0) - _lockedAmounts.Remove(o.name); + _lockedAmounts[tank.name] = tank.maxAmount; + if (tank.maxAmount == 0) + _lockedAmounts.Remove(tank.name); } } From e969d4e6c6ba4bb9632f964e46cf050ee39517cc Mon Sep 17 00:00:00 2001 From: siimav <1120038+siimav@users.noreply.github.com> Date: Mon, 27 Jul 2026 19:58:23 +0300 Subject: [PATCH 7/7] cleanup --- Source/Tanks/RealFuelsWindow.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Source/Tanks/RealFuelsWindow.cs b/Source/Tanks/RealFuelsWindow.cs index 074baf20..4a3c00bf 100644 --- a/Source/Tanks/RealFuelsWindow.cs +++ b/Source/Tanks/RealFuelsWindow.cs @@ -74,9 +74,8 @@ public static void ShowGUI(ModuleFuelTanks module) if (_instance == null) return; // prevent thrashing if we attempt to swap between symmetry counterparts // null-conditional is forbidden so you get this stack! - if (_instance._module != null && + if (_instance._module != null && _instance._module.part != null && - _instance._module.part.symmetryCounterparts != null && _instance._module.part.symmetryCounterparts.Contains(module.part)) return; @@ -1430,7 +1429,7 @@ private void DrawAvailRow(FuelTank tank) } else if (GUI.GetNameOfFocusedControl() != "availPct_" + tank.name && maxRfUnits >= 0.0001) { - _availFillPctBuf[tank.name] = + _availFillPctBuf[tank.name] = Math.Min(100d, fillAmt / maxRfUnits * 100d).ToString("F2"); } @@ -2026,7 +2025,7 @@ private void SyncLockedTank(FuelTank tank) if (_lockedAmounts.ContainsKey(tank.name)) { _lockedAmounts[tank.name] = tank.maxAmount; - if (tank.maxAmount == 0) + if (tank.maxAmount == 0) _lockedAmounts.Remove(tank.name); } }