Add pause menu, laser turret, charger/spawner enemies, and Mars campaign

Implement four feature phases:

Phase 1 - Pause menu: extract bitmap font into shared engine/font
module, add MODE_PAUSED with Resume/Restart/Quit overlay.

Phase 2 - Laser turret hazard: ENT_LASER_TURRET with charge/fire/
cooldown state machine, per-pixel beam raycast, two variants (fixed
and tracking). Registered in entity registry with editor icons.

Phase 3 - Charger and Spawner enemies: charger ground patrol with
detect/telegraph/charge/stun cycle (2s charge timeout), spawner that
periodically creates grunts up to a global cap of 3.

Phase 4 - Mars campaign: two handcrafted levels (mars01 surface,
mars02 base), mars_tileset.png, PARALLAX_STYLE_MARS with salmon sky
and red mesas, THEME_MARS_SURFACE/THEME_MARS_BASE for the procedural
generator with per-theme gravity/tileset/parallax. Moon campaign now
chains moon03 -> mars01 -> mars02 -> victory.

Also fix review findings: deterministic seed on generated level
restart, NULL checks on calloc in spawn functions, charge timeout
to prevent infinite charge on flat terrain, and stop suppressing
stderr in Makefile web-serve target so real errors are visible.
This commit is contained in:
Thomas
2026-03-02 19:34:12 +00:00
parent e5e91247fe
commit d0853fb38d
22 changed files with 1519 additions and 147 deletions

View File

@@ -152,9 +152,12 @@ bool level_load_generated(Level *level, Tilemap *gen_map) {
level->map = *gen_map;
memset(gen_map, 0, sizeof(Tilemap)); /* prevent double-free */
/* Load tileset texture (the generator doesn't do this) */
snprintf(level->map.tileset_path, sizeof(level->map.tileset_path),
"%s", "assets/tiles/tileset.png");
/* Load tileset texture. Use the generator's tileset_path if set,
* otherwise fall back to the default tileset. */
if (!level->map.tileset_path[0]) {
snprintf(level->map.tileset_path, sizeof(level->map.tileset_path),
"%s", "assets/tiles/tileset.png");
}
level->map.tileset = assets_get_texture(level->map.tileset_path);
if (level->map.tileset) {
int tex_w;