v0.9.15 · Living Weapon Trails

SWARM ARCHITECT

Up to four Heroes carving through a horde, against one Dungeon Master building it. A bullet-heaven and a tower-defence game played against each other, in the same match, in real time.

Started
19 Jun2026 — first commit
Commits
56690% in 13 days
Code
68klines, C# + TS + PS + Py
Weapons
5014 fire modes, 9 elements
Monsters
100hand-authored, 10 tiers
Releases
27versions, 0.0.0 → 0.9.15

Two games, one match

One match, two completely different genres playing against each other. The fantasy: a hero power-fantasy carving through hordes, vs. an evil overlord macro-managing a living dungeon.

docs/info/gdd.md §1

Heroes play a bullet-heaven: weapons fire themselves, you kite, you hoover up XP gems, you draft cards on level-up and you snowball. You win by destroying three Power Cores.

The Dungeon Master plays an RTS from a god camera. They spend Souls — a pool that regenerates, and that every dying monster feeds — to summon packs, place reward chests over hidden traps, and edit a live wave timeline. They win by holding the cores to the timer, or by killing everyone.

The Souls rename, and why it stopped being a rename

The DM resource went through four names — Threat, Essence, Rage, and finally Souls — and only the last one changed the design. Souls are not a budget; they are what keeps you manifested. Every monster that dies releases a glowing soul that flies across the battlefield into your meter, and at zero you fade away. The implementation note is the whole philosophy in nine words:

The bar grows when the soul arrives, not before.

0.9.13 — Soul Harvest

The fiction

A developer — the Architect — writes a world in a language that reads less like code than scripture: syntax that compiles into matter. She means it as a game. It works too well. She wires a combat AI straight into her nerves to survive her own creation, and the turn the whole thing pivots on is that the AI is you.

The ten difficulty tiers are not zones. They are rings of the leak spreading outward from the seed — Rotting Fields, Sunken Bog, Thornwood, Ashen Caves, Frostreach, Emberkeep, Stormspire, Voidmarsh, Obsidian Deeps, The Undoing. Climbing them does not make things harder so much as it brings you closer to the compiler.

Three factions thread every ring: the Rot (organic, undead), the Synod (her world's own systems given bodies — a process that won't stop), and the Brood — the part of the world the Architect never wrote.

The three factions

Five bosses

Ten heroes, three maps

Briar Gnash Kael Lyra Tobin Spacebase map Corebase map

The architecture bet

The game is a native Godot executable, and its entire interface — menus, lobby, armory, HUD, dev console — is an HTML app drawn by a transparent OS webview sitting on top of the viewport. The two halves talk over a JSON bridge. The changelog calls it a reverse-hybrid.

That shape exists for one reason: the Steam overlay cannot hook a webview. Two earlier prototypes died proving it — a Phaser build inside Tauri, then a native hybrid in Rust. The current engine keeps the web UI and gets the overlay, because the process Steam launches is a real exe.

Two pillars and nothing between them

There are exactly two pillars and no orchestrator / ‘session’ layer in between. They communicate only through the transport — that is the whole contract.

docs/netcode.md §1

A server and a client, each ticking its own physics frame, connected by a transport that has four configurations from one codebase: single-player is a loopback with zero sockets, listen-host is the same loopback plus a port, dedicated is ENet, remote is ENet or Steam. The host stops drawing its own authoritative nodes and renders through the identical client pipeline a remote player uses — so “the host sees monsters the client doesn't” becomes impossible by construction.

The cost is a double entity: on a host, every enemy is two nodes — a hidden authoritative one and a visible dead-reckoned one. It looks redundant in solo play and it is load-bearing, because it means the solo FPS test exercises the exact multiplayer pipeline. The docs instruct future contributors to decline the optimisation, by name.

Six bytes per monster

Monster positions are not world coordinates. Each is a 12-bit signed half-pixel offset from the receiving player's own hero, with the origin carried once in the snapshot header. Three bytes, ±1024 pixels, bounded so it physically cannot drift into a ghost. Velocity is never sent — clients derive it. Off-screen enemies cost zero. A moving monster on the wire costs six bytes total.

Damage numbers came off the wire entirely: the client synthesises each one from the HP delta it already receives. The tradeoff is written down rather than discovered later — two hits between snapshots merge into one number, and crit gold is gone.

Tooling, all of it in-house

--sim

The game plays itself

A headless balance simulator with an AI hero that auto-drafts level-ups, time-compressed. It drives the real server sim, so what it measures is what the game does. Seeded, so the same (seed, skill, strategy) triple reproduces exactly and you can A/B two builds on one run. There is a bot-versus-bot mode where it plays the Dungeon Master too.

Hero Creator

Sheets in, roster out

A browser tool that ingests heavy multi-animation sprite sheets through a folder picker, repacks them, writes the manifest, the sheet and the portrait, and appends to the roster. All image work is in-browser canvas — no native dependencies. The wire follows automatically.

Map editor

Collision, by hand and by trace

A Python tool that traces and edits collision boxes and writes them straight into the Godot scenes, plus the prop, core and heal-zone data. It landed in a commit that ends “(Untested) [Feel free to test good night!]”.

Icon pipeline

75 icons, keyed and cropped

Local ComfyUI generates every weapon and amplifier icon in one cel-shaded house style on a green screen — magenta for green subjects, so the key can't eat them — auto-keyed to alpha, autocropped, written to the wired filename. Bad roll? Bump the seed offset.

One manifest

Settings declared once

Every setting exists in one JSON file, where the key is the wire name and its prefix is its category. Save defaults, console cvars, UI defaults and the options-menu rows all generate from it. Same pattern for weapons, maps and heroes — each read by both the C# sim and the TypeScript UI.

Build guard

A linter for one CSS unit

The UI scales by CSS zoom, which Chromium does not apply to viewport units — so a raw 100vh breaks on every screen that isn't the dev machine, invisibly. A zero-dependency check wired into dev, build and lint forbids them everywhere except the one authorised file.

Where it stands

0.9.15, shipped as a playable build: 50 weapons, 25 amplifiers, 100 monsters, 10 heroes, 3 maps, 2 game modes, co-op netcode with prediction and interpolation, the full DM role with its wave-timeline editor and soul economy, ten post-processing effects, controller support, keybind remapping, Steam and Discord, an in-game What's New panel that doubles as the roadmap.

1.0 is a grayed-out entry in that same panel: the full three-core run with real win and lose conditions, deeper combat with regen and mitigation, a DM economy that unlocks heavier monsters through a locked gallery you work toward, a new arena, and a real Steam App ID — it currently runs on 480, the shared test one.

The known netcode work is written down with its costs: per-entry ID and mask overhead, projectiles still absolute on the wire every tick, undeltaed gems, and one honest admission — every stress test to date used a single client, so the four-player upload multiplier is the big unmeasured axis.