passport: add the story so far option to view cutscenes and FMVs

Resolves #201
This commit is contained in:
walkawayy 2022-07-21 17:09:39 -04:00 committed by GitHub
parent e2015b9003
commit d8b4fbecf6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 180 additions and 19 deletions

View file

@ -1,6 +1,7 @@
## [Unreleased](https://github.com/rr-/Tomb1Main/compare/stable...develop)
- added the option to make Lara revert to pistols on new level start (#557)
- added the PS1 style UI (#517)
- added the "Story so far..." option in the select level menu to view cutscenes and FMVs (#201)
## [2.9.1](https://github.com/rr-/Tomb1Main/compare/2.9...2.9.1) - 2022-06-03
- fixed crash on centaur hatch (#579, regression from 2.9)

View file

@ -270,6 +270,7 @@ Not all options are turned on by default. Refer to `Tomb1Main.json5` for details
- added level selection to the load game menu
- added the ability to make freshly triggered (runaway) Pierre replace an already existing (runaway) Pierre
- added the PS1 style UI
- added the "Story so far..." option in the select level menu to view cutscenes and FMVs
- changed internal game memory limit from 3.5 MB to 16 MB
- changed moveable limit from 256 to 10240
- changed maximum textures from 2048 to 8192

View file

@ -496,7 +496,7 @@
"HEADING_ITEMS": "ITEMS",
"PASSPORT_SELECT_LEVEL": "Select Level",
"PASSPORT_RESTART_LEVEL": "Restart Level",
"PASSPORT_LOCKED_LEVEL": "Level Locked",
"PASSPORT_STORY_SO_FAR": "Story so far...",
"PASSPORT_LEGACY_SELECT_LEVEL_1": "Legacy saves do not",
"PASSPORT_LEGACY_SELECT_LEVEL_2": "support this feature.",
"PASSPORT_SELECT_MODE": "Select Mode",

View file

@ -124,7 +124,7 @@
"HEADING_ITEMS": "ITEMS",
"PASSPORT_SELECT_LEVEL": "Select Level",
"PASSPORT_RESTART_LEVEL": "Restart Level",
"PASSPORT_LOCKED_LEVEL": "Level Locked",
"PASSPORT_STORY_SO_FAR": "Story so far...",
"PASSPORT_LEGACY_SELECT_LEVEL_1": "Legacy saves do not",
"PASSPORT_LEGACY_SELECT_LEVEL_2": "support this feature.",
"PASSPORT_SELECT_MODE": "Select Mode",

View file

@ -246,6 +246,11 @@ int32_t Game_Stop(void)
g_GameInfo.passport_page == PASSPORT_PAGE_1
&& g_GameInfo.passport_mode == PASSPORT_MODE_SELECT_LEVEL) {
return GF_SELECT_GAME | g_GameInfo.select_level_num;
} else if (
g_GameInfo.passport_page == PASSPORT_PAGE_1
&& g_GameInfo.passport_mode == PASSPORT_MODE_STORY_SO_FAR) {
// page 1: story so far
return GF_STORY_SO_FAR | g_GameInfo.current_save_slot;
} else if (g_GameInfo.passport_page == PASSPORT_PAGE_2) {
return GF_START_GAME
| (g_InvMode == INV_DEATH_MODE ? g_CurrentLevel

View file

@ -54,7 +54,7 @@ static GAME_STRING_ID GameFlow_StringToGameStringID(const char *str)
{ "HEADING_ITEMS", GS_HEADING_ITEMS },
{ "PASSPORT_SELECT_LEVEL", GS_PASSPORT_SELECT_LEVEL },
{ "PASSPORT_RESTART_LEVEL", GS_PASSPORT_RESTART_LEVEL },
{ "PASSPORT_LOCKED_LEVEL", GS_PASSPORT_LOCKED_LEVEL },
{ "PASSPORT_STORY_SO_FAR", GS_PASSPORT_STORY_SO_FAR },
{ "PASSPORT_LEGACY_SELECT_LEVEL_1", GS_PASSPORT_LEGACY_SELECT_LEVEL_1 },
{ "PASSPORT_LEGACY_SELECT_LEVEL_2", GS_PASSPORT_LEGACY_SELECT_LEVEL_2 },
{ "PASSPORT_SELECT_MODE", GS_PASSPORT_SELECT_MODE },
@ -1299,3 +1299,101 @@ GameFlow_InterpretSequence(int32_t level_num, GAMEFLOW_LEVEL_TYPE level_type)
return ret;
}
GAMEFLOW_OPTION
GameFlow_StorySoFar(int32_t level_num, int32_t savegame_level)
{
LOG_INFO("%d", level_num);
GAMEFLOW_SEQUENCE *seq = g_GameFlow.levels[level_num].sequence;
GAMEFLOW_OPTION ret = GF_EXIT_TO_TITLE;
while (seq->type != GFS_END) {
LOG_INFO("seq %d %d", seq->type, seq->data);
switch (seq->type) {
case GFS_LOOP_GAME:
case GFS_STOP_GAME:
case GFS_LEVEL_STATS:
case GFS_TOTAL_STATS:
case GFS_DISPLAY_PICTURE:
case GFS_GIVE_ITEM:
case GFS_REMOVE_GUNS:
case GFS_REMOVE_SCIONS:
case GFS_FIX_PYRAMID_SECRET_TRIGGER:
break;
case GFS_START_GAME:
if (level_num == savegame_level) {
return GF_EXIT_TO_TITLE;
}
break;
case GFS_START_CINE:
ret = Game_Cutscene_Start((int32_t)seq->data);
break;
case GFS_LOOP_CINE:
ret = Game_Cutscene_Loop();
break;
case GFS_STOP_CINE:
ret = Game_Cutscene_Stop((int32_t)seq->data);
break;
case GFS_PLAY_FMV:
FMV_Play((char *)seq->data);
break;
case GFS_EXIT_TO_TITLE:
return GF_EXIT_TO_TITLE;
case GFS_EXIT_TO_LEVEL:
return GF_START_GAME | ((int32_t)seq->data & ((1 << 6) - 1));
case GFS_EXIT_TO_CINE:
return GF_START_CINE | ((int32_t)seq->data & ((1 << 6) - 1));
case GFS_SET_CAM_X:
g_Camera.pos.x = (int32_t)seq->data;
break;
case GFS_SET_CAM_Y:
g_Camera.pos.y = (int32_t)seq->data;
break;
case GFS_SET_CAM_Z:
g_Camera.pos.z = (int32_t)seq->data;
break;
case GFS_SET_CAM_ANGLE:
g_Camera.target_angle = (int32_t)seq->data;
break;
case GFS_FLIP_MAP:
Room_FlipMap();
break;
case GFS_PLAY_SYNCED_AUDIO:
Music_Play((int32_t)seq->data);
break;
case GFS_MESH_SWAP: {
GAMEFLOW_MESH_SWAP_DATA *swap_data = seq->data;
int16_t *temp = g_Meshes
[g_Objects[swap_data->object1_num].mesh_index
+ swap_data->mesh_num];
g_Meshes
[g_Objects[swap_data->object1_num].mesh_index
+ swap_data->mesh_num] = g_Meshes
[g_Objects[swap_data->object2_num].mesh_index
+ swap_data->mesh_num];
g_Meshes
[g_Objects[swap_data->object2_num].mesh_index
+ swap_data->mesh_num] = temp;
break;
}
case GFS_END:
return ret;
}
seq++;
}
return ret;
}

View file

@ -75,5 +75,7 @@ extern GAMEFLOW_DEFAULT_STRING g_GameFlowDefaultStrings[];
GAMEFLOW_OPTION
GameFlow_InterpretSequence(int32_t level_num, GAMEFLOW_LEVEL_TYPE level_type);
GAMEFLOW_OPTION
GameFlow_StorySoFar(int32_t level_num, int32_t savegame_level);
bool GameFlow_LoadFromFile(const char *file_name);
void GameFlow_Shutdown(void);

View file

@ -900,6 +900,11 @@ int32_t Inv_Display(int inv_mode)
&& g_GameInfo.passport_mode == PASSPORT_MODE_SELECT_LEVEL) {
// page 1: select level
return GF_SELECT_GAME | g_GameInfo.select_level_num;
} else if (
g_GameInfo.passport_page == PASSPORT_PAGE_1
&& g_GameInfo.passport_mode == PASSPORT_MODE_STORY_SO_FAR) {
// page 1: story so far
return GF_STORY_SO_FAR | g_GameInfo.current_save_slot;
} else if (g_GameInfo.passport_page == PASSPORT_PAGE_2) {
// page 2: new game
Savegame_InitCurrentInfo();
@ -918,6 +923,11 @@ int32_t Inv_Display(int inv_mode)
&& g_GameInfo.passport_mode == PASSPORT_MODE_SELECT_LEVEL) {
// page 1: select level
return GF_SELECT_GAME | g_GameInfo.select_level_num;
} else if (
g_GameInfo.passport_page == PASSPORT_PAGE_1
&& g_GameInfo.passport_mode == PASSPORT_MODE_STORY_SO_FAR) {
// page 1: story so far
return GF_STORY_SO_FAR | g_GameInfo.current_save_slot;
} else if (g_GameInfo.passport_page == PASSPORT_PAGE_2) {
// page 2: restart level
return GF_RESTART_GAME | g_CurrentLevel;
@ -935,6 +945,11 @@ int32_t Inv_Display(int inv_mode)
&& g_GameInfo.passport_mode == PASSPORT_MODE_SELECT_LEVEL) {
// page 1: select level
return GF_SELECT_GAME | g_GameInfo.select_level_num;
} else if (
g_GameInfo.passport_page == PASSPORT_PAGE_1
&& g_GameInfo.passport_mode == PASSPORT_MODE_STORY_SO_FAR) {
// page 1: story so far
return GF_STORY_SO_FAR | g_GameInfo.current_save_slot;
} else if (g_GameInfo.passport_page == PASSPORT_PAGE_2) {
if (g_CurrentLevel == g_GameFlow.gym_level_num) {
// page 2: new game in gym

View file

@ -98,7 +98,7 @@ void Option_PassportInit(void)
g_SavegameRequester.item_texts = Memory_Alloc(
g_Config.maximum_save_slots * g_SavegameRequester.item_text_len);
m_SelectLevelRequester.item_texts = Memory_Alloc(
g_GameFlow.level_count * m_SelectLevelRequester.item_text_len);
(g_GameFlow.level_count + 1) * m_SelectLevelRequester.item_text_len);
}
void Option_PassportShutdown(void)
@ -181,7 +181,10 @@ static void Option_PassportShowLevelSelect(void)
{
int32_t select = Requester_Display(&m_SelectLevelRequester);
if (select) {
if (select > 0) {
if (select - 1 + g_GameFlow.first_level_num
== Savegame_GetLevelNumber(g_GameInfo.current_save_slot) + 1) {
g_GameInfo.passport_mode = PASSPORT_MODE_STORY_SO_FAR;
} else if (select > 0) {
g_GameInfo.select_level_num =
select - 1 + g_GameFlow.first_level_num;
g_GameInfo.passport_mode = PASSPORT_MODE_SELECT_LEVEL;

View file

@ -53,6 +53,7 @@ bool Savegame_LoadOnlyResumeInfo(int32_t slot_num, GAME_INFO *game_info);
void Savegame_ScanSavedGames(void);
void Savegame_ScanAvailableLevels(REQUEST_INFO *req);
GAMEFLOW_OPTION Savegame_PlayAvailableStory(int32_t slot_num);
bool Savegame_RestartAvailable(int32_t slot_num);
void Savegame_ApplyLogicToCurrentInfo(int level_num);

View file

@ -586,21 +586,23 @@ void Savegame_ScanAvailableLevels(REQUEST_INFO *req)
return;
}
for (int i = g_GameFlow.first_level_num; i <= g_GameFlow.last_level_num;
for (int i = g_GameFlow.first_level_num; i <= savegame_info->level_num;
i++) {
if (i <= savegame_info->level_num) {
req->item_flags[req->items] &= ~RIF_BLOCKED;
sprintf(
&req->item_texts[req->items * req->item_text_len], "%s",
g_GameFlow.levels[i].level_title);
} else {
req->item_flags[req->items] |= RIF_BLOCKED;
sprintf(
&req->item_texts[req->items * req->item_text_len],
g_GameFlow.strings[GS_PASSPORT_LOCKED_LEVEL]);
}
req->item_flags[req->items] &= ~RIF_BLOCKED;
sprintf(
&req->item_texts[req->items * req->item_text_len], "%s",
g_GameFlow.levels[i].level_title);
req->items++;
}
if (g_InvMode == INV_TITLE_MODE) {
req->item_flags[req->items] &= ~RIF_BLOCKED;
sprintf(
&req->item_texts[req->items * req->item_text_len], "%s",
g_GameFlow.strings[GS_PASSPORT_STORY_SO_FAR]);
req->items++;
}
req->requested = 0;
req->line_offset = 0;
}
@ -614,3 +616,29 @@ bool Savegame_RestartAvailable(int32_t slot_num)
SAVEGAME_INFO *savegame_info = &m_SavegameInfo[slot_num];
return savegame_info->features.restart;
}
GAMEFLOW_OPTION Savegame_PlayAvailableStory(int32_t slot_num)
{
SAVEGAME_INFO *savegame_info = &m_SavegameInfo[slot_num];
int32_t gf_option = GF_START_GAME | g_GameFlow.first_level_num;
bool loop_continue = true;
while (loop_continue) {
int32_t gf_direction = gf_option & ~((1 << 6) - 1);
int32_t gf_param = gf_option & ((1 << 6) - 1);
gf_option = GameFlow_StorySoFar(gf_param, savegame_info->level_num);
if (gf_param >= savegame_info->level_num
&& gf_param <= g_GameFlow.last_level_num) {
break;
}
if (gf_direction == GF_EXIT_GAME) {
break;
}
}
return GF_EXIT_TO_TITLE;
}

View file

@ -213,6 +213,11 @@ void Shell_Main(void)
break;
}
case GF_STORY_SO_FAR: {
gf_option = Savegame_PlayAvailableStory(gf_param);
break;
}
case GF_START_CINE:
gf_option = GameFlow_InterpretSequence(gf_param, GFL_CUTSCENE);
break;

View file

@ -994,6 +994,7 @@ typedef enum GAMEFLOW_OPTION {
GF_RESTART_GAME = 8 << 6,
GF_SELECT_GAME = 9 << 6,
GF_START_GYM = 10 << 6,
GF_STORY_SO_FAR = 11 << 6,
} GAMEFLOW_OPTION;
typedef enum GAMEFLOW_SEQUENCE_TYPE {
@ -1031,7 +1032,7 @@ typedef enum GAME_STRING_ID {
GS_HEADING_ITEMS,
GS_PASSPORT_SELECT_LEVEL,
GS_PASSPORT_LOCKED_LEVEL,
GS_PASSPORT_STORY_SO_FAR,
GS_PASSPORT_LEGACY_SELECT_LEVEL_1,
GS_PASSPORT_LEGACY_SELECT_LEVEL_2,
GS_PASSPORT_RESTART_LEVEL,
@ -1181,6 +1182,7 @@ typedef enum PASSPORT_MODE {
PASSPORT_MODE_SHOW_SAVES = 1,
PASSPORT_MODE_NEW_GAME = 2,
PASSPORT_MODE_SELECT_LEVEL = 3,
PASSPORT_MODE_STORY_SO_FAR = 4,
} PASSPORT_MODE;
#pragma pack(push, 1)

View file

@ -19,7 +19,7 @@ GAMEFLOW_DEFAULT_STRING g_GameFlowDefaultStrings[] = {
{ GS_HEADING_ITEMS, "ITEMS" },
{ GS_PASSPORT_SELECT_LEVEL, "Select Level" },
{ GS_PASSPORT_RESTART_LEVEL, "Restart Level" },
{ GS_PASSPORT_LOCKED_LEVEL, "Level Locked" },
{ GS_PASSPORT_STORY_SO_FAR, "Story so far..." },
{ GS_PASSPORT_LEGACY_SELECT_LEVEL_1, "Legacy saves do not" },
{ GS_PASSPORT_LEGACY_SELECT_LEVEL_2, "support this feature." },
{ GS_PASSPORT_SELECT_MODE, "Select Mode" },