Add moon surface intro level with asteroid hazards and unarmed mechanics
Introduce moon01.lvl as the starting level — a pure jump-and-run intro with no gun and no enemies, just platforming over gaps and dodging falling asteroids. The player picks up their gun upon transitioning to level01. New features: - Moon tileset and PARALLAX_STYLE_MOON with crater terrain backgrounds - Asteroid entity (ENT_ASTEROID): falls from sky, damages on contact, explodes on ground with particles, respawns after delay - PLAYER_UNARMED directive disables gun for the level - Pit rescue mechanic: falling costs 1 HP and auto-dashes upward - Gun powerup entity type for future armed-pickup levels - Segment-based procedural level generator with themed rooms - Extended editor with entity palette and improved tile cycling - Web shell improvements for Emscripten builds
This commit is contained in:
100
src/main.c
100
src/main.c
@@ -8,6 +8,10 @@
|
||||
#include <time.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
* Game modes
|
||||
* ═══════════════════════════════════════════════════ */
|
||||
@@ -29,6 +33,10 @@ static char s_edit_path[256] = {0};
|
||||
/* Track whether we came from the editor (for returning after test play) */
|
||||
static bool s_testing_from_editor = false;
|
||||
|
||||
/* Station depth: increments each time we enter a new station level.
|
||||
* Drives escalating difficulty and length. */
|
||||
static int s_station_depth = 0;
|
||||
|
||||
static const char *theme_name(LevelTheme t) {
|
||||
switch (t) {
|
||||
case THEME_PLANET_SURFACE: return "Planet Surface";
|
||||
@@ -110,6 +118,30 @@ static void load_generated_level(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static void load_station_level(void) {
|
||||
LevelGenConfig config = levelgen_station_config(s_gen_seed, s_station_depth);
|
||||
s_station_depth++;
|
||||
|
||||
printf("Generating space station level (depth=%d, gravity=%.0f, segments=%d, difficulty=%.2f)\n",
|
||||
s_station_depth, config.gravity, config.num_segments, config.difficulty);
|
||||
|
||||
Tilemap gen_map;
|
||||
if (!levelgen_generate_station(&gen_map, &config)) {
|
||||
fprintf(stderr, "Failed to generate station level!\n");
|
||||
g_engine.running = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_dump_lvl) {
|
||||
levelgen_dump_lvl(&gen_map, "assets/levels/generated_station.lvl");
|
||||
}
|
||||
|
||||
if (!level_load_generated(&s_level, &gen_map)) {
|
||||
fprintf(stderr, "Failed to load station level!\n");
|
||||
g_engine.running = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Switch to editor mode ── */
|
||||
static void enter_editor(void) {
|
||||
if (s_mode == MODE_PLAY) {
|
||||
@@ -168,7 +200,7 @@ static void game_init(void) {
|
||||
} else if (s_use_procgen) {
|
||||
load_generated_level();
|
||||
} else {
|
||||
if (!level_load(&s_level, "assets/levels/level01.lvl")) {
|
||||
if (!level_load(&s_level, "assets/levels/moon01.lvl")) {
|
||||
fprintf(stderr, "Failed to load level!\n");
|
||||
g_engine.running = false;
|
||||
}
|
||||
@@ -221,6 +253,46 @@ static void game_update(float dt) {
|
||||
r_was_pressed = r_pressed;
|
||||
|
||||
level_update(&s_level, dt);
|
||||
|
||||
/* Check for level exit transition */
|
||||
if (level_exit_triggered(&s_level)) {
|
||||
const char *target = s_level.exit_target;
|
||||
|
||||
if (target[0] == '\0') {
|
||||
/* Empty target = victory / end of game */
|
||||
printf("Level complete! (no next level)\n");
|
||||
/* Loop back to the beginning */
|
||||
level_free(&s_level);
|
||||
if (!level_load(&s_level, "assets/levels/moon01.lvl")) {
|
||||
g_engine.running = false;
|
||||
}
|
||||
} else if (strcmp(target, "generate") == 0) {
|
||||
/* Procedurally generated next level */
|
||||
printf("Transitioning to generated level\n");
|
||||
level_free(&s_level);
|
||||
s_gen_seed = (uint32_t)time(NULL);
|
||||
load_generated_level();
|
||||
} else if (strcmp(target, "generate:station") == 0) {
|
||||
/* Procedurally generated space station level */
|
||||
printf("Transitioning to space station level\n");
|
||||
level_free(&s_level);
|
||||
s_gen_seed = (uint32_t)time(NULL);
|
||||
load_station_level();
|
||||
} else {
|
||||
/* Load a specific level file */
|
||||
printf("Transitioning to: %s\n", target);
|
||||
char path[ASSET_PATH_MAX];
|
||||
snprintf(path, sizeof(path), "%s", target);
|
||||
level_free(&s_level);
|
||||
if (!level_load(&s_level, path)) {
|
||||
fprintf(stderr, "Failed to load next level: %s\n", path);
|
||||
/* Fallback to moon01 */
|
||||
if (!level_load(&s_level, "assets/levels/moon01.lvl")) {
|
||||
g_engine.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void game_render(float interpolation) {
|
||||
@@ -275,7 +347,7 @@ int main(int argc, char *argv[]) {
|
||||
printf(" E Open level editor\n");
|
||||
printf(" ESC Quit (or return to editor from test play)\n");
|
||||
printf("\nEditor:\n");
|
||||
printf(" 1-5 Select tool (Pencil/Eraser/Fill/Entity/Spawn)\n");
|
||||
printf(" 1-6 Select tool (Pencil/Eraser/Fill/Entity/Spawn/Exit)\n");
|
||||
printf(" Q/W/E Select layer (Collision/BG/FG)\n");
|
||||
printf(" G Toggle grid\n");
|
||||
printf(" V Toggle all-layer visibility\n");
|
||||
@@ -285,6 +357,7 @@ int main(int argc, char *argv[]) {
|
||||
printf(" Left click Paint/place (canvas) or select (palette)\n");
|
||||
printf(" Right click Pick tile / delete entity\n");
|
||||
printf(" Ctrl+S Save level\n");
|
||||
printf(" Ctrl+O Open/load level\n");
|
||||
printf(" Ctrl++/- Resize level width\n");
|
||||
printf(" P Test play level\n");
|
||||
printf(" ESC Quit editor\n");
|
||||
@@ -294,6 +367,29 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
srand((unsigned)time(NULL));
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
/* Check URL query string for ?edit or ?edit=filename */
|
||||
{
|
||||
const char *qs = emscripten_run_script_string(
|
||||
"window.location.search || ''");
|
||||
if (qs && strstr(qs, "edit")) {
|
||||
s_use_editor = true;
|
||||
/* Check for ?edit=filename */
|
||||
const char *eq = strstr(qs, "edit=");
|
||||
if (eq) {
|
||||
eq += 5; /* skip "edit=" */
|
||||
/* Copy until & or end of string */
|
||||
int len = 0;
|
||||
while (eq[len] && eq[len] != '&' && len < (int)sizeof(s_edit_path) - 1) {
|
||||
s_edit_path[len] = eq[len];
|
||||
len++;
|
||||
}
|
||||
s_edit_path[len] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!engine_init()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user