Skip to content

Commit

Permalink
game-flow: merge reader via libtrx
Browse files Browse the repository at this point in the history
  • Loading branch information
rr- committed Jan 29, 2025
1 parent c15b833 commit 4ec06bd
Show file tree
Hide file tree
Showing 18 changed files with 647 additions and 1,012 deletions.
413 changes: 183 additions & 230 deletions src/tr2/game/game_flow/reader.c → src/libtrx/game/game_flow/reader.c

Large diffs are not rendered by default.

286 changes: 286 additions & 0 deletions src/libtrx/game/game_flow/reader_tr1.def.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
// NOTE: this is an included file, not a compile unit on its own.
// This is to avoid exposing symbols.

static DECLARE_SEQUENCE_EVENT_HANDLER_FUNC(M_HandleTotalStatsEvent);
static DECLARE_SEQUENCE_EVENT_HANDLER_FUNC(M_HandleMeshSwapEvent);

static void M_LoadLevelItemDrops(
JSON_OBJECT *obj, const GAME_FLOW *gf, GF_LEVEL *level);

static GF_SEQUENCE_EVENT_TYPE m_LevelArgSequenceEvents[] = {
GFS_LOAD_LEVEL,
GFS_PLAY_LEVEL,
(GF_SEQUENCE_EVENT_TYPE)-1,
};

static GF_LEVEL_SETTINGS m_DefaultSettings = {
.water_color = { .r = 0.6, .g = 0.7, .b = 1.0 },
.draw_distance_fade = 12.0f,
.draw_distance_max = 20.0f,
};

static M_SEQUENCE_EVENT_HANDLER m_SequenceEventHandlers[] = {
// clang-format off
// Events without arguments
{ GFS_FLIP_MAP, NULL, NULL },
{ GFS_REMOVE_WEAPONS, NULL, NULL },
{ GFS_REMOVE_SCIONS, NULL, NULL },
{ GFS_REMOVE_AMMO, NULL, NULL },
{ GFS_REMOVE_MEDIPACKS, NULL, NULL },
{ GFS_EXIT_TO_TITLE, NULL, NULL },
{ GFS_LEVEL_STATS, NULL, NULL },
{ GFS_LEVEL_COMPLETE, NULL, NULL },

// Events with integer arguments
{ GFS_LOAD_LEVEL, M_HandleIntEvent, "level_id" },
{ GFS_PLAY_LEVEL, M_HandleIntEvent, "level_id" },
{ GFS_PLAY_CUTSCENE, M_HandleIntEvent, "cutscene_id" },
{ GFS_PLAY_FMV, M_HandleIntEvent, "fmv_id" },
{ GFS_PLAY_MUSIC, M_HandleIntEvent, "music_track" },
{ GFS_SET_CAMERA_ANGLE, M_HandleIntEvent, "value" },
{ GFS_SETUP_BACON_LARA, M_HandleIntEvent, "anchor_room" },

// Special cases with custom handlers
{ GFS_LOADING_SCREEN, M_HandlePictureEvent, NULL },
{ GFS_DISPLAY_PICTURE, M_HandlePictureEvent, NULL },
{ GFS_TOTAL_STATS, M_HandleTotalStatsEvent, NULL },
{ GFS_ADD_ITEM, M_HandleAddItemEvent, NULL },
{ GFS_MESH_SWAP, M_HandleMeshSwapEvent, NULL },

// Sentinel to mark the end of the table
{ (GF_SEQUENCE_EVENT_TYPE)-1, NULL, NULL },
// clang-format on
};

static DECLARE_SEQUENCE_EVENT_HANDLER_FUNC(M_HandleTotalStatsEvent)
{
const char *const path =
JSON_ObjectGetString(event_obj, "background_path", NULL);
if (path == NULL) {
Shell_ExitSystem("Missing picture path");
return -1;
}
if (event != NULL) {
char *const event_data = extra_data;
strcpy(event_data, path);
event->data = event_data;
}
return strlen(path) + 1;
}

GF_SEQUENCE_EVENT_TYPE *M_GetLevelArgSequenceEvents(void)
{
return m_LevelArgSequenceEvents;
}

