tr2/game-flow: use event handler mapping

This commit is contained in:
Marcin Kurczewski 2025-01-28 00:55:12 +01:00
parent 617d6b08cc
commit 9fb0a128bc
5 changed files with 337 additions and 219 deletions

View file

@ -324,8 +324,8 @@
{"type": "play_level"},
{"type": "play_music", "music_track": 41},
{"type": "level_stats"},
{"type": "level_complete"},
{"type": "play_fmv", "fmv_id": 7},
{"type": "level_complete"},
],
},

View file

@ -25,8 +25,6 @@ typedef enum {
#if TR_VERSION == 1
GFSC_RESTART,
GFSC_SELECT,
#elif TR_VERSION == 2
GFSC_MID_STORY,
#endif
GFSC_STORY,
} GAME_FLOW_SEQUENCE_CONTEXT;

View file

@ -11,10 +11,335 @@
#include "game/shell.h"
#include "global/vars.h"
#include <libtrx/config.h>
#include <libtrx/enum_map.h>
#include <libtrx/game/game_string_table.h>
#include <libtrx/log.h>
#define DECLARE_EVENT_HANDLER(name) \
GAME_FLOW_COMMAND name( \
const GAME_FLOW_LEVEL *const level, \
const GAME_FLOW_SEQUENCE_EVENT *const event, \
GAME_FLOW_SEQUENCE_CONTEXT seq_ctx, void *const seq_ctx_arg)
static DECLARE_EVENT_HANDLER(M_HandleExitToTitle);
static DECLARE_EVENT_HANDLER(M_HandlePicture);
static DECLARE_EVENT_HANDLER(M_HandlePlayLevel);
static DECLARE_EVENT_HANDLER(M_HandlePlayCutscene);
static DECLARE_EVENT_HANDLER(M_HandlePlayMusic);
static DECLARE_EVENT_HANDLER(M_HandlePlayFMV);
static DECLARE_EVENT_HANDLER(M_HandleLevelComplete);
static DECLARE_EVENT_HANDLER(M_HandleLevelStats);
static DECLARE_EVENT_HANDLER(M_HandleEnableSunset);
static DECLARE_EVENT_HANDLER(M_HandleTotalStats);
static DECLARE_EVENT_HANDLER(M_HandleSetCameraAngle);
static DECLARE_EVENT_HANDLER(M_HandleDisableFloor);
static DECLARE_EVENT_HANDLER(M_HandleAddItem);
static DECLARE_EVENT_HANDLER(M_HandleAddSecretReward);
static DECLARE_EVENT_HANDLER(M_HandleRemoveWeapons);
static DECLARE_EVENT_HANDLER(M_HandleRemoveAmmo);
static DECLARE_EVENT_HANDLER(M_HandleSetStartAnim);
static DECLARE_EVENT_HANDLER(M_HandleSetNumSecrets);
static DECLARE_EVENT_HANDLER((*m_EventHandlers[GFS_NUMBER_OF])) = {
// clang-format off
[GFS_EXIT_TO_TITLE] = M_HandleExitToTitle,
[GFS_DISPLAY_PICTURE] = M_HandlePicture,
[GFS_PLAY_LEVEL] = M_HandlePlayLevel,
[GFS_PLAY_CUTSCENE] = M_HandlePlayCutscene,
[GFS_PLAY_MUSIC] = M_HandlePlayMusic,
[GFS_PLAY_FMV] = M_HandlePlayFMV,
[GFS_LEVEL_COMPLETE] = M_HandleLevelComplete,
[GFS_LEVEL_STATS] = M_HandleLevelStats,
[GFS_TOTAL_STATS] = M_HandleTotalStats,
[GFS_ENABLE_SUNSET] = M_HandleEnableSunset,
[GFS_SET_CAMERA_ANGLE] = M_HandleSetCameraAngle,
[GFS_DISABLE_FLOOR] = M_HandleDisableFloor,
[GFS_ADD_ITEM] = M_HandleAddItem,
[GFS_ADD_SECRET_REWARD] = M_HandleAddSecretReward,
[GFS_REMOVE_WEAPONS] = M_HandleRemoveWeapons,
[GFS_REMOVE_AMMO] = M_HandleRemoveAmmo,
[GFS_SET_START_ANIM] = M_HandleSetStartAnim,
[GFS_SET_NUM_SECRETS] = M_HandleSetNumSecrets,
// clang-format on
};
static DECLARE_EVENT_HANDLER(M_HandleExitToTitle)
{
return (GAME_FLOW_COMMAND) { .action = GF_EXIT_TO_TITLE };
}
static DECLARE_EVENT_HANDLER(M_HandlePicture)
{
GAME_FLOW_COMMAND gf_cmd = { .action = GF_NOOP };
if (seq_ctx == GFSC_SAVED) {
return gf_cmd;
}
const GAME_FLOW_DISPLAY_PICTURE_DATA *const data = event->data;
PHASE *const phase = Phase_Picture_Create((PHASE_PICTURE_ARGS) {
.file_name = data->path,
.display_time = data->display_time,
.fade_in_time = data->fade_in_time,
.fade_out_time = data->fade_out_time,
.display_time_includes_fades = true,
});
gf_cmd = PhaseExecutor_Run(phase);
Phase_Picture_Destroy(phase);
return gf_cmd;
}
static DECLARE_EVENT_HANDLER(M_HandlePlayLevel)
{
GAME_FLOW_COMMAND gf_cmd = { .action = GF_NOOP };
if (seq_ctx != GFSC_STORY) {
gf_cmd = GF_RunLevel(level, seq_ctx);
if (gf_cmd.action == GF_LEVEL_COMPLETE) {
gf_cmd.action = GF_NOOP;
}
}
return gf_cmd;
}
static DECLARE_EVENT_HANDLER(M_HandlePlayCutscene)
{
GAME_FLOW_COMMAND gf_cmd = { .action = GF_NOOP };
const int16_t cutscene_num = (int16_t)(intptr_t)event->data;
if (seq_ctx != GFSC_SAVED) {
gf_cmd = GF_DoCutsceneSequence(cutscene_num);
if (gf_cmd.action == GF_LEVEL_COMPLETE) {
gf_cmd.action = GF_NOOP;
}
}
return gf_cmd;
}
static DECLARE_EVENT_HANDLER(M_HandlePlayMusic)
{
Music_Play((int32_t)(intptr_t)event->data, MPM_ALWAYS);
return (GAME_FLOW_COMMAND) { .action = GF_NOOP };
}
static DECLARE_EVENT_HANDLER(M_HandlePlayFMV)
{
GAME_FLOW_COMMAND gf_cmd = { .action = GF_NOOP };
const int16_t fmv_id = (int16_t)(intptr_t)event->data;
if (seq_ctx != GFSC_SAVED) {
if (fmv_id < 0 || fmv_id >= g_GameFlow.fmv_count) {
LOG_ERROR("Invalid FMV number: %d", fmv_id);
} else {
FMV_Play(g_GameFlow.fmvs[fmv_id].path);
}
}
return gf_cmd;
}
static DECLARE_EVENT_HANDLER(M_HandleLevelComplete)
{
GAME_FLOW_COMMAND gf_cmd = { .action = GF_NOOP };
if (seq_ctx != GFSC_NORMAL) {
return gf_cmd;
}
const GAME_FLOW_LEVEL *const current_level = Game_GetCurrentLevel();
START_INFO *const start = GF_GetResumeInfo(current_level);
start->stats = g_SaveGame.current_stats;
start->available = 0;
const GAME_FLOW_LEVEL *const next_level =
GF_GetLevel(current_level->num + 1, current_level->type);
if (next_level != NULL) {
CreateStartInfo(next_level);
g_SaveGame.current_level = next_level->num;
}
if (next_level == NULL || gf_cmd.action != GF_NOOP) {
return gf_cmd;
}
gf_cmd = (GAME_FLOW_COMMAND) {
.action = GF_START_GAME,
.param = next_level->num,
};
return gf_cmd;
}
static DECLARE_EVENT_HANDLER(M_HandleLevelStats)
{
GAME_FLOW_COMMAND gf_cmd = { .action = GF_NOOP };
if (seq_ctx == GFSC_NORMAL) {
const GAME_FLOW_LEVEL *const current_level = Game_GetCurrentLevel();
PHASE *const stats_phase = Phase_Stats_Create((PHASE_STATS_ARGS) {
.background_type = Game_IsInGym() ? BK_TRANSPARENT : BK_OBJECT,
.level_num = current_level->num,
.show_final_stats = false,
.use_bare_style = false,
});
gf_cmd = PhaseExecutor_Run(stats_phase);
Phase_Stats_Destroy(stats_phase);
}
return gf_cmd;
}
static DECLARE_EVENT_HANDLER(M_HandleTotalStats)
{
GAME_FLOW_COMMAND gf_cmd = { .action = GF_NOOP };
if (seq_ctx == GFSC_NORMAL) {
const GAME_FLOW_LEVEL *const current_level = Game_GetCurrentLevel();
START_INFO *const start = GF_GetResumeInfo(current_level);
start->stats = g_SaveGame.current_stats;
g_SaveGame.bonus_flag = true;
PHASE *const phase = Phase_Stats_Create((PHASE_STATS_ARGS) {
.background_type = BK_IMAGE,
.background_path = "data/end.pcx",
.show_final_stats = true,
.use_bare_style = false,
});
gf_cmd = PhaseExecutor_Run(phase);
Phase_Stats_Destroy(phase);
} else {
gf_cmd = (GAME_FLOW_COMMAND) { .action = GF_EXIT_TO_TITLE };
}
return gf_cmd;
}
static DECLARE_EVENT_HANDLER(M_HandleEnableSunset)
{
GAME_FLOW_COMMAND gf_cmd = { .action = GF_NOOP };
if (seq_ctx != GFSC_STORY) {
g_GF_SunsetEnabled = true;
}
return gf_cmd;
}
static DECLARE_EVENT_HANDLER(M_HandleSetCameraAngle)
{
GAME_FLOW_COMMAND gf_cmd = { .action = GF_NOOP };
if (seq_ctx != GFSC_SAVED) {
g_CineTargetAngle = (int16_t)(intptr_t)event->data;
}
return gf_cmd;
}
static DECLARE_EVENT_HANDLER(M_HandleDisableFloor)
{
GAME_FLOW_COMMAND gf_cmd = { .action = GF_NOOP };
if (seq_ctx != GFSC_STORY) {
g_GF_NoFloor = (int16_t)(intptr_t)event->data;
}
return gf_cmd;
}
static DECLARE_EVENT_HANDLER(M_HandleAddItem)
{
GAME_FLOW_COMMAND gf_cmd = { .action = GF_NOOP };
if (seq_ctx != GFSC_STORY) {
const GAME_FLOW_ADD_ITEM_DATA *const data =
(const GAME_FLOW_ADD_ITEM_DATA *)event->data;
GF_InventoryModifier_Add(data->object_id, data->inv_type, data->qty);
}
return gf_cmd;
}
static DECLARE_EVENT_HANDLER(M_HandleAddSecretReward)
{
return M_HandleAddItem(level, event, seq_ctx, seq_ctx_arg);
}
static DECLARE_EVENT_HANDLER(M_HandleRemoveWeapons)
{
GAME_FLOW_COMMAND gf_cmd = { .action = GF_NOOP };
if (seq_ctx != GFSC_STORY && seq_ctx != GFSC_SAVED) {
g_GF_RemoveWeapons = true;
}
return gf_cmd;
}
static DECLARE_EVENT_HANDLER(M_HandleRemoveAmmo)
{
GAME_FLOW_COMMAND gf_cmd = { .action = GF_NOOP };
if (seq_ctx != GFSC_STORY && seq_ctx != GFSC_SAVED) {
g_GF_RemoveAmmo = true;
}
return gf_cmd;
}
static DECLARE_EVENT_HANDLER(M_HandleSetStartAnim)
{
GAME_FLOW_COMMAND gf_cmd = { .action = GF_NOOP };
if (seq_ctx != GFSC_STORY) {
g_GF_LaraStartAnim = (int16_t)(intptr_t)event->data;
}
return gf_cmd;
}
static DECLARE_EVENT_HANDLER(M_HandleSetNumSecrets)
{
GAME_FLOW_COMMAND gf_cmd = { .action = GF_NOOP };
if (seq_ctx != GFSC_STORY) {
g_GF_NumSecrets = (int16_t)(intptr_t)event->data;
}
return gf_cmd;
}
GAME_FLOW_COMMAND GF_InterpretSequence(
const GAME_FLOW_LEVEL *const level, GAME_FLOW_SEQUENCE_CONTEXT seq_ctx,
void *const seq_ctx_arg)
{
LOG_DEBUG(
"running sequence for level=%d type=%d seq_ctx=%d", level->num,
level->type, seq_ctx);
// Initialize global variables
g_GF_NoFloor = 0;
g_GF_SunsetEnabled = false;
g_GF_LaraStartAnim = 0;
g_GF_RemoveAmmo = false;
g_GF_RemoveWeapons = false;
g_CineTargetAngle = DEG_90;
g_GF_NumSecrets = 3;
GF_InventoryModifier_Reset();
GAME_FLOW_COMMAND gf_cmd = { .action = GF_EXIT_TO_TITLE };
const GAME_FLOW_SEQUENCE *const sequence = &level->sequence;
for (int32_t i = 0; i < sequence->length; i++) {
const GAME_FLOW_SEQUENCE_EVENT *const event = &sequence->events[i];
LOG_DEBUG(
"event type=%s(%d) data=0x%x",
ENUM_MAP_TO_STRING(GAME_FLOW_SEQUENCE_EVENT_TYPE, event->type),
event->type, event->data);
// TODO: implement cine skipping
// Handle the event
if (event->type < GFS_NUMBER_OF
&& m_EventHandlers[event->type] != NULL) {
gf_cmd = m_EventHandlers[event->type](
level, event, seq_ctx, seq_ctx_arg);
LOG_DEBUG(
"event type=%s(%d) data=0x%x finished, result: action=%s, "
"param=%d",
ENUM_MAP_TO_STRING(GAME_FLOW_SEQUENCE_EVENT_TYPE, event->type),
event->type, event->data,
ENUM_MAP_TO_STRING(GAME_FLOW_ACTION, gf_cmd.action),
gf_cmd.param);
if (gf_cmd.action != GF_NOOP) {
return gf_cmd;
}
}
// Update sequence context if necessary
if (event->type == GFS_PLAY_LEVEL && seq_ctx == GFSC_SAVED) {
seq_ctx = GFSC_NORMAL;
}
}
if (seq_ctx == GFSC_STORY) {
return (GAME_FLOW_COMMAND) { .action = GF_NOOP };
}
LOG_DEBUG(
"sequence finished: action=%s param=%d",
ENUM_MAP_TO_STRING(GAME_FLOW_ACTION, gf_cmd.action), gf_cmd.param);
return gf_cmd;
}
GAME_FLOW_COMMAND GF_DoDemoSequence(int32_t demo_num)
{
demo_num = Demo_ChooseLevel(demo_num);
@ -26,7 +351,7 @@ GAME_FLOW_COMMAND GF_DoDemoSequence(int32_t demo_num)
LOG_ERROR("Missing demo: %d", demo_num);
return (GAME_FLOW_COMMAND) { .action = GF_NOOP };
}
return GF_InterpretSequence(level, GFSC_NORMAL);
return GF_InterpretSequence(level, GFSC_NORMAL, NULL);
}
GAME_FLOW_COMMAND GF_DoCutsceneSequence(const int32_t cutscene_num)
@ -37,7 +362,7 @@ GAME_FLOW_COMMAND GF_DoCutsceneSequence(const int32_t cutscene_num)
LOG_ERROR("Missing cutscene: %d", cutscene_num);
return (GAME_FLOW_COMMAND) { .action = GF_NOOP };
}
return GF_InterpretSequence(level, GFSC_NORMAL);
return GF_InterpretSequence(level, GFSC_NORMAL, NULL);
}
bool GF_DoFrontendSequence(void)
@ -46,7 +371,7 @@ bool GF_DoFrontendSequence(void)
return false;
}
const GAME_FLOW_COMMAND gf_cmd =
GF_InterpretSequence(g_GameFlow.title_level, GFSC_NORMAL);
GF_InterpretSequence(g_GameFlow.title_level, GFSC_NORMAL, NULL);
return gf_cmd.action == GF_EXIT_GAME;
}
@ -57,12 +382,8 @@ GAME_FLOW_COMMAND GF_DoLevelSequence(
const GAME_FLOW_LEVEL *current_level = start_level;
const int32_t level_count = GF_GetLevelCount(current_level->type);
while (true) {
LOG_DEBUG(
"running sequence for level=%d type=%d seq_ctx=%d",
current_level->num, current_level->type, seq_ctx);
const GAME_FLOW_COMMAND gf_cmd =
GF_InterpretSequence(current_level, seq_ctx);
LOG_DEBUG("sequence finished: %d", gf_cmd.action);
GF_InterpretSequence(current_level, seq_ctx, NULL);
if (g_GameFlow.single_level >= 0) {
return gf_cmd;
@ -92,209 +413,3 @@ GAME_FLOW_COMMAND GF_RunLevel(
return GF_RunGame(level, seq_ctx);
}
}
GAME_FLOW_COMMAND GF_InterpretSequence(
const GAME_FLOW_LEVEL *const level, GAME_FLOW_SEQUENCE_CONTEXT seq_ctx)
{
g_GF_NoFloor = 0;
g_GF_SunsetEnabled = false;
g_GF_LaraStartAnim = 0;
g_GF_RemoveAmmo = false;
g_GF_RemoveWeapons = false;
GF_InventoryModifier_Reset();
g_CineTargetAngle = DEG_90;
g_GF_NumSecrets = 3;
int32_t ntracks = 0;
GAME_FLOW_COMMAND gf_cmd = { .action = GF_EXIT_TO_TITLE };
const GAME_FLOW_SEQUENCE *const sequence = &level->sequence;
for (int32_t i = 0; i < sequence->length; i++) {
const GAME_FLOW_SEQUENCE_EVENT *const event = &sequence->events[i];
LOG_DEBUG(
"event type=%s(%d) data=0x%x",
ENUM_MAP_TO_STRING(GAME_FLOW_SEQUENCE_EVENT_TYPE, event->type),
event->type, event->data);
switch (event->type) {
case GFS_EXIT_TO_TITLE:
return (GAME_FLOW_COMMAND) { .action = GF_EXIT_TO_TITLE };
case GFS_DISPLAY_PICTURE: {
const GAME_FLOW_DISPLAY_PICTURE_DATA *const data =
(GAME_FLOW_DISPLAY_PICTURE_DATA *)event->data;
PHASE *const phase = Phase_Picture_Create((PHASE_PICTURE_ARGS) {
.file_name = data->path,
.display_time = data->display_time,
.fade_in_time = data->fade_in_time,
.fade_out_time = data->fade_out_time,
.display_time_includes_fades = true,
});
PhaseExecutor_Run(phase);
Phase_Picture_Destroy(phase);
break;
}
case GFS_PLAY_LEVEL: {
if (seq_ctx != GFSC_STORY) {
gf_cmd = GF_RunLevel(level, seq_ctx);
if (seq_ctx == GFSC_SAVED) {
seq_ctx = GFSC_NORMAL;
}
if (gf_cmd.action != GF_NOOP
&& gf_cmd.action != GF_LEVEL_COMPLETE) {
return gf_cmd;
}
}
break;
}
case GFS_PLAY_CUTSCENE: {
const int16_t cutscene_num = (int16_t)(intptr_t)event->data;
if (seq_ctx != GFSC_SAVED) {
gf_cmd = GF_DoCutsceneSequence(cutscene_num);
if (gf_cmd.action != GF_NOOP
&& gf_cmd.action != GF_LEVEL_COMPLETE) {
return gf_cmd;
}
}
break;
}
case GFS_PLAY_MUSIC:
Music_Play((int32_t)(intptr_t)event->data, MPM_ALWAYS);
break;
case GFS_PLAY_FMV: {
const int16_t fmv_id = (int16_t)(intptr_t)event->data;
if (seq_ctx != GFSC_SAVED) {
if (fmv_id < 0 || fmv_id >= g_GameFlow.fmv_count) {
LOG_ERROR("Invalid FMV number: %d", fmv_id);
} else {
FMV_Play(g_GameFlow.fmvs[fmv_id].path);
}
}
break;
}
case GFS_LEVEL_STATS: {
if (seq_ctx != GFSC_NORMAL) {
break;
}
const GAME_FLOW_LEVEL *const current_level = Game_GetCurrentLevel();
PHASE *const stats_phase = Phase_Stats_Create((PHASE_STATS_ARGS) {
.background_type = Game_IsInGym() ? BK_TRANSPARENT : BK_OBJECT,
.level_num = current_level->num,
.show_final_stats = false,
.use_bare_style = false,
});
gf_cmd = PhaseExecutor_Run(stats_phase);
Phase_Stats_Destroy(stats_phase);
break;
}
case GFS_LEVEL_COMPLETE: {
if (seq_ctx != GFSC_NORMAL) {
break;
}
const GAME_FLOW_LEVEL *const current_level = Game_GetCurrentLevel();
START_INFO *const start = GF_GetResumeInfo(current_level);
start->stats = g_SaveGame.current_stats;
start->available = 0;
const GAME_FLOW_LEVEL *const next_level =
GF_GetLevel(current_level->num + 1, current_level->type);
if (next_level != NULL) {
CreateStartInfo(next_level);
g_SaveGame.current_level = next_level->num;
}
if (next_level == NULL || gf_cmd.action != GF_NOOP) {
return gf_cmd;
}
gf_cmd = (GAME_FLOW_COMMAND) {
.action = GF_START_GAME,
.param = next_level->num,
};
break;
}
case GFS_ENABLE_SUNSET:
if (seq_ctx != GFSC_STORY) {
g_GF_SunsetEnabled = true;
}
break;
case GFS_TOTAL_STATS:
if (seq_ctx == GFSC_NORMAL) {
const GAME_FLOW_LEVEL *const current_level =
Game_GetCurrentLevel();
START_INFO *const start = GF_GetResumeInfo(current_level);
start->stats = g_SaveGame.current_stats;
g_SaveGame.bonus_flag = true;
PHASE *const phase = Phase_Stats_Create((PHASE_STATS_ARGS) {
.background_type = BK_IMAGE,
.background_path = "data/end.pcx",
.show_final_stats = true,
.use_bare_style = false,
});
gf_cmd = PhaseExecutor_Run(phase);
Phase_Stats_Destroy(phase);
} else {
gf_cmd = (GAME_FLOW_COMMAND) { .action = GF_EXIT_TO_TITLE };
}
break;
case GFS_SET_CAMERA_ANGLE:
if (seq_ctx != GFSC_SAVED) {
g_CineTargetAngle = (int16_t)(intptr_t)event->data;
}
break;
case GFS_DISABLE_FLOOR:
if (seq_ctx != GFSC_STORY) {
g_GF_NoFloor = (int16_t)(intptr_t)event->data;
}
break;
case GFS_ADD_ITEM:
case GFS_ADD_SECRET_REWARD:
const GAME_FLOW_ADD_ITEM_DATA *const data =
(GAME_FLOW_ADD_ITEM_DATA *)event->data;
if (seq_ctx != GFSC_STORY) {
GF_InventoryModifier_Add(
data->object_id, data->inv_type, data->qty);
}
break;
case GFS_REMOVE_WEAPONS:
if (seq_ctx != GFSC_STORY && seq_ctx != GFSC_SAVED) {
g_GF_RemoveWeapons = true;
}
break;
case GFS_REMOVE_AMMO:
if (seq_ctx != GFSC_STORY && seq_ctx != GFSC_SAVED) {
g_GF_RemoveAmmo = true;
}
break;
case GFS_SET_START_ANIM:
if (seq_ctx != GFSC_STORY) {
g_GF_LaraStartAnim = (int16_t)(intptr_t)event->data;
}
break;
case GFS_SET_NUM_SECRETS:
if (seq_ctx != GFSC_STORY) {
g_GF_NumSecrets = (int16_t)(intptr_t)event->data;
}
break;
}
}
if (seq_ctx == GFSC_STORY) {
return (GAME_FLOW_COMMAND) { .action = GF_NOOP };
}
return gf_cmd;
}

View file

@ -5,10 +5,13 @@
#include <libtrx/game/game_flow/sequencer.h>
GAME_FLOW_COMMAND GF_InterpretSequence(
const GAME_FLOW_LEVEL *level, GAME_FLOW_SEQUENCE_CONTEXT seq_ctx);
const GAME_FLOW_LEVEL *level, GAME_FLOW_SEQUENCE_CONTEXT seq_ctx,
void *user_arg);
bool GF_DoFrontendSequence(void);
GAME_FLOW_COMMAND GF_DoDemoSequence(int32_t demo_num);
GAME_FLOW_COMMAND GF_DoCutsceneSequence(int32_t cutscene_num);
GAME_FLOW_COMMAND GF_DoLevelSequence(
const GAME_FLOW_LEVEL *start_level, GAME_FLOW_SEQUENCE_CONTEXT seq_ctx);
GAME_FLOW_COMMAND GF_RunLevel(
const GAME_FLOW_LEVEL *level, GAME_FLOW_SEQUENCE_CONTEXT seq_ctx);

View file

@ -373,7 +373,9 @@ void Shell_Main(void)
GAME_FLOW_COMMAND gf_cmd = g_GameFlow.cmd_init;
bool is_loop_continued = true;
while (is_loop_continued) {
LOG_DEBUG("action=%d param=%x", gf_cmd.action, gf_cmd.param);
LOG_DEBUG(
"action=%s param=%d",
ENUM_MAP_TO_STRING(GAME_FLOW_ACTION, gf_cmd.action), gf_cmd.param);
switch (gf_cmd.action) {
case GF_START_GAME:
case GF_SELECT_GAME: