Add new level transition state machine
This commit is contained in:
132
src/main.c
132
src/main.c
@@ -6,6 +6,7 @@
|
||||
#include "game/editor.h"
|
||||
#include "game/stats.h"
|
||||
#include "game/analytics.h"
|
||||
#include "game/transition.h"
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -24,6 +25,7 @@ typedef enum GameMode {
|
||||
MODE_PLAY,
|
||||
MODE_EDITOR,
|
||||
MODE_PAUSED,
|
||||
MODE_TRANSITION,
|
||||
} GameMode;
|
||||
|
||||
static Level s_level;
|
||||
@@ -56,6 +58,10 @@ static bool s_session_active = false;
|
||||
#define PAUSE_ITEM_COUNT 3
|
||||
static int s_pause_selection = 0; /* 0=Resume, 1=Restart, 2=Quit */
|
||||
|
||||
/* ── Level transition state ── */
|
||||
static TransitionState s_transition;
|
||||
static char s_pending_target[ASSET_PATH_MAX] = {0}; /* exit target stashed during transition */
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
/* JS-initiated level load request (level-select dropdown in shell). */
|
||||
static int s_js_load_request = 0;
|
||||
@@ -321,6 +327,48 @@ static void restart_level(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Level load dispatch — loads the next level based on target string ── */
|
||||
static void dispatch_level_load(const char *target) {
|
||||
if (target[0] == '\0') {
|
||||
/* Empty target = victory / end of game. */
|
||||
printf("Level complete! (no next level)\n");
|
||||
end_session("completed");
|
||||
level_free(&s_level);
|
||||
s_station_depth = 0;
|
||||
s_mars_depth = 0;
|
||||
if (!load_level_file("assets/levels/moon01.lvl")) {
|
||||
g_engine.running = false;
|
||||
}
|
||||
begin_session();
|
||||
} else if (strcmp(target, "generate") == 0) {
|
||||
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) {
|
||||
printf("Transitioning to space station level\n");
|
||||
level_free(&s_level);
|
||||
s_gen_seed = (uint32_t)time(NULL);
|
||||
load_station_level();
|
||||
} else if (strcmp(target, "generate:mars_base") == 0) {
|
||||
printf("Transitioning to Mars Base level\n");
|
||||
level_free(&s_level);
|
||||
s_gen_seed = (uint32_t)time(NULL);
|
||||
load_mars_base_level();
|
||||
} else {
|
||||
printf("Transitioning to: %s\n", target);
|
||||
char path[ASSET_PATH_MAX];
|
||||
snprintf(path, sizeof(path), "%s", target);
|
||||
level_free(&s_level);
|
||||
if (!load_level_file(path)) {
|
||||
fprintf(stderr, "Failed to load next level: %s\n", path);
|
||||
if (!load_level_file("assets/levels/moon01.lvl")) {
|
||||
g_engine.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
* Game callbacks
|
||||
* ═══════════════════════════════════════════════════ */
|
||||
@@ -395,7 +443,9 @@ static void game_update(float dt) {
|
||||
end_session("quit");
|
||||
|
||||
/* Tear down whatever mode we are in. */
|
||||
if (s_mode == MODE_PLAY || s_mode == MODE_PAUSED) {
|
||||
if (s_mode == MODE_PLAY || s_mode == MODE_PAUSED
|
||||
|| s_mode == MODE_TRANSITION) {
|
||||
transition_reset(&s_transition);
|
||||
level_free(&s_level);
|
||||
} else if (s_mode == MODE_EDITOR) {
|
||||
editor_free(&s_editor);
|
||||
@@ -440,6 +490,28 @@ static void game_update(float dt) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_mode == MODE_TRANSITION) {
|
||||
transition_update(&s_transition, dt, &s_level.camera);
|
||||
|
||||
/* Outro finished — swap levels. */
|
||||
if (transition_needs_load(&s_transition)) {
|
||||
dispatch_level_load(s_pending_target);
|
||||
s_pending_target[0] = '\0';
|
||||
|
||||
/* Use the new level's intro style. */
|
||||
TransitionStyle in_style = s_level.map.transition_in;
|
||||
transition_set_in_style(&s_transition, in_style);
|
||||
transition_begin_intro(&s_transition);
|
||||
}
|
||||
|
||||
/* Intro finished — return to play. */
|
||||
if (transition_is_done(&s_transition)) {
|
||||
transition_reset(&s_transition);
|
||||
s_mode = MODE_PLAY;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* ── Play mode ── */
|
||||
|
||||
/* Pause on escape (return to editor during test play) */
|
||||
@@ -492,49 +564,16 @@ static void game_update(float dt) {
|
||||
s_stats.levels_completed++;
|
||||
}
|
||||
|
||||
if (target[0] == '\0') {
|
||||
/* Empty target = victory / end of game */
|
||||
printf("Level complete! (no next level)\n");
|
||||
end_session("completed");
|
||||
/* Loop back to the beginning, reset progression state */
|
||||
level_free(&s_level);
|
||||
s_station_depth = 0;
|
||||
s_mars_depth = 0;
|
||||
if (!load_level_file("assets/levels/moon01.lvl")) {
|
||||
g_engine.running = false;
|
||||
}
|
||||
begin_session();
|
||||
} 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 if (strcmp(target, "generate:mars_base") == 0) {
|
||||
/* Procedurally generated Mars Base level */
|
||||
printf("Transitioning to Mars Base level\n");
|
||||
level_free(&s_level);
|
||||
s_gen_seed = (uint32_t)time(NULL);
|
||||
load_mars_base_level();
|
||||
TransitionStyle out_style = s_level.map.transition_out;
|
||||
|
||||
if (out_style == TRANS_ELEVATOR || out_style == TRANS_TELEPORTER) {
|
||||
/* Animated transition: stash target, start outro. */
|
||||
snprintf(s_pending_target, sizeof(s_pending_target), "%s", target);
|
||||
transition_start_out(&s_transition, out_style);
|
||||
s_mode = MODE_TRANSITION;
|
||||
} 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 (!load_level_file(path)) {
|
||||
fprintf(stderr, "Failed to load next level: %s\n", path);
|
||||
/* Fallback to moon01 */
|
||||
if (!load_level_file("assets/levels/moon01.lvl")) {
|
||||
g_engine.running = false;
|
||||
}
|
||||
}
|
||||
/* Instant transition (none or spacecraft-driven). */
|
||||
dispatch_level_load(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -587,6 +626,10 @@ static void game_render(float interpolation) {
|
||||
/* Render frozen game frame, then overlay the pause menu. */
|
||||
level_render(&s_level, interpolation);
|
||||
pause_render();
|
||||
} else if (s_mode == MODE_TRANSITION) {
|
||||
/* Render the level (frozen) with the transition overlay on top. */
|
||||
level_render(&s_level, interpolation);
|
||||
transition_render(&s_transition);
|
||||
} else {
|
||||
level_render(&s_level, interpolation);
|
||||
}
|
||||
@@ -598,7 +641,8 @@ static void game_shutdown(void) {
|
||||
/* Always free both — editor may have been initialized even if we're
|
||||
* currently in play mode (e.g. shutdown during test play). editor_free
|
||||
* and level_free are safe to call on zeroed/already-freed structs. */
|
||||
if (s_mode == MODE_PLAY || s_mode == MODE_PAUSED || s_testing_from_editor) {
|
||||
if (s_mode == MODE_PLAY || s_mode == MODE_PAUSED
|
||||
|| s_mode == MODE_TRANSITION || s_testing_from_editor) {
|
||||
level_free(&s_level);
|
||||
}
|
||||
if (s_mode == MODE_EDITOR || s_use_editor) {
|
||||
|
||||
Reference in New Issue
Block a user