A modular RTS engine, inspired by Warcraft III custom games, built to share core systems (units, combat, teams) across multiple future game modes.
The first playable prototype is a simplified recreation of the Warcraft III custom map Blood Tournament: place units for two teams, press Start Battle, and watch them fight automatically until one side wins.
Out of scope for now (see project brief): abilities, heroes, buildings, economy, animations, particles, networking, fog of war, save/load, a map editor, and audio.
- Godot 4.7 (GDScript, Forward+ renderer).
Open this folder as a project in Godot and press Run (F5). The main
scene is Scenes/Main.tscn.
- Pick a unit type (Tank / Fighter / Archer / Bat Rider / Giant) and a
team (Blue / Red) in the top-left panels. Bat Rider flies — it rests
elevated at
flight_heightand passes over ground units, and only a unit withcan_attack_flying = true(Archer, or Bat Rider itself) can hit it; melee ground units can't target it at all. Giant's attack knocks its target up and back through the air, over other units, before it lands. - Click inside the arena to place units. Repeat for both teams.
- Press Start Battle. Units automatically seek the nearest enemy, close distance, and attack until one team is eliminated.
- The winning team is announced in a centered banner.
Selection: click any unit at any time to select it — a yellow disc marks it, and a panel in the top-right corner shows its basic stats. Clicking a unit takes priority over placing a new one.
Camera: scroll to zoom, right-click-drag to orbit around the arena.
Debug menu: press backtick (`) to open a panel (bottom-right)
of debug-feature toggles and a color legend, both off by default:
- Pathfinding — attack-range ring, a line + highlight to the selected unit's target, and desired-vs-actual steering vectors, in the arena
- Detailed Stats — expands the top-right stats panel with distance, attack range/cooldown, position, and steering deviation
To add a new toggle: add an entry to Scripts/DebugSettings.gd's _flags
and gate whatever it controls with is_enabled(...) — UI/DebugMenu.gd
picks up the new checkbox automatically, no UI code to write. See that
file's header comment for the full contract.
Scenes/ Scene files (.tscn) — Main arena, Unit
Scripts/ Gameplay logic (.gd) — Team, UnitStats, Unit, HealthBar,
GameManager, Main, DebugInspector, DebugSettings, OrbitCamera
Resources/ Data-driven unit archetypes (.tres) — Tank/Fighter/Archer/
Bat Rider/Giant stats
Assets/ Reserved for future imported art (empty — primitives only today)
UI/ HUD, DebugPanel, DebugMenu scenes/scripts
tools/ Dev scripts — tools/fetch_gut.sh
tests/ GUT test suite
Architecture rationale (why a system is built the way it is, tradeoffs,
gotchas) lives in that system's own doc-comments, not here — e.g. read
Scripts/GameManager.gd's header for the battle-lifecycle design, or
Scripts/Unit.gd's for avoidance/collision. The code is the source of
truth; this file just orients you to where things live.
Automated tests use GUT (addons/gut/,
fetched by tools/fetch_gut.sh rather than vendored) and run fully
headless — no X server, Xvfb, or WSLg required.
-
Install Godot 4.7 and put the
godotbinary onPATH. -
Fetch GUT (gitignored; safe no-op if already present):
./tools/fetch_gut.sh
-
Run an import pass once (
.godot/is gitignored, so a fresh clone has no import metadata yet — GUT's class_names won't resolve without this):godot --headless --editor --quit
godot --headless -s addons/gut/gut_cmdln.gd -gdir=res://tests -gexitExits 0 on success. Re-run the import step above if addons/gut is ever
updated (bump the version in tools/fetch_gut.sh first) or a new addon
is added.
.github/workflows/ci.yml fetches GUT (cached, keyed on
tools/fetch_gut.sh's contents), then runs the import check and GUT
suite on every push to main and every pull request.
tools/screenshot.sh boots the real game headless (Xvfb) and saves a
PNG for visual/manual validation — not run in CI, ad-hoc only. Can place
specific units, start a battle, select a unit (opening the debug
inspector panel), and enable debug-menu flags, all from one command; see
tools/Screenshot.gd's header comment for the full option list.
tools/screenshot.sh
tools/screenshot.sh --out=battle.png --place="Tank:BLUE:-2,0,0;Fighter:RED:2,0,0" --battle --wait=90Defaults to Godot's Compatibility renderer (~5-10s, fine for checking
shapes/positions/UI) rather than this project's actual Forward+ renderer,
so lighting/shadows come out noticeably darker than real gameplay. Pass
--renderer=vulkan for lighting that matches what you'd see in the
editor — it runs on a software Vulkan implementation under Xvfb, so
budget ~30s per screenshot (mostly fixed startup/shader-compile cost,
not sensitive to --wait once above ~8 frames).