Skip to content

Commit

Permalink
gameflow: add bonus level type
Browse files Browse the repository at this point in the history
  • Loading branch information
walkawayy committed Sep 21, 2023
1 parent 2e685dd commit 7de1c3d
Show file tree
Hide file tree
Showing 12 changed files with 99 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## [Unreleased](https://github.com/rr-/Tomb1Main/compare/stable...develop) - ××××-××-××
- added Linux builds and toolchain
- added the bonus level type for custom levels that unlocks if all main game secrets are found (#645)

## [2.16](https://github.com/rr-/Tomb1Main/compare/2.15.3...2.16) - 2023-09-20
- added a new rendering mode called "framebuffer" that lets the game to run at lower resolutions (#114)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ Not all options are turned on by default. Refer to `Tomb1Main_ConfigTool.exe` fo
- you can offer a custom Gym level
- you can change the main menu backdrop
- you can specify the anchor room for Bacon Lara
- you can add bonus levels that unlock if all main game secrets are found
- added automatic calculation of secret counts (no longer having to fiddle with the .exe to get correct secret stats)
- added save game crystals game mode (enabled via gameflow)
- added per-level customizable water color (with customizable blue component)
Expand Down
11 changes: 10 additions & 1 deletion src/game/game/game.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,15 @@ bool Game_Start(int32_t level_num, GAMEFLOW_LEVEL_TYPE level_type)
}
break;

case GFL_BONUS:
Savegame_CarryCurrentInfoToNextLevel(level_num - 1, level_num);
Savegame_ApplyLogicToCurrentInfo(level_num);
Level_InitialiseFlags();
if (!Level_Initialise(level_num)) {
return false;
}
break;

default:
Level_InitialiseFlags();
if (!Level_Initialise(level_num)) {
Expand Down Expand Up @@ -257,7 +266,7 @@ int32_t Game_Loop(GAMEFLOW_LEVEL_TYPE level_type)
Stats_GetSecrets();

bool ask_for_save = g_Config.enable_save_crystals
&& level_type == GFL_NORMAL
&& (level_type == GFL_NORMAL || level_type == GFL_BONUS)
&& g_CurrentLevel != g_GameFlow.first_level_num
&& g_CurrentLevel != g_GameFlow.gym_level_num;

Expand Down
52 changes: 50 additions & 2 deletions src/game/gameflow.c
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,18 @@ static bool GameFlow_LoadLevelSequence(
}
seq->data = (void *)(intptr_t)tmp;

} else if (!strcmp(type_str, "exit_to_bonus_level")) {
seq->type = GFS_EXIT_TO_BONUS_LEVEL;
int tmp =
json_object_get_int(jseq_obj, "level_id", JSON_INVALID_NUMBER);
if (tmp == JSON_INVALID_NUMBER) {
LOG_ERROR(
"level %d, sequence %s: 'level_id' must be a number",
level_num, type_str);
return false;
}
seq->data = (void *)tmp;

} else if (!strcmp(type_str, "exit_to_cine")) {
seq->type = GFS_EXIT_TO_CINE;
int tmp =
Expand Down Expand Up @@ -677,6 +689,8 @@ static bool GameFlow_LoadScriptLevels(struct json_object_s *obj)
g_GameFlow.gym_level_num = -1;
g_GameFlow.first_level_num = -1;
g_GameFlow.last_level_num = -1;
g_GameFlow.first_bonus_num = -1;
g_GameFlow.last_bonus_num = -1;
g_GameFlow.title_level_num = -1;
g_GameFlow.level_count = jlvl_arr->length;

Expand Down Expand Up @@ -740,6 +754,12 @@ static bool GameFlow_LoadScriptLevels(struct json_object_s *obj)
g_GameFlow.first_level_num = level_num;
}
g_GameFlow.last_level_num = level_num;
} else if (!strcmp(tmp_s, "bonus")) {
cur->level_type = GFL_BONUS;
if (g_GameFlow.first_bonus_num == -1) {
g_GameFlow.first_bonus_num = level_num;
}
g_GameFlow.last_bonus_num = level_num;
} else if (!strcmp(tmp_s, "cutscene")) {
cur->level_type = GFL_CUTSCENE;
} else if (!strcmp(tmp_s, "current")) {
Expand Down Expand Up @@ -1053,6 +1073,7 @@ void GameFlow_Shutdown(void)
case GFS_LEVEL_STATS:
case GFS_EXIT_TO_TITLE:
case GFS_EXIT_TO_LEVEL:
case GFS_EXIT_TO_BONUS_LEVEL:
case GFS_EXIT_TO_CINE:
case GFS_SET_CAM_X:
case GFS_SET_CAM_Y:
Expand Down Expand Up @@ -1149,6 +1170,7 @@ GameFlow_InterpretSequence(int32_t level_num, GAMEFLOW_LEVEL_TYPE level_type)
switch (seq->type) {
case GFS_EXIT_TO_TITLE:
case GFS_EXIT_TO_LEVEL:
case GFS_EXIT_TO_BONUS_LEVEL:
case GFS_EXIT_TO_CINE:
case GFS_PLAY_FMV:
case GFS_LEVEL_STATS:
Expand Down Expand Up @@ -1188,7 +1210,11 @@ GameFlow_InterpretSequence(int32_t level_num, GAMEFLOW_LEVEL_TYPE level_type)
return ret;
}
if (level_type == GFL_SAVED) {
level_type = GFL_NORMAL;
if (level_num >= g_GameFlow.first_bonus_num) {
level_type = GFL_BONUS;
} else {
level_type = GFL_NORMAL;
}
}
break;

Expand Down Expand Up @@ -1223,7 +1249,15 @@ GameFlow_InterpretSequence(int32_t level_num, GAMEFLOW_LEVEL_TYPE level_type)
case GFS_TOTAL_STATS:
if (g_Config.enable_total_stats && level_type != GFL_SAVED) {
GAMEFLOW_DISPLAY_PICTURE_DATA *data = seq->data;
Stats_ShowTotal(data->path);
if (level_type == GFL_BONUS) {
Stats_ShowTotal(
data->path, g_GameFlow.first_bonus_num,
g_GameFlow.last_bonus_num);
} else {
Stats_ShowTotal(
data->path, g_GameFlow.first_level_num,
g_GameFlow.last_level_num);
}
}
break;

Expand Down Expand Up @@ -1276,6 +1310,13 @@ GameFlow_InterpretSequence(int32_t level_num, GAMEFLOW_LEVEL_TYPE level_type)
return GF_START_GAME
| ((int32_t)(intptr_t)seq->data & ((1 << 6) - 1));

case GFS_EXIT_TO_BONUS_LEVEL: {
if (!g_GameInfo.bonus_level_unlock) {
break;
}
return GF_START_BONUS | ((int32_t)seq->data & ((1 << 6) - 1));
}

case GFS_EXIT_TO_CINE:
return GF_START_CINE
| ((int32_t)(intptr_t)seq->data & ((1 << 6) - 1));
Expand Down Expand Up @@ -1441,6 +1482,13 @@ GameFlow_StorySoFar(int32_t level_num, int32_t savegame_level)
return GF_START_GAME
| ((int32_t)(intptr_t)seq->data & ((1 << 6) - 1));

case GFS_EXIT_TO_BONUS_LEVEL: {
if (savegame_level < g_GameFlow.first_bonus_num) {
break;
}
return GF_START_BONUS | ((int32_t)seq->data & ((1 << 6) - 1));
}

case GFS_EXIT_TO_CINE:
return GF_START_CINE
| ((int32_t)(intptr_t)seq->data & ((1 << 6) - 1));
Expand Down
2 changes: 2 additions & 0 deletions src/game/gameflow.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ typedef struct GAMEFLOW {
int32_t gym_level_num;
int32_t first_level_num;
int32_t last_level_num;
int32_t first_bonus_num;
int32_t last_bonus_num;
int32_t title_level_num;
int32_t level_count;
char *savegame_fmt_legacy;
Expand Down
2 changes: 2 additions & 0 deletions src/game/option/option_passport.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ static void Option_PassportShowNewGame(void)
g_GameInfo.bonus_flag = 0;
break;
}
g_GameInfo.bonus_level_unlock = false;
g_GameInfo.current_save_slot = -1;
g_GameInfo.passport_mode = PASSPORT_MODE_NEW_GAME;
g_GameInfo.save_initial_version = SAVEGAME_CURRENT_VERSION;
Expand Down Expand Up @@ -484,6 +485,7 @@ void Option_Passport(INVENTORY_ITEM *inv_item)
} else {
g_GameInfo.save_initial_version =
SAVEGAME_CURRENT_VERSION;
g_GameInfo.bonus_level_unlock = false;
}
} else if (
g_InvMode == INV_SAVE_MODE
Expand Down
9 changes: 5 additions & 4 deletions src/game/savegame/savegame.c
Original file line number Diff line number Diff line change
Expand Up @@ -638,15 +638,16 @@ GAMEFLOW_OPTION Savegame_PlayAvailableStory(int32_t slot_num)

int32_t gf_option = GF_START_GAME | g_GameFlow.first_level_num;

bool loop_continue = true;
while (loop_continue) {
while (1) {
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) {
if ((gf_param >= savegame_info->level_num
&& gf_param <= g_GameFlow.last_level_num)
|| (gf_param >= savegame_info->level_num
&& gf_param <= g_GameFlow.last_bonus_num)) {
break;
}

Expand Down
16 changes: 13 additions & 3 deletions src/game/savegame/savegame_bson.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ static bool Savegame_BSON_LoadDiscontinuedStartInfo(
static bool Savegame_BSON_LoadDiscontinuedEndInfo(
struct json_array_s *end_arr, GAME_INFO *game_info);
static bool Savegame_BSON_LoadMisc(
struct json_object_s *misc_obj, GAME_INFO *game_info);
struct json_object_s *misc_obj, GAME_INFO *game_info,
uint16_t header_version);
static bool Savegame_BSON_LoadInventory(struct json_object_s *inv_obj);
static bool Savegame_BSON_LoadFlipmaps(struct json_object_s *flipmap_obj);
static bool Savegame_BSON_LoadCameras(struct json_array_s *cameras_arr);
Expand Down Expand Up @@ -370,14 +371,20 @@ static bool Savegame_BSON_LoadDiscontinuedEndInfo(
}

static bool Savegame_BSON_LoadMisc(
struct json_object_s *misc_obj, GAME_INFO *game_info)
struct json_object_s *misc_obj, GAME_INFO *game_info,
uint16_t header_version)
{
assert(game_info);
if (!misc_obj) {
LOG_ERROR("Malformed save: invalid or missing misc info");
return false;
}
game_info->bonus_flag = json_object_get_int(misc_obj, "bonus_flag", 0);
if (header_version >= VERSION_3) {
game_info->bonus_level_unlock =
json_object_get_bool(misc_obj, "bonus_level_unlock", 0);
LOG_DEBUG("Load bonus_level_unlock: %d", game_info->bonus_level_unlock);
}
return true;
}

Expand Down Expand Up @@ -929,6 +936,8 @@ static struct json_object_s *Savegame_BSON_DumpMisc(GAME_INFO *game_info)
assert(game_info);
struct json_object_s *misc_obj = json_object_new();
json_object_append_int(misc_obj, "bonus_flag", game_info->bonus_flag);
json_object_append_bool(
misc_obj, "bonus_level_unlock", game_info->bonus_level_unlock);
return misc_obj;
}

Expand Down Expand Up @@ -1278,7 +1287,8 @@ bool Savegame_BSON_LoadFromFile(MYFILE *fp, GAME_INFO *game_info)
}

