Auto Warz — Pipe & Fluid System Design¶
Document Type: Design Document Version: 0.3 Last Updated: 2026-03-13 Vault destination:
01_Design DocumentsRelated 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:
PipeSegmentnode — auto-connects to all adjacent pipe faces; arm-based visual (dark hub + coloured arms per connected face);fluid_idandfluid_amounttracked per segmentPipeSystem— scene-level registry and tick coordinator;_rebuild_networks()BFS correctly identifies connected segments and bridges across PipeConnectors by checking bothinput_faceandoutput_facesidesPipeNetwork— 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 connectorsFluidDefinitionresource class and all 4 fluid.tresfiles- 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, colourColor(0.18, 0.18, 0.20) - Centre hub:
BoxMesh(0.22, 0.22, 0.22)at y=0.14, colourColor(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¶
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 |