___ ___ __ __ __ __ _ _ _ ___ ___ ___
/ __/ _ \| \/ | \/ | /_\ | \| | \| __| _ \
| (_| (_) | |\/| | |\/| |/ _ \| .` | |) | _|| /
\___\___/|_| |_|_| |_/_/ \_\_|\_|___/|___|_|_\
___ _ _ ___ _ _ ___ ___ ___
|_ _| \| | / __| || |_ _| __| __|
| || .` | | (__| __ || || _|| _|
|___|_|\_| \___|_||_|___|___|_|
A modern remake of the 1986 vertical run-and-gun (Ikari Warriors, SNK) — twin-stick chaos, grenades-vs-armor, one-hit deaths, and the War Chest 💰: a shared coin economy where every kill mints and every revive spends.
![]() |
![]() |
![]() |
![]() |
☝️ Four sectors, four real runs. Clockwise: 1/6 Staging Ground — the opening push,
rounds bouncing off armor · 3/6 Bridge Gunship — a boss strafing run while a sniper paints you
· 5/6 Crashed Convoy — a ×10 streak with an elite winding up · 6/6 The Foundry Core — the
minefield on the way to the Colossus.
Captured by tools/gif_capture.gd straight off the scripted bot playing the real game
(start_arcade() jumps to each zone), so every number on those HUDs was earned — no
posed sim states, no mockups, no concept art.
Stills: boarding a tank · the Colossus last stand · the Endless War shop.
⚠️ Status: 1.2 — gold. Campaign runs studio splash → Foundry Colossus 🏭, Endless War is deep, all side modes ship (Boss Rush · Arcade · Chapter Select · Daily Run), and the feel stack is real. Art is owned procedural art (tools/gen_*.py) + bespoke generated boss/vehicle/desert pieces over a Kenney-CC0 FX base — no longer greybox, and no longer third-party-derived (every encumbered sprite was replaced; seeASSETS.md). CI is live:.github/workflows/ci.ymlruns a staticlintjob (tools/lint_sim.gddeterminism gate +tools/lint_assets.gd+python3 tools/i18n_check.pystring extraction gate), then import + boot-smoke + the full golden-checksum suite (the runner prints the exact method/assertion pair; the pinned figure lives in 🧪 Headless test suite) across Linux-x86_64 · macOS-arm64 · Windows-x86_64, failing on anySCRIPT ERRORor a missingPASSline — plus packaged-export smoke tests on Linux and Windows, an advisory (continue-on-error) perf job, and a nightly 3-hour soak run. The engine version is pinned intools/versions.lock, the one source CI reads, so the build can't drift from what the determinism goldens were recorded against.docs/PLAN.mdis the aspirational P0–P7 master plan — the sim code is the source of truth for what exists.
⬇️ Latest release — v1.2.0 — no build step, just unzip and run. 🎮
| 🖥️ Platform | 📦 File | 📏 | 📝 |
|---|---|---|---|
| 🍎 macOS — Apple Silicon | CommanderInChief-macos-arm64.zip |
41 MB | arm64-only, recommended for M-series |
| 🍏 macOS — universal | CommanderInChief-macos.zip |
70 MB | Apple Silicon and Intel |
| 🐧 Linux | CommanderInChief-linux.zip |
39 MB | x86_64 — keep the .pck beside the binary |
| 🪟 Windows | CommanderInChief-windows.zip |
48 MB | x86_64 — keep the .pck beside the .exe |
🔐 Both macOS builds are ad-hoc signed, not notarized — first launch needs right-click → Open. Godot ships no arm64-only macOS template, so the native build is the universal one thinned with
lipoand re-signed (see the note inexport_presets.cfg).
🏷️ Version history
gitGraph
commit id: "v0.1.0 · P0 greybox"
commit id: "deterministic core"
commit id: "v0.2.0 · P3 playable"
commit id: "art swap"
commit id: "v0.2.5 · rebrand"
commit id: "history purge"
commit id: "v0.3.0 · public" tag: "v0.3.0"
commit id: "v1.0.0 · launch" tag: "v1.0.0"
commit id: "v1.1.0 · polish" tag: "v1.1.0"
commit id: "v1.2.0 · gold" tag: "v1.2.0"
| 🏷️ | 📅 | 🎯 What it marks |
|---|---|---|
v0.1.0 |
2026-07-08 | P0 — first commit, already carrying the deterministic core, 16.16 fixed-point and 3-OS CI |
v0.2.0 |
2026-07-16 | P3 declared — the README stops describing a prototype and starts describing the game |
v0.2.5 |
2026-07-21 | Commander In Chief — the rebrand + animated boot splash |
v0.3.0 |
2026-07-27 | First public release — clean history, binaries for three platforms |
v1.0.0 |
2026-08-24 | 1.0 launch — safe-area docs + HUD/menu 1.0 sweep, ultrawide pillarbox verified |
v1.1.0 |
2026-08-24 | 1.1 polish — HUD Container, GL gate, roll gate, filter NEAREST, safe-area |
v1.2.0 |
2026-08-24 | 1.2 gold — final AAA sweep, all 5 tells closed, store-ready |
| 🧭 | ||
|---|---|---|
| 🏗️ Architecture | 🎮 Controls | 🕹️ Modes |
| 👹 The Roster | 🎁 Drops & the Wheel | 🧪 Testing |
| 📻 The Radio | 🌐 Netcode | 🚀 Quick Start |
| 🎨 Art | 📥 Download | 🗺️ Roadmap |
| 🤝 Contributing | 📜 License |
Everything the game is lives in a deterministic, render-free core. The scene tree is just a camera pointed at it. 📽️
flowchart LR
subgraph VIEW ["🖼️ src/main.gd + src/view/ — floats allowed"]
IN["🎮 Input<br/>(floats)"] --> Q["_quantize_axis<br/>clamp to ±256"]
EV["✨ events → shake, hit-stop,<br/>particles, SFX, banners"]
HUD["📊 hud.gd · menu.gd<br/>art.gd · sfx.gd"]
end
subgraph SIM ["🔒 src/sim/ — deterministic core"]
Q --> STEP["SimWorld.step()<br/>60 Hz fixed order"]
STEP --> F["Fixed 16.16 int math<br/>SimRng xoshiro128**<br/>NO floats · NO Time.* · NO scene tree"]
STEP --> CS["checksum()<br/>FNV-1a over full state"]
end
STEP -.->|"events (checksum-EXCLUDED)"| EV
CS ==>|"golden values"| T["🧪 test_determinism.gd"]
src/sim/—SimWorld.step(inputs)advances one tick: players → tanks → projectiles → enemies → bunkers → spawner/boss/colossus/gates/camera/observer. State is plainArray[Dictionary]. Bit-identical across x86_64 and Apple Silicon.src/main.gd+src/view/— the only view. Owns all floats, reads sim state, draws, and turns per-tick events into feel. The sim never knows juice exists.SimWorld.events— rebuilt every tick, excluded from the checksum: view and audio triggers never break determinism goldens. 🔓
🔐 The golden-checksum discipline (read before touching src/sim/)
Any change to sim logic, hashed state, or step order shifts the committed GOLDEN
checksums and fails test_determinism.gd — by design. When intentional:
set GOLDEN = [], run, paste the printed values back, note why in the comment.
test_checksum_coverage.gd is the tripwire — every sim entity field must be
classified hashed-or-excluded. If the idempotency or cross-run checks fail
instead, that's real non-determinism: fix it, never re-record over it. 🚨
| Action | ⌨️ Keyboard (P1) | 🎮 Gamepad |
|---|---|---|
| Move | WASD | Left stick |
| Aim (decoupled) | Mouse or arrow keys | Right stick |
| Fire / tank cannon 🔫 — always on, there is no fire button: aim is the weapon | — | — |
| Grenade 💣 — hold through the apex = AIRBURST 🎈 | E (Shift = alt) / RMB | L1 |
| Dodge roll (i-frames) 🤸 | C | B |
| Interact 🚜 (board/exit tank · gunner seat on an occupied hull · salvage hulks · plant claymores) | F | X |
| Revive (spend War Chest) 💸 — dedicated input; works for yourself or a downed partner | Space | Y |
| Supply wheel (hold, flick, release) 🎡 | Q | BACK |
| Toggle local 2P 👥 | F2 | — |
| Toggle Endless War ♾️ | F3 | — |
| Restart 🔁 | R | — |
| Mode | What it is |
|---|---|
| 🏔️ Campaign | 6 gated sectors (SimWorld.FINAL_GATE_INDEX) → the Foundry Colossus finale (Last Stand rule: no revives; kill converts your War Chest to score. VICTOLY!). Gates 2 & 4 fork: < CACHE (free crate ringed by mines) vs BOUNTY > (two elites, one marked) — walking a side IS the choice 🛣️ |
| ♾️ Endless War | Escalating waves, between-wave shop intermissions, minibosses that fly in over 7s and escalate by tier (tighter spray, extra mortars by w15), contested parachute supply drops rushers try to steal 🪂, and the wave-7+ Broadcast Tower debut |
| 🎯 Arcade / Chapter Select | Jump straight to any of the 6 chapters (jump_to_chapter) — the same authored world, entered mid-way by priming every streaming cursor forward. Every streamed pick is a pure function of absolute world position, so chapter 4 looks like chapter 4 always does 🗺️ |
| 🥊 Boss Rush | BOSS_RUSH_COUNT = 3 gunships back-to-back, then the Colossus caps the run. Non-linear HP escalation (BOSS_RUSH_HP_STEPS = [0, 14, 32]) — the ladder steepens, it does not just scale 📈 |
| 👥 Local 2P co-op | Shared War Chest, revive tether, per-device input glyphs. The field keeps advancing on a downed player — being down is a state, not a pause ⏱️ |
| 📅 Daily Run | Seed-of-the-day challenge run (Hall entries wear a *DAILY tag) |
| 🎞️ Watch Last Run | Every run records inputs → user://last_run.replay, replayable from the menu |
| 💀 NG+ HARD / 🛟 ASSIST (2-hit) | Difficulty toggles, both checksum-honest |
flowchart LR
T([🎮 TITLE]) --> C[🏔️ Campaign]
T --> M{{🕹️ MODES}}
T --> D[📅 Daily Run]
T --> W[🎞️ Watch Last Run]
M --> A[🎯 Arcade]
M --> B[🥊 Boss Rush]
M --> E[♾️ Endless War]
A --> CS[🗺️ Chapter Select 1-6]
C --> F([🏭 Foundry Colossus])
B --> F
CS --> F
E --> WV[🌊 Waves + 🛒 Shop]
All four sim modes — campaign · arcade · endless · boss_rush — share one
SimWorld, one checksum, and one golden-checksum suite. 🔒
Persistent carrots 🥕: top-40 Hall of Fame, career totals, and best score/wave/distance
survive across runs (user://ikari_best.cfg, atomic tmp+bak writes).
Every threat telegraphs before it resolves — that's the readable chaos pillar. 📖
| Threat | The deal | The counter |
|---|---|---|
| 🏃 Rushers & red elites | The 1986 grammar — touch = death, elites drop capsules | Shoot, dodge, kite |
| 💥 Grenadier | Telegraphed lob, edge-detect markers | Move off your ground |
| 🔭 Sniper | Laser paint line before the shot | Break the line |
| 🌿 Ghillie sniper | Cloaked ambush, bullet-immune dug in | Flush it out |
| 🛡️ Riot shield | Front-arc bullet block | Flank, grenade, or Rend through it |
| 🧨 Sapper | Lays a live mine trail | Mind your feet |
| 🏅 Courier | Flees with a fat bounty (4× elite) | Cut it off |
| 🐸 Frogman | Submerged in rivers, grenades-only until it surfaces to lunge | Kill it on the surface |
| 🛸 Drone | Hovering strafer, spawns marked (3× trophy bounty + crown) | Watch the tether, claim the crown |
| 🔩 MG Nest | 3-hit armor, alarm lock-on, teaching card | Break its line or flank — it cracks under fire |
| 📡 Broadcast Tower | Rooted rally mast (endless w7+): every ground trooper in its 140px aura runs +25%, and it holds the wave open | Kill the mast, break the rally — 5 hits or one grenade |
| 🚙 Technical | The fastest thing on the field. Cruises, revs 30t (dashed line = still aiming), then locks a charge lane (solid line = committed) — 3 px/t, can't steer | Sidestep the lane; one shot drops it; smoke denies the lock; water kills the charge 🌊 |
| 🛢️ Explosive barrels | Bullets & enemy contact detonate; 8-tick chain fuse goes white-hot | Feed the chain — or flee it |
| 🗼 Mortar Observer | Shells you for stalling | Kill him or push forward |
| 🚁 Bridge Gunship | Gate boss every 3rd gate — bullets chip, grenades chunk. Chin-turret charge glow telegraphs each spray; mortars lead your walk while the hull keeps drifting | When it dies, see below 👇 |
| 🪂 Downed Pilot | Ejects from any dead gunship, staggers for the enemy line. TOUCH to rescue (+100¢). Shooting him pays NOTHING. Tank treads grab him, the airstrike spares him, roll-touch works | Reach him before the top edge — the label turns red ESCAPING! at the cliff |
| 🏭 Foundry Colossus | The world ends here: 3 phases, pure armor, inverts the scroll | Grenades only. Last Stand. 🫡 |
Rare capsules (elite drops, weighted table): 🔵 PIERCE · 🟠 SPREAD · 🩷 TRIPLE (permanent 3-fan — stacks with Spread into a 5-fan!) · 🔴 REND (shears riot shields) · 🟢 CLAYMORE · ⚪ SMOKE (breaks locks) · 🟡 FLASHBANG (field-wide stun, fair re-arm).
The wheel 🎡 went 8-way (hold Q / BACK, flick, release — coin prices from the shared War Chest):
| Direction | Buy | Effect |
|---|---|---|
| ⬅️ | AMMO +30 | MG belt top-up |
| ⬆️ | GRENADES +4 | The armor-opener |
| ➡️ | FLAK VEST | Absorbs exactly one hit 🦺 — campaign price creeps 60→75→90→105→120¢ per buy |
| ⬇️ | AIRSTRIKE | Called in, telegraphed, spares the submerged and the pilot |
| SANDBAGS 40¢ | Plants a 36×10 cover segment along your aim 🧱 — blocks bullets & rusher pathing both ways, dies to one grenade or tank treads, field cap 6 | |
| SUPPLY CALL ★1 | Spends a Commendation (never coins) for a free useful supply 🎖️ |
- Commendation tokens ★ — minted by play, never bought: the 20-kill surge and every
flawless gate pay one (cap 2). A death burns one. Spend on the wheel's
↗️ socket — the score→power bridge the panel voted 9/9 for. - Tank Crew 👥🚜 — P2 can INTERACT an occupied tank: independent twin-stick coax gunner seat on his own ammo, +25% fuel tax, driver exit promotes the gunner, the bail window covers the whole crew.
- Tank Hulks 🛢️ — dead hulls smolder as two-way bullet cover for ~17s; INTERACT salvages +2 grenades and strips the cover. Keep the wall or take the ammo.
- Airburst 💣🎈 — keep holding the grenade button since the throw and the charge pops at the arc's apex: tap for range, hold for the pop.
A single radio-filtered Commander (with a Spotter on the second channel and one panicked Pilot) barks the big beats — 14 lines, ≤8 words each, priority-laddered so the radio never talks over itself and never wears out on the hundredth replay:
flowchart LR
D["💀 defeat / Last Stand<br/>(interrupts everything)"] --> N["🚫 denials & pilot calls"]
N --> W["⚠️ tactical warnings"]
W --> F["🎖️ praise & flavor"]
| Voice | Owns | Filter |
|---|---|---|
| 🎖️ Radio Commander | "War Chest empty. You're on your own." · "Overlord out." · Last Stand · VICTOLY! | 1.1 kHz bandpass + overdrive — tactical radio |
| 🎯 Spotter | Pilot down · shop locked · "Clip dry! Bash 'em!" | Lighter, crisp |
| 🪂 Downed Pilot | "Don't shoot! I'm worth a hundred coins!" (0.4s behind the callout) | Dry close-mic — he's HERE |
Concussion muffles the radio too — by intent. A stunned soldier hears underwater radio. 🌊
godot --headless --path . --import # 📦 once after cloning, and after ANY new class_name script
tools/run_tests.sh # 🧪 full suite, private user://
SUITE=mechanics tools/run_tests.sh # 🔍 one suite (substring of the filename)
SUITE=perf tools/run_tests.sh # ⏱️ opt-in timing suite (excluded from the default run)
tools/run_tests.sh -s res://tools/lint_sim.gd # 🧹 determinism lint — run it yourself after sim edits🏠 Use
tools/run_tests.sh, not rawgodot, whenever anything else might be running.user://is keyed on the project name, not the checkout path, so every worktree and every concurrent session shares oneikari_best.cfgand onegodot.log. The wrapper hands the run a privateHOMEso siblings can't clobber each other — and it owns the shutdown-leak gate, which the in-engine gate structurally cannot see (Godot prints leak diagnostics on stdout, after its file logger is already gone). 🕳️
🔧 The raw invocation still works — here's when it doesn't
godot --headless --path . -s res://tests/run_tests.gd # ⚠️ no leak gate, shared user://The wrapper is a wrapper, not a runner change, so this is still a valid way to run the suite when
you are definitely alone on the machine. What you give up: the shutdown-leak gate, and the
private user://. The save/settings tests stash and restore your real config, so two
concurrent raw runs corrupt each other's fixtures — and a sibling Godot rotating the log away makes
the engine-error gate fail a perfectly clean diff with "no log carried this run's marker". 🌀
1,166 test methods / 37,400+ assertions — fixed-point math, seeded RNG streams, the 1986 mechanic
grammar, the War Chest economy, tank/observer/gates/water/gunship/colossus, every archetype's behavior
contract (nest armor, technical charge lock, pilot rescue/grace/forfeit), Endless War waves & shop,
lockstep loopback, replay integrity, checksum coverage classification, and the campaign+endless golden
determinism runs. (The PASS — line prints the exact pair.)
📐 Why the method count is exact and the assertion count is a floor. The method count is identical on every platform, so it can be pinned — and it is pinned, by
test_assets.gd::test_readme_test_method_count_matches_the_suite: that test recounts the suite and fails if this line drifts, so adding a test without updating this number turns the build red. Assertion totals genuinely vary by platform and environment — on commit4c7ae78the same code measured 37,436 on Linux and macOS but 37,451 on Windows — so no single exact assertion number is correct everywhere. Pinning one wouldn't just go stale, it would be unfalsifiable; a floor is the honest form. 🎯
test_perf.gd asserts wall-clock microseconds, so it sits in run_tests.gd's OPT_IN_SUITES:
the default full run skips it (that's why the method count above is 1000, not 1002), and CI
runs it in an advisory continue-on-error job. A shared runner is a noisy neighbour and a
randomly-red gate stops being read.
🧷 Gotcha: the runner counts a method green even if a runtime error aborts it mid-way — watch for
SCRIPT ERRORlines, and hold dict references acrosssim.step()(dead entities are swept from the arrays).
⚠️ Read this before believing the diagram.src/net/lockstep.gdhas zero production callers — nothing insrc/main.gdor the menus constructs aLockstepSession, and no transport is wired toon_send. Online co-op is not playable. What exists is a ~260-line loop plustests/test_lockstep.gd, whoseFakeWireis a deterministic in-memory wire with latency jitter only: it never drops, duplicates, corrupts-by-network, disconnects, or times out a packet (the one corruption test flips a payload byte by hand to prove the checksum exchange notices). So the test proves the sim is deterministic enough for lockstep and that the loop's stall/catch-up/desync-flag logic is self-consistent — it proves nothing about behavior on a real lossy network. Treat the section below as the intended design. 🎯
src/net/lockstep.gd — transport-agnostic; only encoded inputs cross the wire. 📡
sequenceDiagram
autonumber
participant A as 🎮 Peer A (SimWorld)
participant B as 🎮 Peer B (SimWorld)
A->>B: SimInput @ tick T+delay (3 ticks ahead)
B->>A: SimInput @ tick T+delay
Note over A,B: each sim advances ONLY when both inputs for the next tick exist
A-->>B: checksum every 60 ticks
B-->>A: checksum every 60 ticks
Note over A,B: mismatch ⇒ 🚨 desync flagged — exercised by a two-sim in-memory loopback
Steam Networking Messages is meant to plug into on_send / receive_remote_input without
touching the loop — that transport has not been written, so the seam is untested against a real
socket. Per docs/PLAN.md online 2P is a post-launch beat, not a launch promise; local 2P and
Remote Play Together are the co-op that actually ships.
- Grab Godot 4.7.2 (stable)
macos.universal.zipfrom https://godotengine.org/download/archive/4.7.2-stable/ — native on M1–M4. 🍎 - Run it:
…or open the editor, Import →
/Applications/Godot.app/Contents/MacOS/Godot --path /path/to/commander-in-chief
project.godot→ ⌘R. - Gatekeeper complains? Right-click → Open once, or
xattr -dr com.apple.quarantine Godot.app. 🔓
Determinism across architectures is by construction (pure 64-bit int math) and asserted by the golden suite — run it locally on your arch; the values must match the committed goldens recorded on Apple Silicon. 🤝
project.godot Godot 4.7 project (640×360 virtual res, 60 Hz physics)
src/sim/ 🔒 Deterministic core — int-only, no engine RNG, no Time.*
src/main.gd|.tscn 🖼️ The view + input quantization boundary
src/view/ art.gd (bake registry/tint/glyphs) · hud.gd · menu.gd · sfx.gd
src/net/ replay.gd (run recorder, live) · lockstep.gd (design sketch, no callers)
tests/ Headless runner + 38 suites (SUITE=<name> filter, golden checksums)
tools/ gen_*.py (the live sprite generators) · lint_sim.gd/lint_assets.gd · i18n_check.py (CI gates)
screenshots.gd · smoke.gd · validate_replay.gd
docs/PLAN.md 📜 The aspirational P0–P7 master plan
Design viewport is 640x360 (project.godot:31 window/size/viewport_width|height = 640×360, window/stretch/mode="viewport" scale_mode="integer" aspect="keep"). The engine keeps integer letterbox — the playfield stays pixel-perfect; only chrome reflows.
| Viewport | How it presents | Bars |
|---|---|---|
| 16:9 — 640x360, 1280×720, 1920×1080 | Native — full window, no bars | None |
| 21:9 — 3440x1440 | Centered 640x360 viewport at integer scale, pillarboxed | Black bars left/right |
| 32:9 — 5120x1440 | Same — wider pillarbox, same centered playfield | Black bars left/right |
| Portrait / ultrawide mismatch | Letterboxed top/bottom | Black bars top/bottom |
Safe-area rule (responsive-ui): HUD and menu chrome never sit on black bars or under a notch. Both resolve the safe-area as get_visible_rect() ∩ DisplayServer.get_display_safe_area() and apply it as MarginContainer safe insets:
src/view/hud.gd:_hud_safe_band()/_resolve_hud_safe_band()→_safe_rect()→_update_safe_margins()— band the HUD may occupy, thenHudOutermargin_left/right/top/bottom = safe inset + PLATE_ORIGIN (2px), soband_rows()and the plate stay centered in visible pixels.src/main.gd:_safe_viewport_rect()/_refresh_safe_rect()/_safe_band_rect()— centered viewport rect (pillarbox excluded) and the rebake onsize_changed, delegating toHudIcons._safe_rect()so the HUD's pip resolver stays the single source for notch + pillarbox._hud_icons._update_safe_margins()is called on every resize.src/view/menu.gd—PRESET_FULL_RECT+size_changed → _on_menu_viewport_changed → queue_redraw(), so the menu stays centered in visible pixels on ultrawide (21:9, 32:9).
get_visible_rect() excludes the pillarbox bars; get_display_safe_area() insets for the OS notch / Dynamic Island (zero rect = no notch, no-op). MarginContainer insets keep every band, label plate, and world-space claim_label_slot() rect off the bars and off the notch — 16:9 is byte-identical (band 0..640, 2px inset), ultrawide and notched devices narrow the band and the insets grow.
![]() |
![]() |
| Desert firefight — one-hit twin-stick | The Foundry Colossus 🏭 — campaign finale |
The art is a hybrid pipeline, not one source:
| Layer | Source | Where |
|---|---|---|
| Units · structures · props | ✅ Owned procedural art (tools/gen_entities.py) |
assets/art/{decor,p2,mil2,cast2} + 7 top-level |
| Player + enemy soldier sprites, action poses, and projectile cards | Bespoke generative-AI art (OpenAI built-in image generation; prompt/provenance record included) | assets/troops/, assets/projectiles/ |
| Menus · HUD · icons · FX cards | ✅ Owned procedural art (tools/gen_ui_chrome.py · gen_ui_icons.py · gen_ui_glyphs.py · gen_fx_cards.py) |
assets/art/{ui,hud,icons,fx} |
| Desert flora | Bespoke generative-AI art (nano-banana; pipeline + prompts recorded in assets/art/desert_assets_source.md) — the painterly cacti, scrub and tumbleweed |
assets/art/ (owned, gen-AI) |
| Bosses · player tank | Bespoke generative-AI art, service unrecorded — the gunship/colossus/tank silhouettes. See ASSETS.md |
assets/art/ (owned, gen-AI, unverified) |
| Ground · projectiles · FX | Kenney CC0 | assets/cc0/ |
| Radio VO (56 mp3) | Commissioned TTS (ElevenLabs) — owned by the project | assets/vo/ |
| Combat shouts (118 mp3) | Commissioned TTS (ElevenLabs, ar/fa lines) | assets/audio/ |
| Text | PixelOperator8 — CC0, Jayvee Enaguas | assets/fonts/ |
The desert→jungle look is a per-sprite olive tint + 1px readability outline applied in the
view (src/view/art.gd), not baked in — so a new asset joins one palette family regardless
of which source it came from.
✅ Every asset is cleared, and the history is clean. All 266 PNGs under
assets/are owned procedural art (tools/gen_*.py), owned generative-AI pieces, or CC0, and the 174 voice mp3 were synthesized in-house and are owned by the project. The old third-party art was purged from every commit on 2026-07-27 — that was the last blocker, and clearing it is what let this repo go public. Full map:ASSETS.md. Generative-AI assets are sanctioned (the earlier no-AI policy was dropped — seeCLAUDE.md); the gen-AI boss/vehicle/desert art and the synthesized VO already ship in-game. 📋 Full asset-licensing map + the open-source audit trail:ASSETS.md.
🖼️ Promo / key-art variants
![]() |
![]() |
Marketing key-art variants (generative-AI). Full 4K titled poster lives in the asset drop, not the repo.
Accessibility baked into the view 🧏: colorblind-safe palette routing (Art.safe),
reduce-motion mode (steady lights instead of strobes/pulses), photosensitivity
discipline (no kill-flash spam), SFX/music/rumble toggles, device-adaptive glyphs.
Three states, and the wording is deliberately unflattering where it has to be: ✅ done · 🔨 in progress · ⬜ planned.
flowchart LR
A[✅ Deterministic core] --> B[✅ Playable start→finish]
B --> C[✅ Owned art + public release]
C --> D[🔨 AAA polish pass]
D --> E[⬜ Online netplay]
D --> F[⬜ Steam release]
E --> G[⬜ 1.0]
F --> G
| Milestone | Where it actually stands | |
|---|---|---|
| ✅ | Deterministic core | 16.16 fixed-point, seeded xoshiro128**, no floats/RNG/wall-clock/scene-tree in src/sim — enforced by tools/lint_sim.gd in CI, not by convention. Golden-checksum suite proves bit-identical x86_64 ⇄ arm64 |
| ✅ | Playable start→finish | 6 gated sectors → Foundry Colossus, plus Arcade/Chapter Select, Endless War, Boss Rush, Daily Run, replay, local 2P |
| ✅ | Owned art + audio | Every sprite owned-procedural, owned generative-AI, or CC0; voice lines synthesized in-house. Provenance map in ASSETS.md |
| ✅ | Public release | Clean git history, MIT on the code, v0.3.0 binaries for macOS (arm64 + universal) · Linux · Windows |
| 🔨 | AAA polish pass | A blind consumer reviewer plus a sim-reading behaviour lens grade the build each cycle and the findings get fixed. 1 of 16 cycles run. Known-open from cycle 1: boss health bar clips the bottom edge, rebind helper text bleeds its frame, sectors reuse one desert tileset despite distinct names |
| ⬜ | Online netplay | src/net/lockstep.gd is ~260 lines with zero production callers and no transport. The determinism proof it depends on is real; the netplay is not. Don't read the file as a feature |
| ⬜ | Steam release | SteamBridge is an offline-first facade — it no-ops without the SDK. No store page, no Playtest |
| ⬜ | Localization freeze | 3 languages shipped (locale/strings.{es,fr,ja}.po), not the 12-language freeze the plan costs |
| ⬜ | Human-in-the-loop production | No external playtests, no hired artist or art director, no external QA or compat matrix. Built by one owner plus agents — see the caveat at the top of docs/PLAN.md |
📌 "P3" in this repo means playable start→finish with all modes in — not that
docs/PLAN.md's P3 exit criteria were met. The plan is costed for a 4-person team that does not exist here. The sim code is the source of truth for what ships.
Small, well-tested changes are very welcome. Two things this codebase cares about more than most:
| 🎯 | Rule | Why |
|---|---|---|
| 🔒 | Nothing non-deterministic in src/sim |
No floats, no randi(), no Time.*, no scene-tree access. tools/lint_sim.gd fails CI on any of them |
| 📏 | A sim change moves the golden checksums — by design | Re-record deliberately: set GOLDEN = [], run, paste back, and write why with the arithmetic. Never re-record to turn a red green |
| 🧪 | New behaviour brings a check that fails first | Watch it go red for the stated reason before the fix, or it is pinning the wrong thing |
| 📐 | Route through the arbiters | main.band_rows() owns stacked HUD bands; main.claim_label_slot() owns world-space labels. Drawing directly bypasses their ratchets silently |
godot --headless --path . --import # once after cloning
tools/run_tests.sh # full suite under a private user://
tools/run_tests.sh -s res://tools/lint_sim.gdLooking for something to fix? FINDINGS.md is the open defect inventory —
every entry adversarially verified, each with a measured rate. Read its header first: 62 of
115 findings died on their own last time anyone re-checked. 🔎
CONTRIBUTING.md is the long-form guide — dev setup, the determinism
commandments, golden re-recording, and the PR checklist. CLAUDE.md carries the
hard-won gotcha ledger it points at — each entry once cost someone an hour. 🕳️
Found a security issue? SECURITY.md — please don't file those publicly. 🛡️
MIT for the code — see LICENSE. It does not cover the bundled assets.
| 📄 | Covers |
|---|---|
LICENSE |
Source code — plain MIT, nothing else in the file |
NOTICE.md |
What MIT does not grant, third-party components, and the likeness position on assets/vo/ + assets/ui/intro/keyart.png |
ASSETS.md |
Per-directory provenance map for every shipped asset |
🎭 If you redistribute this repo, read
NOTICE.mdfirst. The commander VO imitates an identifiable public figure and the boot-splash key art depicts them —assets/vo/andassets/ui/intro/keyart.pngboth. The project owns the recordings and the image; right of publicity does not belong to it, and that question travels with both. 🪪
⚔️ VICTOLY awaits. Feed the War Chest. Rescue the pilot. Sidestep the lane. ⚔️
Built at 60 Hz. Bit-identical everywhere. The sim never knows juice exists. ✨