static DECLARE_SEQUENCE_EVENT_HANDLER_FUNC(M_HandleMeshSwapEvent)
{
const GAME_OBJECT_ID object1_id =
M_GetObjectFromJSONValue(JSON_ObjectGetValue(event_obj, "object1_id"));
if (object1_id == NO_OBJECT) {
Shell_ExitSystem("'object1_id' is invalid");
}

const GAME_OBJECT_ID object2_id =
M_GetObjectFromJSONValue(JSON_ObjectGetValue(event_obj, "object2_id"));
if (object2_id == NO_OBJECT) {
Shell_ExitSystem("'object2_id' is invalid");
}

const int32_t mesh_num =
JSON_ObjectGetInt(event_obj, "mesh_id", JSON_INVALID_NUMBER);
if (mesh_num == JSON_INVALID_NUMBER) {
Shell_ExitSystem("'mesh_id' must be a number");
}

if (event != NULL) {
GF_MESH_SWAP_DATA *const swap_data = extra_data;
swap_data->object1_id = object1_id;
swap_data->object2_id = object2_id;
swap_data->mesh_num = mesh_num;
event->data = swap_data;
}
return sizeof(GF_MESH_SWAP_DATA);
}

static void M_LoadSettings(
JSON_OBJECT *const obj, GF_LEVEL_SETTINGS *const settings)
{
{
const double value = JSON_ObjectGetDouble(
obj, "draw_distance_fade", JSON_INVALID_NUMBER);
if (value != JSON_INVALID_NUMBER) {
settings->draw_distance_fade = value;
}
}

{
const double value =
JSON_ObjectGetDouble(obj, "draw_distance_max", JSON_INVALID_NUMBER);
if (value != JSON_INVALID_NUMBER) {
settings->draw_distance_max = value;
}
}

{
JSON_ARRAY *const tmp_arr = JSON_ObjectGetArray(obj, "water_color");
if (tmp_arr != NULL) {
settings->water_color.r =
JSON_ArrayGetDouble(tmp_arr, 0, settings->water_color.r);
settings->water_color.g =
JSON_ArrayGetDouble(tmp_arr, 1, settings->water_color.g);
settings->water_color.b =
JSON_ArrayGetDouble(tmp_arr, 2, settings->water_color.b);
}
}
}

static void M_LoadLevelGameSpecifics(
JSON_OBJECT *const jlvl_obj, const GAME_FLOW *const gf,
GF_LEVEL *const level)
{
level->settings = gf->settings;
M_LoadSettings(jlvl_obj, &level->settings);

level->unobtainable.pickups =
JSON_ObjectGetInt(jlvl_obj, "unobtainable_pickups", 0);
level->unobtainable.kills =
JSON_ObjectGetInt(jlvl_obj, "unobtainable_kills", 0);
level->unobtainable.secrets =
JSON_ObjectGetInt(jlvl_obj, "unobtainable_secrets", 0);

{
JSON_VALUE *const tmp = JSON_ObjectGetValue(jlvl_obj, "lara_type");
if (tmp == NULL) {
level->lara_type = O_LARA;
} else {
level->lara_type = M_GetObjectFromJSONValue(tmp);
}
if (level->lara_type == NO_OBJECT) {
Shell_ExitSystemFmt(
"level %d: 'lara_type' must be a valid game object id",
level->num);
}
}

M_LoadLevelItemDrops(jlvl_obj, gf, level);
}

static M_SEQUENCE_EVENT_HANDLER *M_GetSequenceEventHandlers(void)
{
return m_SequenceEventHandlers;
}

