fix: v3.0.0 -- thresholds on the free-fraction scale the assembly actually uses

Disassembling Cosmoteer.dll (tools/ildump.py) shows PartThresholdToggle
compares plain floats with no percent logic, and that a
NetworkResourceInput's Value is the free fraction (0..1) of the connected
network. v2.1.0 compared that against 2000000, so it always vented.

Thresholds are now 5% / 10% free as local constants. New guard
test_threshold_amounts_match_value_scale fails on the old values. README
corrects the earlier wrong diagnosis of v2.0.0.

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:23:02 +02:00
co-authored by Claude Fable 5
parent da331e955c
commit c5ddc78495
5 changed files with 294 additions and 52 deletions
+16 -16
View File
@@ -18,41 +18,41 @@
// --- Is the heat network running out of room? -------------------------------
//
// Measured locally, exactly the way the heat exchanger decides whether it can
// still push heat into the network (heat_exchanger.rules: IsNetworkHeatNonFull
// reads a bare NetworkResourceInput). A NetworkResourceInput's value is how much
// the network can still accept.
// Measured locally with a bare NetworkResourceInput, the same component an
// overclocked part pushes its heat through (base_part_overclock.rules:
// NetworkHeatInput), so this is exactly the producer's view of the network.
//
// Reading a thermal battery's own HeatStorage does NOT work for this. That
// storage is wrapped by a NetworkResourceStore, and v2.0.0 held heat forever --
// batteries visibly full, parts cooking -- because a local percentage threshold
// on it never crossed. This measures the network itself instead, from the part
// that acts on the answer, so no cross-part broadcast is involved at all.
//
// Amounts are resource units: 1 status heat = 1000 resource units
// (statuses/heat/heat.rules: STATUS_TO_RESOURCE_RATIO).
// Its Value, from Cosmoteer.dll (PartNetworkResourceInput.get_Value, see
// tools/ildump.py), is the FREE FRACTION of the connected heat storage:
// 1 - stored / total capacity -> 1.0 empty, 0.0 full, 0 if nothing attached
// and a ThresholdToggle compares that against MinAmount/MaxAmount as plain
// floats, so the amounts below are fractions too ("5%" is 0.05).
AutoRad_NetworkSpace
{
Type = NetworkResourceInput
ResourceType = heat
Mode = DistributeProportionallyByAvailable
Categories = [ thermal_storage ]
}
// Under ~2000 status heat of headroom left: vent.
// Vent while less than VENT_BELOW_FREE of the network is still free.
AutoRad_SpaceLow
{
Type = ThresholdToggle
ValueFrom = AutoRad_NetworkSpace
MaxAmount = 2000000
VENT_BELOW_FREE = 5%
MaxAmount = &VENT_BELOW_FREE
}
// Back above ~6000 status heat of headroom: safe to bank heat again.
// Hold heat again once at least HOLD_ABOVE_FREE is free. The gap between the
// two is a deadband so radiators do not chatter at the threshold.
AutoRad_SpaceAmple
{
Type = ThresholdToggle
ValueFrom = AutoRad_NetworkSpace
MinAmount = 6000000
HOLD_ABOVE_FREE = 10%
MinAmount = &HOLD_ABOVE_FREE
}
AutoRad_LowTrigger