forked from tas/major_tom
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:
@@ -538,6 +538,24 @@ static SegmentType pick_segment_type(LevelTheme theme, int index, int total) {
|
||||
if (r < 0.90f) return SEG_FLAT;
|
||||
return SEG_PIT;
|
||||
|
||||
case THEME_MARS_SURFACE:
|
||||
/* Red dusty exterior: spacey, wide-open, few obstacles.
|
||||
Charger enemies, occasional pits, wind gusts. */
|
||||
if (r < 0.35f) return SEG_FLAT;
|
||||
if (r < 0.55f) return SEG_PIT;
|
||||
if (r < 0.75f) return SEG_ARENA;
|
||||
if (r < 0.90f) return SEG_PLATFORMS;
|
||||
return SEG_SHAFT;
|
||||
|
||||
case THEME_MARS_BASE:
|
||||
/* Indoor Mars facility: very vertical, narrow corridors,
|
||||
90-degree turns, heavy turret/spawner presence. */
|
||||
if (r < 0.30f) return SEG_CORRIDOR;
|
||||
if (r < 0.55f) return SEG_SHAFT;
|
||||
if (r < 0.70f) return SEG_ARENA;
|
||||
if (r < 0.85f) return SEG_PLATFORMS;
|
||||
return SEG_FLAT;
|
||||
|
||||
default:
|
||||
/* Only pick content types (exclude TRANSITION and CLIMB connectors) */
|
||||
return (SegmentType)rng_range(0, SEG_SHAFT);
|
||||
@@ -758,6 +776,8 @@ static float gravity_for_theme(LevelTheme theme) {
|
||||
case THEME_PLANET_SURFACE: return 400.0f;
|
||||
case THEME_PLANET_BASE: return 600.0f;
|
||||
case THEME_SPACE_STATION: return 750.0f;
|
||||
case THEME_MARS_SURFACE: return 370.0f; /* Mars: ~0.38g */
|
||||
case THEME_MARS_BASE: return 700.0f; /* artificial gravity */
|
||||
default: return 600.0f;
|
||||
}
|
||||
}
|
||||
@@ -768,6 +788,8 @@ static SDL_Color bg_color_for_theme(LevelTheme theme) {
|
||||
case THEME_PLANET_SURFACE: return (SDL_Color){12, 8, 20, 255};
|
||||
case THEME_PLANET_BASE: return (SDL_Color){10, 14, 22, 255};
|
||||
case THEME_SPACE_STATION: return (SDL_Color){5, 5, 18, 255};
|
||||
case THEME_MARS_SURFACE: return (SDL_Color){30, 12, 8, 255};
|
||||
case THEME_MARS_BASE: return (SDL_Color){18, 10, 8, 255};
|
||||
default: return (SDL_Color){15, 15, 30, 255};
|
||||
}
|
||||
}
|
||||
@@ -778,6 +800,8 @@ static ParallaxStyle parallax_style_for_theme(LevelTheme theme) {
|
||||
case THEME_PLANET_SURFACE: return PARALLAX_STYLE_ALIEN_SKY;
|
||||
case THEME_PLANET_BASE: return PARALLAX_STYLE_INTERIOR;
|
||||
case THEME_SPACE_STATION: return PARALLAX_STYLE_DEEP_SPACE;
|
||||
case THEME_MARS_SURFACE: return PARALLAX_STYLE_MARS;
|
||||
case THEME_MARS_BASE: return PARALLAX_STYLE_INTERIOR;
|
||||
default: return PARALLAX_STYLE_DEFAULT;
|
||||
}
|
||||
}
|
||||
@@ -787,6 +811,8 @@ static const char *theme_label(LevelTheme t) {
|
||||
case THEME_PLANET_SURFACE: return "surface";
|
||||
case THEME_PLANET_BASE: return "base";
|
||||
case THEME_SPACE_STATION: return "station";
|
||||
case THEME_MARS_SURFACE: return "mars_surf";
|
||||
case THEME_MARS_BASE: return "mars_base";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
@@ -891,14 +917,18 @@ bool levelgen_generate(Tilemap *map, const LevelGenConfig *config) {
|
||||
* to give each theme its own vertical zone. Single-theme levels
|
||||
* stay at the standard 23-tile height. */
|
||||
bool has_surface = false, has_base = false, has_station = false;
|
||||
bool has_mars_surf = false, has_mars_base = false;
|
||||
for (int i = 0; i < num_segs; i++) {
|
||||
if (seg_themes[i] == THEME_PLANET_SURFACE) has_surface = true;
|
||||
if (seg_themes[i] == THEME_PLANET_BASE) has_base = true;
|
||||
if (seg_themes[i] == THEME_SPACE_STATION) has_station = true;
|
||||
if (seg_themes[i] == THEME_MARS_SURFACE) has_mars_surf = true;
|
||||
if (seg_themes[i] == THEME_MARS_BASE) has_mars_base = true;
|
||||
}
|
||||
|
||||
/* Go tall if we have theme variety that benefits from verticality */
|
||||
uses_tall = (has_surface && (has_base || has_station)) ||
|
||||
(has_mars_surf && has_mars_base) ||
|
||||
(has_station && num_segs >= 5);
|
||||
|
||||
if (uses_tall) {
|
||||
@@ -914,9 +944,11 @@ bool levelgen_generate(Tilemap *map, const LevelGenConfig *config) {
|
||||
}
|
||||
switch (seg_themes[i]) {
|
||||
case THEME_PLANET_SURFACE:
|
||||
case THEME_MARS_SURFACE:
|
||||
seg_ground[i] = ZONE_LOW_GROUND;
|
||||
break;
|
||||
case THEME_PLANET_BASE:
|
||||
case THEME_MARS_BASE:
|
||||
seg_ground[i] = ZONE_HIGH_GROUND;
|
||||
break;
|
||||
case THEME_SPACE_STATION:
|
||||
@@ -1092,6 +1124,13 @@ bool levelgen_generate(Tilemap *map, const LevelGenConfig *config) {
|
||||
map->has_bg_color = true;
|
||||
map->parallax_style = (int)parallax_style_for_theme(primary_theme);
|
||||
|
||||
/* Select tileset based on theme. Mars themes use a dedicated tileset;
|
||||
* all others use the default. level_load_generated() loads the texture. */
|
||||
if (primary_theme == THEME_MARS_SURFACE || primary_theme == THEME_MARS_BASE) {
|
||||
snprintf(map->tileset_path, sizeof(map->tileset_path),
|
||||
"%s", "assets/tiles/mars_tileset.png");
|
||||
}
|
||||
|
||||
/* Exit zone at the far-right end of the level.
|
||||
* Placed just inside the right border wall, 2 tiles wide, 3 tiles tall
|
||||
* sitting on the ground row. Target "generate" chains to another
|
||||
|
||||
Reference in New Issue
Block a user