Skip to content

Replace thermomachine dynamic per-tick power with flat target-based draw#617

Draft
Copilot wants to merge 2 commits intomasterfrom
copilot/fix-thermomachine-power-calculation
Draft

Replace thermomachine dynamic per-tick power with flat target-based draw#617
Copilot wants to merge 2 commits intomasterfrom
copilot/fix-thermomachine-power-calculation

Conversation

Copy link

Copilot AI commented Mar 9, 2026

Multiple thermomachines on the same gas network race on the shared area power total: each tick they remove their old registration and add a new one, causing compounding interference when two machines process in the same tick.

Changes

  • New recalculate_power() proc: computes a stable active_power_usage from the target temperature offset from room temp (T20C), registered once via standard set_active_power()/set_idle_power():
    var/temperature_difference = abs(target_temperature - T20C)
    active_power_usage = max(idle_power_usage, (heat_capacity * temperature_difference) / 1000)
  • Removed: last_registered_power variable and the entire per-tick addStaticPower/removeStaticPower dance from process_atmos()
  • recalculate_power() called on all state-changing paths: RefreshParts(), ui_act() (power toggle + target change), CtrlClick(), freezer/on/Initialize(), coldroom/Initialize(), freezer/AltClick(), heater/AltClick()
  • Restored conditional update_parents() in process_atmos() — only fires when temperature actually changed by >1K, matching the heat_exchanger pattern
  • Removed duplicate investigate_log in CtrlClick() that was referencing the wrong usr variable
Original prompt

Problem

Thermomachines currently attempt to dynamically calculate power usage based on the temperature delta of gas passing through them each tick. This causes issues when multiple thermomachines are connected to the same gas network:

  1. Each machine removes its old power registration and adds a new one every tick
  2. When two machines process in the same tick, they interfere with each other's calculations
  3. The area power system uses a single total value per channel, not per-machine tracking
  4. This causes unpredictable power draws that can compound incorrectly

The original fix (marked with // PENTEST FIX - THERMOMACHINE POWER CALCULATION) tried to address negative power by tracking last_registered_power, but the fundamental issue is that multiple machines modifying the same area power total in the same tick causes race conditions.

Solution

Replace the dynamic per-tick power calculation with a flat power draw based on the target temperature difference from room temperature (similar to how most other machinery works):

  1. Calculate power draw based on how far the target temperature is from room temperature (T20C)
  2. Use this as the machine's active_power_usage (static, doesn't change every tick)
  3. Only recalculate when the target temperature changes or machine turns on/off
  4. Add a safety check to ensure power draw can never go negative
  5. Remove the dynamic per-tick power registration system

This approach:

  • Makes power draw predictable and stable
  • Prevents machines from interfering with each other
  • Follows the pattern used by other machinery in the codebase
  • Lore explanation: "Thermomachines run at full power based on their target setting because the heating/cooling elements and compressors operate at constant draw, even if the pipes are well-insulated"

Implementation Details

Modify code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm:

  1. Remove the dynamic power calculation from process_atmos()
  2. Remove the last_registered_power variable and all references to it
  3. Add a new proc recalculate_power() that calculates active_power_usage based on target temperature
  4. Call recalculate_power() when target temperature changes or machine state changes
  5. Use standard set_active_power() and set_idle_power() procs instead of manual registration
  6. Add max(0, ...) safety checks to ensure power never goes negative

The power calculation should be:

var/temperature_difference = abs(target_temperature - T20C)
active_power_usage = max(idle_power_usage, (heat_capacity * temperature_difference) / 1000)

Ensure the fix is applied to:

  • The main process_atmos() method
  • The ui_act() method when target temperature changes
  • The CtrlClick() method for turning on/off
  • Any other places where thermomachine state changes

This pull request was created from Copilot chat.


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

…ased power draw

Co-authored-by: Ossa88 <8151625+Ossa88@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix thermomachine power calculation issues due to tick interference Replace thermomachine dynamic per-tick power with flat target-based draw Mar 9, 2026
@github-actions github-actions bot requested review from Constellado and Ossa88 March 9, 2026 03:17
@Ossa88
Copy link
Collaborator

Ossa88 commented Mar 9, 2026

I kinda wanted to see how this system functioned as it was kinda interesting.

None the less CoPilot basically undid my last PR, which only worked when a single thermo machine was on the network, and totally failed the second multiple thermo machines on the same network.

For whatever reason I never thought to think of testing two machines both fighting to heat up, and cool down, the same pipe network. Nor did I realize that each instance of the machine registering how much power it was consuming... all shared the same registration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants