fix: measure the network, not a battery's storage
v2.0.0 held heat forever: thermal batteries visibly full, parts taking heat damage, radiators still idle. That is the dangerous direction. The ceiling read a thermal battery's own HeatStorage with a percentage threshold. That storage is wrapped by a NetworkResourceStore, and the local read never crossed 98% however full the network actually got, so the battery broadcast "hold" permanently. Measure the network's remaining room directly instead, from the radiator itself, with a bare NetworkResourceInput -- the same way the heat exchanger decides whether it can still push heat. Vent below ~2000 status heat of headroom, hold again above ~6000. Absolute headroom is also the better metric: what matters is room against the generation rate, not a percentage of whatever storage happens to be installed. Polarity is now fail-safe. Holding requires positive evidence of headroom, so anything missing or unmeasurable vents rather than holds -- the opposite of the failure above. This deletes both broadcasters. The thermal battery and base_part_overclock are no longer patched at all, and the mod touches two parts instead of four. The launcher ammo signal stays; it is confirmed working in both directions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011oLWY1G6S8U35bi2FxVWni
This commit is contained in:
@@ -52,52 +52,50 @@ about **172 status/sec per tile** while fully overflowing, so roughly **2
|
|||||||
seconds of continuous, total overflow** before fire risk even arms. Radiators
|
seconds of continuous, total overflow** before fire risk even arms. Radiators
|
||||||
react within **0.5 s** (`HeatDissipationConverter { Interval = 0.5 }`).
|
react within **0.5 s** (`HeatDissipationConverter { Interval = 0.5 }`).
|
||||||
|
|
||||||
At a 98% ceiling on a single 15000-capacity thermal battery, the overshoot
|
With ~2000 status heat of headroom left when venting starts, one overclocked
|
||||||
during that reaction window is about 625 status heat spread over 16 tiles —
|
large reactor takes about 0.7 s to consume it, against a 0.5 s radiator reaction
|
||||||
roughly **39 per tile**, against a threshold of 350. Holding the network near
|
time — so the overshoot lands well under the 350-per-tile threshold at which
|
||||||
full is safe with a wide margin, and every percent of ceiling given up is
|
anything begins to happen. Holding the network near full is safe with a wide
|
||||||
ammunition thrown away for nothing.
|
margin, and headroom given up beyond that is ammunition thrown away for nothing.
|
||||||
|
|
||||||
The genuine constraint is not the threshold. It is total radiator throughput
|
The genuine constraint is not the threshold. It is total radiator throughput
|
||||||
(1100/sec each) versus sustained generation. If you are short on radiators you
|
(1100/sec each) versus sustained generation. If you are short on radiators you
|
||||||
overflow continuously and no threshold value saves you — which is what the
|
overflow continuously and no threshold value saves you.
|
||||||
safety net below is for.
|
|
||||||
|
|
||||||
## What the mod does
|
## What the mod does
|
||||||
|
|
||||||
Radiators vent when **any** of these is true:
|
A radiator vents when **either**:
|
||||||
|
|
||||||
1. The heat network is not asking them to hold — the thermal battery is at or
|
1. **The heat network is running out of room.** The radiator measures this
|
||||||
above 98% full (dropping the hold again at 94%, a deadband so radiators do
|
itself, with a bare `NetworkResourceInput` — the same way the heat exchanger
|
||||||
not chatter at the threshold).
|
decides whether it can still push heat (`heat_exchanger.rules`:
|
||||||
2. No launcher on the network is set to Thermal Canister Missiles. If you are
|
`IsNetworkHeatNonFull`). Vent below ~2000 status heat of remaining headroom,
|
||||||
not spending heat as ammunition there is nothing to hoard, so radiators
|
hold again above ~6000, a deadband so radiators do not chatter.
|
||||||
|
2. **No launcher on the network is set to Thermal Canister Missiles.** If you
|
||||||
|
are not spending heat as ammunition there is nothing to hoard, so radiators
|
||||||
behave exactly as in vanilla.
|
behave exactly as in vanilla.
|
||||||
3. **Safety net:** some part is actually overflowing heat right now. This reads
|
|
||||||
the engine's own `OverflowHeatStorage`, so it fires the moment the network
|
|
||||||
genuinely refuses heat — regardless of thresholds, pipe topology, or whether
|
|
||||||
your radiator capacity was ever sufficient. Latched for 3 seconds so a
|
|
||||||
stream of per-tick overflow events becomes one steady signal.
|
|
||||||
|
|
||||||
Your manual radiator on/off switch still overrides all of it. The automation can
|
Your manual radiator on/off switch still overrides both. The automation can only
|
||||||
only idle a radiator you have left switched on.
|
idle a radiator you have left switched on.
|
||||||
|
|
||||||
Ships with no thermal battery have no broadcaster, so nothing changes for them.
|
The design is **fail-safe**: holding heat requires positive evidence that the
|
||||||
The signal polarity is deliberately "hold", not "vent", so that the absence of
|
network has room to spare. Anything missing, disconnected or unmeasurable ends
|
||||||
any part of this mod's machinery always falls back to vanilla behaviour.
|
up venting, which is vanilla behaviour. Headroom is measured in absolute heat
|
||||||
|
rather than as a percentage, because what actually matters is headroom against
|
||||||
|
your generation rate — 2000 status heat is under a second of one overclocked
|
||||||
|
large reactor.
|
||||||
|
|
||||||
No stat or number is changed. Only *when* radiators run.
|
No stat or number is changed. Only *when* radiators run.
|
||||||
|
|
||||||
## Layout
|
## Layout
|
||||||
|
|
||||||
```
|
```
|
||||||
mod.rules manifest + patch actions
|
mod.rules manifest + patch actions
|
||||||
components/thermal_battery_vent.rules decides when there is surplus
|
components/radiator_auto.rules measures network headroom, decides, acts
|
||||||
components/launcher_ammo_signal.rules reports thermal ammo in use
|
components/launcher_ammo_signal.rules reports thermal ammo in use
|
||||||
components/overclock_overflow_signal.rules safety net, reports real overflow
|
tests/ offline validation suite
|
||||||
components/radiator_auto.rules combines the signals into one gate
|
tools/dotnet_enum.py reads component schemas out of Cosmoteer.dll
|
||||||
tests/ offline validation suite
|
install.sh validate + copy into the Mods folder
|
||||||
install.sh validate + copy into the Mods folder
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
@@ -141,11 +139,24 @@ holding heat back for your launchers.
|
|||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
Working, confirmed in game on 2026-08-29 against game data version 0.30.4c.
|
**Partly confirmed.** Against game data 0.30.4c, as of 2026-08-29:
|
||||||
|
|
||||||
Getting there took four builds that each loaded without a single error and did
|
- Confirmed: the gate closes and idles a radiator when thermal ammo is in use.
|
||||||
nothing, for four different reasons. Cosmoteer reports none of them, so each one
|
- Confirmed: with no launcher on thermal ammo, behaviour is vanilla.
|
||||||
now has a mutation-verified test:
|
- **Not yet confirmed: the release path.** v2.0.0 held heat *forever* — thermal
|
||||||
|
batteries visibly full, parts taking heat damage, radiators still idle.
|
||||||
|
|
||||||
|
That failure was the dangerous direction, and it came from reading the wrong
|
||||||
|
thing. v2.0.0 put a percentage threshold on a thermal battery's own
|
||||||
|
`HeatStorage`, which is wrapped by a `NetworkResourceStore`; that local read
|
||||||
|
never crossed its ceiling however full the network got. v2.1.0 measures the
|
||||||
|
network's remaining room directly instead, from the radiator, using the same
|
||||||
|
bare `NetworkResourceInput` the heat exchanger uses — and the polarity is now
|
||||||
|
fail-safe, so an unmeasurable signal vents rather than holds.
|
||||||
|
|
||||||
|
Getting this far took five builds that each loaded without a single error and
|
||||||
|
misbehaved silently. Cosmoteer reports none of these, so each has a
|
||||||
|
mutation-verified test:
|
||||||
|
|
||||||
| Silent failure | Guard |
|
| Silent failure | Guard |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -153,16 +164,14 @@ now has a mutation-verified test:
|
|||||||
| A field name that does not exist in the component's real schema is ignored rather than reported | `test_component_fields_exist_in_schema`, read from the assembly's own metadata |
|
| A field name that does not exist in the component's real schema is ignored rather than reported | `test_component_fields_exist_in_schema`, read from the assembly's own metadata |
|
||||||
| A network component no route reaches never attaches, and reads as absent forever | `test_network_components_are_routed` |
|
| A network component no route reaches never attaches, and reads as absent forever | `test_network_components_are_routed` |
|
||||||
| An `Overrides` value naming a component that is not there reverts the gate to nothing | `test_override_values_name_real_components` |
|
| An `Overrides` value naming a component that is not there reverts the gate to nothing | `test_override_values_name_real_components` |
|
||||||
|
| A percentage threshold on a network-wrapped `ResourceStorage` never crosses, however full the network is | no test — avoid the pattern; measure the network, not a part's storage |
|
||||||
|
|
||||||
The third was the actual cause. A network component is only attached if a
|
Routing was the cause of the first three builds doing nothing. A network
|
||||||
**route** reaches it: the part's `NetworkPort` anchors on one component, and a
|
component is only attached if a **route** reaches it: the part's `NetworkPort`
|
||||||
`NetworkRouter` bridges that anchor to every other network component on the
|
anchors on one component, and a `NetworkRouter` bridges that anchor to every
|
||||||
part. That is how vanilla connects the radiator's own
|
other network component on the part. That is how vanilla connects the radiator's
|
||||||
`NetworkedThermalDissipationLoadValue`, and how the amplification pump reaches
|
own `NetworkedThermalDissipationLoadValue`, and how the amplification pump
|
||||||
the resonance beam turret. Routes now run from each part's anchor —
|
reaches the resonance beam turret. Many parts ship no router at all.
|
||||||
`NetworkedHeatBuffer` on the radiator through its existing `Router`, plus a new
|
|
||||||
`NetworkRouter` on the thermal battery, the missile launcher and
|
|
||||||
`base_part_overclock`, none of which ships one.
|
|
||||||
|
|
||||||
None of this is documented: the wiki's modding pages cover no network components
|
None of this is documented: the wiki's modding pages cover no network components
|
||||||
at all, so the game's own `.rules` files are the only reference.
|
at all, so the game's own `.rules` files are the only reference.
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
// ABOUT ME
|
|
||||||
// Components merged into base_part_overclock, the shared base for every
|
|
||||||
// overclockable part. They form the mod's safety net.
|
|
||||||
//
|
|
||||||
// Why it exists: the threshold band on the thermal battery is a prediction. It
|
|
||||||
// can be wrong -- if the ship generates heat faster than its radiators can
|
|
||||||
// clear it, or if heat pipe topology cannot deliver to the batteries quickly
|
|
||||||
// enough, heat overflows regardless of what percentage the band is set to.
|
|
||||||
//
|
|
||||||
// OverflowHeatStorage is the engine's own ground truth for that: heat only ever
|
|
||||||
// reaches it when the network has already refused the heat, which is the exact
|
|
||||||
// moment the part starts cooking itself. Any part in that state forces every
|
|
||||||
// radiator on the network to vent, ignoring the band entirely.
|
|
||||||
//
|
|
||||||
// The timer holds the signal for a few seconds so a stream of per-tick overflow
|
|
||||||
// events becomes one steady "vent now" rather than a flicker.
|
|
||||||
//
|
|
||||||
// Carried as a NetworkValue rather than a NetworkToggle: vanilla marks every
|
|
||||||
// NetworkToggle IncludeInPhysical = false, so they do not carry a signal on a
|
|
||||||
// real ship.
|
|
||||||
|
|
||||||
AutoRad_OverflowTimer
|
|
||||||
{
|
|
||||||
Type = Timer
|
|
||||||
Duration = 3
|
|
||||||
StartExpired = true
|
|
||||||
StartTrigger = OverflowHeatStorage
|
|
||||||
ResetTrigger = OverflowHeatStorage
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoRad_One { Type = StaticValue; Value = 1 }
|
|
||||||
AutoRad_Zero { Type = StaticValue; Value = 0 }
|
|
||||||
|
|
||||||
AutoRad_OverflowValue
|
|
||||||
{
|
|
||||||
Type = ToggleValueSelector
|
|
||||||
Toggle = AutoRad_OverflowTimer
|
|
||||||
ToggleOnValue = AutoRad_One
|
|
||||||
ToggleOffValue = AutoRad_Zero
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoRad_NetworkedForceVent
|
|
||||||
{
|
|
||||||
Type = NetworkValue
|
|
||||||
ValueSource = AutoRad_OverflowValue
|
|
||||||
Categories = [ auto_radiator_force_vent ]
|
|
||||||
IncludeInBlueprints = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routes the broadcast onto the heat network. NetworkHeatInput is what this
|
|
||||||
// base's thermal ports anchor on (BASE_THERMAL_PORT: RouteSource =
|
|
||||||
// &../HEAT_NETWORK_INPUT).
|
|
||||||
AutoRad_Router
|
|
||||||
{
|
|
||||||
Type = NetworkRouter
|
|
||||||
RouteGenerators
|
|
||||||
[
|
|
||||||
{
|
|
||||||
Type = Bidirectional
|
|
||||||
Routes
|
|
||||||
[
|
|
||||||
[NetworkHeatInput, AutoRad_NetworkedForceVent, 0]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,31 +1,99 @@
|
|||||||
// ABOUT ME
|
// ABOUT ME
|
||||||
// Components merged into the Radiator. They receive the three network values
|
// Components merged into the Radiator. They decide whether this radiator should
|
||||||
// this mod broadcasts and combine them into a single gate, AutoPowerToggle,
|
// vent or hold, and combine that into a single gate, AutoPowerToggle, which
|
||||||
// which mod.rules then substitutes for the radiator's vanilla PowerToggle on
|
// mod.rules substitutes for the radiator's vanilla PowerToggle on the parts of
|
||||||
// the parts of the radiator that actually dissipate heat.
|
// the radiator that actually dissipate heat.
|
||||||
//
|
|
||||||
// Each broadcast arrives as a number, 1 or 0, summarised across the network by
|
|
||||||
// a NetworkValueReceiver and turned back into a toggle by a threshold at 0.5.
|
|
||||||
// A network with no broadcaster at all yields 0, which is why a ship without
|
|
||||||
// thermal batteries or thermal launchers keeps vanilla behaviour.
|
|
||||||
//
|
//
|
||||||
// The player's manual on/off switch (PowerToggle) is preserved and still wins:
|
// The player's manual on/off switch (PowerToggle) is preserved and still wins:
|
||||||
// a radiator the player has switched off stays off. The automation can only
|
// a radiator the player has switched off stays off. The automation can only
|
||||||
// idle a radiator the player has left on.
|
// idle a radiator the player has left on.
|
||||||
//
|
//
|
||||||
// A radiator vents when ANY of these hold:
|
// A radiator vents when EITHER:
|
||||||
// - the network is not asking it to hold (no battery, or battery full)
|
// - the heat network is running out of room, or
|
||||||
// - no launcher on the network is using thermal ammunition
|
// - no launcher on the network is using thermal ammunition
|
||||||
// - some part is overflowing heat right now (safety net, overrides all)
|
//
|
||||||
|
// The design is fail-safe: holding requires positive evidence that the network
|
||||||
|
// has room to spare. Anything missing, disconnected or unmeasurable results in
|
||||||
|
// venting, which is vanilla behaviour.
|
||||||
|
|
||||||
AutoRad_HoldSignal
|
// --- 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.
|
||||||
|
//
|
||||||
|
// 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).
|
||||||
|
|
||||||
|
AutoRad_NetworkSpace
|
||||||
{
|
{
|
||||||
Type = NetworkValueReceiver
|
Type = NetworkResourceInput
|
||||||
Categories = [ auto_radiator_hold ]
|
ResourceType = heat
|
||||||
Mode = Max
|
Mode = DistributeProportionallyByAvailable
|
||||||
AllowUncategorized = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Under ~2000 status heat of headroom left: vent.
|
||||||
|
AutoRad_SpaceLow
|
||||||
|
{
|
||||||
|
Type = ThresholdToggle
|
||||||
|
ValueFrom = AutoRad_NetworkSpace
|
||||||
|
MaxAmount = 2000000
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back above ~6000 status heat of headroom: safe to bank heat again.
|
||||||
|
AutoRad_SpaceAmple
|
||||||
|
{
|
||||||
|
Type = ThresholdToggle
|
||||||
|
ValueFrom = AutoRad_NetworkSpace
|
||||||
|
MinAmount = 6000000
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoRad_LowTrigger
|
||||||
|
{
|
||||||
|
Type = ToggleTrigger
|
||||||
|
Toggle = AutoRad_SpaceLow
|
||||||
|
TriggerWhenOn = true
|
||||||
|
TriggerWhenOff = false
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoRad_AmpleTrigger
|
||||||
|
{
|
||||||
|
Type = ToggleTrigger
|
||||||
|
Toggle = AutoRad_SpaceAmple
|
||||||
|
TriggerWhenOn = true
|
||||||
|
TriggerWhenOff = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// The deadband, so radiators do not chatter at the threshold.
|
||||||
|
AutoRad_VentLatch
|
||||||
|
{
|
||||||
|
Type = TriggeredToggle
|
||||||
|
OnTrigger = AutoRad_LowTrigger
|
||||||
|
OffTrigger = AutoRad_AmpleTrigger
|
||||||
|
}
|
||||||
|
|
||||||
|
// OR the raw low reading back in, so a ship that loads already out of room
|
||||||
|
// vents at once instead of waiting for a crossing that never comes.
|
||||||
|
AutoRad_MustVent
|
||||||
|
{
|
||||||
|
Type = MultiToggle
|
||||||
|
Toggles = [ AutoRad_VentLatch, AutoRad_SpaceLow ]
|
||||||
|
Mode = Any
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Is anything actually spending heat as ammunition? ----------------------
|
||||||
|
//
|
||||||
|
// Broadcast by the missile launcher over the heat network. Confirmed working in
|
||||||
|
// game: with a launcher on Thermal Canister ammo this reads 1, and with the
|
||||||
|
// launcher on anything else it reads 0 and radiators behave exactly as vanilla.
|
||||||
|
|
||||||
AutoRad_ThermalAmmoSignal
|
AutoRad_ThermalAmmoSignal
|
||||||
{
|
{
|
||||||
Type = NetworkValueReceiver
|
Type = NetworkValueReceiver
|
||||||
@@ -34,26 +102,6 @@ AutoRad_ThermalAmmoSignal
|
|||||||
AllowUncategorized = false
|
AllowUncategorized = false
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoRad_ForceVentSignal
|
|
||||||
{
|
|
||||||
Type = NetworkValueReceiver
|
|
||||||
Categories = [ auto_radiator_force_vent ]
|
|
||||||
Mode = Max
|
|
||||||
AllowUncategorized = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// "value is not zero", written the way vanilla writes it (the thermal pump's
|
|
||||||
// UtilizationToggle). The signals are only ever 0 or 1, so this is equivalent
|
|
||||||
// to a 0.5 threshold while sticking to a form the game demonstrably honours.
|
|
||||||
AutoRad_IsHolding
|
|
||||||
{
|
|
||||||
Type = ThresholdToggle
|
|
||||||
ValueFrom = AutoRad_HoldSignal
|
|
||||||
MinAmount = 0
|
|
||||||
MaxAmount = 0
|
|
||||||
Invert = true
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoRad_ThermalAmmoInUse
|
AutoRad_ThermalAmmoInUse
|
||||||
{
|
{
|
||||||
Type = ThresholdToggle
|
Type = ThresholdToggle
|
||||||
@@ -63,30 +111,22 @@ AutoRad_ThermalAmmoInUse
|
|||||||
Invert = true
|
Invert = true
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoRad_ForceVent
|
// --- The gate ---------------------------------------------------------------
|
||||||
{
|
|
||||||
Type = ThresholdToggle
|
|
||||||
ValueFrom = AutoRad_ForceVentSignal
|
|
||||||
MinAmount = 0
|
|
||||||
MaxAmount = 0
|
|
||||||
Invert = true
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoRad_VentAllowed
|
AutoRad_VentAllowed
|
||||||
{
|
{
|
||||||
Type = MultiToggle
|
Type = MultiToggle
|
||||||
Toggles
|
Toggles
|
||||||
[
|
[
|
||||||
{ Toggle = AutoRad_IsHolding; Invert = true }
|
AutoRad_MustVent
|
||||||
{ Toggle = AutoRad_ThermalAmmoInUse; Invert = true }
|
{ Toggle = AutoRad_ThermalAmmoInUse; Invert = true }
|
||||||
AutoRad_ForceVent
|
|
||||||
]
|
]
|
||||||
Mode = Any
|
Mode = Any
|
||||||
}
|
}
|
||||||
|
|
||||||
// The replacement gate. PowerToggle is the radiator's vanilla UIToggle, so the
|
// PowerToggle is the radiator's vanilla UIToggle, so the player's switch remains
|
||||||
// player's switch remains the master control: it can turn a radiator off, and
|
// the master control: it can turn a radiator off, and the automation can only
|
||||||
// the automation can only idle one the player has left on.
|
// idle one the player has left on.
|
||||||
AutoPowerToggle
|
AutoPowerToggle
|
||||||
{
|
{
|
||||||
Type = MultiToggle
|
Type = MultiToggle
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
// ABOUT ME
|
|
||||||
// Components merged into the Thermal Battery. The battery is the ship's heat
|
|
||||||
// gauge, so it is what decides whether the heat network has surplus worth
|
|
||||||
// venting, and broadcasts that decision to every radiator on the network.
|
|
||||||
//
|
|
||||||
// The broadcast is a NetworkValue carrying 1 (hold) or 0 (vent), not a
|
|
||||||
// NetworkToggle. Every NetworkToggle in vanilla sets IncludeInPhysical = false
|
|
||||||
// -- they exist only for blueprint stat display and do not carry a signal on a
|
|
||||||
// real ship. NetworkValue has no such restriction and is what the thermal
|
|
||||||
// amplification pump uses to drive actual physical behaviour.
|
|
||||||
//
|
|
||||||
// Polarity is deliberate: the battery broadcasts "HOLD", not "vent". A ship
|
|
||||||
// with no thermal battery has no broadcaster, every radiator reads 0, and
|
|
||||||
// radiators behave exactly as in vanilla.
|
|
||||||
//
|
|
||||||
// Heat storage fullness causes no damage on its own -- damage only occurs when
|
|
||||||
// the network refuses heat and it overflows onto the producing part
|
|
||||||
// (base_part_overclock.rules: HeatDistributionStorage adds InOrder, network
|
|
||||||
// first, OverflowHeatStorage second). So the band sits high on purpose, keeping
|
|
||||||
// the network full as a deep ammunition reserve for Thermal Canister Missiles.
|
|
||||||
//
|
|
||||||
// All names are prefixed to avoid colliding with vanilla component names.
|
|
||||||
|
|
||||||
// Vent when the battery is essentially full.
|
|
||||||
AutoRad_VentCeiling
|
|
||||||
{
|
|
||||||
Type = ThresholdToggle
|
|
||||||
ValueFrom = HeatStorage
|
|
||||||
MinAmount = 98%
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop venting once there is real headroom again.
|
|
||||||
AutoRad_VentFloor
|
|
||||||
{
|
|
||||||
Type = ThresholdToggle
|
|
||||||
ValueFrom = HeatStorage
|
|
||||||
MaxAmount = 94%
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoRad_CeilingTrigger
|
|
||||||
{
|
|
||||||
Type = ToggleTrigger
|
|
||||||
Toggle = AutoRad_VentCeiling
|
|
||||||
TriggerWhenOn = true
|
|
||||||
TriggerWhenOff = false
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoRad_FloorTrigger
|
|
||||||
{
|
|
||||||
Type = ToggleTrigger
|
|
||||||
Toggle = AutoRad_VentFloor
|
|
||||||
TriggerWhenOn = true
|
|
||||||
TriggerWhenOff = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// The deadband: latches on at the ceiling, stays on until the floor is reached.
|
|
||||||
// Without this the radiators would chatter on and off at the threshold.
|
|
||||||
AutoRad_VentLatch
|
|
||||||
{
|
|
||||||
Type = TriggeredToggle
|
|
||||||
OnTrigger = AutoRad_CeilingTrigger
|
|
||||||
OffTrigger = AutoRad_FloorTrigger
|
|
||||||
}
|
|
||||||
|
|
||||||
// OR the raw ceiling back in, so a ship that loads already above the ceiling
|
|
||||||
// vents immediately instead of waiting for a threshold crossing that never comes.
|
|
||||||
AutoRad_VentActive
|
|
||||||
{
|
|
||||||
Type = MultiToggle
|
|
||||||
Toggles = [ AutoRad_VentLatch, AutoRad_VentCeiling ]
|
|
||||||
Mode = Any
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoRad_One { Type = StaticValue; Value = 1 }
|
|
||||||
AutoRad_Zero { Type = StaticValue; Value = 0 }
|
|
||||||
|
|
||||||
// 1 while the network should hold its heat, 0 while it should vent.
|
|
||||||
AutoRad_HoldValue
|
|
||||||
{
|
|
||||||
Type = ToggleValueSelector
|
|
||||||
Toggle = AutoRad_VentActive
|
|
||||||
ToggleOnValue = AutoRad_Zero
|
|
||||||
ToggleOffValue = AutoRad_One
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoRad_NetworkedHold
|
|
||||||
{
|
|
||||||
Type = NetworkValue
|
|
||||||
ValueSource = AutoRad_HoldValue
|
|
||||||
Categories = [ auto_radiator_hold ]
|
|
||||||
IncludeInBlueprints = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// A network component is only actually attached to the network if a route
|
|
||||||
// reaches it. The part's NetworkPort anchors on one component -- here
|
|
||||||
// NetworkedHeatStorage -- and a NetworkRouter bridges that anchor to every
|
|
||||||
// other network component on the part. The thermal battery ships no router,
|
|
||||||
// because vanilla has nothing else to reach, so the broadcast above needs one
|
|
||||||
// or it is never connected to anything and reads as absent.
|
|
||||||
AutoRad_Router
|
|
||||||
{
|
|
||||||
Type = NetworkRouter
|
|
||||||
RouteGenerators
|
|
||||||
[
|
|
||||||
{
|
|
||||||
Type = Bidirectional
|
|
||||||
Routes
|
|
||||||
[
|
|
||||||
[NetworkedHeatStorage, AutoRad_NetworkedHold, 0]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -7,16 +7,21 @@
|
|||||||
// pull from the same pool, with the same Mode, in the same category, and
|
// pull from the same pool, with the same Mode, in the same category, and
|
||||||
// Data/ships/priorities.rules defines no ordering between them.
|
// Data/ships/priorities.rules defines no ordering between them.
|
||||||
//
|
//
|
||||||
// The patches below add components to four vanilla parts and then repoint the
|
// The patches below add components to two vanilla parts -- the radiator, which
|
||||||
// radiator's heat dissipation, steam plume, dissipation readout and power
|
// decides and acts, and the missile launcher, which reports whether thermal
|
||||||
// indicator from the raw PowerToggle to the AutoPowerToggle gate defined in
|
// ammunition is in use -- and then repoint the radiator's heat dissipation,
|
||||||
// components/radiator_auto.rules.
|
// steam plume, dissipation readout and power indicator from the raw PowerToggle
|
||||||
|
// to the AutoPowerToggle gate defined in components/radiator_auto.rules.
|
||||||
|
//
|
||||||
|
// The radiator measures the heat network's remaining room itself, so nothing
|
||||||
|
// patches the thermal battery or the overclock base. Holding requires positive
|
||||||
|
// evidence of headroom; anything missing or unmeasurable results in venting.
|
||||||
//
|
//
|
||||||
// Nothing here changes a stat or a number. It only changes when radiators run.
|
// Nothing here changes a stat or a number. It only changes when radiators run.
|
||||||
|
|
||||||
ID = ganter.auto_radiator
|
ID = ganter.auto_radiator
|
||||||
Name = "Auto Radiator"
|
Name = "Auto Radiator"
|
||||||
Version = 2.0.0
|
Version = 2.1.0
|
||||||
CompatibleGameVersions = ["0.30.4c", "0.30.4"]
|
CompatibleGameVersions = ["0.30.4c", "0.30.4"]
|
||||||
ModifiesGameplay = true
|
ModifiesGameplay = true
|
||||||
Author = "Tobias Ganter"
|
Author = "Tobias Ganter"
|
||||||
@@ -27,26 +32,16 @@ Description = "Radiators only vent surplus heat instead of running constantly.\n
|
|||||||
"to keep radiators busy, and your missiles reload at whatever rate your reactors "\
|
"to keep radiators busy, and your missiles reload at whatever rate your reactors "\
|
||||||
"happen to produce.\n\n"\
|
"happen to produce.\n\n"\
|
||||||
"With this mod, radiators hold off while the heat network still has room, keeping "\
|
"With this mod, radiators hold off while the heat network still has room, keeping "\
|
||||||
"your thermal batteries topped up as a deep missile reserve, and vent once the "\
|
"your thermal batteries topped up as a deep missile reserve, and vent as the "\
|
||||||
"network is nearly full.\n\n"\
|
"network runs out of room.\n\n"\
|
||||||
"- Only applies when a launcher is actually set to Thermal Canister Missiles. "\
|
"- Only applies when a launcher is actually set to Thermal Canister Missiles. "\
|
||||||
"Otherwise radiators behave exactly as in vanilla.\n\n"\
|
"Otherwise radiators behave exactly as in vanilla.\n\n"\
|
||||||
"- Ships with no thermal battery are unaffected.\n\n"\
|
|
||||||
"- Your manual radiator on/off switch still overrides everything.\n\n"\
|
"- Your manual radiator on/off switch still overrides everything.\n\n"\
|
||||||
"- Safety net: if any part is actually overflowing heat, every radiator vents "\
|
"- Fail-safe: holding heat requires measured headroom on the network. If that "\
|
||||||
"immediately regardless of the threshold."
|
"headroom runs low, or cannot be measured at all, radiators vent."
|
||||||
|
|
||||||
Actions
|
Actions
|
||||||
[
|
[
|
||||||
// --- Broadcasters -----------------------------------------------------
|
|
||||||
|
|
||||||
// The thermal battery decides when the network has surplus.
|
|
||||||
{
|
|
||||||
Action = AddBase
|
|
||||||
AddBaseTo = "<ships/terran/thermal_battery/thermal_battery.rules>/Part/Components"
|
|
||||||
BaseToAdd = &<components/thermal_battery_vent.rules>
|
|
||||||
}
|
|
||||||
|
|
||||||
// The missile launcher reports whether it is set to thermal ammunition.
|
// The missile launcher reports whether it is set to thermal ammunition.
|
||||||
{
|
{
|
||||||
Action = AddBase
|
Action = AddBase
|
||||||
@@ -54,13 +49,7 @@ Actions
|
|||||||
BaseToAdd = &<components/launcher_ammo_signal.rules>
|
BaseToAdd = &<components/launcher_ammo_signal.rules>
|
||||||
}
|
}
|
||||||
|
|
||||||
// Every overclockable part reports when it is actually overflowing heat.
|
|
||||||
{
|
|
||||||
Action = AddBase
|
|
||||||
AddBaseTo = "<ships/base_part_overclock.rules>/Part/Components"
|
|
||||||
BaseToAdd = &<components/overclock_overflow_signal.rules>
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Receiver ---------------------------------------------------------
|
// --- Receiver ---------------------------------------------------------
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -82,9 +71,8 @@ Actions
|
|||||||
AddTo = "<ships/terran/radiator/radiator.rules>/Part/Components/Router/RouteGenerators/0/Routes"
|
AddTo = "<ships/terran/radiator/radiator.rules>/Part/Components/Router/RouteGenerators/0/Routes"
|
||||||
ManyToAdd
|
ManyToAdd
|
||||||
[
|
[
|
||||||
[NetworkedHeatBuffer, AutoRad_HoldSignal, 0]
|
[NetworkedHeatBuffer, AutoRad_NetworkSpace, 0]
|
||||||
[NetworkedHeatBuffer, AutoRad_ThermalAmmoSignal, 0]
|
[NetworkedHeatBuffer, AutoRad_ThermalAmmoSignal, 0]
|
||||||
[NetworkedHeatBuffer, AutoRad_ForceVentSignal, 0]
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -443,7 +443,7 @@ def test_component_fields_exist_in_schema():
|
|||||||
# the components existed on the parts, and every signal read zero.
|
# the components existed on the parts, and every signal read zero.
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
NETWORK_COMPONENT_TYPES = {"NetworkValue", "NetworkValueReceiver"}
|
NETWORK_COMPONENT_TYPES = {"NetworkValue", "NetworkValueReceiver", "NetworkResourceInput"}
|
||||||
|
|
||||||
|
|
||||||
def collect_route_names(node, found=None):
|
def collect_route_names(node, found=None):
|
||||||
|
|||||||
Reference in New Issue
Block a user