Skip to content

Auto Warz — Pipe & Fluid System Design

Document Type: Design Document Version: 0.3 Last Updated: 2026-03-13 Vault destination: 01_Design Documents Related Documents: [01_Master GDD](<./01_Master GDD.md>) · [04_Factory & Resource System Design](<./04_Factory & Resource System Design.md>) · [06_Vertical Layers & Core Power System](<./06_Vertical Layers & Core Power System.md>) · [01_Technical Architecture Document](<../02_Technical Documents/01_Technical Architecture Document.md>) Folder: 01_Design Documents


Overview

Fluids are a distinct logistics tier from belts — they move through pipes, obey pressure and gravity, and have physical properties (viscosity, density, temperature) that affect how they behave. Managing fluid networks is a separate design problem from belt logistics: players must think about pressure sources, heat loss, pipe ratings, and flow rate, not just routing.

Fluids have phase = LIQUID or phase = GAS, and ride pipes rather than belts as a consequence of that phase. This is a rule in code, not a per-resource flag — see Canonical Design Facts §12a. ResourceTier no longer exists. Note that the split is currently structural rather than checked: fluids move through fluid_inventory, items through inventory, and nothing validates a resource against its carrier.


Implementation Status (M2.3 — Complete)

The following are implemented and shipped:

  • PipeSegment node — auto-connects to all adjacent pipe faces; arm-based visual (dark hub + coloured arms per connected face); fluid_id and fluid_amount tracked per segment
  • PipeSystem — scene-level registry and tick coordinator; _rebuild_networks() BFS correctly identifies connected segments and bridges across PipeConnectors by checking both input_face and output_face sides
  • PipeNetwork — pair-equalisation fluid simulation (see #Fluid Simulation — Pair Equalisation)
  • PipeConnector — directional pass-through; blue input face / green output face; 10-unit internal buffer; supports output→input chaining with adjacent connectors
  • FluidDefinition resource class and all 4 fluid .tres files
  • Session config pipe_complexity — Basic / Standard / Full

The following are deferred to M2.3.1 or later: - Full temperature simulation (heat loss, solidification, pipe damage) - Flammability and fire propagation - Gravity modifiers (vertical levels not yet in game) - Fluid network diagnostic overlay (pressure/temperature per segment) - Fluid Pump building - Heat Exchanger, Pipe Heater buildings - Pipe HP and rupture system


Fluid Properties

Every fluid definition (FluidDefinition.tres) carries:

Property Type Description
id String e.g. fld_liquid_metal
display_name String e.g. "Liquid Metal"
viscosity float 0.1 (water-thin) → 1.0 (thick). Higher = slower flow rate
density float kg/m³ equivalent. Higher = more pump power required
flammable bool If true, pipe rupture near a heat source causes fire
min_temp float °C — below this the fluid solidifies and blocks the pipe
max_temp float °C — above this standard pipes take damage
base_temp float °C — temperature when first produced by a building
colour Color Visual tint shown inside pipe segments

Phase 1 Fluid Definitions

Fluid Viscosity Density Flammable Min Temp Max Temp Base Temp
fld_coolant 0.2 1.1 No -10°C 80°C 20°C
fld_fuel 0.3 0.8 Yes -40°C 60°C 20°C
fld_liquid_metal 0.9 7.8 No 450°C 1200°C 850°C
fld_chemical_solvent 0.4 1.3 Yes 5°C 120°C 20°C

Fluid Simulation — Pair Equalisation

Implemented in M2.3. This replaces the greedy pressure-sort propagation originally specified.

Why Pair Equalisation

Greedy sort-by-pressure propagation causes oscillation on looping networks: the highest-pressure segment pushes its fluid aggressively each tick, then becomes low-pressure the next tick, causing back-and-forth flickering that never converges. This is especially visible with 3+ segments in a ring.

Pair equalisation eliminates this by iterating all connected segment pairs exactly once per tick — not propagating greedily from a sorted list.

Algorithm

func _equalise_fluid(segments: Array[PipeSegment]) -> void:
    var visited_pairs: Dictionary = {}

    for seg_a: PipeSegment in segments:
        for dir: int in range(4):
            var offset: Vector2i = BeltSegment.direction_offset(dir as BeltSegment.Direction)
            var neighbour_pos: Vector2i = seg_a.grid_pos + offset
            var seg_b: PipeSegment = _pipe_system.get_pipe_at(neighbour_pos)
            if seg_b == null or seg_b.fluid_id != seg_a.fluid_id:
                continue

            # Deduplicate — each pair processed once per tick
            var key: String = _pair_key(seg_a.grid_pos, seg_b.grid_pos)
            if visited_pairs.has(key):
                continue
            visited_pairs[key] = true

            var diff: float = seg_a.fluid_amount - seg_b.fluid_amount
            if absf(diff) < 0.5:  # dead-band — prevents micro-oscillation at equilibrium
                continue

            var transfer: float = diff * 0.4  # rate — 40% of imbalance per tick
            transfer = clampf(transfer, -seg_b.fluid_amount, seg_a.fluid_amount)
            seg_a.fluid_amount -= transfer
            seg_b.fluid_amount += transfer

Tuning Knobs

Parameter Current Value Effect of Increasing
Transfer rate 0.4 Faster fill, may overshoot on tiny networks
Dead-band 0.5 units Raise if micro-flicker returns on a specific layout

Properties

  • Stable on loops — ring networks converge to equilibrium within ~10 ticks
  • O(edges) per tick — scales with connections, not segment count
  • No sort step — eliminates the main oscillation source

Pipe Connector

The Pipe Connector is the fluid equivalent of the IoConnector for belts — a directional pass-through placed anywhere on the grid.

Property Value
Footprint 1×1 cell
input_face Set by R key at placement (blue face)
output_face Always opposite in Phase 1 (green face)
Buffer 10 fluid units
E key Stub — future research unlock for output face rotation
Chaining Output→input chains supported — connector pushes to adjacent connector's buffer if faces align and buffer is empty

Connector Chaining Rules

A PipeConnector tick_push() checks in order: 1. Adjacent PipeConnector on output face whose input_face == opposite_dir(output_face) → transfer if buffer empty 2. Adjacent pipe segment on output face → transfer if segment has capacity 3. Adjacent building on output face → call receive_fluid() if building accepts

Cases where chaining does NOT transfer (stall harmlessly, no crash): - Output→output (two connectors facing same direction into each other) - Input→input (two connectors facing away from each other)


Pipe Segment Visual

PipeSegments use an arm-based visual computed from which faces are connected:

  • Base slab: BoxMesh(0.95, 0.06, 0.95) at y=0.03, colour Color(0.18, 0.18, 0.20)
  • Centre hub: BoxMesh(0.22, 0.22, 0.22) at y=0.14, colour Color(0.25, 0.55, 1.0)
  • Per-face arm (NORTH/SOUTH): BoxMesh(0.22, 0.22, 0.5) at y=0.14, same blue
  • Per-face arm (EAST/WEST): BoxMesh(0.5, 0.22, 0.22) at y=0.14, same blue

_update_visual() is called: - After _update_connected_faces() — updates both the new segment AND all neighbours reciprocally - After _remove_face_from_neighbours() — on segment removal, neighbours lose their arm toward the removed cell - After load — PipeSystem.refresh_all_visuals() rebuilds all visuals from saved connectivity


Pipe Types

Pipe Type Max Temp Heat Loss Notes Unlock
Standard Pipe 150°C 3°C/seg Available from start
Insulated Pipe 1300°C 0.3°C/seg Required for Liquid Metal Basic Insulation research
Underground Pipe 150°C 1.5°C/seg Passes under buildings/belts Basic Excavation research
Large Pipe 150°C 3°C/seg 2× flow rate Advanced Piping research
Reinforced Pipe 200°C 3°C/seg Higher HP, rupture resistant Industrial research

Note: Temperature simulation is deferred to M2.3.1. These values are defined for completeness but not active in the current build.


Pressure Model

(Specified; pressure simulation active in Standard and Full modes. Basic mode uses fixed flow rate.)

Flow Rate Formula

flow_rate = (source_pressure - destination_pressure)
            × (1 / viscosity)
            × pipe_diameter_modifier
            × gravity_modifier

Pressure Sources

Source Pressure Output
Producer building output 80 kPa
Fluid Pump (standard) +60 kPa
Fluid Pump (industrial, research) +120 kPa
Gravity (descending level) +30 kPa per level
Gravity (ascending level) -40 kPa per level

Pressure Loss

pressure_loss_per_segment = 2 kPa (standard pipe)

Temperature System

(Specified; temperature simulation deferred to M2.3.1 — not active in current build.)

Heat Loss Per Segment

Pipe Type Heat Loss
Standard Pipe 3°C per segment
Insulated Pipe 0.3°C per segment
Underground Pipe 1.5°C per segment

Solidification

If fluid temperature drops below min_temp, it solidifies and blocks the pipe at that segment. Clear by: placing a Pipe Heater adjacent, removing and replacing the segment, or raising source temperature.

Pipe Damage

If fluid exceeds max_temp in a standard pipe, the pipe takes damage each tick. At 0 HP the pipe ruptures.


Buildings

Fluid Pump (deferred to M2.3.1)

Boosts pressure at its placement point. Required for long runs and ascending vertical levels.

Property Value
Footprint 1×1
Power draw 15 PU
Pressure boost +60 kPa (standard) / +120 kPa (industrial)

Storage Tank (implemented as BuildingDefinition .tres — fluid logic M2.3.1)

Property Value
Footprint 2×2
Capacity 1000 fluid units

Heat Exchanger (M2.3.1)

Transfers heat between two separate fluid networks.

Pipe Heater (M2.3.1)

Heats fluid in adjacent pipe segment. Required for Liquid Metal runs without Insulated Pipe.


Flammability & Pipe Rupture (M2.3.1)

When a flammable fluid pipe ruptures: 1. Fluid spills onto surrounding cells (3×3 area) 2. If adjacent heat source present → fire starts 3. Fire spreads each tick 4. Suppressed by Fire Suppressor building or burns out naturally


Session Configuration

Pipe complexity is a host-configurable session option set at session creation.

Mode Pressure Temperature Supply Chain Multiplier
Basic ❌ Off ❌ Off ×1.0
Standard ✅ On ❌ Off ×1.15
Full ✅ On ✅ On ×1.30

Default: Standard

# In session_config (GameManager.gd)
"pipe_complexity": "basic"  # "basic" | "standard" | "full"

const PIPE_COMPLEXITY_MULTIPLIER = {
    "basic": 1.0,
    "standard": 1.15,
    "full": 1.30
}

Simulation Tick Order

P1 — Buildings produce (including fluid outputs)
P2 — IoConnectors tick_pull
P2b — Pipe Connectors tick_pull
P3 — IoConnectors tick_push
P3b — Pipe Connectors tick_push
P3c — Pipe segments propagate pressure  (Standard/Full modes only)
P3d — Pipe segments equalise fluid      (pair-equalisation, all modes)
P3e — Pipe segments update temperature  (Full mode only — M2.3.1)
P4 — Belts advance
P5 — Genesis tasks

GDScript Data Structures

# FluidDefinition.gd
class_name FluidDefinition
extends Resource

@export var id: String
@export var display_name: String
@export var viscosity: float       # 0.1 – 1.0
@export var density: float
@export var flammable: bool
@export var min_temp: float
@export var max_temp: float
@export var base_temp: float
@export var colour: Color

# PipeSegment.gd (node, not resource)
class_name PipeSegment
extends Node3D

var grid_pos: Vector2i
var fluid_id: String
var fluid_amount: float            # current fill (0 – max_capacity)
var connected_faces: Array[bool]   # [N, E, S, W] — drives visual

# PipeConnector.gd
class_name PipeConnector
extends Node3D

var grid_pos: Vector2i
var input_face: BeltSegment.Direction
var output_face: BeltSegment.Direction
var held_fluid: String
var held_amount: float
const BUFFER_MAX: float = 10.0

Open items for this document are tracked in docs/open-items.md, area pipes.

Revision History

Version Date Changes
0.2 2026-08-28 Corrected the claim that ResourceTier.FLUID = 4 prevents fluids riding belts. Nothing enforces it; the separation is structural, and ResourceTier is being removed in favour of substance + phase.
0.1 2026-02-27 Initial document — pressure model, temperature system, fluid properties, pipe types, buildings, flammability, vertical integration, tick order, GDScript structures
0.2 2026-02-27 Session configuration added — Basic / Standard / Full modes
0.3 2026-03-13 M2.3 implementation recorded — pair-equalisation algorithm (replaces greedy propagation), PipeConnector chaining rules, arm-based visual spec, implementation status section (implemented vs deferred), data structures updated to match actual node architecture