From a1e72ac8ba4264cf745472cc9ced4971d3431356 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Wed, 14 Aug 2024 17:04:56 +0200 Subject: [PATCH] gun: fix holsters and back mesh problems Resolves #1437. --- CHANGELOG.md | 4 +- src/game/gameflow.c | 16 +--- src/game/gun.h | 9 +- src/game/gun/gun.c | 115 +++++++++++++++++++----- src/game/gun/gun_pistols.c | 36 ++------ src/game/gun/gun_rifle.c | 26 ++---- src/game/inventory/inventory_func.c | 5 ++ src/game/lara.h | 3 + src/game/lara/lara.c | 97 ++++++++++---------- src/game/lara/lara_state.c | 46 ++++------ src/game/objects/creatures/bacon_lara.c | 10 +-- src/game/objects/general/pickup.c | 33 +------ src/game/phase/phase_demo.c | 4 +- src/game/savegame/savegame.c | 50 +++++++++-- src/game/savegame/savegame_bson.c | 19 +++- src/game/savegame/savegame_legacy.c | 8 +- src/global/types.h | 12 ++- 17 files changed, 279 insertions(+), 214 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e07aab05a..424f7e259 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,11 @@ - fixed the ingame timer being skewed upon inventory open (#1420, regression from 4.1) - fixed Lara able to reach triggers through closed doors (#1419, regression from 1.1.4) - fixed Lara voiding when loading the game on a closed door (#1419) -- fixed underwater caustics not resumed smoothly when unpausing (#1423, regression 3.2) +- fixed underwater caustics not resumed smoothly when unpausing (#1423, regression from 3.2) - fixed collision issues with drawbridges, trapdoors, and bridges when stacked over each other, over slopes, and near the ground (#606) - fixed an issue with a missing Spanish config tool translation for the target mode (#1439) +- fixed carrying over unexpected guns in holsters to the next level under rare scenarios (#1437, regression from 2.4) +- fixed item cheats not updating Lara holster and backpack meshes (#1437) - improved initial level load time by lazy-loading audio samples (LostArtefacts/TR2X#114) ## [4.2](https://github.com/LostArtefacts/TR1X/compare/4.1.2...4.2) - 2024-07-14 diff --git a/src/game/gameflow.c b/src/game/gameflow.c index 4d5377715..f689fc301 100644 --- a/src/game/gameflow.c +++ b/src/game/gameflow.c @@ -1115,6 +1115,10 @@ GameFlow_InterpretSequence(int32_t level_num, GAMEFLOW_LEVEL_TYPE level_type) break; case GFS_LOOP_GAME: + if (level_type != GFL_SAVED + && level_num != g_GameFlow.first_level_num) { + Lara_RevertToPistolsIfNeeded(); + } Phase_Set(PHASE_GAME, NULL); ret = Phase_Run(); if (ret.command != GF_PHASE_CONTINUE) { @@ -1258,18 +1262,6 @@ GameFlow_InterpretSequence(int32_t level_num, GAMEFLOW_LEVEL_TYPE level_type) (const GAMEFLOW_GIVE_ITEM_DATA *)seq->data; Inv_AddItemNTimes( give_item_data->object_num, give_item_data->quantity); - if (g_Lara.gun_type == LGT_UNARMED) { - if (Inv_RequestItem(O_PISTOL_ITEM)) { - g_GameInfo.current[level_num].gun_type = LGT_PISTOLS; - } else if (Inv_RequestItem(O_SHOTGUN_ITEM)) { - g_GameInfo.current[level_num].gun_type = LGT_SHOTGUN; - } else if (Inv_RequestItem(O_MAGNUM_ITEM)) { - g_GameInfo.current[level_num].gun_type = LGT_MAGNUMS; - } else if (Inv_RequestItem(O_UZI_ITEM)) { - g_GameInfo.current[level_num].gun_type = LGT_UZIS; - } - Lara_InitialiseMeshes(level_num); - } } break; diff --git a/src/game/gun.h b/src/game/gun.h index 504658f11..996f1de4f 100644 --- a/src/game/gun.h +++ b/src/game/gun.h @@ -14,5 +14,10 @@ int32_t Gun_FireWeapon( void Gun_HitTarget(ITEM_INFO *item, GAME_VECTOR *hitpos, int16_t damage); void Gun_DrawFlash(LARA_GUN_TYPE weapon_type, int32_t clip); GAME_OBJECT_ID Gun_GetLaraAnim(LARA_GUN_TYPE gun_type); -GAME_OBJECT_ID Gun_GetPistolsAnim(LARA_GUN_TYPE gun_type); -GAME_OBJECT_ID Gun_GetRifleAnim(LARA_GUN_TYPE gun_type); +GAME_OBJECT_ID Gun_GetWeaponAnim(LARA_GUN_TYPE gun_type); +void Gun_UpdateLaraMeshes(GAME_OBJECT_ID object_id); +void Gun_SetLaraBackMesh(LARA_GUN_TYPE weapon_type); +void Gun_SetLaraHandLMesh(LARA_GUN_TYPE weapon_type); +void Gun_SetLaraHandRMesh(LARA_GUN_TYPE weapon_type); +void Gun_SetLaraHolsterLMesh(LARA_GUN_TYPE weapon_type); +void Gun_SetLaraHolsterRMesh(LARA_GUN_TYPE weapon_type); diff --git a/src/game/gun/gun.c b/src/game/gun/gun.c index 15ae09bcd..9a0979510 100644 --- a/src/game/gun/gun.c +++ b/src/game/gun/gun.c @@ -4,12 +4,14 @@ #include "game/gun/gun_rifle.h" #include "game/input.h" #include "game/inventory.h" +#include "game/lara.h" #include "game/output.h" #include "game/random.h" #include "global/const.h" #include "global/vars.h" #include "math/matrix.h" +#include #include #include @@ -73,6 +75,9 @@ void Gun_Control(void) g_Lara.gun_status = LGS_UNDRAW; } break; + + default: + break; } } @@ -94,12 +99,15 @@ void Gun_Control(void) } Gun_Rifle_Draw(g_Lara.gun_type); break; + + default: + break; } break; case LGS_UNDRAW: - g_Lara.mesh_ptrs[LM_HEAD] = - g_Meshes[g_Objects[O_LARA].mesh_index + LM_HEAD]; + Lara_SwapSingleMesh(LM_HEAD, O_LARA); + switch (g_Lara.gun_type) { case LGT_PISTOLS: case LGT_MAGNUMS: @@ -110,17 +118,19 @@ void Gun_Control(void) case LGT_SHOTGUN: Gun_Rifle_Undraw(g_Lara.gun_type); break; + + default: + break; } break; case LGS_READY: - g_Lara.mesh_ptrs[LM_HEAD] = - g_Meshes[g_Objects[O_LARA].mesh_index + LM_HEAD]; + Lara_SwapSingleMesh(LM_HEAD, O_LARA); + switch (g_Lara.gun_type) { case LGT_PISTOLS: if (g_Lara.pistols.ammo && g_Input.action) { - g_Lara.mesh_ptrs[LM_HEAD] = - g_Meshes[g_Objects[O_UZI_ANIM].mesh_index + LM_HEAD]; + Lara_SwapSingleMesh(LM_HEAD, O_UZI_ANIM); } if (g_Camera.type != CAM_CINEMATIC && g_Camera.type != CAM_LOOK) { g_Camera.type = CAM_COMBAT; @@ -130,8 +140,7 @@ void Gun_Control(void) case LGT_MAGNUMS: if (g_Lara.magnums.ammo && g_Input.action) { - g_Lara.mesh_ptrs[LM_HEAD] = - g_Meshes[g_Objects[O_UZI_ANIM].mesh_index + LM_HEAD]; + Lara_SwapSingleMesh(LM_HEAD, O_UZI_ANIM); } if (g_Camera.type != CAM_CINEMATIC && g_Camera.type != CAM_LOOK) { g_Camera.type = CAM_COMBAT; @@ -141,8 +150,7 @@ void Gun_Control(void) case LGT_UZIS: if (g_Lara.uzis.ammo && g_Input.action) { - g_Lara.mesh_ptrs[LM_HEAD] = - g_Meshes[g_Objects[O_UZI_ANIM].mesh_index + LM_HEAD]; + Lara_SwapSingleMesh(LM_HEAD, O_UZI_ANIM); } if (g_Camera.type != CAM_CINEMATIC && g_Camera.type != CAM_LOOK) { g_Camera.type = CAM_COMBAT; @@ -152,14 +160,16 @@ void Gun_Control(void) case LGT_SHOTGUN: if (g_Lara.shotgun.ammo && g_Input.action) { - g_Lara.mesh_ptrs[LM_HEAD] = - g_Meshes[g_Objects[O_UZI_ANIM].mesh_index + LM_HEAD]; + Lara_SwapSingleMesh(LM_HEAD, O_UZI_ANIM); } if (g_Camera.type != CAM_CINEMATIC && g_Camera.type != CAM_LOOK) { g_Camera.type = CAM_COMBAT; } Gun_Rifle_Control(g_Lara.gun_type); break; + + default: + break; } break; } @@ -208,7 +218,7 @@ GAME_OBJECT_ID Gun_GetLaraAnim(const LARA_GUN_TYPE gun_type) } } -GAME_OBJECT_ID Gun_GetPistolsAnim(const LARA_GUN_TYPE gun_type) +GAME_OBJECT_ID Gun_GetWeaponAnim(const LARA_GUN_TYPE gun_type) { switch (gun_type) { case LGT_PISTOLS: @@ -217,14 +227,6 @@ GAME_OBJECT_ID Gun_GetPistolsAnim(const LARA_GUN_TYPE gun_type) return O_MAGNUM_ANIM; case LGT_UZIS: return O_UZI_ANIM; - default: - return NO_OBJECT; - } -} - -GAME_OBJECT_ID Gun_GetRifleAnim(const LARA_GUN_TYPE gun_type) -{ - switch (gun_type) { case LGT_SHOTGUN: return O_SHOTGUN_ANIM; default: @@ -271,3 +273,74 @@ void Gun_DrawFlash(LARA_GUN_TYPE weapon_type, int32_t clip) Output_DrawPolygons(g_Meshes[g_Objects[O_GUN_FLASH].mesh_index], clip); } } + +void Gun_UpdateLaraMeshes(const GAME_OBJECT_ID object_id) +{ + const bool lara_has_pistols = Inv_RequestItem(O_PISTOL_ITEM) + || Inv_RequestItem(O_MAGNUM_ITEM) || Inv_RequestItem(O_UZI_ITEM); + + LARA_GUN_TYPE back_gun_type = LGT_UNARMED; + LARA_GUN_TYPE holsters_gun_type = LGT_UNARMED; + + if (!Inv_RequestItem(O_SHOTGUN_ITEM) && object_id == O_SHOTGUN_ITEM) { + back_gun_type = LGT_SHOTGUN; + } else if (!lara_has_pistols && object_id == O_PISTOL_ITEM) { + holsters_gun_type = LGT_PISTOLS; + } else if (!lara_has_pistols && object_id == O_MAGNUM_ITEM) { + holsters_gun_type = LGT_MAGNUMS; + } else if (!lara_has_pistols && object_id == O_UZI_ITEM) { + holsters_gun_type = LGT_UZIS; + } + + if (back_gun_type != LGT_UNARMED) { + Gun_SetLaraBackMesh(back_gun_type); + } + + if (holsters_gun_type != LGT_UNARMED) { + Gun_SetLaraHolsterLMesh(holsters_gun_type); + Gun_SetLaraHolsterRMesh(holsters_gun_type); + } +} + +void Gun_SetLaraHandLMesh(const LARA_GUN_TYPE weapon_type) +{ + const GAME_OBJECT_ID object_id = + weapon_type == LGT_UNARMED ? O_LARA : Gun_GetWeaponAnim(weapon_type); + assert(object_id != NO_OBJECT); + Lara_SwapSingleMesh(LM_HAND_L, object_id); +} + +void Gun_SetLaraHandRMesh(const LARA_GUN_TYPE weapon_type) +{ + const GAME_OBJECT_ID object_id = + weapon_type == LGT_UNARMED ? O_LARA : Gun_GetWeaponAnim(weapon_type); + assert(object_id != NO_OBJECT); + Lara_SwapSingleMesh(LM_HAND_R, object_id); +} + +void Gun_SetLaraBackMesh(const LARA_GUN_TYPE weapon_type) +{ + const GAME_OBJECT_ID object_id = + weapon_type == LGT_UNARMED ? O_LARA : Gun_GetWeaponAnim(weapon_type); + assert(object_id != NO_OBJECT); + Lara_SwapSingleMesh(LM_TORSO, object_id); + g_Lara.back_gun_type = weapon_type; +} + +void Gun_SetLaraHolsterLMesh(const LARA_GUN_TYPE weapon_type) +{ + const GAME_OBJECT_ID object_id = + weapon_type == LGT_UNARMED ? O_LARA : Gun_GetWeaponAnim(weapon_type); + assert(object_id != NO_OBJECT); + Lara_SwapSingleMesh(LM_THIGH_L, object_id); + g_Lara.holsters_gun_type = weapon_type; +} + +void Gun_SetLaraHolsterRMesh(const LARA_GUN_TYPE weapon_type) +{ + const GAME_OBJECT_ID object_id = + weapon_type == LGT_UNARMED ? O_LARA : Gun_GetWeaponAnim(weapon_type); + assert(object_id != NO_OBJECT); + Lara_SwapSingleMesh(LM_THIGH_R, object_id); + g_Lara.holsters_gun_type = weapon_type; +} diff --git a/src/game/gun/gun_pistols.c b/src/game/gun/gun_pistols.c index 2126e3425..a156937b7 100644 --- a/src/game/gun/gun_pistols.c +++ b/src/game/gun/gun_pistols.c @@ -110,43 +110,23 @@ void Gun_Pistols_Ready(const LARA_GUN_TYPE weapon_type) void Gun_Pistols_DrawMeshes(const LARA_GUN_TYPE weapon_type) { - const GAME_OBJECT_ID object_id = Gun_GetPistolsAnim(weapon_type); - if (object_id == NO_OBJECT) { - return; - } - g_Lara.mesh_ptrs[LM_HAND_L] = - g_Meshes[g_Objects[object_id].mesh_index + LM_HAND_L]; - g_Lara.mesh_ptrs[LM_HAND_R] = - g_Meshes[g_Objects[object_id].mesh_index + LM_HAND_R]; - g_Lara.mesh_ptrs[LM_THIGH_L] = - g_Meshes[g_Objects[O_LARA].mesh_index + LM_THIGH_L]; - g_Lara.mesh_ptrs[LM_THIGH_R] = - g_Meshes[g_Objects[O_LARA].mesh_index + LM_THIGH_R]; + Gun_SetLaraHandLMesh(weapon_type); + Gun_SetLaraHandRMesh(weapon_type); + Gun_SetLaraHolsterLMesh(LGT_UNARMED); + Gun_SetLaraHolsterRMesh(LGT_UNARMED); } void Gun_Pistols_UndrawMeshLeft(const LARA_GUN_TYPE weapon_type) { - const GAME_OBJECT_ID object_id = Gun_GetPistolsAnim(weapon_type); - if (object_id == NO_OBJECT) { - return; - } - g_Lara.mesh_ptrs[LM_THIGH_L] = - g_Meshes[g_Objects[object_id].mesh_index + LM_THIGH_L]; - g_Lara.mesh_ptrs[LM_HAND_L] = - g_Meshes[g_Objects[O_LARA].mesh_index + LM_HAND_L]; + Gun_SetLaraHandLMesh(LGT_UNARMED); + Gun_SetLaraHolsterLMesh(weapon_type); Sound_Effect(SFX_LARA_HOLSTER, &g_LaraItem->pos, SPM_NORMAL); } void Gun_Pistols_UndrawMeshRight(const LARA_GUN_TYPE weapon_type) { - const GAME_OBJECT_ID object_id = Gun_GetPistolsAnim(weapon_type); - if (object_id == NO_OBJECT) { - return; - } - g_Lara.mesh_ptrs[LM_THIGH_R] = - g_Meshes[g_Objects[object_id].mesh_index + LM_THIGH_R]; - g_Lara.mesh_ptrs[LM_HAND_R] = - g_Meshes[g_Objects[O_LARA].mesh_index + LM_HAND_R]; + Gun_SetLaraHandRMesh(LGT_UNARMED); + Gun_SetLaraHolsterRMesh(weapon_type); Sound_Effect(SFX_LARA_HOLSTER, &g_LaraItem->pos, SPM_NORMAL); } diff --git a/src/game/gun/gun_rifle.c b/src/game/gun/gun_rifle.c index d8b614f4c..730f84d35 100644 --- a/src/game/gun/gun_rifle.c +++ b/src/game/gun/gun_rifle.c @@ -84,30 +84,16 @@ void Gun_Rifle_Undraw(const LARA_GUN_TYPE weapon_type) void Gun_Rifle_DrawMeshes(const LARA_GUN_TYPE weapon_type) { - const GAME_OBJECT_ID object_id = Gun_GetRifleAnim(weapon_type); - if (object_id == NO_OBJECT) { - return; - } - g_Lara.mesh_ptrs[LM_HAND_L] = - g_Meshes[g_Objects[object_id].mesh_index + LM_HAND_L]; - g_Lara.mesh_ptrs[LM_HAND_R] = - g_Meshes[g_Objects[object_id].mesh_index + LM_HAND_R]; - g_Lara.mesh_ptrs[LM_TORSO] = - g_Meshes[g_Objects[O_LARA].mesh_index + LM_TORSO]; + Gun_SetLaraHandLMesh(weapon_type); + Gun_SetLaraHandRMesh(weapon_type); + Gun_SetLaraBackMesh(LGT_UNARMED); } void Gun_Rifle_UndrawMeshes(const LARA_GUN_TYPE weapon_type) { - const GAME_OBJECT_ID object_id = Gun_GetRifleAnim(weapon_type); - if (object_id == NO_OBJECT) { - return; - } - g_Lara.mesh_ptrs[LM_HAND_L] = - g_Meshes[g_Objects[O_LARA].mesh_index + LM_HAND_L]; - g_Lara.mesh_ptrs[LM_HAND_R] = - g_Meshes[g_Objects[O_LARA].mesh_index + LM_HAND_R]; - g_Lara.mesh_ptrs[LM_TORSO] = - g_Meshes[g_Objects[object_id].mesh_index + LM_TORSO]; + Gun_SetLaraHandLMesh(LGT_UNARMED); + Gun_SetLaraHandRMesh(LGT_UNARMED); + Gun_SetLaraBackMesh(weapon_type); } void Gun_Rifle_Ready(const LARA_GUN_TYPE weapon_type) diff --git a/src/game/inventory/inventory_func.c b/src/game/inventory/inventory_func.c index f7c8bc06c..14927807e 100644 --- a/src/game/inventory/inventory_func.c +++ b/src/game/inventory/inventory_func.c @@ -1,3 +1,4 @@ +#include "game/gun.h" #include "game/inventory.h" #include "game/inventory/inventory_vars.h" #include "game/items.h" @@ -11,6 +12,10 @@ bool Inv_AddItem(const GAME_OBJECT_ID object_id) { + if (Object_IsObjectType(object_id, g_GunObjects)) { + Gun_UpdateLaraMeshes(object_id); + } + const GAME_OBJECT_ID inv_object_id = Inv_GetItemOption(object_id); if (!g_Objects[inv_object_id].loaded) { return false; diff --git a/src/game/lara.h b/src/game/lara.h index 0f1d84d3c..ba00d7c93 100644 --- a/src/game/lara.h +++ b/src/game/lara.h @@ -19,6 +19,7 @@ void Lara_InitialiseInventory(int32_t level_num); void Lara_InitialiseMeshes(int32_t level_num); void Lara_SwapMeshExtra(void); +void Lara_SwapSingleMesh(LARA_MESH mesh, GAME_OBJECT_ID); bool Lara_IsNearItem(const XYZ_32 *pos, int32_t distance); void Lara_UseItem(GAME_OBJECT_ID object_num); int16_t Lara_GetNearestEnemy(void); @@ -30,3 +31,5 @@ bool Lara_MovePosition(ITEM_INFO *item, XYZ_32 *vec); void Lara_Push(ITEM_INFO *item, COLL_INFO *coll, bool spaz_on, bool big_push); void Lara_TakeDamage(int16_t damage, bool hit_status); + +void Lara_RevertToPistolsIfNeeded(void); diff --git a/src/game/lara/lara.c b/src/game/lara/lara.c index bf1fa8cb0..82a0e684e 100644 --- a/src/game/lara/lara.c +++ b/src/game/lara/lara.c @@ -3,6 +3,7 @@ #include "config.h" #include "game/camera.h" #include "game/collide.h" +#include "game/gameflow.h" #include "game/gun.h" #include "game/input.h" #include "game/inventory.h" @@ -209,11 +210,16 @@ void Lara_SwapMeshExtra(void) if (!g_Objects[O_LARA_EXTRA].loaded) { return; } - for (int i = 0; i < LM_NUMBER_OF; i++) { - g_Lara.mesh_ptrs[i] = g_Meshes[g_Objects[O_LARA_EXTRA].mesh_index + i]; + for (LARA_MESH mesh = LM_FIRST; mesh < LM_NUMBER_OF; mesh++) { + Lara_SwapSingleMesh(mesh, O_LARA_EXTRA); } } +void Lara_SwapSingleMesh(const LARA_MESH mesh, const GAME_OBJECT_ID object_id) +{ + g_Lara.mesh_ptrs[mesh] = g_Meshes[g_Objects[object_id].mesh_index + mesh]; +} + void Lara_Animate(ITEM_INFO *item) { int16_t *command; @@ -514,7 +520,9 @@ void Lara_InitialiseInventory(int32_t level_num) resume->flags.got_shotgun = 0; resume->flags.got_magnums = 0; resume->flags.got_uzis = 0; - resume->gun_type = LGT_UNARMED; + resume->equipped_gun_type = LGT_UNARMED; + resume->holsters_gun_type = LGT_UNARMED; + resume->back_gun_type = LGT_UNARMED; resume->gun_status = LGS_ARMLESS; } @@ -586,71 +594,62 @@ void Lara_InitialiseInventory(int32_t level_num) } g_Lara.gun_status = resume->gun_status; + g_Lara.gun_type = resume->equipped_gun_type; + g_Lara.request_gun_type = resume->equipped_gun_type; + g_Lara.holsters_gun_type = resume->holsters_gun_type; + g_Lara.back_gun_type = resume->back_gun_type; - if (g_Config.revert_to_pistols - && g_GameInfo.current[level_num].flags.got_pistols != 0) { - g_Lara.request_gun_type = LGT_PISTOLS; - g_Lara.gun_type = LGT_PISTOLS; - } else { - g_Lara.gun_type = resume->gun_type; - g_Lara.request_gun_type = resume->gun_type; + Lara_InitialiseMeshes(level_num); + Gun_InitialiseNewWeapon(); +} + +void Lara_RevertToPistolsIfNeeded(void) +{ + if (!g_Config.revert_to_pistols || !Inv_RequestItem(O_PISTOL_ITEM)) { + return; } - Lara_InitialiseMeshes(level_num); + g_Lara.gun_type = LGT_PISTOLS; + + if (g_Lara.gun_status != LGS_ARMLESS) { + g_Lara.holsters_gun_type = LGT_UNARMED; + } + if (Inv_RequestItem(O_SHOTGUN_ITEM)) { + g_Lara.back_gun_type = LGT_SHOTGUN; + } else { + g_Lara.back_gun_type = LGT_UNARMED; + } Gun_InitialiseNewWeapon(); + Gun_SetLaraHolsterLMesh(g_Lara.holsters_gun_type); + Gun_SetLaraHolsterRMesh(g_Lara.holsters_gun_type); + Gun_SetLaraBackMesh(g_Lara.back_gun_type); } void Lara_InitialiseMeshes(int32_t level_num) { - RESUME_INFO *resume = &g_GameInfo.current[level_num]; + const RESUME_INFO *const resume = &g_GameInfo.current[level_num]; if (resume->flags.costume) { - for (int i = 0; i < LM_NUMBER_OF; i++) { - int32_t use_orig_mesh = i == LM_HEAD; - g_Lara.mesh_ptrs[i] = g_Meshes - [g_Objects[use_orig_mesh ? O_LARA : O_LARA_EXTRA].mesh_index - + i]; + for (LARA_MESH mesh = LM_FIRST; mesh < LM_NUMBER_OF; mesh++) { + Lara_SwapSingleMesh(mesh, mesh == LM_HEAD ? O_LARA : O_LARA_EXTRA); } return; } - for (int i = 0; i < LM_NUMBER_OF; i++) { - g_Lara.mesh_ptrs[i] = g_Meshes[g_Objects[O_LARA].mesh_index + i]; + for (LARA_MESH mesh = LM_FIRST; mesh < LM_NUMBER_OF; mesh++) { + Lara_SwapSingleMesh(mesh, O_LARA); } - GAME_OBJECT_ID holster_object_num = NO_OBJECT; - GAME_OBJECT_ID back_object_num = NO_OBJECT; - switch (resume->gun_type) { - case LGT_SHOTGUN: - if (resume->flags.got_pistols) { - holster_object_num = O_PISTOL_ANIM; - } - break; - case LGT_PISTOLS: - holster_object_num = O_PISTOL_ANIM; - break; - case LGT_MAGNUMS: - holster_object_num = O_MAGNUM_ANIM; - break; - case LGT_UZIS: - holster_object_num = O_UZI_ANIM; - break; - } - - if (resume->flags.got_shotgun) { - back_object_num = O_SHOTGUN_ANIM; - } + LARA_GUN_TYPE holsters_gun_type = resume->holsters_gun_type; + LARA_GUN_TYPE back_gun_type = resume->back_gun_type; - if (holster_object_num != NO_OBJECT) { - g_Lara.mesh_ptrs[LM_THIGH_L] = - g_Meshes[g_Objects[holster_object_num].mesh_index + LM_THIGH_L]; - g_Lara.mesh_ptrs[LM_THIGH_R] = - g_Meshes[g_Objects[holster_object_num].mesh_index + LM_THIGH_R]; + if (holsters_gun_type != LGT_UNKNOWN) { + Gun_SetLaraHolsterLMesh(holsters_gun_type); + Gun_SetLaraHolsterRMesh(holsters_gun_type); } - if (back_object_num != NO_OBJECT) { - g_Lara.mesh_ptrs[LM_TORSO] = - g_Meshes[g_Objects[back_object_num].mesh_index + LM_TORSO]; + if (back_gun_type != LGT_UNKNOWN) { + Gun_SetLaraBackMesh(back_gun_type); } } diff --git a/src/game/lara/lara_state.c b/src/game/lara/lara_state.c index ff7af4126..c2f2f2e18 100644 --- a/src/game/lara/lara_state.c +++ b/src/game/lara/lara_state.c @@ -3,6 +3,7 @@ #include "config.h" #include "game/input.h" #include "game/items.h" +#include "game/lara.h" #include "game/lara/lara_look.h" #include "game/objects/effects/twinkle.h" #include "game/room.h" @@ -683,85 +684,70 @@ void Lara_State_DieMidas(ITEM_INFO *item, COLL_INFO *coll) case 5: g_Lara.mesh_effects |= (1 << LM_FOOT_L); g_Lara.mesh_effects |= (1 << LM_FOOT_R); - g_Lara.mesh_ptrs[LM_FOOT_L] = - g_Meshes[g_Objects[O_LARA_EXTRA].mesh_index + LM_FOOT_L]; - g_Lara.mesh_ptrs[LM_FOOT_R] = - g_Meshes[g_Objects[O_LARA_EXTRA].mesh_index + LM_FOOT_R]; + Lara_SwapSingleMesh(LM_FOOT_L, O_LARA_EXTRA); + Lara_SwapSingleMesh(LM_FOOT_R, O_LARA_EXTRA); break; case 70: g_Lara.mesh_effects |= (1 << LM_CALF_L); - g_Lara.mesh_ptrs[LM_CALF_L] = - g_Meshes[(&g_Objects[O_LARA_EXTRA])->mesh_index + LM_CALF_L]; + Lara_SwapSingleMesh(LM_CALF_L, O_LARA_EXTRA); break; case 90: g_Lara.mesh_effects |= (1 << LM_THIGH_L); - g_Lara.mesh_ptrs[LM_THIGH_L] = - g_Meshes[(&g_Objects[O_LARA_EXTRA])->mesh_index + LM_THIGH_L]; + Lara_SwapSingleMesh(LM_THIGH_L, O_LARA_EXTRA); break; case 100: g_Lara.mesh_effects |= (1 << LM_CALF_R); - g_Lara.mesh_ptrs[LM_CALF_R] = - g_Meshes[(&g_Objects[O_LARA_EXTRA])->mesh_index + LM_CALF_R]; + Lara_SwapSingleMesh(LM_CALF_R, O_LARA_EXTRA); break; case 120: g_Lara.mesh_effects |= (1 << LM_HIPS); g_Lara.mesh_effects |= (1 << LM_THIGH_R); - g_Lara.mesh_ptrs[LM_HIPS] = - g_Meshes[(&g_Objects[O_LARA_EXTRA])->mesh_index + LM_HIPS]; - g_Lara.mesh_ptrs[LM_THIGH_R] = - g_Meshes[(&g_Objects[O_LARA_EXTRA])->mesh_index + LM_THIGH_R]; + Lara_SwapSingleMesh(LM_HIPS, O_LARA_EXTRA); + Lara_SwapSingleMesh(LM_THIGH_R, O_LARA_EXTRA); break; case 135: g_Lara.mesh_effects |= (1 << LM_TORSO); - g_Lara.mesh_ptrs[LM_TORSO] = - g_Meshes[(&g_Objects[O_LARA_EXTRA])->mesh_index + LM_TORSO]; + Lara_SwapSingleMesh(LM_TORSO, O_LARA_EXTRA); break; case 150: g_Lara.mesh_effects |= (1 << LM_UARM_L); - g_Lara.mesh_ptrs[LM_UARM_L] = - g_Meshes[(&g_Objects[O_LARA_EXTRA])->mesh_index + LM_UARM_L]; + Lara_SwapSingleMesh(LM_UARM_L, O_LARA_EXTRA); break; case 163: g_Lara.mesh_effects |= (1 << LM_LARM_L); - g_Lara.mesh_ptrs[LM_LARM_L] = - g_Meshes[(&g_Objects[O_LARA_EXTRA])->mesh_index + LM_LARM_L]; + Lara_SwapSingleMesh(LM_LARM_L, O_LARA_EXTRA); break; case 174: g_Lara.mesh_effects |= (1 << LM_HAND_L); - g_Lara.mesh_ptrs[LM_HAND_L] = - g_Meshes[(&g_Objects[O_LARA_EXTRA])->mesh_index + LM_HAND_L]; + Lara_SwapSingleMesh(LM_HAND_L, O_LARA_EXTRA); break; case 186: g_Lara.mesh_effects |= (1 << LM_UARM_R); - g_Lara.mesh_ptrs[LM_UARM_R] = - g_Meshes[(&g_Objects[O_LARA_EXTRA])->mesh_index + LM_UARM_R]; + Lara_SwapSingleMesh(LM_UARM_R, O_LARA_EXTRA); break; case 195: g_Lara.mesh_effects |= (1 << LM_LARM_R); - g_Lara.mesh_ptrs[LM_LARM_R] = - g_Meshes[(&g_Objects[O_LARA_EXTRA])->mesh_index + LM_LARM_R]; + Lara_SwapSingleMesh(LM_LARM_R, O_LARA_EXTRA); break; case 218: g_Lara.mesh_effects |= (1 << LM_HAND_R); - g_Lara.mesh_ptrs[LM_HAND_R] = - g_Meshes[(&g_Objects[O_LARA_EXTRA])->mesh_index + LM_HAND_R]; + Lara_SwapSingleMesh(LM_HAND_R, O_LARA_EXTRA); break; case 225: g_Lara.mesh_effects |= (1 << LM_HEAD); - g_Lara.mesh_ptrs[LM_HEAD] = - g_Meshes[(&g_Objects[O_LARA_EXTRA])->mesh_index + LM_HEAD]; + Lara_SwapSingleMesh(LM_HEAD, O_LARA_EXTRA); break; } diff --git a/src/game/objects/creatures/bacon_lara.c b/src/game/objects/creatures/bacon_lara.c index 44e54999e..ebbcbeac9 100644 --- a/src/game/objects/creatures/bacon_lara.c +++ b/src/game/objects/creatures/bacon_lara.c @@ -138,14 +138,14 @@ void BaconLara_Draw(ITEM_INFO *item) int16_t *old_mesh_ptrs[LM_NUMBER_OF]; - for (int i = 0; i < LM_NUMBER_OF; i++) { - old_mesh_ptrs[i] = g_Lara.mesh_ptrs[i]; - g_Lara.mesh_ptrs[i] = g_Meshes[g_Objects[O_BACON_LARA].mesh_index + i]; + for (LARA_MESH mesh = LM_FIRST; mesh < LM_NUMBER_OF; mesh++) { + old_mesh_ptrs[mesh] = g_Lara.mesh_ptrs[mesh]; + Lara_SwapSingleMesh(mesh, O_BACON_LARA); } Lara_Draw(item); - for (int i = 0; i < LM_NUMBER_OF; i++) { - g_Lara.mesh_ptrs[i] = old_mesh_ptrs[i]; + for (LARA_MESH mesh = LM_FIRST; mesh < LM_NUMBER_OF; mesh++) { + g_Lara.mesh_ptrs[mesh] = old_mesh_ptrs[mesh]; } } diff --git a/src/game/objects/general/pickup.c b/src/game/objects/general/pickup.c index 69eebc9a6..b4ca48ca7 100644 --- a/src/game/objects/general/pickup.c +++ b/src/game/objects/general/pickup.c @@ -1,6 +1,7 @@ #include "game/objects/general/pickup.h" #include "config.h" +#include "game/gun.h" #include "game/input.h" #include "game/inventory.h" #include "game/items.h" @@ -49,45 +50,13 @@ static const OBJECT_BOUNDS m_PickUpBoundsUW = { }, }; -static void PickUp_AddGunToMesh( - const GAME_OBJECT_ID obj_num, const ITEM_INFO *lara_item); static void PickUp_GetItem( int16_t item_num, ITEM_INFO *item, ITEM_INFO *lara_item); static void PickUp_GetAllAtLaraPos(ITEM_INFO *item, ITEM_INFO *lara_item); -static void PickUp_AddGunToMesh( - const GAME_OBJECT_ID obj_num, const ITEM_INFO *const lara_item) -{ - const bool lara_has_pistols = Inv_RequestItem(O_PISTOL_ITEM) - || Inv_RequestItem(O_MAGNUM_ITEM) || Inv_RequestItem(O_UZI_ITEM); - - if (!Inv_RequestItem(O_SHOTGUN_ITEM) && obj_num == O_SHOTGUN_ITEM) { - g_Lara.mesh_ptrs[LM_TORSO] = - g_Meshes[g_Objects[O_SHOTGUN_ANIM].mesh_index + LM_TORSO]; - } else if (!lara_has_pistols && obj_num == O_PISTOL_ITEM) { - g_Lara.mesh_ptrs[LM_THIGH_L] = - g_Meshes[g_Objects[O_PISTOL_ANIM].mesh_index + LM_THIGH_L]; - g_Lara.mesh_ptrs[LM_THIGH_R] = - g_Meshes[g_Objects[O_PISTOL_ANIM].mesh_index + LM_THIGH_R]; - } else if (!lara_has_pistols && obj_num == O_MAGNUM_ITEM) { - g_Lara.mesh_ptrs[LM_THIGH_L] = - g_Meshes[g_Objects[O_MAGNUM_ANIM].mesh_index + LM_THIGH_L]; - g_Lara.mesh_ptrs[LM_THIGH_R] = - g_Meshes[g_Objects[O_MAGNUM_ANIM].mesh_index + LM_THIGH_R]; - } else if (!lara_has_pistols && obj_num == O_UZI_ITEM) { - g_Lara.mesh_ptrs[LM_THIGH_L] = - g_Meshes[g_Objects[O_UZI_ANIM].mesh_index + LM_THIGH_L]; - g_Lara.mesh_ptrs[LM_THIGH_R] = - g_Meshes[g_Objects[O_UZI_ANIM].mesh_index + LM_THIGH_R]; - } -} - static void PickUp_GetItem( int16_t item_num, ITEM_INFO *item, ITEM_INFO *lara_item) { - if (Object_IsObjectType(item->object_number, g_GunObjects)) { - PickUp_AddGunToMesh(item->object_number, lara_item); - } Overlay_AddPickup(item->object_number); Inv_AddItem(item->object_number); item->status = IS_INVISIBLE; diff --git a/src/game/phase/phase_demo.c b/src/game/phase/phase_demo.c index 24ab609ef..4b82dc620 100644 --- a/src/game/phase/phase_demo.c +++ b/src/game/phase/phase_demo.c @@ -142,7 +142,9 @@ static void Phase_Demo_Start(void *arg) resume_info->flags.got_pistols = 1; resume_info->pistol_ammo = 1000; resume_info->gun_status = LGS_ARMLESS; - resume_info->gun_type = LGT_PISTOLS; + resume_info->equipped_gun_type = LGT_PISTOLS; + resume_info->holsters_gun_type = LGT_PISTOLS; + resume_info->back_gun_type = LGT_UNARMED; resume_info->lara_hitpoints = LARA_MAX_HITPOINTS; Random_SeedDraw(0xD371F947); diff --git a/src/game/savegame/savegame.c b/src/game/savegame/savegame.c index 80d2985fa..a8eea5c6f 100644 --- a/src/game/savegame/savegame.c +++ b/src/game/savegame/savegame.c @@ -247,7 +247,9 @@ void Savegame_ApplyLogicToCurrentInfo(int level_num) current->flags.got_shotgun = 0; current->flags.got_magnums = 0; current->flags.got_uzis = 0; - current->gun_type = LGT_UNARMED; + current->equipped_gun_type = LGT_UNARMED; + current->holsters_gun_type = LGT_UNARMED; + current->back_gun_type = LGT_UNARMED; current->gun_status = LGS_ARMLESS; } @@ -265,7 +267,9 @@ void Savegame_ApplyLogicToCurrentInfo(int level_num) current->flags.got_shotgun = 0; current->flags.got_magnums = 0; current->flags.got_uzis = 0; - current->gun_type = LGT_PISTOLS; + current->equipped_gun_type = LGT_PISTOLS; + current->holsters_gun_type = LGT_PISTOLS; + current->back_gun_type = LGT_UNARMED; current->gun_status = LGS_ARMLESS; } @@ -278,7 +282,42 @@ void Savegame_ApplyLogicToCurrentInfo(int level_num) current->shotgun_ammo = 1234; current->magnum_ammo = 1234; current->uzi_ammo = 1234; - current->gun_type = LGT_UZIS; + current->equipped_gun_type = LGT_UZIS; + current->holsters_gun_type = LGT_UZIS; + } + + // Fallback logic to figure out holster and back gun items for versions 4.2 + // and earlier, as well as TombATI saves, where these values are missing. + // Make educated guesses based on the type of gun equipped. + if (current->holsters_gun_type == LGT_UNKNOWN) { + switch (current->equipped_gun_type) { + case LGT_PISTOLS: + case LGT_MAGNUMS: + case LGT_UZIS: + current->holsters_gun_type = current->equipped_gun_type; + break; + case LGT_SHOTGUN: + if (current->flags.got_pistols) { + current->holsters_gun_type = LGT_PISTOLS; + } else if (current->flags.got_magnums) { + current->holsters_gun_type = LGT_MAGNUMS; + } else if (current->flags.got_uzis) { + current->holsters_gun_type = LGT_UZIS; + } else { + current->holsters_gun_type = LGT_UNARMED; + } + break; + default: + current->holsters_gun_type = LGT_UNARMED; + break; + } + } + if (current->back_gun_type == LGT_UNKNOWN) { + if (current->flags.got_shotgun) { + current->back_gun_type = LGT_SHOTGUN; + } else { + current->back_gun_type = LGT_UNARMED; + } } } @@ -299,7 +338,6 @@ void Savegame_PersistGameToCurrentInfo(int level_num) { // Persist Lara's inventory to the current info. // Used to carry over Lara's inventory between levels. - RESUME_INFO *current = &g_GameInfo.current[level_num]; current->lara_hitpoints = g_LaraItem->hit_points; @@ -343,7 +381,9 @@ void Savegame_PersistGameToCurrentInfo(int level_num) current->num_big_medis = Inv_RequestItem(O_BIGMEDI_ITEM); current->num_scions = Inv_RequestItem(O_SCION_ITEM); - current->gun_type = g_Lara.gun_type; + current->equipped_gun_type = g_Lara.gun_type; + current->holsters_gun_type = g_Lara.holsters_gun_type; + current->back_gun_type = g_Lara.back_gun_type; if (g_Lara.gun_status == LGS_READY) { current->gun_status = LGS_READY; } else { diff --git a/src/game/savegame/savegame_bson.c b/src/game/savegame/savegame_bson.c index c416bd84a..48365d158 100644 --- a/src/game/savegame/savegame_bson.c +++ b/src/game/savegame/savegame_bson.c @@ -245,7 +245,12 @@ static bool Savegame_BSON_LoadResumeInfo( json_object_get_int(resume_obj, "num_big_medis", 0); resume->num_scions = json_object_get_int(resume_obj, "num_scions", 0); resume->gun_status = json_object_get_int(resume_obj, "gun_status", 0); - resume->gun_type = json_object_get_int(resume_obj, "gun_type", 0); + resume->equipped_gun_type = + json_object_get_int(resume_obj, "gun_type", LGT_UNARMED); + resume->holsters_gun_type = + json_object_get_int(resume_obj, "holsters_gun_type", LGT_UNKNOWN); + resume->back_gun_type = + json_object_get_int(resume_obj, "back_gun_type", LGT_UNKNOWN); resume->flags.available = json_object_get_bool(resume_obj, "available", 0); resume->flags.got_pistols = @@ -314,7 +319,10 @@ static bool Savegame_BSON_LoadDiscontinuedStartInfo( json_object_get_int(start_obj, "num_big_medis", 0); start->num_scions = json_object_get_int(start_obj, "num_scions", 0); start->gun_status = json_object_get_int(start_obj, "gun_status", 0); - start->gun_type = json_object_get_int(start_obj, "gun_type", 0); + start->equipped_gun_type = + json_object_get_int(start_obj, "gun_type", LGT_UNARMED); + start->holsters_gun_type = LGT_UNKNOWN; + start->back_gun_type = LGT_UNKNOWN; start->flags.available = json_object_get_bool(start_obj, "available", 0); start->flags.got_pistols = @@ -939,7 +947,12 @@ static struct json_array_s *Savegame_BSON_DumpResumeInfo( resume_obj, "num_big_medis", resume->num_big_medis); json_object_append_int(resume_obj, "num_scions", resume->num_scions); json_object_append_int(resume_obj, "gun_status", resume->gun_status); - json_object_append_int(resume_obj, "gun_type", resume->gun_type); + json_object_append_int( + resume_obj, "gun_type", resume->equipped_gun_type); + json_object_append_int( + resume_obj, "holsters_gun_type", resume->holsters_gun_type); + json_object_append_int( + resume_obj, "back_gun_type", resume->back_gun_type); json_object_append_bool( resume_obj, "available", resume->flags.available); json_object_append_bool( diff --git a/src/game/savegame/savegame_legacy.c b/src/game/savegame/savegame_legacy.c index 8f2985311..d4947e2e0 100644 --- a/src/game/savegame/savegame_legacy.c +++ b/src/game/savegame/savegame_legacy.c @@ -428,7 +428,9 @@ static void Savegame_Legacy_ReadResumeInfo(MYFILE *fp, GAME_INFO *game_info) Savegame_Legacy_Read(¤t->num_big_medis, sizeof(uint8_t)); Savegame_Legacy_Read(¤t->num_scions, sizeof(uint8_t)); Savegame_Legacy_Read(¤t->gun_status, sizeof(int8_t)); - Savegame_Legacy_Read(¤t->gun_type, sizeof(int8_t)); + Savegame_Legacy_Read(¤t->equipped_gun_type, sizeof(int8_t)); + current->holsters_gun_type = LGT_UNKNOWN; + current->back_gun_type = LGT_UNKNOWN; uint16_t flags; Savegame_Legacy_Read(&flags, sizeof(uint16_t)); current->flags.available = flags & 1 ? 1 : 0; @@ -530,6 +532,8 @@ bool Savegame_Legacy_LoadFromFile(MYFILE *fp, GAME_INFO *game_info) Savegame_Legacy_Skip(sizeof(int32_t)); // save counter Savegame_Legacy_ReadResumeInfo(fp, game_info); + g_Lara.holsters_gun_type = LGT_UNKNOWN; + g_Lara.back_gun_type = LGT_UNKNOWN; Lara_InitialiseInventory(g_CurrentLevel); SAVEGAME_LEGACY_ITEM_STATS item_stats = { 0 }; @@ -688,7 +692,7 @@ void Savegame_Legacy_SaveToFile(MYFILE *fp, GAME_INFO *game_info) Savegame_Legacy_Write(¤t->num_big_medis, sizeof(uint8_t)); Savegame_Legacy_Write(¤t->num_scions, sizeof(uint8_t)); Savegame_Legacy_Write(¤t->gun_status, sizeof(int8_t)); - Savegame_Legacy_Write(¤t->gun_type, sizeof(int8_t)); + Savegame_Legacy_Write(¤t->equipped_gun_type, sizeof(int8_t)); uint16_t flags = 0; flags |= current->flags.available ? 1 : 0; flags |= current->flags.got_pistols ? 2 : 0; diff --git a/src/global/types.h b/src/global/types.h index 6c9a7e0bb..5d3fe866f 100644 --- a/src/global/types.h +++ b/src/global/types.h @@ -707,6 +707,7 @@ typedef enum LARA_GUN_STATE { } LARA_GUN_STATE; typedef enum LARA_GUN_TYPE { + LGT_UNKNOWN = -1, // for legacy saves LGT_UNARMED = 0, LGT_PISTOLS = 1, LGT_MAGNUMS = 2, @@ -731,6 +732,7 @@ typedef enum LARA_MESH { LM_LARM_L = 12, LM_HAND_L = 13, LM_HEAD = 14, + LM_FIRST = LM_HIPS, LM_NUMBER_OF = 15, } LARA_MESH; @@ -1414,8 +1416,10 @@ typedef struct FX_INFO { typedef struct LARA_INFO { int16_t item_number; int16_t gun_status; - int16_t gun_type; - int16_t request_gun_type; + LARA_GUN_TYPE gun_type; + LARA_GUN_TYPE request_gun_type; + LARA_GUN_TYPE holsters_gun_type; + LARA_GUN_TYPE back_gun_type; int16_t calc_fall_speed; int16_t water_status; int16_t pose_count; @@ -1477,7 +1481,9 @@ typedef struct RESUME_INFO { uint8_t num_big_medis; uint8_t num_scions; int8_t gun_status; - int8_t gun_type; + LARA_GUN_TYPE equipped_gun_type; + LARA_GUN_TYPE holsters_gun_type; + LARA_GUN_TYPE back_gun_type; union { uint16_t all; struct {