diff --git a/src/libtrx/game/game_flow/sequencer.c b/src/libtrx/game/game_flow/sequencer.c index d6948a7b7..66862edef 100644 --- a/src/libtrx/game/game_flow/sequencer.c +++ b/src/libtrx/game/game_flow/sequencer.c @@ -1,65 +1,56 @@ #include "game/game_flow/sequencer.h" -#include "config.h" -#include "game/inventory_ring/control.h" -#include "game/objects/vars.h" -#include "game/phase.h" +#include "debug.h" +#include "enum_map.h" +#include "game/game_flow/sequencer_priv.h" -GF_COMMAND GF_EnterPhotoMode(void) +GF_COMMAND GF_InterpretSequence( + const GF_LEVEL *const level, GF_SEQUENCE_CONTEXT seq_ctx, + void *const seq_ctx_arg) { - PHASE *const subphase = Phase_PhotoMode_Create(); - const GF_COMMAND gf_cmd = PhaseExecutor_Run(subphase); - Phase_PhotoMode_Destroy(subphase); - return gf_cmd; -} - -GF_COMMAND GF_PauseGame(void) -{ - PHASE *const subphase = Phase_Pause_Create(); - const GF_COMMAND gf_cmd = PhaseExecutor_Run(subphase); - Phase_Pause_Destroy(subphase); - return gf_cmd; -} - -GF_COMMAND GF_ShowInventory(const INVENTORY_MODE mode) -{ - PHASE *const phase = Phase_Inventory_Create(mode); - const GF_COMMAND gf_cmd = PhaseExecutor_Run(phase); - Phase_Inventory_Destroy(phase); - return gf_cmd; -} - -GF_COMMAND GF_ShowInventoryKeys(const GAME_OBJECT_ID receptacle_type_id) -{ - if (g_Config.gameplay.enable_auto_item_selection) { - const GAME_OBJECT_ID object_id = Object_GetCognateInverse( - receptacle_type_id, g_KeyItemToReceptacleMap); - InvRing_SetRequestedObjectID(object_id); + ASSERT(level != nullptr); + LOG_DEBUG( + "running sequence for level=%d type=%d seq_ctx=%d", level->num, + level->type, seq_ctx); + + GF_PreSequenceHook(); + + GF_COMMAND gf_cmd = { .action = GF_EXIT_TO_TITLE }; + + const GF_SEQUENCE *const sequence = &level->sequence; + for (int32_t i = 0; i < sequence->length; i++) { + const GF_SEQUENCE_EVENT *const event = &sequence->events[i]; + LOG_DEBUG( + "event type=%s(%d) data=0x%x", + ENUM_MAP_TO_STRING(GF_SEQUENCE_EVENT_TYPE, event->type), + event->type, event->data); + + if (GF_ShouldSkipSequenceEvent(level, event)) { + continue; + } + + // Handle the event + if (event->type < GFS_NUMBER_OF + && GF_GetSequenceEventHandler(event->type) != nullptr) { + gf_cmd = GF_GetSequenceEventHandler(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(GF_SEQUENCE_EVENT_TYPE, event->type), + event->type, event->data, + ENUM_MAP_TO_STRING(GF_ACTION, gf_cmd.action), gf_cmd.param); + if (gf_cmd.action != GF_NOOP) { + return gf_cmd; + } + } + + // Update sequence context if necessary + seq_ctx = GF_SwitchSequenceContext(event, seq_ctx); } - return GF_ShowInventory(INV_KEYS_MODE); -} - -GF_COMMAND GF_RunDemo(const int32_t demo_num) -{ - PHASE *const demo_phase = Phase_Demo_Create(demo_num); - const GF_COMMAND gf_cmd = PhaseExecutor_Run(demo_phase); - Phase_Demo_Destroy(demo_phase); - return gf_cmd; -} -GF_COMMAND GF_RunCutscene(const int32_t cutscene_num) -{ - PHASE *const cutscene_phase = Phase_Cutscene_Create(cutscene_num); - const GF_COMMAND gf_cmd = PhaseExecutor_Run(cutscene_phase); - Phase_Cutscene_Destroy(cutscene_phase); - return gf_cmd; -} - -GF_COMMAND GF_RunGame( - const GF_LEVEL *const level, const GF_SEQUENCE_CONTEXT seq_ctx) -{ - PHASE *const phase = Phase_Game_Create(level, seq_ctx); - const GF_COMMAND gf_cmd = PhaseExecutor_Run(phase); - Phase_Game_Destroy(phase); + LOG_DEBUG( + "sequence finished: action=%s param=%d", + ENUM_MAP_TO_STRING(GF_ACTION, gf_cmd.action), gf_cmd.param); return gf_cmd; } diff --git a/src/libtrx/game/game_flow/sequencer_misc.c b/src/libtrx/game/game_flow/sequencer_misc.c new file mode 100644 index 000000000..612703b43 --- /dev/null +++ b/src/libtrx/game/game_flow/sequencer_misc.c @@ -0,0 +1,100 @@ +#include "config.h" +#include "game/demo.h" +#include "game/game_flow/common.h" +#include "game/game_flow/sequencer.h" +#include "game/game_flow/vars.h" +#include "game/inventory_ring/control.h" +#include "game/objects/vars.h" +#include "game/phase.h" +#include "log.h" + +GF_COMMAND GF_EnterPhotoMode(void) +{ + PHASE *const subphase = Phase_PhotoMode_Create(); + const GF_COMMAND gf_cmd = PhaseExecutor_Run(subphase); + Phase_PhotoMode_Destroy(subphase); + return gf_cmd; +} + +GF_COMMAND GF_PauseGame(void) +{ + PHASE *const subphase = Phase_Pause_Create(); + const GF_COMMAND gf_cmd = PhaseExecutor_Run(subphase); + Phase_Pause_Destroy(subphase); + return gf_cmd; +} + +GF_COMMAND GF_ShowInventory(const INVENTORY_MODE mode) +{ + PHASE *const phase = Phase_Inventory_Create(mode); + const GF_COMMAND gf_cmd = PhaseExecutor_Run(phase); + Phase_Inventory_Destroy(phase); + return gf_cmd; +} + +GF_COMMAND GF_ShowInventoryKeys(const GAME_OBJECT_ID receptacle_type_id) +{ + if (g_Config.gameplay.enable_auto_item_selection) { + const GAME_OBJECT_ID object_id = Object_GetCognateInverse( + receptacle_type_id, g_KeyItemToReceptacleMap); + InvRing_SetRequestedObjectID(object_id); + } + return GF_ShowInventory(INV_KEYS_MODE); +} + +GF_COMMAND GF_RunDemo(const int32_t demo_num) +{ + PHASE *const demo_phase = Phase_Demo_Create(demo_num); + const GF_COMMAND gf_cmd = PhaseExecutor_Run(demo_phase); + Phase_Demo_Destroy(demo_phase); + return gf_cmd; +} + +GF_COMMAND GF_RunCutscene(const int32_t cutscene_num) +{ + PHASE *const cutscene_phase = Phase_Cutscene_Create(cutscene_num); + const GF_COMMAND gf_cmd = PhaseExecutor_Run(cutscene_phase); + Phase_Cutscene_Destroy(cutscene_phase); + return gf_cmd; +} + +GF_COMMAND GF_RunGame( + const GF_LEVEL *const level, const GF_SEQUENCE_CONTEXT seq_ctx) +{ + PHASE *const phase = Phase_Game_Create(level, seq_ctx); + const GF_COMMAND gf_cmd = PhaseExecutor_Run(phase); + Phase_Game_Destroy(phase); + return gf_cmd; +} + +GF_COMMAND GF_DoFrontendSequence(void) +{ + if (g_GameFlow.title_level == nullptr) { + return (GF_COMMAND) { .action = GF_NOOP }; + } + return GF_InterpretSequence(g_GameFlow.title_level, GFSC_NORMAL, nullptr); +} + +GF_COMMAND GF_DoDemoSequence(int32_t demo_num) +{ + demo_num = Demo_ChooseLevel(demo_num); + if (demo_num < 0) { + return (GF_COMMAND) { .action = GF_NOOP }; + } + const GF_LEVEL *const level = GF_GetLevel(GFLT_DEMOS, demo_num); + if (level == nullptr) { + LOG_ERROR("Missing demo: %d", demo_num); + return (GF_COMMAND) { .action = GF_NOOP }; + } + return GF_InterpretSequence(level, GFSC_NORMAL, nullptr); +} + +GF_COMMAND GF_DoCutsceneSequence(const int32_t cutscene_num) +{ + const GF_LEVEL *const level = GF_GetLevel(GFLT_CUTSCENES, cutscene_num); + if (level == nullptr) { + LOG_ERROR("Missing cutscene: %d", cutscene_num); + return (GF_COMMAND) { .action = GF_NOOP }; + } + return GF_InterpretSequence(level, GFSC_NORMAL, nullptr); +} diff --git a/src/libtrx/game/game_flow/sequencer_priv.h b/src/libtrx/game/game_flow/sequencer_priv.h new file mode 100644 index 000000000..e123163fe --- /dev/null +++ b/src/libtrx/game/game_flow/sequencer_priv.h @@ -0,0 +1,10 @@ +#pragma once + +extern void GF_PreSequenceHook(void); +extern GF_SEQUENCE_CONTEXT GF_SwitchSequenceContext( + const GF_SEQUENCE_EVENT *event, GF_SEQUENCE_CONTEXT seq_ctx); +extern bool GF_ShouldSkipSequenceEvent( + const GF_LEVEL *level, const GF_SEQUENCE_EVENT *event); +extern GF_COMMAND ( + *GF_GetSequenceEventHandler(GF_SEQUENCE_EVENT_TYPE event_type))( + const GF_LEVEL *, const GF_SEQUENCE_EVENT *, GF_SEQUENCE_CONTEXT, void *); diff --git a/src/libtrx/include/libtrx/game/enum_map.def b/src/libtrx/include/libtrx/game/enum_map.def index d985fbc15..c1a699ad1 100644 --- a/src/libtrx/include/libtrx/game/enum_map.def +++ b/src/libtrx/include/libtrx/game/enum_map.def @@ -56,3 +56,63 @@ ENUM_MAP_DEFINE(UNDERWATER_MUSIC_MODE, UMM_QUIET, "quiet") ENUM_MAP_DEFINE(UNDERWATER_MUSIC_MODE, UMM_FULL_NO_AMBIENT, "full_no_ambient") ENUM_MAP_DEFINE(UNDERWATER_MUSIC_MODE, UMM_QUIET_NO_AMBIENT, "quiet_no_ambient") ENUM_MAP_DEFINE(UNDERWATER_MUSIC_MODE, UMM_NONE, "none") + +#if TR_VERSION == 1 +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_LOAD_LEVEL, "load_level") +#endif +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_PLAY_LEVEL, "play_level") +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_PLAY_FMV, "play_fmv") +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_PLAY_CUTSCENE, "play_cutscene") +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_PLAY_MUSIC, "play_music") +#if TR_VERSION == 1 +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_LOADING_SCREEN, "loading_screen") +#endif +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_DISPLAY_PICTURE, "display_picture") +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_LEVEL_STATS, "level_stats") +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_TOTAL_STATS, "total_stats") +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_EXIT_TO_TITLE, "exit_to_title") +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_LEVEL_COMPLETE, "level_complete") +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_SET_CAMERA_ANGLE, "set_cutscene_angle") +#if TR_VERSION == 1 +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_FLIP_MAP, "flip_map") +#endif +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_ADD_ITEM, "give_item") +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_REMOVE_WEAPONS, "remove_weapons") +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_REMOVE_AMMO, "remove_ammo") +#if TR_VERSION == 1 +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_REMOVE_SCIONS, "remove_scions") +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_REMOVE_MEDIPACKS, "remove_medipacks") +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_MESH_SWAP, "mesh_swap") +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_SETUP_BACON_LARA, "setup_bacon_lara") +#elif TR_VERSION == 2 +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_ENABLE_SUNSET, "enable_sunset") +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_SET_NUM_SECRETS, "set_secret_count") +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_SET_START_ANIM, "set_lara_start_anim") +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_DISABLE_FLOOR, "disable_floor") +ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_ADD_SECRET_REWARD, "add_secret_reward") +#endif + +ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_TITLE, "title") +ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_NORMAL, "normal") +ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_CUTSCENE, "cutscene") +ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_GYM, "gym") +#if TR_VERSION == 1 +ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_BONUS, "bonus") +ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_DUMMY, "dummy") +ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_CURRENT, "current") +#endif + +ENUM_MAP_DEFINE(GF_ACTION, GF_NOOP, "noop") +ENUM_MAP_DEFINE(GF_ACTION, GF_START_GAME, "play_level") +ENUM_MAP_DEFINE(GF_ACTION, GF_START_SAVED_GAME, "load_saved_game") +ENUM_MAP_DEFINE(GF_ACTION, GF_START_CINE, "play_cutscene") +ENUM_MAP_DEFINE(GF_ACTION, GF_START_DEMO, "play_demo") +ENUM_MAP_DEFINE(GF_ACTION, GF_START_FMV, "play_fmv") +ENUM_MAP_DEFINE(GF_ACTION, GF_EXIT_TO_TITLE, "exit_to_title") +ENUM_MAP_DEFINE(GF_ACTION, GF_LEVEL_COMPLETE, "level_complete") +ENUM_MAP_DEFINE(GF_ACTION, GF_EXIT_GAME, "exit_game") +ENUM_MAP_DEFINE(GF_ACTION, GF_SELECT_GAME, "select_level") +#if TR_VERSION == 1 +ENUM_MAP_DEFINE(GF_ACTION, GF_RESTART_GAME, "restart_level") +ENUM_MAP_DEFINE(GF_ACTION, GF_STORY_SO_FAR, "story_so_far") +#endif diff --git a/src/libtrx/include/libtrx/game/game_flow/sequencer.h b/src/libtrx/include/libtrx/game/game_flow/sequencer.h index 57c4bca9e..96dc7753f 100644 --- a/src/libtrx/include/libtrx/game/game_flow/sequencer.h +++ b/src/libtrx/include/libtrx/game/game_flow/sequencer.h @@ -10,3 +10,11 @@ GF_COMMAND GF_ShowInventoryKeys(GAME_OBJECT_ID receptacle_type_id); GF_COMMAND GF_RunDemo(int32_t demo_num); GF_COMMAND GF_RunCutscene(int32_t cutscene_num); GF_COMMAND GF_RunGame(const GF_LEVEL *level, GF_SEQUENCE_CONTEXT seq_ctx); + +GF_COMMAND GF_DoFrontendSequence(void); +GF_COMMAND GF_TitleSequence(void); +GF_COMMAND GF_DoDemoSequence(int32_t demo_num); +GF_COMMAND GF_DoCutsceneSequence(int32_t cutscene_num); + +extern GF_COMMAND GF_InterpretSequence( + const GF_LEVEL *level, GF_SEQUENCE_CONTEXT seq_ctx, void *seq_ctx_arg); diff --git a/src/libtrx/meson.build b/src/libtrx/meson.build index df45ea2d7..afe6c4c67 100644 --- a/src/libtrx/meson.build +++ b/src/libtrx/meson.build @@ -113,6 +113,7 @@ sources = [ 'game/game_flow/common.c', 'game/game_flow/reader.c', 'game/game_flow/sequencer.c', + 'game/game_flow/sequencer_misc.c', 'game/game_flow/vars.c', 'game/game_string.c', 'game/game_string_table/common.c', diff --git a/src/tr1/game/game_flow/sequencer.c b/src/tr1/game/game_flow/sequencer.c index df3a2051a..7eb7b50a6 100644 --- a/src/tr1/game/game_flow/sequencer.c +++ b/src/tr1/game/game_flow/sequencer.c @@ -12,8 +12,6 @@ #include "global/vars.h" #include -#include -#include #include #include @@ -324,77 +322,51 @@ static DECLARE_EVENT_HANDLER(M_HandleSetupBaconLara) return (GF_COMMAND) { .action = GF_NOOP }; } -GF_COMMAND GF_InterpretSequence( - const GF_LEVEL *const level, GF_SEQUENCE_CONTEXT seq_ctx, - void *const seq_ctx_arg) +void GF_PreSequenceHook(void) { - ASSERT(level != NULL); - LOG_DEBUG( - "running sequence for level=%d type=%d seq_ctx=%d", level->num, - level->type, seq_ctx); - g_GameInfo.remove_guns = false; g_GameInfo.remove_scions = false; g_GameInfo.remove_ammo = false; g_GameInfo.remove_medipacks = false; +} - GF_COMMAND gf_cmd = { .action = GF_EXIT_TO_TITLE }; - - const GF_SEQUENCE *const sequence = &level->sequence; - for (int32_t i = 0; i < sequence->length; i++) { - const GF_SEQUENCE_EVENT *const event = &sequence->events[i]; - LOG_DEBUG( - "event type=%s(%d) data=0x%x", - ENUM_MAP_TO_STRING(GF_SEQUENCE_EVENT_TYPE, event->type), - event->type, event->data); - - // Skip cinematic levels - if (!g_Config.gameplay.enable_cine && level->type == GFL_CUTSCENE) { - bool skip; - switch (event->type) { - case GFS_EXIT_TO_TITLE: - case GFS_LEVEL_COMPLETE: - case GFS_PLAY_FMV: - case GFS_LEVEL_STATS: - case GFS_TOTAL_STATS: - skip = false; - break; - default: - skip = true; - break; - } - if (skip) { - continue; - } - } - - // 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(GF_SEQUENCE_EVENT_TYPE, event->type), - event->type, event->data, - ENUM_MAP_TO_STRING(GF_ACTION, gf_cmd.action), gf_cmd.param); - if (gf_cmd.action != GF_NOOP) { - return gf_cmd; - } - } +GF_SEQUENCE_CONTEXT GF_SwitchSequenceContext( + const GF_SEQUENCE_EVENT *const event, const GF_SEQUENCE_CONTEXT seq_ctx) +{ + if (event->type != GFS_LOAD_LEVEL && event->type != GFS_PLAY_LEVEL) { + return seq_ctx; + } + switch (seq_ctx) { + case GFSC_SAVED: + case GFSC_RESTART: + case GFSC_SELECT: + return GFSC_NORMAL; + default: + return seq_ctx; + } +} - // Update sequence context if necessary - if (event->type == GFS_LOAD_LEVEL || event->type == GFS_PLAY_LEVEL) { - if (seq_ctx == GFSC_SAVED || seq_ctx == GFSC_RESTART - || seq_ctx == GFSC_SELECT) { - seq_ctx = GFSC_NORMAL; - } +bool GF_ShouldSkipSequenceEvent( + const GF_LEVEL *const level, const GF_SEQUENCE_EVENT *const event) +{ + // Skip cinematic levels + if (!g_Config.gameplay.enable_cine && level->type == GFL_CUTSCENE) { + switch (event->type) { + case GFS_EXIT_TO_TITLE: + case GFS_LEVEL_COMPLETE: + case GFS_PLAY_FMV: + case GFS_LEVEL_STATS: + case GFS_TOTAL_STATS: + return false; + default: + return true; } } + return false; +} - LOG_DEBUG( - "sequence finished: action=%s param=%d", - ENUM_MAP_TO_STRING(GF_ACTION, gf_cmd.action), gf_cmd.param); - return gf_cmd; +GF_COMMAND (*GF_GetSequenceEventHandler(GF_SEQUENCE_EVENT_TYPE event_type))( + const GF_LEVEL *, const GF_SEQUENCE_EVENT *, GF_SEQUENCE_CONTEXT, void *) +{ + return m_EventHandlers[event_type]; } diff --git a/src/tr1/game/game_flow/sequencer.h b/src/tr1/game/game_flow/sequencer.h index 1ff112609..4b5de20a4 100644 --- a/src/tr1/game/game_flow/sequencer.h +++ b/src/tr1/game/game_flow/sequencer.h @@ -1,13 +1,5 @@ #pragma once -#include "./types.h" - #include -GF_COMMAND -GF_InterpretSequence( - const GF_LEVEL *level, GF_SEQUENCE_CONTEXT seq_ctx, void *seq_ctx_arg); - -GF_COMMAND GF_DoDemoSequence(int32_t demo_num); -GF_COMMAND GF_DoCutsceneSequence(int32_t cutscene_num); GF_COMMAND GF_PlayAvailableStory(int32_t slot_num); diff --git a/src/tr1/game/game_flow/sequencer_misc.c b/src/tr1/game/game_flow/sequencer_misc.c index 4ab9388cf..fae7403d6 100644 --- a/src/tr1/game/game_flow/sequencer_misc.c +++ b/src/tr1/game/game_flow/sequencer_misc.c @@ -2,10 +2,22 @@ #include "game/game_flow/common.h" #include "game/game_flow/sequencer.h" #include "game/game_flow/vars.h" +#include "game/level.h" #include "game/savegame.h" +#include #include +GF_COMMAND GF_TitleSequence(void) +{ + GameStringTable_Apply(nullptr); + const GF_LEVEL *const title_level = GF_GetTitleLevel(); + if (!Level_Initialise(title_level)) { + return (GF_COMMAND) { .action = GF_EXIT_GAME }; + } + return GF_ShowInventory(INV_TITLE_MODE); +} + GF_COMMAND GF_PlayAvailableStory(const int32_t slot_num) { const int32_t savegame_level = Savegame_GetLevelNumber(slot_num); @@ -24,27 +36,3 @@ GF_COMMAND GF_PlayAvailableStory(const int32_t slot_num) } return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; } - -GF_COMMAND GF_DoCutsceneSequence(const int32_t cutscene_num) -{ - const GF_LEVEL *const level = GF_GetLevel(GFLT_CUTSCENES, cutscene_num); - if (level == NULL) { - LOG_ERROR("Missing cutscene: %d", cutscene_num); - return (GF_COMMAND) { .action = GF_NOOP }; - } - return GF_InterpretSequence(level, GFSC_NORMAL, NULL); -} - -GF_COMMAND GF_DoDemoSequence(int32_t demo_num) -{ - demo_num = Demo_ChooseLevel(demo_num); - if (demo_num < 0) { - return (GF_COMMAND) { .action = GF_NOOP }; - } - const GF_LEVEL *const level = GF_GetLevel(GFLT_DEMOS, demo_num); - if (level == NULL) { - LOG_ERROR("Missing cutscene: %d", demo_num); - return (GF_COMMAND) { .action = GF_NOOP }; - } - return GF_InterpretSequence(level, GFSC_NORMAL, NULL); -} diff --git a/src/tr1/game/level.h b/src/tr1/game/level.h index af3dd4302..fe62be257 100644 --- a/src/tr1/game/level.h +++ b/src/tr1/game/level.h @@ -2,5 +2,5 @@ #include "game/game_flow/types.h" -void Level_Load(const GF_LEVEL *level); bool Level_Initialise(const GF_LEVEL *level); +void Level_Load(const GF_LEVEL *level); diff --git a/src/tr1/game/shell.c b/src/tr1/game/shell.c index 6412eaee3..28898fb5e 100644 --- a/src/tr1/game/shell.c +++ b/src/tr1/game/shell.c @@ -21,12 +21,12 @@ #include "specific/s_shell.h" #include +#include #include #include #include #include #include -#include #include #include @@ -192,9 +192,8 @@ void Shell_Main(void) g_GameInfo.current_save_slot = -1; - GF_COMMAND gf_cmd = { .action = GF_EXIT_TO_TITLE }; - bool intro_played = false; - bool loop_continue = true; + GF_COMMAND gf_cmd = GF_DoFrontendSequence(); + bool loop_continue = !Shell_IsExiting(); while (loop_continue) { LOG_INFO( "action=%s param=%d", ENUM_MAP_TO_STRING(GF_ACTION, gf_cmd.action), @@ -210,6 +209,12 @@ void Shell_Main(void) break; } + case GF_SELECT_GAME: { + const GF_LEVEL *const level = GF_GetLevel(GFLT_MAIN, gf_cmd.param); + gf_cmd = GF_InterpretSequence(level, GFSC_SELECT, NULL); + break; + } + case GF_START_SAVED_GAME: { const int16_t slot_num = gf_cmd.param; const int16_t level_num = Savegame_GetLevelNumber(slot_num); @@ -230,12 +235,6 @@ void Shell_Main(void) break; } - case GF_SELECT_GAME: { - const GF_LEVEL *const level = GF_GetLevel(GFLT_MAIN, gf_cmd.param); - gf_cmd = GF_InterpretSequence(level, GFSC_SELECT, NULL); - break; - } - case GF_STORY_SO_FAR: gf_cmd = GF_PlayAvailableStory(gf_cmd.param); break; @@ -252,31 +251,20 @@ void Shell_Main(void) gf_cmd = (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; break; - case GF_EXIT_TO_TITLE: { - const GF_LEVEL *const level = GF_GetTitleLevel(); - g_GameInfo.current_save_slot = -1; - if (!intro_played) { - GF_InterpretSequence(level, GFSC_NORMAL, NULL); - intro_played = true; - } - - if (!Level_Initialise(level)) { - gf_cmd = (GF_COMMAND) { .action = GF_EXIT_GAME }; - break; + case GF_EXIT_TO_TITLE: + if (g_GameFlow.title_level == NULL) { + Shell_ExitSystem("Title disabled"); + } else { + gf_cmd = GF_TitleSequence(); } - - gf_cmd = GF_ShowInventory(INV_TITLE_MODE); break; - } case GF_EXIT_GAME: loop_continue = false; break; default: - Shell_ExitSystemFmt( - "MAIN: Unknown action %x %d", gf_cmd.action, gf_cmd.param); - return; + ASSERT_FAIL(); } } diff --git a/src/tr1/global/enum_map.def b/src/tr1/global/enum_map.def index a3273887a..23cecc0e2 100644 --- a/src/tr1/global/enum_map.def +++ b/src/tr1/global/enum_map.def @@ -33,45 +33,3 @@ ENUM_MAP_DEFINE(TARGET_LOCK_MODE, TLM_NONE, "no-lock") ENUM_MAP_DEFINE(MUSIC_LOAD_CONDITION, MUSIC_LOAD_NEVER, "never") ENUM_MAP_DEFINE(MUSIC_LOAD_CONDITION, MUSIC_LOAD_NON_AMBIENT, "non-ambient") ENUM_MAP_DEFINE(MUSIC_LOAD_CONDITION, MUSIC_LOAD_ALWAYS, "always") - -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_LOAD_LEVEL, "load_level") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_PLAY_LEVEL, "play_level") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_PLAY_FMV, "play_fmv") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_PLAY_CUTSCENE, "play_cutscene") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_PLAY_MUSIC, "play_music") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_LOADING_SCREEN, "loading_screen") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_DISPLAY_PICTURE, "display_picture") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_LEVEL_STATS, "level_stats") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_TOTAL_STATS, "total_stats") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_EXIT_TO_TITLE, "exit_to_title") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_LEVEL_COMPLETE, "level_complete") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_SET_CAMERA_ANGLE, "set_cutscene_angle") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_FLIP_MAP, "flip_map") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_REMOVE_WEAPONS, "remove_weapons") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_REMOVE_SCIONS, "remove_scions") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_REMOVE_AMMO, "remove_ammo") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_REMOVE_MEDIPACKS, "remove_medipacks") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_ADD_ITEM, "give_item") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_MESH_SWAP, "mesh_swap") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_SETUP_BACON_LARA, "setup_bacon_lara") - -ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_TITLE, "title") -ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_NORMAL, "normal") -ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_CUTSCENE, "cutscene") -ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_GYM, "gym") -ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_BONUS, "bonus") -ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_DUMMY, "dummy") -ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_CURRENT, "current") - -ENUM_MAP_DEFINE(GF_ACTION, GF_NOOP, "noop") -ENUM_MAP_DEFINE(GF_ACTION, GF_START_GAME, "play_level") -ENUM_MAP_DEFINE(GF_ACTION, GF_START_SAVED_GAME, "load_saved_game") -ENUM_MAP_DEFINE(GF_ACTION, GF_START_CINE, "play_cutscene") -ENUM_MAP_DEFINE(GF_ACTION, GF_START_DEMO, "play_demo") -ENUM_MAP_DEFINE(GF_ACTION, GF_START_FMV, "play_fmv") -ENUM_MAP_DEFINE(GF_ACTION, GF_EXIT_TO_TITLE, "exit_to_title") -ENUM_MAP_DEFINE(GF_ACTION, GF_LEVEL_COMPLETE, "level_complete") -ENUM_MAP_DEFINE(GF_ACTION, GF_EXIT_GAME, "exit_game") -ENUM_MAP_DEFINE(GF_ACTION, GF_RESTART_GAME, "restart_level") -ENUM_MAP_DEFINE(GF_ACTION, GF_SELECT_GAME, "select_level") -ENUM_MAP_DEFINE(GF_ACTION, GF_STORY_SO_FAR, "story_so_far") diff --git a/src/tr2/decomp/decomp.c b/src/tr2/decomp/decomp.c index 9d56c4f71..ae929cc9a 100644 --- a/src/tr2/decomp/decomp.c +++ b/src/tr2/decomp/decomp.c @@ -33,17 +33,6 @@ typedef enum { static CAMERA_INFO m_LocalCamera = {}; -GF_COMMAND TitleSequence(void) -{ - GameStringTable_Apply(NULL); - const GF_LEVEL *const title_level = GF_GetTitleLevel(); - if (!Level_Initialise(title_level, GFSC_NORMAL)) { - return (GF_COMMAND) { .action = GF_EXIT_GAME }; - } - - return GF_ShowInventory(INV_TITLE_MODE); -} - void CutscenePlayer_Control(const int16_t item_num) { ITEM *const item = &g_Items[item_num]; diff --git a/src/tr2/decomp/decomp.h b/src/tr2/decomp/decomp.h index 50e276228..80e85fcc9 100644 --- a/src/tr2/decomp/decomp.h +++ b/src/tr2/decomp/decomp.h @@ -9,7 +9,6 @@ // they'll need to be properly modularized. The same applies to all files // within the decomp/ directory which are scheduled for extensive refactoring. -GF_COMMAND TitleSequence(void); void Game_SetCutsceneTrack(int32_t track); void CutscenePlayer_Control(int16_t item_num); void Lara_Control_Cutscene(int16_t item_num); diff --git a/src/tr2/game/game_flow/sequencer.c b/src/tr2/game/game_flow/sequencer.c index d6bd6efcc..247edde82 100644 --- a/src/tr2/game/game_flow/sequencer.c +++ b/src/tr2/game/game_flow/sequencer.c @@ -1,19 +1,13 @@ #include "game/game_flow/sequencer.h" -#include "decomp/decomp.h" #include "decomp/savegame.h" #include "game/fmv.h" #include "game/game.h" #include "game/game_flow.h" #include "game/music.h" #include "game/phase.h" -#include "game/shell.h" #include "global/vars.h" -#include -#include -#include -#include #include #define DECLARE_EVENT_HANDLER(name) \ @@ -282,16 +276,8 @@ static DECLARE_EVENT_HANDLER(M_HandleSetNumSecrets) return gf_cmd; } -GF_COMMAND GF_InterpretSequence( - const GF_LEVEL *const level, GF_SEQUENCE_CONTEXT seq_ctx, - void *const seq_ctx_arg) +void GF_PreSequenceHook(void) { - ASSERT(level != NULL); - 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; @@ -300,47 +286,26 @@ GF_COMMAND GF_InterpretSequence( g_CineTargetAngle = DEG_90; g_GF_NumSecrets = 3; GF_InventoryModifier_Reset(); +} - GF_COMMAND gf_cmd = { .action = GF_EXIT_TO_TITLE }; - - const GF_SEQUENCE *const sequence = &level->sequence; - for (int32_t i = 0; i < sequence->length; i++) { - const GF_SEQUENCE_EVENT *const event = &sequence->events[i]; - LOG_DEBUG( - "event type=%s(%d) data=0x%x", - ENUM_MAP_TO_STRING(GF_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(GF_SEQUENCE_EVENT_TYPE, event->type), - event->type, event->data, - ENUM_MAP_TO_STRING(GF_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; - } +GF_SEQUENCE_CONTEXT GF_SwitchSequenceContext( + const GF_SEQUENCE_EVENT *const event, const GF_SEQUENCE_CONTEXT seq_ctx) +{ + // Update sequence context if necessary + if (event->type == GFS_PLAY_LEVEL && seq_ctx == GFSC_SAVED) { + return GFSC_NORMAL; } + return seq_ctx; +} - if (seq_ctx == GFSC_STORY) { - return (GF_COMMAND) { .action = GF_NOOP }; - } +bool GF_ShouldSkipSequenceEvent( + const GF_LEVEL *const level, const GF_SEQUENCE_EVENT *const event) +{ + return false; +} - LOG_DEBUG( - "sequence finished: action=%s param=%d", - ENUM_MAP_TO_STRING(GF_ACTION, gf_cmd.action), gf_cmd.param); - return gf_cmd; +GF_COMMAND (*GF_GetSequenceEventHandler(GF_SEQUENCE_EVENT_TYPE event_type))( + const GF_LEVEL *, const GF_SEQUENCE_EVENT *, GF_SEQUENCE_CONTEXT, void *) +{ + return m_EventHandlers[event_type]; } diff --git a/src/tr2/game/game_flow/sequencer.h b/src/tr2/game/game_flow/sequencer.h index d6738a494..ea7eaccd0 100644 --- a/src/tr2/game/game_flow/sequencer.h +++ b/src/tr2/game/game_flow/sequencer.h @@ -4,11 +4,5 @@ #include -GF_COMMAND GF_InterpretSequence( - const GF_LEVEL *level, GF_SEQUENCE_CONTEXT seq_ctx, void *user_arg); - -bool GF_DoFrontendSequence(void); -GF_COMMAND GF_DoDemoSequence(int32_t demo_num); -GF_COMMAND GF_DoCutsceneSequence(int32_t cutscene_num); GF_COMMAND GF_DoLevelSequence( const GF_LEVEL *start_level, GF_SEQUENCE_CONTEXT seq_ctx); diff --git a/src/tr2/game/game_flow/sequencer_misc.c b/src/tr2/game/game_flow/sequencer_misc.c index ddf857cf1..2683a6d1a 100644 --- a/src/tr2/game/game_flow/sequencer_misc.c +++ b/src/tr2/game/game_flow/sequencer_misc.c @@ -3,43 +3,21 @@ #include "game/game_flow/common.h" #include "game/game_flow/sequencer.h" #include "game/game_flow/vars.h" +#include "game/level.h" +#include #include #include -GF_COMMAND GF_DoDemoSequence(int32_t demo_num) +GF_COMMAND GF_TitleSequence(void) { - demo_num = Demo_ChooseLevel(demo_num); - if (demo_num < 0) { - return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; + GameStringTable_Apply(NULL); + const GF_LEVEL *const title_level = GF_GetTitleLevel(); + if (!Level_Initialise(title_level, GFSC_NORMAL)) { + return (GF_COMMAND) { .action = GF_EXIT_GAME }; } - const GF_LEVEL *const level = GF_GetLevel(GFLT_DEMOS, demo_num); - if (level == NULL) { - LOG_ERROR("Missing demo: %d", demo_num); - return (GF_COMMAND) { .action = GF_NOOP }; - } - return GF_InterpretSequence(level, GFSC_NORMAL, NULL); -} - -GF_COMMAND GF_DoCutsceneSequence(const int32_t cutscene_num) -{ - const GF_LEVEL *const level = GF_GetLevel(GFLT_CUTSCENES, cutscene_num); - if (level == NULL) { - LOG_ERROR("Missing cutscene: %d", cutscene_num); - return (GF_COMMAND) { .action = GF_NOOP }; - } - return GF_InterpretSequence(level, GFSC_NORMAL, NULL); -} - -bool GF_DoFrontendSequence(void) -{ - if (g_GameFlow.title_level == NULL) { - return false; - } - const GF_COMMAND gf_cmd = - GF_InterpretSequence(g_GameFlow.title_level, GFSC_NORMAL, NULL); - return gf_cmd.action == GF_EXIT_GAME; + return GF_ShowInventory(INV_TITLE_MODE); } GF_COMMAND GF_DoLevelSequence( diff --git a/src/tr2/game/level.h b/src/tr2/game/level.h index 5eb19c724..5a6825b92 100644 --- a/src/tr2/game/level.h +++ b/src/tr2/game/level.h @@ -1,7 +1,8 @@ #pragma once #include "game/game_flow/types.h" -#include "global/types.h" + +#include bool Level_Initialise(const GF_LEVEL *level, GF_SEQUENCE_CONTEXT seq_ctx); bool Level_Load(const GF_LEVEL *level); diff --git a/src/tr2/game/shell/common.c b/src/tr2/game/shell/common.c index 7273eaa6c..86fb78c9e 100644 --- a/src/tr2/game/shell/common.c +++ b/src/tr2/game/shell/common.c @@ -21,12 +21,12 @@ #include "global/vars.h" #include +#include #include #include #include #include #include -#include #include #include @@ -358,23 +358,13 @@ void Shell_Main(void) GameBuf_Init(GAMEBUF_MEM_CAP); - const bool is_frontend_fail = GF_DoFrontendSequence(); - if (Shell_IsExiting()) { - Config_Write(); - return; - } - - if (is_frontend_fail) { - Shell_ExitSystem("GameMain: failed in GF_DoFrontendSequence()"); - return; - } - - GF_COMMAND gf_cmd = g_GameFlow.cmd_init; - bool is_loop_continued = true; - while (is_loop_continued) { - LOG_DEBUG( + GF_COMMAND gf_cmd = GF_DoFrontendSequence(); + bool loop_continue = !Shell_IsExiting(); + while (loop_continue) { + LOG_INFO( "action=%s param=%d", ENUM_MAP_TO_STRING(GF_ACTION, gf_cmd.action), gf_cmd.param); + switch (gf_cmd.action) { case GF_START_GAME: case GF_SELECT_GAME: @@ -424,13 +414,16 @@ void Shell_Main(void) return; } } else { - gf_cmd = TitleSequence(); + gf_cmd = GF_TitleSequence(); } break; - default: - is_loop_continued = false; + case GF_EXIT_GAME: + loop_continue = false; break; + + default: + ASSERT_FAIL(); } } diff --git a/src/tr2/global/enum_map.def b/src/tr2/global/enum_map.def index a1f1abd60..7fa3fb712 100644 --- a/src/tr2/global/enum_map.def +++ b/src/tr2/global/enum_map.def @@ -12,38 +12,3 @@ ENUM_MAP_DEFINE(RENDER_MODE, RM_HARDWARE, "hardware") ENUM_MAP_DEFINE(ASPECT_MODE, AM_ANY, "any") ENUM_MAP_DEFINE(ASPECT_MODE, AM_4_3, "4:3") ENUM_MAP_DEFINE(ASPECT_MODE, AM_16_9, "16:9") - -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_EXIT_TO_TITLE, "exit_to_title") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_DISPLAY_PICTURE, "display_picture") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_ENABLE_SUNSET, "enable_sunset") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_REMOVE_WEAPONS, "remove_weapons") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_REMOVE_AMMO, "remove_ammo") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_LEVEL_COMPLETE, "level_complete") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_LEVEL_STATS, "level_stats") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_TOTAL_STATS, "total_stats") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_SET_NUM_SECRETS, "set_secret_count") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_SET_CAMERA_ANGLE, "set_cutscene_angle") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_SET_START_ANIM, "set_lara_start_anim") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_PLAY_LEVEL, "play_level") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_PLAY_CUTSCENE, "play_cutscene") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_PLAY_FMV, "play_fmv") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_DISABLE_FLOOR, "disable_floor") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_ADD_ITEM, "give_item") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_ADD_SECRET_REWARD, "add_secret_reward") -ENUM_MAP_DEFINE(GF_SEQUENCE_EVENT_TYPE, GFS_PLAY_MUSIC, "play_music") - -ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_TITLE, "title") -ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_NORMAL, "normal") -ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_CUTSCENE, "cutscene") -ENUM_MAP_DEFINE(GF_LEVEL_TYPE, GFL_GYM, "gym") - -ENUM_MAP_DEFINE(GF_ACTION, GF_NOOP, "noop") -ENUM_MAP_DEFINE(GF_ACTION, GF_START_GAME, "play_level") -ENUM_MAP_DEFINE(GF_ACTION, GF_START_SAVED_GAME, "load_saved_game") -ENUM_MAP_DEFINE(GF_ACTION, GF_START_CINE, "play_cutscene") -ENUM_MAP_DEFINE(GF_ACTION, GF_START_DEMO, "play_demo") -ENUM_MAP_DEFINE(GF_ACTION, GF_START_FMV, "play_fmv") -ENUM_MAP_DEFINE(GF_ACTION, GF_EXIT_TO_TITLE, "exit_to_title") -ENUM_MAP_DEFINE(GF_ACTION, GF_LEVEL_COMPLETE, "level_complete") -ENUM_MAP_DEFINE(GF_ACTION, GF_EXIT_GAME, "exit_game") -ENUM_MAP_DEFINE(GF_ACTION, GF_SELECT_GAME, "select_level")