5. Balance definitions & constants

Kind-specific server balance lives in server/crates/rules/src/defs.rs; faction availability, buildables, trainables, upgrade ids, and ability carriers live in server/crates/rules/src/faction.rs; terrain movement/cover/concealment hooks live in server/crates/rules/src/terrain.rs. Grass, bare road, and all four marked road tiles share open-ground passability, cover, concealment, and line-of-sight behavior; road applies a 1.5x movement-speed multiplier. server/crates/rules/src/balance.rs is the stable public re-export surface for timings, tile size, starting resources, supply caps, mining amounts, support-weapon constants, body dimensions, upgrade and ability scalars, and stat helpers. Its internal server/crates/rules/src/balance/*.rs modules group those Rust-owned values by domain, and its unit_stats(kind) and building_stats(kind) helpers read the defs table. Default attack range, damage, cooldown, weapon class, and weapon-policy metadata are exposed through server/crates/rules/src/combat.rs weapon profiles; current profile values mirror the defs records so legacy attack_profile(kind) and weapon_class(kind) callers remain behavior-compatible. Direct-fire damage, miss policy, tank-facing modifiers, and over-penetration policy consume the selected weapon profile instead of inferring those behaviors only from the firing entity kind. The Panzerfausts Training Centre upgrade unlocks a separate Barracks-trained Panzerfaust unit. It inherits Rifleman combat and movement but carries one disposable anti-armor shot that deals 100 base damage with 50% armor penetration and no tank-facing modifier. The shot is detached at launch; the same Panzerfaust immediately returns to normal movement and rifle combat while the projectile travels, preserving its kind, orders, HP, control-group identity, and trench. Tanks also have a live secondary tank_coax profile owned by combat rules: 6-tile range, 4 small-arms damage, 6-tick cooldown, no Tank armor-facing multiplier, and direct-fire overpenetration. client/src/config.js is the stable public facade for the subset the UI/render/fog needs (costs, supply, sight, sizes, colors, and command-card descriptors). Its internal client/src/config/timing.js, client/src/config/rules_mirror.js, and client/src/config/factions.js modules hold Rust-owned mirror data and helpers; its client/src/config/presentation.js module holds client-owned presentation data. Keep both sides in sync; run node scripts/check-faction-catalog-parity.mjs to mechanically compare the Rust-authoritative default faction catalog to the client descriptors. The server wiki’s /wiki/stats page is generated from the same Rust definitions and faction catalogs. Its unit/building damage, range, cooldown, and weapon columns intentionally show primary/default weapon stats only; secondary weapons such as the Tank coax are documented here until generated secondary-weapon rows exist. For changes that affect visible stats, faction availability, upgrades, or ability metadata, run node scripts/check-wiki.mjs; it includes the wiki route/table checks and the client catalog parity check.

server/src/config.rs and server/crates/sim/src/config.rs are compatibility shims for Rust-owned balance exports while call sites are migrated. They should not accumulate server-shell or sim-only implementation constants. Those values belong beside the module that owns the behavior.

Final source-of-truth map and guardrails

Use server/crates/rules/src/defs.rs for unit/building stat records, costs, supply, sight, ranges, footprints, body dimensions, and build/train timing. Use server/crates/rules/src/faction.rs for faction catalogs: buildables, trainables, research rows, ability carriers, command-card descriptors, and ability/upgrade metadata exported by Rust. Use server/crates/rules/src/balance.rs as the stable public balance surface; the internal balance/timing.rs, balance/map.rs, balance/economy.rs, balance/supply.rs, balance/bodies.rs, balance/support_weapons.rs, balance/upgrades.rs, balance/abilities.rs, and balance/stats.rs files group shared scalar constants and helper definitions without changing ownership or exported names. Sim-only behavior constants belong beside the sim module that owns the behavior rather than in the compatibility config shims.

Phase 4 left the movement/arrival recovery constants (STUCK_EPS_PX, STUCK_ARRIVAL_TICKS, TOLERANT_ARRIVAL_RADIUS_PX, SIDESTEP_*, STATIC_BLOCKED_REPATH_TICKS, ARRIVE_RADIUS_INTERMEDIATE_PX, VEHICLE_WAYPOINT_ACCEPTANCE_RADIUS_PX, SCOUT_CAR_FINAL_GOAL_TOLERANCE_PX, SCOUT_CAR_STUCK_RECOVERY_TRIGGER_TICKS, SCOUT_CAR_REVERSE_RECOVERY_DISTANCE_PX, and SCOUT_CAR_RECOVERY_COOLDOWN_TICKS) on the rts_rules::balance::* surface as explicit compatibility exports. They are sim-movement policy, not client-mirrored balance values; moving them beside rts-sim movement should be a later API/design migration rather than a silent Phase 4 source shuffle.

client/src/config.js mirrors only the subset needed by UI, rendering, fog previews, and command cards while preserving the historical public import path. Rust-owned mirrored values live behind client/src/config/timing.js, client/src/config/rules_mirror.js, and client/src/config/factions.js; they include gameplay-visible costs, supply, sight, footprints, body dimensions, client-visible timing/range/duration constants, faction legality, upgrade metadata, ability descriptors exported by the Rust faction catalog, ability effect fields exported in the rules dump—including Smoke Plus cloud radius and duration—and resource starting amounts. Client-owned values live in client/src/config/presentation.js and include render colors, camera defaults, fog overlay alpha, command-card layout hints, local presentation labels/icons that are not exported by Rust, and resource render labels/sizes.

Run node scripts/check-faction-catalog-parity.mjs after changing Rust-owned values that are mirrored into client/src/config.js. The check runs the Rust rts-rules faction catalog dump, including the clientConfig parity payload, and compares the client mirror for catalogs, stat fields, body dimensions, resource amounts, upgrade metadata, ability compact/order-stage codes, and Rust-owned ability descriptors/effect fields. Run node scripts/check-wiki.mjs as well when a change affects visible stats, faction availability, upgrades, or ability metadata that appears on the generated /wiki/stats page.

ConstantBefore cleanupAfter cleanupMirror impact
MORTAR_FIRE_TOLERANCE_RADSim-only mortar aim tolerance exported from server/crates/sim/src/config.rs beside mirrored balance constantsSim-local server/crates/sim/src/game/mortar.rs FIRE_TOLERANCE_RAD, owned by mortar firing behaviorNone; it is not mirrored into client/src/config.js and does not change wire shape or balance values

Client mirror boundary inventory

Phase 1 records the current source-of-truth map before later phases add broader mechanical checks. The faction parity check guards the current structured clientConfig payload shape for constants, unit/building stat fields, vehicle bodies, resource amounts, upgrades, ability effect fields, catalog ids, fixture-only catalog presence, and playable faction exposure. This is an inventory only; it does not change balance, gameplay, or client rendering. The remaining split-blocking structured parity gap was command budget, which remains sim command-service policy mirrored by the client outside the Rust rules dump. Phase 2 keeps client/src/config.js hand-authored and guarded by validation-only checks rather than generating client config from Rust; generation would blur the current Rust-owned mirror/client-owned presentation boundary and make future config splits harder to review.

Value/pathRust ownerJS mirror pathCategoryCurrent checkerProposed future checkerClient-only exclusion reasonCompact version impact
TICK_HZ, TICK_MS, TILE_SIZE, simulation timing scalarsserver/crates/rules/src/balance.rs, re-exported by server/src/config.rs and server/crates/sim/src/config.rsclient/src/config.js TICK_HZ, SNAPSHOT_MS, interpolation delay constantsbalance scalarscripts/check-faction-catalog-parity.mjs checks TICK_HZ and client-visible duration constants against the Rust rules dumpExtend the structured dump if another timing scalar becomes client-visibleInterpolation delay is client-only smoothing; TICK_HZ is mirroredNo compact impact unless snapshot cadence or compact fields change
Unit and building costs, supply, sight, footprint/radius, train/build times, and command-card stat rowsserver/crates/rules/src/defs.rs and server/crates/rules/src/balance.rs; faction legality in server/crates/rules/src/faction.rsclient/src/config.js STATS, WORKER_BUILDABLE, FACTION_CATALOGSbalance scalar / faction catalog factscripts/check-faction-catalog-parity.mjs checks catalog legality, costs, supply, sight, ranges, build ticks, building footprints, requirements, train lists, research lists, and non-body unit render radius; node scripts/check-wiki.mjs covers generated wiki stats when runFuture work can move client-only labels/icons into Rust catalogs if they should become authoritativeClient-only labels/icons in STATS are presentation unless the Rust catalog exports them; STATS.size for body-driven vehicles is presentation because the Rust-owned body dimensions are checked separatelyNo compact impact
Vehicle/body dimensionsserver/crates/rules/src/balance.rs *_BODY_* constantsclient/src/config.js TANK_BODY, ANTI_TANK_GUN_BODY, ARTILLERY_BODY, SCOUT_CAR_BODY, SCOUT_PLANE_BODY, COMMAND_CAR_BODYbalance scalarscripts/check-faction-catalog-parity.mjs checks every client body length, width, and clearance against the Rust rules dumpKeep adding new body records to the dump/check when body-driven units are addedNone; client uses these for art, selection, and advisory placement previews, including Tank Trap preview rejection for vehicle-body units, while Rust collision is authoritative; Scout Plane body dimensions are render/selection metadata only and do not create a ground collision bodyNo compact impact
Ability descriptors, carrier lists, target mode, range, cooldown, cost, queueability, autocast, command-card label/icon/hotkey/titleserver/crates/rules/src/faction.rs plus scalar constants in server/crates/rules/src/balance.rsclient/src/config.js ABILITIES and imported ABILITY idsfaction catalog fact / balance scalarscripts/check-faction-catalog-parity.mjs checks exported command-card descriptors, carriers, target mode, range, cooldown, cost, queueability, autocast, compact codes, and Rust-owned effect fields present on descriptors such as radius, delay, duration, pull multipliers, speed, and damage; protocol parity checks ability compact codesFuture effect fields should be added to the Rust dump and descriptor assertion when they become client-visibleNot UI-only when the field is exported by Rust faction catalogs or balance constants; purely local affordance copy belongs in the documented exclusion listCode changes may affect compact ability/order-stage codes; descriptor-only changes do not
Upgrade descriptors, research building, prerequisites, cost, and research timeserver/crates/rules/src/faction.rs plus server/crates/rules/src/balance.rs upgrade constantsclient/src/config.js UPGRADES and imported UPGRADE idsfaction catalog fact / balance scalarscripts/check-faction-catalog-parity.mjs checks research building, list membership, upgrade costs, research ticks, and prerequisite upgrade idsLabels/icons/descriptions can be moved into Rust catalogs later if they should become authoritativeLabels/icons/descriptions are client-only today unless moved into the Rust catalogNo compact impact unless upgrade ids/codes change
Entrenchment dig-in/combat/trench-footprint constantsserver/crates/rules/src/balance/entrenchment.rsclient/src/config.js ENTRENCHMENT_* exportsbalance scalarscripts/check-faction-catalog-parity.mjs checks dig-in ticks, range bonus, direct-damage reduction, area-damage reduction, and trench radius against the Rust rules dumpKeep future client-visible entrenchment scalars in the structured dumpNot client-only because these constants describe authoritative trench timing/combat/footprint rules, even before all phases consume themNo compact impact unless the trench snapshot shape changes
Resource node starting amounts and economy resource namesserver/crates/rules/src/balance.rs STEEL_PATCH_AMOUNT, OIL_GEYSER_AMOUNT; fixed Steel/Oil economy fields in sim/protocolclient/src/config.js RESOURCE_AMOUNTS, KIND.STEEL, KIND.OIL, HUD/resource render helpersbalance scalar / wire DTOscripts/check-faction-catalog-parity.mjs checks node starting amounts; protocol parity checks resource kind codesAdd future client-visible resource amounts to the rules dump/checkResource render labels and sizes are client presentation; amounts affect minimap/tooltips/render assumptionsResource kind/code changes affect protocol/compact; amount changes do not
Human command budget values BASE_COMMAND_SUPPLY_CAP, COMMAND_CAR_SUPPLY_CAP_BONUSserver/crates/sim/src/command_budget.rs, applied by server/crates/sim/src/game/services/commands.rs as command admission policyclient/src/config.js exports consumed by client/src/command_budget.js, selection, control groups, and HUD budget displaysim command-service policy mirrored by clientscripts/check-faction-catalog-parity.mjs compares the sim-owned command-budget dump with the client exports; server command-budget tests and client contract tests exercise each side; node tests/client_contracts.mjs snapshots public client/src/config.js export names for split safetyKeep command budget in the sim-owned dump/check unless a future design intentionally moves ownership into rts-rules::balance; keep public config export snapshots current when adding or removing exportsNot client-only because the client mirror previews the server’s command acceptance cap; not Rust rules-owned unless a later design explicitly moves ownershipNo compact impact
PLAYER_PALETTEserver/src/lobby/mod.rsclient/src/config.js PLAYER_PALETTEserver-owned presentation data mirrored by clienttests/protocol_parity.mjs source-scrapes the lobby paletteStructured lobby/config dumpNot client-only because server assigns lobby/start colors and sends them on the wireNo compact impact
Terrain, health, selection, placement, and drag colorsNone in Rust; rendering-only choicesclient/src/config.js COLORS except resource identity colors that should stay consistent with Steel/Oil presentationUI-only presentation dataNoneExclusion list in future config parity checkClient owns visual palette; it does not affect simulation, wire DTO shape, or authoritative fogNo compact impact
Fog overlay alphaAuthoritative fog visibility is in sim snapshots; alpha is not a Rust valueclient/src/config.js FOG_EXPLORED_ALPHA, FOG_UNEXPLORED_ALPHAUI-only presentation dataNoneExclusion list in future config parity checkClient owns opacity; Rust owns which tiles/entities are visibleNo compact impact
Camera defaultsNone in Rustclient/src/config.js CAMERAUI-only presentation dataNoneExclusion list in future config parity checkClient-only input/render affordanceNo compact impact
Anti-tank gun field-of-fire previewserver/crates/rules/src/balance.rs ANTI_TANK_GUN_FIELD_OF_FIRE_RAD is authoritative at 35 degrees totalclient/src/config.js ANTI_TANK_GUN_FIELD_OF_FIRE_RADbalance scalar / advisory UI mirrorscripts/check-faction-catalog-parity.mjs checks the client preview against the Rust field-of-fire constantKeep the preview Rust-owned because it represents the authoritative deployed firing arcNot client-only: the client preview represents an authoritative firing arcNo compact impact

Parity exclusions after the split

The structured rules dump intentionally excludes client-owned presentation values that do not have Rust catalog or balance ownership: global terrain and selection colors, fog overlay alpha, camera defaults, command-card layout hints, and resource render labels/sizes. STATS labels and icons remain client-owned until they are exported by a Rust catalog. STATS.size for units with a Rust-owned body record is also presentation-only; the parity check enforces those units through their *_BODY_* length, width, and clearance values instead.

5.0 Faction economy contract

The faction rollout keeps Steel, Oil, and Supply as the global economy contract. Faction catalogs decide which global units, buildings, upgrades, and abilities are legal for a player and define starting Steel/Oil values plus starting entity loadouts, but they still use fixed steel, oil, supplyUsed, and supplyCap fields. Unknown non-empty faction ids do not fall back to the Kriegsia catalog in lower-level economy queries: catalog-gated build/train/research/gather, production-anchor, and supply checks return empty or false. Start-map resource nodes remain Steel and Oil nodes. Score values, replay analysis values, command-card costs, affordability checks, refunds, and supply reservation are intentionally Steel/Oil/Supply-shaped. Catalog existence is not lifecycle admission. server/crates/rules/src/faction.rs may contain playable catalogs, explicit fixture catalogs, and future catalog data, but server/src/lobby/faction_validation.rs decides which ids can enter normal lobby, AI, replay, branch, self-play, dev, match-history, and post-match paths. Fixture-only and reserved/future ids must not inherit Kriegsia economy behavior or appear in product selectors just because their catalog rows are dumpable.

Approved direct Steel/Oil/Supply modules for this plan are:

Generic resources are deferred. If a future faction needs a non-Steel/Oil resource, that work must be a separate migration across player state, snapshots, compact transport, replay artifacts, observer analysis, scoring, HUD rows, command-card costs, protocol parity, and prediction/WASM compatibility.

5.1 Target theme and MVP combat loop

The target gameplay direction is a simplified World War II-inspired battlefield with fictional, faction-agnostic sides. This is not a historical simulation. The theme should support readable gameplay, clear unit roles, and strong terrain identity without national or regime-specific iconography.

MVP scope:

Core unit roles:

Terrain rules:

Intended progression:

5.2 Current implementation constants

The current implementation uses the themed unit/building names below. Combat is handled by the shared attack model plus the support-weapon setup/teardown state, tank turret aim gates, and tank hull-facing damage modifiers for anti-tank hits against tank victims. Tanks keep their active movement path while firing on either Move or AttackMove orders and keep their base 5-tile weapon range while moving. After a tank has spent three seconds (90 ticks) without path-driven translation or hull rotation, its range has linearly expanded to 14 tiles; any later path-driven movement or hull rotation resets it to the base range. A stationary Tank that survives enemy direct AP damage autonomously turns its hull toward the damage source without changing its order, target, path, turret aim, or stationary range. The first Tank cannon, Anti-Tank Gun, or Panzerfaust hit locks its source for three seconds. Hits during that window neither redirect the hull nor extend the lock; after it expires, the next qualifying hit may establish a new lock. Active movement paths and zero oil prevent this response, while Hold Position explicitly allows it. Riflemen upgraded with Methamphetamines gain permanent moving fire, keep advancing while firing with normal accuracy, and move at tank speed. Panzerfausts upgraded with Methamphetamines receive the normal Rifleman moving fire, tank-speed movement, and 25% faster rifle attacks plus the Panzerfaust windup boost. Machine Gunners upgraded with Methamphetamines move at unupgraded Rifleman speed and use half-length setup/teardown timers; other mobile combat units still hold position once a target is in weapon range. Scout cars also fire while moving using an independent rear machine-gun facing. They are unarmored light vehicles and do not receive armored damage reduction, but anti-tank guns do not roll their infantry miss chance against them. Plain Move tanks, scout cars, and Methamphetamines-upgraded Riflemen and Panzerfausts only fire at enemies already in weapon range. Their active AttackMove orders use the same moving-fire policy while they are still following the player-issued path: auto-acquisition can aim and fire only at targets that are currently inside weapon range and pass hostile, visibility, smoke, line-of-sight, and blocker checks, but it cannot replace the commanded destination with an enemy-directed path. Non-moving-fire AttackMove units pause for fireable in-range targets and then resume the original destination. Direct Attack orders and post-arrival behavior remain stationary. Tank auto-targeting first checks in-range Anti-Tank Guns, Tanks, and Mortar Teams, in that order, before generic acquisition. This priority applies when the tank acquires after firing or when its committed target stops being fireable; a still-fireable target prepared during reload is not reranked on the ready tick. Priority never considers out-of-range targets or overrides explicit player attack orders. Forest-specific rules are future work. The unit, building, and resource-node tables below are the human-readable form of the authoritative rules::defs records.

Default auto-acquisition groups already-legal targets before applying weapon fit or distance: unit attackers prefer non-economy combat units, then economy workers (Worker and Golem), then buildings and other non-unit cleanup targets. Small-arms default weapons prefer soft targets (ArmorClass::Small) over armored targets within the same target group, but they still fire at armor or buildings when no better legal target exists. Completed Tank Traps are excluded from automatic acquisition for every unit and weapon, including ground Attack Move and route obstruction; only a direct Attack on a Tank Trap may target it. That explicit command captures the visible completed Tank Trap cluster within 4 tiles, while vehicle pathfinding continues routing around the obstacles. Anti-armor default weapons prefer anti-armor threats and armored units over ordinary soft units. Anti-Tank Guns cannot auto-acquire or accept direct Attack orders against infantry-sized targets: Workers, Golems, Riflemen, Panzerfausts, and Machine Gunners. Crewed support weapons and other non-infantry targets remain legal. Tanks keep a narrower immediate-threat override for targets already in relevant range: Anti-Tank Guns are first, then other anti-armor threats, armored obstacles, support weapons, and only then ordinary soft targets. The rules-owned TargetFacts surface records the current target-policy facts for every EntityKind, including the Tank coax infantry-priority group. The live coax policy ranks Riflemen and Machine Gunners first, economy workers (Worker and Golem) second, and fallback legal targets such as vehicles, buildings, support weapons, and field obstacles last; resource nodes are not legal coax targets. Default weapons perform a full acquisition pass when ready and targetless, immediately after each shot to prepare the next engagement during reload, or when the prepared target is no longer fireable on the ready tick. Reload, setup, and aiming ticks revalidate only that committed target; they do not rerank nearby enemies. A targetless travelling Attack Move may scan during reload so a non-Mortar unit stops at the first in-range engagement, then follows the same committed-target rule. A still-fireable prepared target therefore receives the next shot even if a higher-priority threat appeared during reload. The post-shot pass may then select that higher-priority threat for the following cycle. Tank coax fire has no independent retained target slot, so its acquisition pass runs only when the coax cooldown is ready. This cadence is limited to default attacks; future grenades, satchels, or demolition attacks need separate attack profiles and explicit activation/autocast policy instead of being folded into default targeting.

Unit stats (hp, dmg, range[tiles], cooldown[ticks], speed[px/tick], sight[tiles], cost, supply, buildTicks):

kindhpdmgrangecdspeedsightsteeloilsupbuildTicks
worker4041242.0105001396 (~13.2s)
golem160161242.010004396 (~13.2s); provisional free Ekat worker-like economy body trained at Zamok; mines at 4x worker load; can be consumed by Ekat for full heal
rifleman4555161.6112501300 (~10s)
panzerfaust455 rifle / 100 launcher516 rifle / one lifetime launcher1.6115551300 (~10s); requires completed Panzerfausts research
machine_gunner554661.281175102400 (~13s)
mortar_team7540 outer / 100 inner AOE5-17601.610100403460 (~15s); trained at Gun Works (steelworks kind)
anti_tank_gun45100 deployed / 75 packed20 deployed / 5 packed721.529150403440 (~15s); requires Gun Works (steelworks kind) and AT Guns (anti_tank_gun_unlock) researched in R&D Complex
artillery20075 AP inner / 75-20 outer AOE10-35 artillery fire901.67150504600 (~20s); requires Gun Works (steelworks kind) and Artillery (artillery_unlock) researched in R&D Complex; rendered at 75% of its prior size with a matching 75%-of-Tank gameplay footprint; 2/3-tile inner and 2-tile outer blast radii; soft target with no armor damage reduction
scout_car1006762.3515125503480 (~16s)
scout_plane400002.619507500; launched instantly from a selected ready Command Car without a City Centre requirement; unlimited independent active sorties; non-combat recon with 2-tile orbit radius and a 30-second total lifetime from launch, including transit, followed by despawn; 30-second caster-local cooldown, no ground collision reservation, and 48x34 px client render body
tank29260 cannon; 4 coax5 moving / 14 fully stationary cannon; 6 coax72 cannon; 6 coax2.094251508750 (~25s); requires Vehicle Works (factory kind) and Tank Production (tank_unlock) researched in R&D Complex; coax is a secondary small-arms weapon that fires through the current turret arc
command_car1500002.358150754450 (~15s); trained at Vehicle Works (factory kind) and requires a completed R&D Complex, but no Tank Production research; no weapon; Scout Car-style movement with a smaller jeep-sized body
ekat1500001.6120000; Ekat faction hero; no default attack; no passive regeneration; consumes nearby Golems for recovery

Building stats (hp, sight, cost, footprint tiles wxh, buildTicks, extra). Building sight is measured outward from the footprint edge, not from only the building center, so sight 1 reveals the full footprint plus a one-tile perimeter around it. Sight 0 buildings do not reveal fog tiles:

kindplayer-facing namehpsightcostfootbuildTicksnotes
city_centreCity Centre3001450 steel + 150 oil3x3750trains workers; no supply; players start with one free
zamokZamok600103x30Ekat start building; no supply; trains Golem; no research in first playable slice
depotSupply Depot11011002x2300disabled in the current experiment (not buildable and no command-card button); retained for replay and fixture compatibility; no supply
barracksBarracks16511503x2200trains rifleman, machine_gunner, and panzerfaust; Machine Gunner requires a completed Training Centre and Panzerfaust requires completed Panzerfausts research; requires a City Centre
training_centreTraining Centre2001100 steel + 50 oil3x2560shared prerequisite before either advanced path; unlocks machine_gunner training at barracks and researches Methamphetamines, Panzerfausts, and Entrenchment; requires a City Centre and Barracks
research_complexR&D Complex1651100 steel + 100 oil3x3450research-only building for AT Guns, Artillery, Tank Production, Mortar Autocast, and Smoke Plus; requires a City Centre and Training Centre
factoryVehicle Works2001125 steel + 125 oil3x3749Mobile Warfare path building; trains scout_car immediately, then tank and command_car after Tank Production research; requires a City Centre and Training Centre
steelworksGun Works2001150 steel + 100 oil3x3599Superior Firepower path building; trains mortar_team immediately, Anti-Tank Guns after AT Guns, and Artillery after Artillery research; requires a City Centre and Training Centre
tank_trapTank Trap120030 steel + 0 oil1x1300engineer-built vehicle obstacle available from the worker build card after a completed Training Centre; A-clicking a completed trap captures every currently visible completed trap within 4 tiles as one cluster-clear order; workers deconstruct completed traps in 150 ticks and refund the cost to the deconstructing player; sparse orthogonal pairs close the single tile between them for vehicle movement only; armored, no trains, no supply, no weapon, no fog reveal, not an elimination building
pump_jackPump Jack50150 steel + 0 oil1x1600contextual oil extractor built by workers on live oil patches; may be built at any distance, but mines 2 oil per 40 ticks only while its patch is within the 11-tile mining range of an owned or allied completed City Centre/Zamok; ejects friendly footprint occupants when its builder arrives; unarmored, immobile, no trains, no supply, no weapon, and does not block shots or line of sight; no tech requirement

Win: a player is eliminated when they own zero elimination-counting buildings; units and Tank Traps alone do not keep them alive. Last player standing wins; a 1-player match never ends (sandbox/exploration mode). In a 3-4 player match, a connected human who is eliminated receives a one-time gameOver score snapshot immediately while the remaining players keep playing; final match resolution sends gameOver only to players who have not already received one.