mirror of
https://github.com/LostArtefacts/TRX.git
synced 2025-04-28 20:58:07 +03:00
tr1/game-flow: move demos to own level sequence
This commit is contained in:
parent
6614bbfbbf
commit
999f3614cc
12 changed files with 128 additions and 86 deletions
|
@ -87,7 +87,6 @@
|
|||
{"type": "level_stats", "level_id": 2},
|
||||
{"type": "exit_to_level", "level_id": 3},
|
||||
],
|
||||
"demo": true,
|
||||
},
|
||||
|
||||
// Level 3: Lost Valley
|
||||
|
@ -108,7 +107,6 @@
|
|||
{"type": "level_stats", "level_id": 3},
|
||||
{"type": "exit_to_level", "level_id": 4},
|
||||
],
|
||||
"demo": true,
|
||||
},
|
||||
|
||||
// Level 4: Tomb of Qualopec
|
||||
|
@ -506,6 +504,47 @@
|
|||
},
|
||||
],
|
||||
|
||||
"demos": [
|
||||
// Demo 1: City of Vilcabamba
|
||||
{
|
||||
"path": "data/level2.phd",
|
||||
"type": "normal",
|
||||
"music_track": 57,
|
||||
"injections": [
|
||||
"data/injections/vilcabamba_itemrots.bin",
|
||||
"data/injections/vilcabamba_textures.bin",
|
||||
],
|
||||
"sequence": [
|
||||
{"type": "loading_screen", "path": "data/images/peru.webp"},
|
||||
{"type": "load_level"},
|
||||
{"type": "play_level"},
|
||||
{"type": "level_stats", "level_id": 2},
|
||||
{"type": "exit_to_level", "level_id": 3},
|
||||
],
|
||||
},
|
||||
|
||||
// Demo 2: Lost Valley
|
||||
{
|
||||
"path": "data/level3a.phd",
|
||||
"type": "normal",
|
||||
"music_track": 57,
|
||||
"injections": [
|
||||
"data/injections/braid_valley.bin",
|
||||
"data/injections/valley_itemrots.bin",
|
||||
"data/injections/valley_skybox.bin",
|
||||
"data/injections/valley_textures.bin",
|
||||
],
|
||||
"sequence": [
|
||||
{"type": "loading_screen", "path": "data/images/peru.webp"},
|
||||
{"type": "load_level"},
|
||||
{"type": "play_level"},
|
||||
{"type": "level_stats", "level_id": 3},
|
||||
{"type": "exit_to_level", "level_id": 4},
|
||||
],
|
||||
},
|
||||
|
||||
],
|
||||
|
||||
// FMVs
|
||||
"fmvs": [
|
||||
{"path": "fmv/core.avi"},
|
||||
|
|
|
@ -162,6 +162,24 @@
|
|||
},
|
||||
],
|
||||
|
||||
"demos": [
|
||||
{
|
||||
"title": "City of Vilcabamba",
|
||||
"objects": {
|
||||
"key_1": {"name": "Silver Key"},
|
||||
"puzzle_1": {"name": "Gold Idol"},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
"title": "Lost Valley",
|
||||
"objects": {
|
||||
"puzzle_1": {"name": "Machine Cog"},
|
||||
},
|
||||
},
|
||||
|
||||
],
|
||||
|
||||
"objects": {
|
||||
"small_medipack": {"name": "Small Medi Pack"},
|
||||
"large_medipack": {"name": "Large Medi Pack"},
|
||||
|
|
|
@ -315,7 +315,7 @@
|
|||
],
|
||||
|
||||
"demos": [
|
||||
// 0. Venice
|
||||
// Demo 1: Venice
|
||||
{
|
||||
"path": "data/boat.tr2",
|
||||
"music_track": -1,
|
||||
|
@ -326,7 +326,7 @@
|
|||
],
|
||||
},
|
||||
|
||||
// 1. Wreck of the Maria Doria
|
||||
// Demo 2: Wreck of the Maria Doria
|
||||
{
|
||||
"path": "data/keel.tr2",
|
||||
"music_track": 31,
|
||||
|
@ -342,7 +342,7 @@
|
|||
],
|
||||
},
|
||||
|
||||
// 2. Tibetan Foothills
|
||||
// Demo 3: Tibetan Foothills
|
||||
{
|
||||
"path": "data/skidoo.tr2",
|
||||
"music_track": 33,
|
||||
|
|
|
@ -42,6 +42,12 @@ various pieces of global behaviour.
|
|||
// etc
|
||||
},
|
||||
],
|
||||
"demos": [
|
||||
{
|
||||
"path": "data/gym.phd",
|
||||
// etc
|
||||
},
|
||||
],
|
||||
"fmvs": [
|
||||
{"path": "data/snow.rpl"},
|
||||
// etc
|
||||
|
@ -228,7 +234,6 @@ Following are each of the properties available within a level.
|
|||
"water_color": [0.7, 0.5, 0.85],
|
||||
"draw_distance_fade": 34.0,
|
||||
"draw_distance_max": 50.0,
|
||||
"demo": true,
|
||||
"unobtainable_pickups": 1,
|
||||
"unobtainable_kills": 1,
|
||||
"inherit_injections": false,
|
||||
|
@ -257,17 +262,6 @@ Following are each of the properties available within a level.
|
|||
<th>Required</th>
|
||||
<th colspan="2">Description</th>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<td>
|
||||
<code>demo</code>
|
||||
</td>
|
||||
<td>Boolean</td>
|
||||
<td>No</td>
|
||||
<td colspan="2">
|
||||
Flag to indicate that the level has available demo data to play out from
|
||||
the title screen.
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<td>
|
||||
<code>draw_distance_fade</code>
|
||||
|
@ -756,6 +750,12 @@ default gameflow for examples.
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
## Demos
|
||||
The `demos` section shows all the levels that can play a demo when the player
|
||||
leaves the main inventory screen idle for a while or by using the `/demo`
|
||||
command. For the demos to work, these levels need to have demo data built-in.
|
||||
Aside from this requirement, this section works just like the `levels` section.
|
||||
|
||||
## Bonus levels
|
||||
The gameflow supports bonus levels, which are unlocked only when the player
|
||||
collects all secrets in the game's normal levels. These bonus levels behave just
|
||||
|
|
|
@ -91,13 +91,13 @@ void GameStringTable_Apply(const GAME_FLOW_LEVEL *const level)
|
|||
GF_SetLevelTitle(
|
||||
GF_GetLevel(i, GFL_NORMAL), gs_file->levels.entries[i].title);
|
||||
}
|
||||
|
||||
#if TR_VERSION == 2
|
||||
// TODO: TR1 still has everything in a single linear sequence
|
||||
for (int32_t i = 0; i < GF_GetLevelCount(GFL_DEMO); i++) {
|
||||
GF_SetLevelTitle(
|
||||
GF_GetLevel(i, GFL_DEMO), gs_file->demos.entries[i].title);
|
||||
}
|
||||
|
||||
#if TR_VERSION == 2
|
||||
// TODO: TR1 still has cutscene levels in a single linear sequence
|
||||
for (int32_t i = 0; i < GF_GetLevelCount(GFL_CUTSCENE); i++) {
|
||||
GF_SetLevelTitle(
|
||||
GF_GetLevel(i, GFL_CUTSCENE), gs_file->cutscenes.entries[i].title);
|
||||
|
@ -105,11 +105,18 @@ void GameStringTable_Apply(const GAME_FLOW_LEVEL *const level)
|
|||
#endif
|
||||
|
||||
if (level != NULL) {
|
||||
#if TR_VERSION == 1
|
||||
// TODO: TR1 still has everything in a single linear sequence
|
||||
const GS_LEVEL_TABLE *const level_table = &gs_file->levels;
|
||||
#elif TR_VERSION == 2
|
||||
const GS_LEVEL_TABLE *level_table = NULL;
|
||||
#if TR_VERSION == 1
|
||||
// TODO: TR1 still has most levels in a single linear sequence
|
||||
switch (level->type) {
|
||||
default:
|
||||
level_table = &gs_file->levels;
|
||||
break;
|
||||
case GFL_DEMO:
|
||||
level_table = &gs_file->demos;
|
||||
break;
|
||||
}
|
||||
#elif TR_VERSION == 2
|
||||
switch (level->type) {
|
||||
case GFL_NORMAL:
|
||||
case GFL_SAVED:
|
||||
|
|
|
@ -146,9 +146,9 @@ void GameStringTable_LoadFromFile(const char *const path)
|
|||
JSON_OBJECT *root_obj = JSON_ValueAsObject(root);
|
||||
M_LoadTableFromJSON(root_obj, &gs_file->global);
|
||||
M_LoadLevelsFromJSON(root_obj, "levels", GFL_NORMAL, &gs_file->levels);
|
||||
M_LoadLevelsFromJSON(root_obj, "demos", GFL_DEMO, &gs_file->demos);
|
||||
#if TR_VERSION == 2
|
||||
// TODO: TR1 still has everything in a single linear sequence
|
||||
M_LoadLevelsFromJSON(root_obj, "demos", GFL_DEMO, &gs_file->demos);
|
||||
M_LoadLevelsFromJSON(
|
||||
root_obj, "cutscenes", GFL_CUTSCENE, &gs_file->cutscenes);
|
||||
#endif
|
||||
|
|
|
@ -69,8 +69,6 @@ typedef struct {
|
|||
#if TR_VERSION == 1
|
||||
GAME_FLOW_LEVEL_SETTINGS settings;
|
||||
|
||||
bool demo;
|
||||
|
||||
struct {
|
||||
uint32_t pickups;
|
||||
uint32_t kills;
|
||||
|
@ -110,13 +108,13 @@ typedef struct {
|
|||
int32_t cutscene_count;
|
||||
GAME_FLOW_LEVEL *cutscenes;
|
||||
};
|
||||
#endif
|
||||
|
||||
// demos
|
||||
struct {
|
||||
int32_t demo_count;
|
||||
GAME_FLOW_LEVEL *demos;
|
||||
};
|
||||
#endif
|
||||
|
||||
// FMVs
|
||||
struct {
|
||||
|
@ -134,7 +132,6 @@ typedef struct {
|
|||
// global settings
|
||||
struct {
|
||||
float demo_delay;
|
||||
bool has_demo;
|
||||
char *main_menu_background_path;
|
||||
bool enable_tr2_item_drops;
|
||||
bool convert_dropped_guns;
|
||||
|
|
|
@ -40,6 +40,7 @@ typedef struct {
|
|||
TEXTSTRING *text;
|
||||
} M_PRIV;
|
||||
|
||||
static int32_t m_LastDemoNum = 0;
|
||||
static M_PRIV m_Priv;
|
||||
|
||||
static void M_PrepareConfig(M_PRIV *const p);
|
||||
|
@ -70,8 +71,18 @@ static void M_PrepareResumeInfo(M_PRIV *const p)
|
|||
RESUME_INFO *const resume_info = GF_GetResumeInfo(p->level);
|
||||
p->old_resume_info = *resume_info;
|
||||
resume_info->flags.available = 1;
|
||||
resume_info->flags.costume = 0;
|
||||
resume_info->num_medis = 0;
|
||||
resume_info->num_big_medis = 0;
|
||||
resume_info->num_scions = 0;
|
||||
resume_info->flags.got_pistols = 1;
|
||||
resume_info->flags.got_shotgun = 0;
|
||||
resume_info->flags.got_magnums = 0;
|
||||
resume_info->flags.got_uzis = 0;
|
||||
resume_info->pistol_ammo = 1000;
|
||||
resume_info->shotgun_ammo = 0;
|
||||
resume_info->magnum_ammo = 0;
|
||||
resume_info->uzi_ammo = 0;
|
||||
resume_info->gun_status = LGS_ARMLESS;
|
||||
resume_info->equipped_gun_type = LGT_PISTOLS;
|
||||
resume_info->holsters_gun_type = LGT_PISTOLS;
|
||||
|
@ -235,41 +246,13 @@ void Demo_Unpause(void)
|
|||
int32_t Demo_ChooseLevel(const int32_t demo_num)
|
||||
{
|
||||
M_PRIV *const p = &m_Priv;
|
||||
|
||||
bool any_demos = false;
|
||||
for (int32_t i = g_GameFlow.first_level_num; i <= g_GameFlow.last_level_num;
|
||||
i++) {
|
||||
if (g_GameFlow.levels[i].demo) {
|
||||
any_demos = true;
|
||||
}
|
||||
}
|
||||
if (!any_demos) {
|
||||
if (GF_GetDemoCount() <= 0) {
|
||||
return -1;
|
||||
} else if (demo_num < 0 || demo_num >= GF_GetDemoCount()) {
|
||||
return (m_LastDemoNum++) % GF_GetDemoCount();
|
||||
} else {
|
||||
return demo_num;
|
||||
}
|
||||
|
||||
if (demo_num >= 0) {
|
||||
int32_t j = 0;
|
||||
for (int32_t i = g_GameFlow.first_level_num;
|
||||
i <= g_GameFlow.last_level_num; i++) {
|
||||
if (g_GameFlow.levels[i].demo) {
|
||||
if (j == demo_num) {
|
||||
return i;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// pick the next demo
|
||||
int16_t level_num = p->level != NULL ? p->level->num : -1;
|
||||
do {
|
||||
level_num++;
|
||||
if (level_num > g_GameFlow.last_level_num) {
|
||||
level_num = g_GameFlow.first_level_num;
|
||||
}
|
||||
} while (!g_GameFlow.levels[level_num].demo);
|
||||
return level_num;
|
||||
}
|
||||
|
||||
GAME_FLOW_COMMAND Demo_Control(void)
|
||||
|
|
|
@ -56,6 +56,8 @@ int32_t GF_GetLevelCount(const GAME_FLOW_LEVEL_TYPE level_type)
|
|||
return 1;
|
||||
case GFL_GYM:
|
||||
return 1;
|
||||
case GFL_DEMO:
|
||||
return g_GameFlow.demo_count;
|
||||
default:
|
||||
return g_GameFlow.level_count;
|
||||
}
|
||||
|
@ -63,14 +65,7 @@ int32_t GF_GetLevelCount(const GAME_FLOW_LEVEL_TYPE level_type)
|
|||
|
||||
int32_t GF_GetDemoCount(void)
|
||||
{
|
||||
int32_t demo_count = 0;
|
||||
for (int32_t i = g_GameFlow.first_level_num; i <= g_GameFlow.last_level_num;
|
||||
i++) {
|
||||
if (g_GameFlow.levels[i].demo) {
|
||||
demo_count++;
|
||||
}
|
||||
}
|
||||
return demo_count;
|
||||
return g_GameFlow.demo_count;
|
||||
}
|
||||
|
||||
void GF_SetLevelTitle(GAME_FLOW_LEVEL *const level, const char *const title)
|
||||
|
@ -96,6 +91,13 @@ GAME_FLOW_LEVEL *GF_GetLevel(
|
|||
case GFL_TITLE:
|
||||
return &g_GameFlow.levels[g_GameFlow.title_level_num];
|
||||
|
||||
case GFL_DEMO:
|
||||
if (num < 0 || num >= GF_GetDemoCount()) {
|
||||
LOG_ERROR("Invalid demo number: %d", num);
|
||||
return NULL;
|
||||
}
|
||||
return &g_GameFlow.demos[num];
|
||||
|
||||
default:
|
||||
if (num < 0 || num >= GF_GetLevelCount(level_type)) {
|
||||
LOG_ERROR("Invalid level number: %d", num);
|
||||
|
|
|
@ -524,17 +524,6 @@ static void M_LoadLevel(
|
|||
level->music_track = tmp;
|
||||
}
|
||||
|
||||
{
|
||||
const int32_t tmp =
|
||||
JSON_ObjectGetBool(jlvl_obj, "demo", JSON_INVALID_BOOL);
|
||||
if (tmp != JSON_INVALID_BOOL) {
|
||||
level->demo = tmp;
|
||||
gf->has_demo |= tmp;
|
||||
} else {
|
||||
level->demo = false;
|
||||
}
|
||||
}
|
||||
|
||||
level->settings = gf->settings;
|
||||
M_LoadSettings(jlvl_obj, &level->settings);
|
||||
|
||||
|
@ -614,7 +603,6 @@ static void M_LoadLevels(JSON_OBJECT *const obj, GAME_FLOW *const gf)
|
|||
JSON_ARRAY_ELEMENT *jlvl_elem = jlvl_arr->start;
|
||||
int32_t level_num = 0;
|
||||
|
||||
gf->has_demo = 0;
|
||||
gf->gym_level_num = -1;
|
||||
gf->first_level_num = -1;
|
||||
gf->last_level_num = -1;
|
||||
|
@ -633,6 +621,13 @@ static void M_LoadLevels(JSON_OBJECT *const obj, GAME_FLOW *const gf)
|
|||
}
|
||||
}
|
||||
|
||||
static void M_LoadDemos(JSON_OBJECT *obj, GAME_FLOW *const gf)
|
||||
{
|
||||
M_LoadArray(
|
||||
obj, "demos", sizeof(GAME_FLOW_LEVEL), (M_LOAD_ARRAY_FUNC)M_LoadLevel,
|
||||
gf, &gf->demo_count, (void **)&gf->demos, (void *)(intptr_t)GFL_DEMO);
|
||||
}
|
||||
|
||||
static void M_LoadFMV(
|
||||
JSON_OBJECT *const obj, const GAME_FLOW *const gf, GAME_FLOW_FMV *const fmv,
|
||||
size_t idx, void *const user_arg)
|
||||
|
@ -730,6 +725,7 @@ void GF_Load(const char *const path)
|
|||
GAME_FLOW *const gf = &g_GameFlow;
|
||||
M_LoadRoot(root_obj, gf);
|
||||
M_LoadLevels(root_obj, gf);
|
||||
M_LoadDemos(root_obj, gf);
|
||||
M_LoadFMVs(root_obj, gf);
|
||||
|
||||
if (root != NULL) {
|
||||
|
|
|
@ -403,9 +403,9 @@ GAME_FLOW_COMMAND GF_LoadLevel(
|
|||
|
||||
GAME_FLOW_COMMAND GF_DoDemoSequence(int32_t demo_num)
|
||||
{
|
||||
const int32_t level_num = Demo_ChooseLevel(demo_num);
|
||||
if (level_num < 0) {
|
||||
demo_num = Demo_ChooseLevel(demo_num);
|
||||
if (demo_num < 0) {
|
||||
return (GAME_FLOW_COMMAND) { .action = GF_EXIT_TO_TITLE };
|
||||
}
|
||||
return GF_InterpretSequence(level_num, GFL_DEMO);
|
||||
return GF_InterpretSequence(demo_num, GFL_DEMO);
|
||||
}
|
||||
|
|
|
@ -815,7 +815,7 @@ static GAME_FLOW_COMMAND M_Control(INV_RING *const ring)
|
|||
|
||||
static bool M_CheckDemoTimer(const INV_RING *const ring)
|
||||
{
|
||||
if (!g_Config.gameplay.enable_demo || !g_GameFlow.has_demo) {
|
||||
if (!g_Config.gameplay.enable_demo || GF_GetDemoCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue