A roguelike, turn-based tactics game set on a medieval battlefield. You are one soldier in a massive battle between two armies. Survive. Fight. Turn the tide.
Built with Rust, WebAssembly, and SDL2. Runs natively (desktop/ARM) and on the web via Emscripten (WebGL). Mobile-first, playable offline as a PWA.
The Battlefield is a permadeath roguelike where each run places you as a single soldier in a procedurally generated battle between two organized medieval armies. You don't control the army -- you follow orders, fight nearby enemies, and try to survive while contributing to your side's victory.
Every battle is different: terrain, faction pairings, army composition, and commander strategies are all procedurally generated. When you die, you start over in a new battle with new conditions.
| Layer | Technology |
|---|---|
| Language | Rust |
| Web target | WebAssembly via Emscripten |
| Native target | SDL2 (Linux, ARM/Raspberry Pi) |
| Rendering | SDL2 → WebGL (web) / GPU-accelerated (native) |
| Input | Touch (joystick, buttons, pinch) + keyboard/mouse + gamepad |
| Offline support | PWA with service worker |
| Deployment | GitHub Pages via GitHub Actions |
| Art | Tiny Swords by Pixel Frog (itch.io) |
- Rust (latest stable)
- cmake (required by the SDL2 bundled build)
- Any modern browser (Chrome, Firefox, Safari, Edge)
cargo run -p battlefield-sdl# One-time setup: install Emscripten SDK
git clone https://github.com/emscripten-core/emsdk.git ~/emsdk
cd ~/emsdk && ./emsdk install latest && ./emsdk activate latest
rustup target add wasm32-unknown-emscripten
# Before each build session
source ~/emsdk/emsdk_env.sh
# Build
./build-sdl-web.sh
# Serve locally
python3 -m http.server -d web-sdl/dist 8080The build script compiles the SDL crate to WebAssembly via Emscripten, bundles
game assets into a .data file, applies service worker cache-busting, and
copies PWA files into web-sdl/dist/.
cargo testcargo clippy -- -D warnings
cargo fmt --check- TDD -- Write tests first. Every feature starts with a failing test.
- SOLID -- Single responsibility, open/closed, Liskov substitution, interface segregation, dependency inversion.
- Small files -- Split files when they grow too large. Each file should have a clear, focused purpose.
- Low complexity -- Keep functions short. Minimize nesting depth. Extract early returns.
- Idiomatic Rust -- Follow Rust conventions. Zero clippy warnings. Use
cargo fmt. - No anticipation -- Don't code for hypothetical future needs (YAGNI). Build what's needed now.
- Rust project setup with
Cargo.tomland WASM target - HTML Canvas 2D rendering pipeline (initialize canvas, basic draw)
- Canvas setup and game loop (fixed timestep)
- GitHub Actions CI/CD pipeline (build, test, clippy, fmt)
- GitHub Pages deployment
- Sprite sheet loader (parse horizontal strip PNGs into frames)
- Render a single animated unit (Warrior idle) on screen
- 64x64 square grid map with tilemap rendering (Tiny Swords tilesets)
- Camera controls (pan, zoom, smooth follow)
- Turn system (auto-turn: player acts, then AI acts, turn advances)
- Unit placement and movement on the grid (with dust particle FX)
- Sprite facing (horizontal flip for left-facing units)
- Basic melee combat (Warrior attack animation + explosion FX on hit)
- Ranged combat (Archer shoot animation + arrow projectile)
- Health system with HP bars
- Unit death (explosion FX + fade out)
- Touch input: swipe-anywhere movement & attack (short swipe = 1 tile, long swipe = A* pathfinding auto-move)
- Touch input: pinch-to-zoom, two-finger-pan
- Responsive canvas (fill viewport on mobile, DPR scaling)
- Keyboard: arrow keys = movement, WASD = camera pan, mouse wheel = zoom
- Procedural terrain generation (grass, elevation, water, forest, rock)
- 4-bit cardinal bitmask auto-tiling (flat ground + elevated ground)
- Water rendering with animated foam edges
- Elevation rendering with shadows and cliff faces
- All 5 unit types functional (Warrior, Archer, Lancer, Pawn, Monk)
- Building placement (castles, towers, houses, barracks, monastery)
- Two-faction army generation (select from 5 faction colors)
- Lancer: larger sprite (320x320), directional attack/defence, charge ability
- Monk: heal animation + heal effect overlay on target
- Pawn: tool variant animations as melee attack
- Army hierarchy (army, divisions, squads)
- AI commanders with portraits (25 avatars) issuing orders
- Squad-level AI (units following orders, engaging enemies)
- Player receives and responds to orders
- Morale system (units break and flee, Monks rally)
- Permadeath (run ends on player death, death screen with avatar)
- Battle end conditions (victory, defeat, rout)
- Run summary screen (stats on RegularPaper background, Swords decoration)
- Procedural variety (faction pairing, terrain layout, army composition, tilemap color variant)
- New run setup (battle generation with new conditions)
- Meta-progression system (TBD -- unlockable roles, scenarios)
- PWA manifest and service worker (offline play)
- Full UI with asset pack components (buttons, banners, ribbons, cursors, papers, wood table)
- Main menu (Banner title, WoodTable background, Blue/Red buttons)
- In-battle HUD (health bar, morale bar, orders ribbon, action icons, minimap)
- Battle overview toggle (zoomed-out army positions)
- Cloud shadows drifting across battlefield
- Animated decorations (swaying trees, bushes, water rocks)
- Sound effects (combat, movement, orders, ambient)
- Music
- Mobile-optimized HUD layout (bottom action bar)
- Haptic feedback on attack/damage (Vibration API)
- Touch target sizing validation (44px minimum)
- Unit ability balancing (Guard, Volley, Charge, Brace, Heal)
- Terrain and building defense bonus tuning
- Commander AI personality variety and balancing
- Army composition templates (infantry-heavy, cavalry-heavy, balanced, skirmish)
- Battle scenario variety
- Playtesting and tuning
TBD