feat: v4.0.0 -- radiator switch picks the mode, ammo gate removed

ON vents constantly (vanilla). OFF banks heat and vents only when the
network is nearly full. The gate is now PowerToggle OR MustVent.

The launcher's thermal-ammo broadcast is gone: it did not follow the ammo
selection live, and the switch states the player's intent directly. Only
the radiator is patched now. New test_gate_is_power_or_must_vent pins the
semantics.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oLWY1G6S8U35bi2FxVWni
This commit is contained in:
2026-08-29 01:44:06 +02:00
co-authored by Claude Fable 5
parent c5ddc78495
commit ad17425508
5 changed files with 107 additions and 174 deletions
+35
View File
@@ -698,6 +698,41 @@ def test_threshold_amounts_match_value_scale():
)
# ---------------------------------------------------------------------------
# The gate's semantics: radiator button ON = vanilla, always vent; button OFF
# = bank heat, and the mod vents only when the network is nearly full. So the
# gate is PowerToggle OR MustVent, and nothing else -- no ammo criterion, no
# launcher patch, no cross-part signal.
# ---------------------------------------------------------------------------
def test_gate_is_power_or_must_vent():
mod = rules_parser.parse_file(os.path.join(MOD_ROOT, "mod.rules"))
for action in iter_actions(mod):
for field in ("AddBaseTo", "OverrideIn", "AddTo"):
node = action.get(field)
check(
node is None or "missile_launcher" not in node.value,
f"mod.rules still patches the missile launcher via {field}",
)
check(
not os.path.exists(os.path.join(MOD_ROOT, "components", "launcher_ammo_signal.rules")),
"components/launcher_ammo_signal.rules should be gone",
)
auto = rules_parser.parse_file(os.path.join(MOD_ROOT, "components", "radiator_auto.rules"))
gate = auto.get("AutoPowerToggle")
check(gate is not None, "AutoPowerToggle missing")
if gate is None:
return
check(gate.get("Mode") is not None and gate.get("Mode").value == "Any", "AutoPowerToggle must be Mode = Any (OR)")
toggles = gate.get("Toggles")
names = [c.value for c in toggles.children.values()] if toggles is not None and toggles.kind == "list" else []
check(sorted(names) == ["AutoRad_MustVent", "PowerToggle"], f"AutoPowerToggle toggles should be exactly PowerToggle + AutoRad_MustVent, got {names}")
for name in auto.order:
check("Ammo" not in name, f"ammo-signal component still present: {name}")
def test_manifest_is_well_formed():
mod = rules_parser.parse_file(os.path.join(MOD_ROOT, "mod.rules"))
for field in ("ID", "Name", "Version", "CompatibleGameVersions", "ModifiesGameplay"):