if (!Savegame_BSON_LoadMisc(
json_object_get_object(root_obj, "misc"), game_info)) {
json_object_get_object(root_obj, "misc"), game_info,
header.version)) {
goto cleanup;
}

Expand Down
4 changes: 4 additions & 0 deletions src/game/shell.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@ void Shell_Main(void)
break;
}

case GF_START_BONUS:
gf_option = GameFlow_InterpretSequence(gf_param, GFL_BONUS);
break;

case GF_STORY_SO_FAR: {
gf_option = Savegame_PlayAvailableStory(gf_param);
break;
Expand Down
9 changes: 6 additions & 3 deletions src/game/stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ void Stats_Show(int32_t level_num)
g_GameInfo.status &= ~GMS_IN_STATS;
}

void Stats_ShowTotal(const char *filename)
void Stats_ShowTotal(const char *filename, int first_level, int last_level)
{
char buf[100];
char time_str[100];
Expand All @@ -399,8 +399,7 @@ void Stats_ShowTotal(const char *filename)

int16_t secret_flags = 0;

for (int i = g_GameFlow.first_level_num; i <= g_GameFlow.last_level_num;
i++) {
for (int i = first_level; i <= last_level; i++) {
const GAME_STATS *stats = &g_GameInfo.current[i].stats;

total_timer += stats->timer;
Expand All @@ -419,6 +418,10 @@ void Stats_ShowTotal(const char *filename)
total_max_pickup_count += stats->max_pickup_count;
}

// Check for bonus level unlock.
g_GameInfo.bonus_level_unlock =
total_secret_count >= total_max_secret_count;

Text_RemoveAll();

int top_y = 55;
Expand Down
2 changes: 1 addition & 1 deletion src/game/stats.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ int32_t Stats_GetPickups(void);
int32_t Stats_GetKillables(void);
int32_t Stats_GetSecrets(void);
void Stats_Show(int32_t level_num);
void Stats_ShowTotal(const char *filename);
void Stats_ShowTotal(const char *filename, int first_level, int last_level);
4 changes: 4 additions & 0 deletions src/global/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,7 @@ typedef enum GAMEFLOW_LEVEL_TYPE {
GFL_CURRENT = 6, // legacy level type for reading TombATI's savegames
GFL_RESTART = 7,
GFL_SELECT = 8,
GFL_BONUS = 9,
} GAMEFLOW_LEVEL_TYPE;

typedef enum GAMEFLOW_OPTION {
Expand All @@ -969,6 +970,7 @@ typedef enum GAMEFLOW_OPTION {
GF_SELECT_GAME = 9 << 6,
GF_START_GYM = 10 << 6,
GF_STORY_SO_FAR = 11 << 6,
GF_START_BONUS = 12 << 6,
} GAMEFLOW_OPTION;

typedef enum GAMEFLOW_SEQUENCE_TYPE {
Expand All @@ -985,6 +987,7 @@ typedef enum GAMEFLOW_SEQUENCE_TYPE {
GFS_DISPLAY_PICTURE,
GFS_EXIT_TO_TITLE,
GFS_EXIT_TO_LEVEL,
GFS_EXIT_TO_BONUS_LEVEL,
GFS_EXIT_TO_CINE,
GFS_SET_CAM_X,
GFS_SET_CAM_Y,
Expand Down Expand Up @@ -1551,6 +1554,7 @@ typedef enum GAME_STATUS {
typedef struct GAME_INFO {
RESUME_INFO *current;
uint8_t bonus_flag;
bool bonus_level_unlock;
int32_t current_save_slot;
int16_t save_initial_version;
PASSPORT_PAGE passport_page;
Expand Down

0 comments on commit 7de1c3d

Please sign in to comment.