static void M_LoadLevelItemDrops(
JSON_OBJECT *const jlvl_obj, const GAME_FLOW *const gf,
GF_LEVEL *const level)
{
JSON_ARRAY *const drops = JSON_ObjectGetArray(jlvl_obj, "item_drops");
level->item_drops.count = 0;

if (drops != NULL && gf->enable_tr2_item_drops) {
LOG_WARNING(
"TR2 item drops are enabled: gameflow-defined drops for level "
"%d will be ignored",
level->num);
return;
}
if (drops == NULL) {
return;
}

level->item_drops.count = (signed)drops->length;
level->item_drops.data =
Memory_Alloc(sizeof(GF_DROP_ITEM_DATA) * (signed)drops->length);

for (int32_t i = 0; i < level->item_drops.count; i++) {
GF_DROP_ITEM_DATA *data = &level->item_drops.data[i];
JSON_OBJECT *jlvl_data = JSON_ArrayGetObject(drops, i);

data->enemy_num =
JSON_ObjectGetInt(jlvl_data, "enemy_num", JSON_INVALID_NUMBER);
if (data->enemy_num == JSON_INVALID_NUMBER) {
Shell_ExitSystemFmt(
"level %d, item drop %d: 'enemy_num' must be a number",
level->num, i);
}

JSON_ARRAY *object_arr = JSON_ObjectGetArray(jlvl_data, "object_ids");
if (!object_arr) {
Shell_ExitSystemFmt(
"level %d, item drop %d: 'object_ids' must be an array",
level->num, i);
}

data->count = (signed)object_arr->length;
data->object_ids = Memory_Alloc(sizeof(int16_t) * data->count);
for (int32_t j = 0; j < data->count; j++) {
const GAME_OBJECT_ID id =
M_GetObjectFromJSONValue(JSON_ArrayGetValue(object_arr, j));
if (id == NO_OBJECT) {
Shell_ExitSystemFmt(
"level %d, item drop %d, index %d: 'object_id' "
"must be a valid object id",
level->num, i, j);
}
data->object_ids[j] = (int16_t)id;
}
}
}

static void M_LoadRoot(JSON_OBJECT *const obj, GAME_FLOW *const gf)
{
const char *tmp_s;
double tmp_d;
JSON_ARRAY *tmp_arr;

tmp_s = JSON_ObjectGetString(obj, "main_menu_picture", JSON_INVALID_STRING);
if (tmp_s == JSON_INVALID_STRING) {
Shell_ExitSystem("'main_menu_picture' must be a string");
}
gf->main_menu_background_path = Memory_DupStr(tmp_s);

tmp_s =
JSON_ObjectGetString(obj, "savegame_fmt_legacy", JSON_INVALID_STRING);
if (tmp_s == JSON_INVALID_STRING) {
Shell_ExitSystem("'savegame_fmt_legacy' must be a string");
}
gf->savegame_fmt_legacy = Memory_DupStr(tmp_s);

tmp_s = JSON_ObjectGetString(obj, "savegame_fmt_bson", JSON_INVALID_STRING);
if (tmp_s == JSON_INVALID_STRING) {
Shell_ExitSystem("'savegame_fmt_bson' must be a string");
}
gf->savegame_fmt_bson = Memory_DupStr(tmp_s);

tmp_d = JSON_ObjectGetDouble(obj, "demo_delay", -1.0);
if (tmp_d < 0.0) {
Shell_ExitSystem("'demo_delay' must be a positive number");
}
gf->demo_delay = tmp_d;

gf->settings = m_DefaultSettings;
M_LoadSettings(obj, &gf->settings);

tmp_arr = JSON_ObjectGetArray(obj, "injections");
if (tmp_arr) {
gf->injections.count = tmp_arr->length;
gf->injections.data_paths =
Memory_Alloc(sizeof(char *) * tmp_arr->length);
for (size_t i = 0; i < tmp_arr->length; i++) {
const char *const str = JSON_ArrayGetString(tmp_arr, i, NULL);
gf->injections.data_paths[i] = Memory_DupStr(str);
}
} else {
gf->injections.count = 0;
}

gf->enable_tr2_item_drops =
JSON_ObjectGetBool(obj, "enable_tr2_item_drops", false);
gf->convert_dropped_guns =
JSON_ObjectGetBool(obj, "convert_dropped_guns", false);
gf->enable_killer_pushblocks =
JSON_ObjectGetBool(obj, "enable_killer_pushblocks", true);

M_LoadGlobalInjections(obj, gf);
}
Loading

0 comments on commit 4ec06bd

Please sign in to comment.