This commit is contained in:
MontyTRC89 2021-07-14 05:30:04 +02:00
commit 98c62c58bb
30 changed files with 868 additions and 340 deletions

View file

@ -300,7 +300,7 @@ void set_flare_arm(int frame)
Lara.leftArm.frameBase = g_Level.Anims[anim].framePtr;
}
void CreateFlare(short objectNum, int thrown)
void CreateFlare(GAME_OBJECT_ID objectNum, int thrown)
{
short itemNum = CreateItem();
if (itemNum != NO_ITEM)

View file

@ -1,6 +1,8 @@
#pragma once
#include "items.h"
enum GAME_OBJECT_ID : short;
void FlareControl(short item_number);
void ready_flare();
void undraw_flare_meshes();
@ -8,7 +10,7 @@ void draw_flare_meshes();
void undraw_flare();
void draw_flare();
void set_flare_arm(int frame);
void CreateFlare(short object, int thrown);
void CreateFlare(GAME_OBJECT_ID object, int thrown);
void DrawFlareInAir(ITEM_INFO* item);
void DoFlareInHand(int flare_age);
int DoFlareLight(PHD_VECTOR* pos, int flare_age);

View file

@ -699,6 +699,14 @@ GAME_STATUS DoTitle(int index)
InitialiseCamera();
SOUND_Stop();
// Run the level script
GameScriptLevel* level = g_GameFlow->Levels[index];
std::string err;
if (!level->ScriptFileName.empty())
{
g_GameScript->ExecuteScript(level->ScriptFileName, err);
g_GameScript->InitCallbacks();
}
RequiredStartPos = false;
if (InitialiseGame)
{

View file

@ -236,7 +236,7 @@ void DrawAllPickups()
}
void AddDisplayPickup(short objectNumber)
void AddDisplayPickup(GAME_OBJECT_ID objectNumber)
{
DISPLAY_PICKUP* pickup = Pickups;

View file

@ -1,6 +1,8 @@
#pragma once
#define MAX_COLLECTED_PICKUPS 32
enum GAME_OBJECT_ID : short;
typedef struct DISPLAY_PICKUP
{
short life;
@ -13,7 +15,7 @@ void UpdateHealthBar(int flash);
void DrawAirBar(float value);
void UpdateAirBar(int flash);
void DrawDashBar(int value);
void AddDisplayPickup(short objectNumber);
void AddDisplayPickup(GAME_OBJECT_ID objectNumber);
void DrawAllPickups();
void InitialisePickupDisplay();
int FlashIt();

View file

@ -452,7 +452,7 @@ void InitialiseItemArray(int numitems)
item->nextItem = NO_ITEM;
}
short SpawnItem(ITEM_INFO* item, short objectNumber)
short SpawnItem(ITEM_INFO* item, GAME_OBJECT_ID objectNumber)
{
short itemNumber = CreateItem();
if (itemNumber != NO_ITEM)
@ -472,7 +472,7 @@ short SpawnItem(ITEM_INFO* item, short objectNumber)
return itemNumber;
}
int GlobalItemReplace(short search, short replace)
int GlobalItemReplace(short search, GAME_OBJECT_ID replace)
{
int changed = 0;
for (int i = 0; i < g_Level.Rooms.size(); i++)

View file

@ -1,6 +1,8 @@
#pragma once
#include "phd_global.h"
enum GAME_OBJECT_ID : short;
typedef enum AIObjectType
{
NO_AI = 0x0000,
@ -41,7 +43,7 @@ typedef struct ITEM_INFO
int floor;
DWORD touchBits;
DWORD meshBits;
short objectNumber;
GAME_OBJECT_ID objectNumber;
short currentAnimState;
short goalAnimState;
short requiredAnimState;

View file

@ -16,6 +16,7 @@
#include "lara_two_guns.h"
#include "level.h"
#include "input.h"
#include "pickup.h"
using namespace T5M::Renderer;
bool goUp, goDown, goRight, goLeft, goSelect, goDeselect;
@ -2379,28 +2380,7 @@ void init_inventry()
int have_i_got_object(short object_number)
{
if (object_number >= ID_PUZZLE_ITEM1_COMBO1 && object_number <= ID_PUZZLE_ITEM16_COMBO2)
return Lara.PuzzlesCombo[object_number - ID_PUZZLE_ITEM1_COMBO1];
if (object_number >= ID_PUZZLE_ITEM1 && object_number <= ID_PUZZLE_ITEM16)
return Lara.Puzzles[object_number - ID_PUZZLE_ITEM1];
if (object_number >= ID_KEY_ITEM1_COMBO1 && object_number <= ID_KEY_ITEM16_COMBO2)
return Lara.KeysCombo[object_number - ID_KEY_ITEM1_COMBO1];
if (object_number >= ID_KEY_ITEM1 && object_number <= ID_KEY_ITEM16)
return Lara.Keys[object_number - ID_KEY_ITEM1];
if (object_number >= ID_PICKUP_ITEM1_COMBO1 && object_number <= ID_PICKUP_ITEM16_COMBO2)
return Lara.PickupsCombo[object_number - ID_PICKUP_ITEM1_COMBO1];
if (object_number >= ID_PICKUP_ITEM1 && object_number <= ID_PICKUP_ITEM16)
return Lara.Pickups[object_number - ID_PICKUP_ITEM1];
if (object_number == ID_CROWBAR_ITEM)
return Lara.Crowbar;
return 0;
return GetInventoryCount(from_underlying(object_number));
}
void setup_objectlist_startposition2(short newobj)

View file

@ -29,6 +29,11 @@
#include "sound.h"
#include "savegame.h"
#include "tr4_clockwork_beetle.h"
#include "pickup/pickup_ammo.h"
#include "pickup/pickup_key_items.h"
#include "pickup/pickup_weapon.h"
#include "pickup/pickup_consumable.h"
#include "pickup/pickup_misc_items.h"
OBJECT_COLLISION_BOUNDS PickUpBounds = // offset 0xA1338
{
@ -99,306 +104,69 @@ extern int KeyTriggerActive;
extern Inventory g_Inventory;
#endif
static bool SilencerIsEquiped()
{
return Lara.Weapons[WEAPON_UZI].HasSilencer
|| Lara.Weapons[WEAPON_PISTOLS].HasSilencer
|| Lara.Weapons[WEAPON_SHOTGUN].HasSilencer
|| Lara.Weapons[WEAPON_REVOLVER].HasSilencer
|| Lara.Weapons[WEAPON_CROSSBOW].HasSilencer
|| Lara.Weapons[WEAPON_HK].HasSilencer;
}
static bool LaserSightIsEquiped()
{
return Lara.Weapons[WEAPON_REVOLVER].HasLasersight
|| Lara.Weapons[WEAPON_CROSSBOW].HasLasersight
|| Lara.Weapons[WEAPON_HK].HasLasersight;
}
void PickedUpObject(short objectNumber, int count)
void PickedUpObject(GAME_OBJECT_ID objID, int count)
{
switch (objectNumber)
// see if the items fit into one of these easy groups
if (!TryAddWeapon(Lara, objID, count)
&& !TryAddAmmo(Lara, objID, count)
&& !TryAddKeyItem(Lara, objID, count)
&& !TryAddConsumable(Lara, objID, count)
&& !TryAddMiscItem(Lara, objID))
{
case ID_UZI_ITEM:
if (!Lara.Weapons[WEAPON_UZI].Present)
{
Lara.Weapons[WEAPON_UZI].Present = true;
Lara.Weapons[WEAPON_UZI].SelectedAmmo = 0;
}
if (Lara.Weapons[WEAPON_UZI].Ammo[WEAPON_AMMO1] != -1)
Lara.Weapons[WEAPON_UZI].Ammo[WEAPON_AMMO1] += count ? count : 30;
break;
case ID_PISTOLS_ITEM:
if (!Lara.Weapons[WEAPON_PISTOLS].Present)
{
Lara.Weapons[WEAPON_PISTOLS].Present = true;
Lara.Weapons[WEAPON_PISTOLS].SelectedAmmo = 0;
}
Lara.Weapons[WEAPON_PISTOLS].Ammo[WEAPON_AMMO1] = -1;
break;
case ID_SHOTGUN_ITEM:
if (!Lara.Weapons[WEAPON_SHOTGUN].Present)
{
Lara.Weapons[WEAPON_SHOTGUN].Present = true;
Lara.Weapons[WEAPON_SHOTGUN].SelectedAmmo = 0;
}
if (Lara.Weapons[WEAPON_SHOTGUN].Ammo[WEAPON_AMMO1] != -1)
Lara.Weapons[WEAPON_SHOTGUN].Ammo[WEAPON_AMMO1] += count ? count : 36;
break;
case ID_REVOLVER_ITEM:
if (!Lara.Weapons[WEAPON_REVOLVER].Present)
{
Lara.Weapons[WEAPON_REVOLVER].Present = true;
Lara.Weapons[WEAPON_REVOLVER].SelectedAmmo = 0;
}
if (Lara.Weapons[WEAPON_REVOLVER].Ammo[WEAPON_AMMO1] != -1)
Lara.Weapons[WEAPON_REVOLVER].Ammo[WEAPON_AMMO1] += count ? count : 6;
break;
case ID_CROSSBOW_ITEM:
if (!Lara.Weapons[WEAPON_CROSSBOW].Present)
{
Lara.Weapons[WEAPON_CROSSBOW].Present = true;
Lara.Weapons[WEAPON_CROSSBOW].SelectedAmmo = 0;
}
if (Lara.Weapons[WEAPON_CROSSBOW].Ammo[WEAPON_AMMO1] != -1)
Lara.Weapons[WEAPON_CROSSBOW].Ammo[WEAPON_AMMO1] += count ? count : 10;
break;
case ID_HK_ITEM:
if (!Lara.Weapons[WEAPON_HK].Present)
{
Lara.Weapons[WEAPON_HK].Present = true;
Lara.Weapons[WEAPON_HK].SelectedAmmo = 0;
}
if (Lara.Weapons[WEAPON_HK].Ammo[WEAPON_AMMO1] != -1)
Lara.Weapons[WEAPON_HK].Ammo[WEAPON_AMMO1] += count ? count : 30;
break;
case ID_HARPOON_ITEM:
if (!Lara.Weapons[WEAPON_HARPOON_GUN].Present)
{
Lara.Weapons[WEAPON_HARPOON_GUN].Present = true;
Lara.Weapons[WEAPON_HARPOON_GUN].SelectedAmmo = 0;
}
if (Lara.Weapons[WEAPON_HARPOON_GUN].Ammo[WEAPON_AMMO1] != -1)
Lara.Weapons[WEAPON_HARPOON_GUN].Ammo[WEAPON_AMMO1] += count ? count : 10;
break;
case ID_GRENADE_GUN_ITEM:
if (!Lara.Weapons[WEAPON_GRENADE_LAUNCHER].Present)
{
Lara.Weapons[WEAPON_GRENADE_LAUNCHER].Present = true;
Lara.Weapons[WEAPON_GRENADE_LAUNCHER].SelectedAmmo = 0;
}
if (Lara.Weapons[WEAPON_GRENADE_LAUNCHER].Ammo[WEAPON_AMMO1] != -1)
Lara.Weapons[WEAPON_GRENADE_LAUNCHER].Ammo[WEAPON_AMMO1] += count ? count : 10;
break;
case ID_ROCKET_LAUNCHER_ITEM:
if (!Lara.Weapons[WEAPON_ROCKET_LAUNCHER].Present)
{
Lara.Weapons[WEAPON_ROCKET_LAUNCHER].Present = true;
Lara.Weapons[WEAPON_ROCKET_LAUNCHER].SelectedAmmo = 0;
}
if (Lara.Weapons[WEAPON_ROCKET_LAUNCHER].Ammo[WEAPON_AMMO1] != -1)
Lara.Weapons[WEAPON_ROCKET_LAUNCHER].Ammo[WEAPON_AMMO1] += count ? count : 10;
break;
case ID_SHOTGUN_AMMO1_ITEM:
if (Lara.Weapons[WEAPON_SHOTGUN].Ammo[WEAPON_AMMO1] != -1)
Lara.Weapons[WEAPON_SHOTGUN].Ammo[WEAPON_AMMO1] += count ? count : 36;
break;
case ID_SHOTGUN_AMMO2_ITEM:
if (Lara.Weapons[WEAPON_SHOTGUN].Ammo[WEAPON_AMMO2] != -1)
Lara.Weapons[WEAPON_SHOTGUN].Ammo[WEAPON_AMMO2] += count ? count : 36;
break;
case ID_HK_AMMO_ITEM:
if (Lara.Weapons[WEAPON_HK].Ammo[WEAPON_AMMO1] != -1)
Lara.Weapons[WEAPON_HK].Ammo[WEAPON_AMMO1] += count ? count : 30;
break;
case ID_CROSSBOW_AMMO1_ITEM:
if (Lara.Weapons[WEAPON_CROSSBOW].Ammo[WEAPON_AMMO1] != -1)
Lara.Weapons[WEAPON_CROSSBOW].Ammo[WEAPON_AMMO1] += count ? count : 10;
break;
case ID_CROSSBOW_AMMO2_ITEM:
if (Lara.Weapons[WEAPON_CROSSBOW].Ammo[WEAPON_AMMO2] != -1)
Lara.Weapons[WEAPON_CROSSBOW].Ammo[WEAPON_AMMO2] += count ? count : 10;
break;
case ID_CROSSBOW_AMMO3_ITEM:
if (Lara.Weapons[WEAPON_CROSSBOW].Ammo[WEAPON_AMMO3] != -1)
Lara.Weapons[WEAPON_CROSSBOW].Ammo[WEAPON_AMMO3] += count ? count : 10;
break;
case ID_GRENADE_AMMO1_ITEM:
if (Lara.Weapons[WEAPON_GRENADE_LAUNCHER].Ammo[WEAPON_AMMO1] != -1)
Lara.Weapons[WEAPON_GRENADE_LAUNCHER].Ammo[WEAPON_AMMO1] += count ? count : 10;
break;
case ID_GRENADE_AMMO2_ITEM:
if (Lara.Weapons[WEAPON_GRENADE_LAUNCHER].Ammo[WEAPON_AMMO2] != -1)
Lara.Weapons[WEAPON_GRENADE_LAUNCHER].Ammo[WEAPON_AMMO2] += count ? count : 10;
break;
case ID_GRENADE_AMMO3_ITEM:
if (Lara.Weapons[WEAPON_GRENADE_LAUNCHER].Ammo[WEAPON_AMMO3] != -1)
Lara.Weapons[WEAPON_GRENADE_LAUNCHER].Ammo[WEAPON_AMMO3] += count ? count : 10;
break;
case ID_REVOLVER_AMMO_ITEM:
if (Lara.Weapons[WEAPON_REVOLVER].Ammo[WEAPON_AMMO1] != -1)
Lara.Weapons[WEAPON_REVOLVER].Ammo[WEAPON_AMMO1] += count ? count : 6;
break;
case ID_ROCKET_LAUNCHER_AMMO_ITEM:
if (Lara.Weapons[WEAPON_ROCKET_LAUNCHER].Ammo[WEAPON_AMMO1] != -1)
Lara.Weapons[WEAPON_ROCKET_LAUNCHER].Ammo[WEAPON_AMMO1] += count ? count : 10;
break;
case ID_HARPOON_AMMO_ITEM:
if (Lara.Weapons[WEAPON_HARPOON_GUN].Ammo[WEAPON_AMMO1] != -1)
Lara.Weapons[WEAPON_HARPOON_GUN].Ammo[WEAPON_AMMO1] += count ? count : 10;
break;
case ID_UZI_AMMO_ITEM:
if (Lara.Weapons[WEAPON_UZI].Ammo[WEAPON_AMMO1] != -1)
Lara.Weapons[WEAPON_UZI].Ammo[WEAPON_AMMO1] += count ? count : 30;
break;
case ID_FLARE_INV_ITEM:
if (Lara.NumFlares != -1)
Lara.NumFlares += count ? count : 12;
break;
case ID_SILENCER_ITEM:
if (!SilencerIsEquiped())
Lara.Silencer = true;
break;
case ID_LASERSIGHT_ITEM:
if (!LaserSightIsEquiped())
Lara.Lasersight = true;
break;
case ID_BIGMEDI_ITEM:
if (Lara.NumLargeMedipacks != -1)
Lara.NumLargeMedipacks += count ? count : 1;
break;
case ID_SMALLMEDI_ITEM:
if (Lara.NumSmallMedipacks != -1)
Lara.NumSmallMedipacks += count ? count : 1;
break;
case ID_BINOCULARS_ITEM:
Lara.Binoculars = true;
break;
case ID_WATERSKIN1_EMPTY:
Lara.small_waterskin = 1;
break;
case ID_WATERSKIN2_EMPTY:
Lara.big_waterskin = 1;
break;
case ID_GOLDROSE_ITEM:
IsAtmospherePlaying = 0;
S_CDPlay(TRACK_FOUND_SECRET, FALSE);
Lara.Secrets++;
Savegame.Level.Secrets++;
Savegame.Game.Secrets++;
break;
case ID_CROWBAR_ITEM:
Lara.Crowbar = true;
break;
case ID_DIARY_ITEM:
Lara.Diary.Present = true;
break;
case ID_CLOCKWORK_BEETLE:
Lara.hasBeetleThings |= 1 << 0;
break;
case ID_CLOCKWORK_BEETLE_COMBO1:
Lara.hasBeetleThings |= 1 << 1;
break;
case ID_CLOCKWORK_BEETLE_COMBO2:
Lara.hasBeetleThings |= 1 << 2;
break;
default:
if (objectNumber >= ID_PUZZLE_ITEM1 && objectNumber <= ID_PUZZLE_ITEM16)
Lara.Puzzles[objectNumber - ID_PUZZLE_ITEM1] += count ? count : 1;
else if (objectNumber >= ID_PUZZLE_ITEM1_COMBO1 && objectNumber <= ID_PUZZLE_ITEM16_COMBO2)
Lara.PuzzlesCombo[objectNumber - ID_PUZZLE_ITEM1_COMBO1] += count ? count : 1;
else if (objectNumber >= ID_KEY_ITEM1 && objectNumber <= ID_KEY_ITEM16)
Lara.Keys[objectNumber - ID_KEY_ITEM1] += count ? count : 1;
else if (objectNumber >= ID_KEY_ITEM1_COMBO1 && objectNumber <= ID_KEY_ITEM16_COMBO2)
Lara.KeysCombo[objectNumber - ID_KEY_ITEM1_COMBO1] += count ? count : 1;
else if (objectNumber >= ID_PICKUP_ITEM1 && objectNumber <= ID_PICKUP_ITEM16)
Lara.Pickups[objectNumber - ID_PICKUP_ITEM1] += count ? count : 1;
else if (objectNumber >= ID_PICKUP_ITEM1_COMBO1 && objectNumber <= ID_PICKUP_ITEM16_COMBO2)
Lara.PickupsCombo[objectNumber - ID_PICKUP_ITEM1_COMBO1] += count ? count : 1;
else if (objectNumber >= ID_EXAMINE1 && objectNumber <= ID_EXAMINE8)
Lara.Examines[objectNumber - ID_EXAMINE1] = 1;
else if (objectNumber >= ID_EXAMINE1_COMBO1 && objectNumber <= ID_EXAMINE8_COMBO2)
Lara.ExaminesCombo[objectNumber - ID_EXAMINE1_COMBO1] = 1;
break;
// item isn't any of the above; do nothing
}
#ifndef NEW_INV
g_Inventory.LoadObjects(false);
#endif
}
void RemoveObjectFromInventory(short objectNumber, int count)
int GetInventoryCount(GAME_OBJECT_ID objID)
{
if (objectNumber >= ID_PUZZLE_ITEM1 && objectNumber <= ID_PUZZLE_ITEM8)
Lara.Puzzles[objectNumber - ID_PUZZLE_ITEM1] -= std::min(count, Lara.Puzzles[objectNumber - ID_PUZZLE_ITEM1]);
auto boolResult = HasWeapon(Lara, objID);
if (boolResult.has_value())
{
return int{ boolResult.value() };
}
else if (objectNumber >= ID_PUZZLE_ITEM1_COMBO1 && objectNumber <= ID_PUZZLE_ITEM8_COMBO2)
Lara.PuzzlesCombo[objectNumber - ID_PUZZLE_ITEM1_COMBO1] -= std::min(count, Lara.PuzzlesCombo[objectNumber - ID_PUZZLE_ITEM1_COMBO1]);
auto intResult = GetAmmoCount(Lara, objID);
if (intResult.has_value())
{
return intResult.value();
}
else if (objectNumber >= ID_KEY_ITEM1 && objectNumber <= ID_KEY_ITEM8)
Lara.Keys[objectNumber - ID_KEY_ITEM1] -= std::min(count, Lara.Keys[objectNumber - ID_KEY_ITEM1]);
intResult = GetKeyItemCount(Lara, objID);
if (intResult.has_value())
{
return intResult.value();
}
else if (objectNumber >= ID_KEY_ITEM1_COMBO1 && objectNumber <= ID_KEY_ITEM8_COMBO2)
Lara.KeysCombo[objectNumber - ID_KEY_ITEM1_COMBO1] -= std::min(count, Lara.KeysCombo[objectNumber - ID_KEY_ITEM1_COMBO1]);
intResult = GetConsumableCount(Lara, objID);
if (intResult.has_value())
{
return intResult.value();
}
else if (objectNumber >= ID_PICKUP_ITEM1 && objectNumber <= ID_PICKUP_ITEM4)
Lara.Pickups[objectNumber - ID_PICKUP_ITEM1] -= std::min(count, Lara.Pickups[objectNumber - ID_PICKUP_ITEM1]);
else if (objectNumber >= ID_PICKUP_ITEM1_COMBO1 && objectNumber <= ID_PICKUP_ITEM4_COMBO2)
Lara.PickupsCombo[objectNumber - ID_PICKUP_ITEM1_COMBO1] -= std::min(count, Lara.PickupsCombo[objectNumber - ID_PICKUP_ITEM1_COMBO1]);
else if (objectNumber >= ID_EXAMINE1 && objectNumber <= ID_EXAMINE3)
Lara.Examines[objectNumber - ID_EXAMINE1] = 0;
else if (objectNumber >= ID_EXAMINE1_COMBO1 && objectNumber <= ID_EXAMINE3_COMBO2)
Lara.PickupsCombo[objectNumber - ID_EXAMINE1_COMBO1] = 0;
boolResult = HasMiscItem(Lara, objID);
if (boolResult.has_value())
{
return int{ boolResult.value() };
}
return 0;
}
void RemoveObjectFromInventory(GAME_OBJECT_ID objID, int count)
{
// see if the items fit into one of these easy groups
if (!TryRemoveWeapon(Lara, objID, count)
&& !TryRemoveAmmo(Lara, objID, count)
&& !TryRemoveKeyItem(Lara, objID, count)
&& !TryRemoveConsumable(Lara, objID, count)
&& !TryRemoveMiscItem(Lara, objID))
{
// item isn't any of the above; do nothing
}
#ifndef NEW_INV
g_Inventory.LoadObjects(false);
#endif

View file

@ -2,9 +2,12 @@
#include "collide.h"
enum GAME_OBJECT_ID : short;
void InitialisePickup(short itemNumber);
void PickedUpObject(short objectNumber, int count);
void RemoveObjectFromInventory(short objectNumber, int count);
void PickedUpObject(GAME_OBJECT_ID objectNumber, int count);
void RemoveObjectFromInventory(GAME_OBJECT_ID objectNumber, int count);
int GetInventoryCount(GAME_OBJECT_ID objID);
void CollectCarriedItems(ITEM_INFO* item);
int PickupTrigger(short itemNum);
void PickupCollision(short itemNum, ITEM_INFO* l, COLL_INFO* coll);

View file

@ -0,0 +1,80 @@
#include "framework.h"
#include "pickup_ammo.h"
#include "objectslist.h"
#include <array>
#include "lara_struct.h"
#include "pickuputil.h"
struct AmmoPickupInfo
{
GAME_OBJECT_ID id;
LARA_WEAPON_TYPE laraWeaponType;
WeaponAmmoType ammoType;
int amt;
};
static constexpr std::array<AmmoPickupInfo, 14> kAmmo{ {
{ID_PISTOLS_AMMO_ITEM, WEAPON_PISTOLS, WEAPON_AMMO1, 0},
{ID_UZI_AMMO_ITEM, WEAPON_UZI, WEAPON_AMMO1, 30},
{ID_SHOTGUN_AMMO1_ITEM, WEAPON_SHOTGUN, WEAPON_AMMO1, 36},
{ID_SHOTGUN_AMMO2_ITEM, WEAPON_SHOTGUN, WEAPON_AMMO2, 36},
{ID_CROSSBOW_AMMO1_ITEM, WEAPON_CROSSBOW, WEAPON_AMMO1, 10},
{ID_CROSSBOW_AMMO2_ITEM, WEAPON_CROSSBOW, WEAPON_AMMO2, 10},
{ID_CROSSBOW_AMMO3_ITEM, WEAPON_CROSSBOW, WEAPON_AMMO3, 10},
{ID_REVOLVER_AMMO_ITEM, WEAPON_REVOLVER, WEAPON_AMMO1, 6},
{ID_HK_AMMO_ITEM, WEAPON_HK, WEAPON_AMMO1, 30},
{ID_GRENADE_AMMO1_ITEM, WEAPON_GRENADE_LAUNCHER, WEAPON_AMMO1, 10},
{ID_GRENADE_AMMO2_ITEM, WEAPON_GRENADE_LAUNCHER, WEAPON_AMMO2, 10},
{ID_GRENADE_AMMO3_ITEM, WEAPON_GRENADE_LAUNCHER, WEAPON_AMMO3, 10},
{ID_ROCKET_LAUNCHER_AMMO_ITEM, WEAPON_ROCKET_LAUNCHER, WEAPON_AMMO1, 10},
{ID_HARPOON_AMMO_ITEM, WEAPON_HARPOON_GUN, WEAPON_AMMO1, 10} }
};
static bool TryModifyAmmo(LaraInfo& lara, GAME_OBJECT_ID obj, int amt, bool add)
{
int arrPos = GetArrSlot(kAmmo, obj);
if (-1 == arrPos)
{
return false;
}
AmmoPickupInfo info = kAmmo[arrPos];
auto currentAmmo = lara.Weapons[info.laraWeaponType].Ammo[info.ammoType];
if (!currentAmmo.hasInfinite())
{
auto defaultModify = add ? info.amt : -info.amt;
auto newVal = int{ currentAmmo.getCount() } + (amt ? amt : defaultModify);
lara.Weapons[info.laraWeaponType].Ammo[info.ammoType] = std::max(0, newVal);
}
return true;
}
// We need the extra bool because amt might be zero to signify the
// default amount
bool TryAddAmmo(LaraInfo& lara, GAME_OBJECT_ID obj, int amt)
{
return TryModifyAmmo(lara, obj, amt, true);
}
bool TryRemoveAmmo(LaraInfo & lara, GAME_OBJECT_ID obj, int amt)
{
return TryModifyAmmo(lara, obj, -amt, false);
}
std::optional<int> GetAmmoCount(LaraInfo& lara, GAME_OBJECT_ID obj)
{
int arrPos = GetArrSlot(kAmmo, obj);
if (-1 == arrPos)
{
return std::nullopt;
}
AmmoPickupInfo info = kAmmo[arrPos];
if (!lara.Weapons[info.laraWeaponType].Ammo[info.ammoType].hasInfinite())
{
return lara.Weapons[info.laraWeaponType].Ammo[info.ammoType].getCount();
}
// -1 means infinite ammo
return -1;
}

View file

@ -0,0 +1,7 @@
#pragma once
enum GAME_OBJECT_ID : short;
struct LaraInfo;
bool TryAddAmmo(LaraInfo &, GAME_OBJECT_ID id, int amt = 0);
bool TryRemoveAmmo(LaraInfo &, GAME_OBJECT_ID id, int amt = 0);
std::optional<int> GetAmmoCount(LaraInfo &, GAME_OBJECT_ID id);

View file

@ -0,0 +1,63 @@
#pragma once
#include "framework.h"
#include "pickup_consumable.h"
#include "objectslist.h"
#include <array>
#include "lara_struct.h"
#include "pickuputil.h"
struct ConsumablePickupInfo
{
GAME_OBJECT_ID id;
// Pointer to array of consumable in question
int LaraInfo::* count;
// How many of the item to give the player if the caller
// does not specify; i.e. default amount per pickup
int amt;
};
static constexpr std::array<ConsumablePickupInfo, 3> kConsumables = { {
{ID_SMALLMEDI_ITEM, &LaraInfo::NumSmallMedipacks, 1},
{ID_BIGMEDI_ITEM, &LaraInfo::NumLargeMedipacks, 1},
{ID_FLARE_INV_ITEM, &LaraInfo::NumFlares, 12} }
};
static bool TryModifyConsumable(LaraInfo & lara, GAME_OBJECT_ID obj, int amt, bool add)
{
int arrPos = GetArrSlot(kConsumables, obj);
if (-1 == arrPos)
{
return false;
}
ConsumablePickupInfo info = kConsumables[arrPos];
if (lara.*(info.count) != -1)
{
auto defaultModify = add ? info.amt : -info.amt;
auto newVal = lara.*(info.count) + (amt ? amt : defaultModify);
lara.*(info.count) = std::max(0, newVal);
}
return true;
}
bool TryAddConsumable(LaraInfo & lara, GAME_OBJECT_ID obj, int amt)
{
return TryModifyConsumable(lara, obj, amt, true);
}
bool TryRemoveConsumable(LaraInfo & lara, GAME_OBJECT_ID obj, int amt)
{
return TryModifyConsumable(lara, obj, -amt, false);
}
std::optional<int> GetConsumableCount(LaraInfo& lara, GAME_OBJECT_ID obj)
{
int arrPos = GetArrSlot(kConsumables, obj);
if (-1 == arrPos)
{
return std::nullopt;
}
ConsumablePickupInfo info = kConsumables[arrPos];
return lara.*(info.count);
}

View file

@ -0,0 +1,6 @@
#pragma once
enum GAME_OBJECT_ID : short;
struct LaraInfo;
bool TryAddConsumable(LaraInfo&, GAME_OBJECT_ID id, int amt = 0);
bool TryRemoveConsumable(LaraInfo&, GAME_OBJECT_ID id, int amt = 0);
std::optional<int> GetConsumableCount(LaraInfo&, GAME_OBJECT_ID id);

View file

@ -0,0 +1,98 @@
#include "framework.h"
#include "pickup_misc_items.h"
#include "lara_struct.h"
#include "objectslist.h"
template <size_t N> struct KeyPickupInfo
{
// the array in the LaraInfo struct that holds the amount of
// each type of key item (puzzle, key, examine, combos, etc)
int(LaraInfo::* arr)[N];
// The range of GAME_OBJECT_IDs that correspond to each type
// of key item
// ID of first and last objects of this type
GAME_OBJECT_ID min;
GAME_OBJECT_ID max;
constexpr KeyPickupInfo<N>(int(LaraInfo::* a_arr)[N], GAME_OBJECT_ID a_min, GAME_OBJECT_ID a_max) : arr{ a_arr }, min{ a_min }, max{ a_max } {};
};
static constexpr int kDefaultPickupAmt = 1;
// This has to be a tuple since the pointers-to-arrays all have different types
// due to differing array sizes.
static constexpr std::tuple kKeyPickupInfos = std::make_tuple(
KeyPickupInfo{&LaraInfo::Puzzles, ID_PUZZLE_ITEM1, ID_PUZZLE_ITEM16},
KeyPickupInfo{&LaraInfo::PuzzlesCombo, ID_PUZZLE_ITEM1_COMBO1, ID_PUZZLE_ITEM16_COMBO2},
KeyPickupInfo{&LaraInfo::Keys, ID_KEY_ITEM1, ID_KEY_ITEM16},
KeyPickupInfo{&LaraInfo::KeysCombo, ID_KEY_ITEM1_COMBO1, ID_KEY_ITEM16_COMBO2},
KeyPickupInfo{&LaraInfo::Pickups, ID_PICKUP_ITEM1, ID_PICKUP_ITEM16},
KeyPickupInfo{&LaraInfo::PickupsCombo, ID_PICKUP_ITEM1_COMBO1, ID_PICKUP_ITEM16_COMBO2},
KeyPickupInfo{&LaraInfo::Examines, ID_EXAMINE1, ID_EXAMINE8},
KeyPickupInfo{&LaraInfo::ExaminesCombo, ID_EXAMINE1_COMBO1, ID_EXAMINE8_COMBO2}
);
static constexpr auto nInfos = std::tuple_size<decltype(kKeyPickupInfos)>{};
// Test against min and max of kKeyPickupInfos[N].
// if found, return the pointer to the LaraInfo array
// as well as the position within that array.
template<size_t N> static constexpr std::pair<int *, size_t> TestAgainstRange(LaraInfo & info, GAME_OBJECT_ID obj)
{
auto pickupInfo = std::get<N>(kKeyPickupInfos);
int min = pickupInfo.min;
int max = pickupInfo.max;
if (obj >= min && obj <= max)
{
return std::make_pair(info.*(pickupInfo.arr), obj - min);
}
return std::make_pair(nullptr, 0);
}
// Test against each kKeyPickupInfos item.
// This is recursive because I couldn't find a simpler
// way to iterate across a tuple.
template<size_t N> static std::pair<int *, size_t> GetArrInternal(LaraInfo & info, GAME_OBJECT_ID obj)
{
auto h = TestAgainstRange<N>(info, obj);
return h.first ? h : GetArrInternal<N-1>(info, obj);
}
// Base case for recursion
template<> static std::pair<int *, size_t> GetArrInternal<0>(LaraInfo & info, GAME_OBJECT_ID obj)
{
return TestAgainstRange<0>(info, obj);
}
static bool TryModifyKeyItem(LaraInfo & info, GAME_OBJECT_ID obj, int amt, bool add)
{
// kick off the recursion starting at the last element
auto result = GetArrInternal<nInfos - 1>(info, obj);
if (result.first) {
auto defaultModify = add ? kDefaultPickupAmt : -kDefaultPickupAmt;
auto newVal = int{result.first[result.second]} + (amt ? amt : defaultModify);
result.first[result.second] = std::max(0, newVal);
return true;
}
return false;
}
bool TryAddKeyItem(LaraInfo & info, GAME_OBJECT_ID obj, int count)
{
return TryModifyKeyItem(info, obj, count, true);
}
bool TryRemoveKeyItem(LaraInfo & info, GAME_OBJECT_ID obj, int count)
{
return TryModifyKeyItem(info, obj, -count, false);
}
std::optional<int> GetKeyItemCount(LaraInfo & info, GAME_OBJECT_ID obj)
{
// kick off the recursion starting at the last element
auto result = GetArrInternal<nInfos - 1>(info, obj);
if (result.first) {
return result.first[result.second];
}
return std::nullopt;
}

View file

@ -0,0 +1,8 @@
#pragma once
enum GAME_OBJECT_ID : short;
struct LaraInfo;
bool TryAddKeyItem(LaraInfo& lara, GAME_OBJECT_ID obj, int count);
bool TryRemoveKeyItem(LaraInfo& lara, GAME_OBJECT_ID obj, int count);
std::optional<int> GetKeyItemCount(LaraInfo& lara, GAME_OBJECT_ID obj);

View file

@ -0,0 +1,120 @@
#include "framework.h"
#include "pickup_misc_items.h"
#include "objectslist.h"
#include <array>
#include "lara_struct.h"
#include "pickuputil.h"
auto SilencerIsEquipped(LaraInfo& lara)
{
return lara.Weapons[WEAPON_UZI].HasSilencer
|| lara.Weapons[WEAPON_PISTOLS].HasSilencer
|| lara.Weapons[WEAPON_SHOTGUN].HasSilencer
|| lara.Weapons[WEAPON_REVOLVER].HasSilencer
|| lara.Weapons[WEAPON_CROSSBOW].HasSilencer
|| lara.Weapons[WEAPON_HK].HasSilencer;
};
auto LaserSightIsEquipped(LaraInfo& lara)
{
return lara.Weapons[WEAPON_REVOLVER].HasLasersight
|| lara.Weapons[WEAPON_CROSSBOW].HasLasersight
|| lara.Weapons[WEAPON_HK].HasLasersight;
};
static bool TryModifyMiscCount(LaraInfo & lara, GAME_OBJECT_ID obj, bool add)
{
auto modifyBeetleCount = [&](int bit)
{
if (add)
{
lara.hasBeetleThings |= 1 << bit;
}
else
{
lara.hasBeetleThings &= ~(1 << bit);
}
};
switch (obj) {
case ID_SILENCER_ITEM:
lara.Silencer = add && !SilencerIsEquipped(lara);
break;
case ID_LASERSIGHT_ITEM:
lara.Lasersight = add && !LaserSightIsEquipped(lara);
break;
case ID_BINOCULARS_ITEM:
lara.Binoculars = add;
break;
case ID_CROWBAR_ITEM:
lara.Crowbar = add;
break;
case ID_DIARY_ITEM:
lara.Diary.Present = add;
break;
//todo.. what if the waterskin is already full?
case ID_WATERSKIN1_EMPTY:
lara.small_waterskin = int{ add };
break;
case ID_WATERSKIN2_EMPTY:
lara.big_waterskin = int{ add };
break;
case ID_CLOCKWORK_BEETLE:
modifyBeetleCount(0);
break;
case ID_CLOCKWORK_BEETLE_COMBO1:
modifyBeetleCount(1);
break;
case ID_CLOCKWORK_BEETLE_COMBO2:
modifyBeetleCount(2);
break;
default:
return false;
}
return true;
}
bool TryAddMiscItem(LaraInfo & lara, GAME_OBJECT_ID obj)
{
return TryModifyMiscCount(lara, obj, true);
}
bool TryRemoveMiscItem(LaraInfo & lara, GAME_OBJECT_ID obj)
{
return TryModifyMiscCount(lara, obj, false);
}
std::optional<bool> HasMiscItem(LaraInfo& lara, GAME_OBJECT_ID obj)
{
auto HasBeetle = [&](int bit)
{
return lara.hasBeetleThings &= 1 << bit;
};
switch (obj) {
//TODO does Lara "HAVE" a silencer if it's combined but not in her inventory?
case ID_SILENCER_ITEM:
return lara.Silencer || SilencerIsEquipped(lara);
case ID_LASERSIGHT_ITEM:
return lara.Lasersight || LaserSightIsEquipped(lara);
case ID_BINOCULARS_ITEM:
return lara.Binoculars;
case ID_CROWBAR_ITEM:
return lara.Crowbar;
case ID_DIARY_ITEM:
return lara.Diary.Present;
//TODO what about other waterskins?
case ID_WATERSKIN1_EMPTY:
return lara.small_waterskin;
case ID_WATERSKIN2_EMPTY:
return lara.big_waterskin;
case ID_CLOCKWORK_BEETLE:
return HasBeetle(0);
case ID_CLOCKWORK_BEETLE_COMBO1:
return HasBeetle(1);
case ID_CLOCKWORK_BEETLE_COMBO2:
return HasBeetle(2);
default:
return std::nullopt;
}
}

View file

@ -0,0 +1,7 @@
#pragma once
enum GAME_OBJECT_ID : short;
struct LaraInfo;
bool TryAddMiscItem(LaraInfo& lara, GAME_OBJECT_ID obj);
bool TryRemoveMiscItem(LaraInfo& lara, GAME_OBJECT_ID obj);
std::optional<bool> HasMiscItem(LaraInfo& lara, GAME_OBJECT_ID obj);

View file

@ -0,0 +1,87 @@
#include "framework.h"
#include "pickup_weapon.h"
#include "objectslist.h"
#include <array>
#include "pickup_ammo.h"
#include "lara_struct.h"
#include "pickuputil.h"
struct WeaponPickupInfo
{
GAME_OBJECT_ID id;
// when the player picks up a weapon they
// get one clip's worth of the following ammo
GAME_OBJECT_ID ammoID;
LARA_WEAPON_TYPE laraWeaponType;
};
static constexpr std::array<WeaponPickupInfo, 9> kWeapons{ {
{ID_PISTOLS_ITEM, ID_PISTOLS_AMMO_ITEM, WEAPON_PISTOLS},
{ID_UZI_ITEM, ID_UZI_AMMO_ITEM, WEAPON_UZI},
{ID_SHOTGUN_ITEM, ID_SHOTGUN_AMMO1_ITEM, WEAPON_SHOTGUN},
{ID_CROSSBOW_ITEM, ID_CROSSBOW_AMMO1_ITEM, WEAPON_CROSSBOW},
{ID_REVOLVER_ITEM, ID_REVOLVER_AMMO_ITEM, WEAPON_REVOLVER},
{ID_HK_ITEM, ID_HK_AMMO_ITEM, WEAPON_HK},
{ID_GRENADE_GUN_ITEM, ID_GRENADE_AMMO1_ITEM, WEAPON_GRENADE_LAUNCHER},
{ID_ROCKET_LAUNCHER_ITEM, ID_ROCKET_LAUNCHER_AMMO_ITEM, WEAPON_ROCKET_LAUNCHER},
{ID_HARPOON_ITEM, ID_HARPOON_AMMO_ITEM, WEAPON_HARPOON_GUN} }
};
static int GetWeapon(GAME_OBJECT_ID obj)
{
for (int i = 0; i < kWeapons.size(); ++i)
{
if (obj == kWeapons[i].id)
{
return i;
}
}
return -1;
}
static bool TryModifyWeapon(LaraInfo& lara, GAME_OBJECT_ID obj, int ammoAmt, bool add)
{
int arrPos = GetArrSlot(kWeapons, obj);
if (-1 == arrPos)
{
return false;
}
WeaponPickupInfo info = kWeapons[arrPos];
// set the SelectedAmmo type to 0 if adding the weapon for the
// first time. Note that this refers to the index of the weapon's
// ammo array, and not the weapon's actual ammunition count
if (!lara.Weapons[info.laraWeaponType].Present) {
lara.Weapons[info.laraWeaponType].SelectedAmmo = 0;
}
lara.Weapons[info.laraWeaponType].Present = add;
auto ammoID = info.ammoID;
return add ? TryAddAmmo(lara, ammoID, ammoAmt) : TryRemoveAmmo(lara, ammoID, ammoAmt);
}
// Adding a weapon will either give the player the weapon + amt ammo
// or, if they already have the weapon, simply the ammo.
bool TryAddWeapon(LaraInfo& lara, GAME_OBJECT_ID obj, int amt)
{
return TryModifyWeapon(lara, obj, amt, true);
}
// Removing a weapon is the reverse of the above; it will remove the weapon
// (if it's there) and amt ammo.
bool TryRemoveWeapon(LaraInfo& lara, GAME_OBJECT_ID obj, int amt)
{
return TryModifyWeapon(lara, obj, amt, false);
}
std::optional<bool> HasWeapon(LaraInfo& lara, GAME_OBJECT_ID obj)
{
int arrPos = GetArrSlot(kWeapons, obj);
if (-1 == arrPos)
{
return std::nullopt;
}
WeaponPickupInfo info = kWeapons[arrPos];
return lara.Weapons[info.laraWeaponType].Present;
}

View file

@ -0,0 +1,7 @@
#pragma once
enum GAME_OBJECT_ID : short;
struct LaraInfo;
bool TryAddWeapon(LaraInfo& lara, GAME_OBJECT_ID obj, int amt = 0);
bool TryRemoveWeapon(LaraInfo& lara, GAME_OBJECT_ID obj, int amt = 0);
std::optional<bool> HasWeapon(LaraInfo &, GAME_OBJECT_ID id);

View file

@ -0,0 +1,16 @@
#pragma once
enum GAME_OBJECT_ID : short;
// Given an array and an id, iterate through the array until we find
// an ID that matches the ID we've passed in.
template <typename T, size_t N> int GetArrSlot(std::array<T, N> const & arr, GAME_OBJECT_ID obj)
{
for (int i = 0; i < arr.size(); ++i)
{
if (obj == arr[i].id)
{
return i;
}
}
return -1;
}

View file

@ -153,7 +153,7 @@ void PuzzleHoleCollision(short itemNum, ITEM_INFO* l, COLL_INFO* coll)
}
}
RemoveObjectFromInventory(item->objectNumber - (ID_PUZZLE_HOLE1 - ID_PUZZLE_ITEM1), 1);
RemoveObjectFromInventory(static_cast<GAME_OBJECT_ID>(item->objectNumber - (ID_PUZZLE_HOLE1 - ID_PUZZLE_ITEM1)), 1);
if (flag == 1)
{
@ -209,7 +209,7 @@ void PuzzleDoneCollision(short itemNum, ITEM_INFO* l, COLL_INFO* coll)
void PuzzleDone(ITEM_INFO* item, short itemNum)
{
item->objectNumber += (ID_PUZZLE_DONE1 - ID_PUZZLE_HOLE1);
item->objectNumber += GAME_OBJECT_ID{ ID_PUZZLE_DONE1 - ID_PUZZLE_HOLE1 };
item->animNumber = Objects[item->objectNumber].animIndex;
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
item->requiredAnimState = 0;
@ -325,7 +325,7 @@ void KeyHoleCollision(short itemNum, ITEM_INFO* l, COLL_INFO* coll)
l->animNumber = LA_KEYCARD_USE;
else
{
RemoveObjectFromInventory(item->objectNumber - (ID_KEY_HOLE1 - ID_KEY_ITEM1), 1);
RemoveObjectFromInventory(static_cast<GAME_OBJECT_ID>(item->objectNumber - (ID_KEY_HOLE1 - ID_KEY_ITEM1)), 1);
l->animNumber = LA_USE_KEY;
}
l->currentAnimState = LS_INSERT_KEY;

View file

@ -510,7 +510,7 @@ bool SaveGame::readItem()
itemNumber = CreateItem();
ITEM_INFO* item = &g_Level.Items[itemNumber];
item->objectNumber = LEB128::ReadInt16(m_stream);
item->objectNumber = from_underlying(LEB128::ReadInt16(m_stream));
OBJECT_INFO* obj = &Objects[item->objectNumber];
@ -542,7 +542,7 @@ bool SaveGame::readItem()
// Some post-processing things
if (obj->isPuzzleHole && (item->status == ITEM_DEACTIVATED || item->status == ITEM_ACTIVE))
item->objectNumber += NUM_PUZZLES;
item->objectNumber += GAME_OBJECT_ID{ NUM_PUZZLES };
if (item->objectNumber >= ID_SMASH_OBJECT1 && item->objectNumber <= ID_SMASH_OBJECT8 && (item->flags & ONESHOT))
item->meshBits = 0x100;
@ -784,7 +784,7 @@ bool SaveGame::readLaraChunks(ChunkId* chunkId, int maxSize, int arg)
ITEM_INFO* weaponItem = &g_Level.Items[Lara.weaponItem];
weaponItem->objectNumber = LEB128::ReadInt16(m_stream);
weaponItem->objectNumber = from_underlying(LEB128::ReadInt16(m_stream));
weaponItem->animNumber = LEB128::ReadInt16(m_stream);
weaponItem->frameNumber = LEB128::ReadInt16(m_stream);
weaponItem->currentAnimState = LEB128::ReadInt16(m_stream);
@ -996,7 +996,7 @@ bool SaveGame::readItemChunks(ChunkId* chunkId, int maxSize, int itemNumber)
ITEM_INFO* enemy = (ITEM_INFO*)LEB128::ReadLong(m_stream);
creature->enemy = AddPtr(enemy, ITEM_INFO, malloc_buffer);
creature->aiTarget.objectNumber = LEB128::ReadInt16(m_stream);
creature->aiTarget.objectNumber = from_underlying(LEB128::ReadInt16(m_stream));
creature->aiTarget.roomNumber = LEB128::ReadInt16(m_stream);
creature->aiTarget.boxNumber = LEB128::ReadInt16(m_stream);
creature->aiTarget.flags = LEB128::ReadInt16(m_stream);

View file

@ -79,7 +79,7 @@ void SarcophagusCollision(short itemNum, ITEM_INFO* l, COLL_INFO* coll)
{
if (Objects[currentItem->objectNumber].isPickup)
{
PickedUpObject(currentItem->objectNumber, 0);
PickedUpObject(static_cast<GAME_OBJECT_ID>(currentItem->objectNumber), 0);
currentItem->status = ITEM_ACTIVE;
currentItem->itemFlags[3] = 1;
AddDisplayPickup(currentItem->objectNumber);

View file

@ -1,6 +1,6 @@
#pragma once
typedef enum GAME_OBJECT_ID
enum GAME_OBJECT_ID : short
{
/* Lara Primary Slot */
ID_LARA,
@ -999,6 +999,24 @@ typedef enum GAME_OBJECT_ID
ID_NUMBER_OBJECTS, // NEED TO BE AT THE END !!!!
};
template <typename T, typename Enum> constexpr bool is_underlying = std::is_same_v<T, std::underlying_type_t<Enum>>;
template <typename T, typename Enum> constexpr bool same_or_underlying = std::is_same_v<T, Enum> || is_underlying<T, Enum>;
// += operator that only allows addition of shorts or GAME_OBJECT_IDs. This is to
// allow us to see and manually review any places where we're adding something that
// might be the wrong type.
template <typename T> std::enable_if_t<same_or_underlying<T, GAME_OBJECT_ID>, GAME_OBJECT_ID> & operator+=(GAME_OBJECT_ID lhs, T rhs)
{
lhs += GAME_OBJECT_ID{ rhs };
return lhs;
}
template <typename T> std::enable_if_t<std::is_same_v<T, std::underlying_type_t<GAME_OBJECT_ID>>, GAME_OBJECT_ID>
from_underlying (T rhs)
{
return GAME_OBJECT_ID{ rhs };
}
typedef enum SPRITE_TYPES
{
SPR_FIRE0,

View file

@ -11,6 +11,9 @@
#include "tomb4fx.h"
#include "effect2.h"
#include "pickup.h"
#include "newinv2.h"
#include <iostream>
#include "InventorySlots.h"
extern GameFlow* g_GameFlow;
GameScript* g_GameScript;
@ -21,8 +24,31 @@ GameScript::GameScript(sol::state* lua) : LuaHandler{ lua }
m_lua->set_function("SetAmbientTrack", &GameScript::SetAmbientTrack);
m_lua->set_function("PlayAudioTrack", &GameScript::PlayAudioTrack);
m_lua->set_function("InventoryAdd", &GameScript::InventoryAdd);
m_lua->set_function("InventoryRemove", &GameScript::InventoryRemove);
m_lua->set_function("GiveInvItem", &GameScript::InventoryAdd);
m_lua->set_function("TakeInvItem", &GameScript::InventoryRemove);
m_lua->set_function("GetInvItemCount", &GameScript::InventoryGetCount);
m_lua->set_function("SetInvItemCount", &GameScript::InventorySetCount);
// Put all the data in InvItem's metatable
m_lua->set("InvItemMeta", sol::as_table(kInventorySlots));
// Make the metatable's __index refer to itself so that requests
// to InvItem will go through to the metatable (and thus the
// kInventorySlot members)
m_lua->safe_script("InvItemMeta.__index = InvItemMeta");
// Don't allow InvItem to have new elements put into it
m_lua->safe_script("InvItemMeta.__newindex = function() error('InvItem is read-only') end");
// Protect the metatable
m_lua->safe_script("InvItemMeta.__metatable = 'metatable is protected'");
auto tab = m_lua->create_named_table("InvItem");
m_lua->safe_script("setmetatable(InvItem, InvItemMeta)");
// point InvItemMeta array from the table
m_lua->safe_script("InvItemMeta = nil");
GameScriptItemInfo::Register(m_lua);
GameScriptPosition::Register(m_lua);
@ -306,24 +332,26 @@ void GameScript::Earthquake(int strength)
}
// Inventory
void GameScript::InventoryAdd(int slot, int count)
void GameScript::InventoryAdd(GAME_OBJECT_ID slot, sol::optional<int> count)
{
PickedUpObject(slot, count);
PickedUpObject(slot, count.value_or(0));
}
void GameScript::InventoryRemove(int slot, int count)
void GameScript::InventoryRemove(GAME_OBJECT_ID slot, sol::optional<int> count)
{
RemoveObjectFromInventory(slot, count);
RemoveObjectFromInventory(static_cast<GAME_OBJECT_ID>(inventry_objects_list[slot].object_number), count.value_or(0));
}
void GameScript::InventoryGetCount(int slot)
int GameScript::InventoryGetCount(GAME_OBJECT_ID slot)
{
return GetInventoryCount(slot);
}
void GameScript::InventorySetCount(int slot, int count)
void GameScript::InventorySetCount(GAME_OBJECT_ID slot, int count)
{
// add the amount we'd need to add to get to count
int currAmt = GetInventoryCount(slot);
InventoryAdd(slot, count - currAmt);
}
void GameScript::InventoryCombine(int slot1, int slot2)

View file

@ -96,10 +96,10 @@ public:
void Earthquake(int strength);
// Inventory
static void InventoryAdd(int slot, int count);
static void InventoryRemove(int slot, int count);
void InventoryGetCount(int slot);
void InventorySetCount(int slot, int count);
static void InventoryAdd(GAME_OBJECT_ID slot, sol::optional<int> count);
static void InventoryRemove(GAME_OBJECT_ID slot, sol::optional<int> count);
static int InventoryGetCount(GAME_OBJECT_ID slot);
static void InventorySetCount(GAME_OBJECT_ID slot, int count);
void InventoryCombine(int slot1, int slot2);
void InventorySeparate(int slot);

View file

@ -0,0 +1,218 @@
#pragma once
#include "objectslist.h"
#include <unordered_map>
#include <string>
static const std::unordered_map<std::string, GAME_OBJECT_ID> kInventorySlots{
{"PISTOLS_ITEM", ID_PISTOLS_ITEM },
{"PISTOLS_AMMO_ITEM", ID_PISTOLS_AMMO_ITEM },
{"UZI_ITEM", ID_UZI_ITEM },
{"UZI_AMMO_ITEM", ID_UZI_AMMO_ITEM },
{"SHOTGUN_ITEM", ID_SHOTGUN_ITEM },
{"SHOTGUN_AMMO1_ITEM", ID_SHOTGUN_AMMO1_ITEM },
{"SHOTGUN_AMMO2_ITEM", ID_SHOTGUN_AMMO2_ITEM },
{"REVOLVER_ITEM", ID_REVOLVER_ITEM },
{"REVOLVER_AMMO_ITEM", ID_REVOLVER_AMMO_ITEM },
{"CROSSBOW_ITEM", ID_CROSSBOW_ITEM },
{"CROSSBOW_AMMO1_ITEM", ID_CROSSBOW_AMMO1_ITEM },
{"CROSSBOW_AMMO2_ITEM", ID_CROSSBOW_AMMO2_ITEM },
{"CROSSBOW_AMMO3_ITEM", ID_CROSSBOW_AMMO3_ITEM },
{"HK_ITEM", ID_HK_ITEM },
{"HK_AMMO_ITEM", ID_HK_AMMO_ITEM },
{"GRENADE_GUN_ITEM", ID_GRENADE_GUN_ITEM },
{"GRENADE_AMMO1_ITEM", ID_GRENADE_AMMO1_ITEM },
{"GRENADE_AMMO2_ITEM", ID_GRENADE_AMMO2_ITEM },
{"GRENADE_AMMO3_ITEM", ID_GRENADE_AMMO3_ITEM },
{"HARPOON_ITEM", ID_HARPOON_ITEM },
{"HARPOON_AMMO_ITEM", ID_HARPOON_AMMO_ITEM },
{"ROCKET_LAUNCHER_ITEM", ID_ROCKET_LAUNCHER_ITEM },
{"ROCKET_LAUNCHER_AMMO_ITEM", ID_ROCKET_LAUNCHER_AMMO_ITEM },
{"LASERSIGHT_ITEM", ID_LASERSIGHT_ITEM },
{"SILENCER_ITEM", ID_SILENCER_ITEM },
{"BIGMEDI_ITEM", ID_BIGMEDI_ITEM },
{"SMALLMEDI_ITEM", ID_SMALLMEDI_ITEM },
{"BINOCULARS_ITEM", ID_BINOCULARS_ITEM },
{"FLARE_INV_ITEM", ID_FLARE_INV_ITEM },
{"TIMEX_ITEM", ID_TIMEX_ITEM },
{"PC_LOAD_INV_ITEM", ID_PC_LOAD_INV_ITEM },
{"PC_LOAD_SAVE_ITEM", ID_PC_LOAD_SAVE_ITEM },
{"BURNING_TORCH_ITEM", ID_BURNING_TORCH_ITEM },
{"CROWBAR_ITEM", ID_CROWBAR_ITEM },
{"DIARY_ITEM", ID_DIARY_ITEM },
{"COMPASS_ITEM", ID_COMPASS_ITEM },
{"CLOCKWORK_BEETLE", ID_CLOCKWORK_BEETLE },
{"CLOCKWORK_BEETLE_COMBO1", ID_CLOCKWORK_BEETLE_COMBO1 },
{"CLOCKWORK_BEETLE_COMBO2", ID_CLOCKWORK_BEETLE_COMBO2 },
{"WATERSKIN1_EMPTY", ID_WATERSKIN1_EMPTY },
{"WATERSKIN2_EMPTY", ID_WATERSKIN2_EMPTY },
{"OPEN_DIARY_ITEM", ID_OPEN_DIARY_ITEM },
{"GOLDROSE_ITEM", ID_GOLDROSE_ITEM },
{"PUZZLE_ITEM1", ID_PUZZLE_ITEM1 },
{"PUZZLE_ITEM2", ID_PUZZLE_ITEM2 },
{"PUZZLE_ITEM3", ID_PUZZLE_ITEM3 },
{"PUZZLE_ITEM4", ID_PUZZLE_ITEM4 },
{"PUZZLE_ITEM5", ID_PUZZLE_ITEM5 },
{"PUZZLE_ITEM6", ID_PUZZLE_ITEM6 },
{"PUZZLE_ITEM7", ID_PUZZLE_ITEM7 },
{"PUZZLE_ITEM8", ID_PUZZLE_ITEM8 },
{"PUZZLE_ITEM9", ID_PUZZLE_ITEM9 },
{"PUZZLE_ITEM10", ID_PUZZLE_ITEM10 },
{"PUZZLE_ITEM11", ID_PUZZLE_ITEM11 },
{"PUZZLE_ITEM12", ID_PUZZLE_ITEM12 },
{"PUZZLE_ITEM13", ID_PUZZLE_ITEM13 },
{"PUZZLE_ITEM14", ID_PUZZLE_ITEM14 },
{"PUZZLE_ITEM15", ID_PUZZLE_ITEM15 },
{"PUZZLE_ITEM16", ID_PUZZLE_ITEM16 },
{"PUZZLE_ITEM1_COMBO1", ID_PUZZLE_ITEM1_COMBO1 },
{"PUZZLE_ITEM1_COMBO2", ID_PUZZLE_ITEM1_COMBO2 },
{"PUZZLE_ITEM2_COMBO1", ID_PUZZLE_ITEM2_COMBO1 },
{"PUZZLE_ITEM2_COMBO2", ID_PUZZLE_ITEM2_COMBO2 },
{"PUZZLE_ITEM3_COMBO1", ID_PUZZLE_ITEM3_COMBO1 },
{"PUZZLE_ITEM3_COMBO2", ID_PUZZLE_ITEM3_COMBO2 },
{"PUZZLE_ITEM4_COMBO1", ID_PUZZLE_ITEM4_COMBO1 },
{"PUZZLE_ITEM4_COMBO2", ID_PUZZLE_ITEM4_COMBO2 },
{"PUZZLE_ITEM5_COMBO1", ID_PUZZLE_ITEM5_COMBO1 },
{"PUZZLE_ITEM5_COMBO2", ID_PUZZLE_ITEM5_COMBO2 },
{"PUZZLE_ITEM6_COMBO1", ID_PUZZLE_ITEM6_COMBO1 },
{"PUZZLE_ITEM6_COMBO2", ID_PUZZLE_ITEM6_COMBO2 },
{"PUZZLE_ITEM7_COMBO1", ID_PUZZLE_ITEM7_COMBO1 },
{"PUZZLE_ITEM7_COMBO2", ID_PUZZLE_ITEM7_COMBO2 },
{"PUZZLE_ITEM8_COMBO1", ID_PUZZLE_ITEM8_COMBO1 },
{"PUZZLE_ITEM8_COMBO2", ID_PUZZLE_ITEM8_COMBO2 },
{"PUZZLE_ITEM9_COMBO1", ID_PUZZLE_ITEM9_COMBO1 },
{"PUZZLE_ITEM9_COMBO2", ID_PUZZLE_ITEM9_COMBO2 },
{"PUZZLE_ITEM10_COMBO1", ID_PUZZLE_ITEM10_COMBO1 },
{"PUZZLE_ITEM10_COMBO2", ID_PUZZLE_ITEM10_COMBO2 },
{"PUZZLE_ITEM11_COMBO1", ID_PUZZLE_ITEM11_COMBO1 },
{"PUZZLE_ITEM11_COMBO2", ID_PUZZLE_ITEM11_COMBO2 },
{"PUZZLE_ITEM12_COMBO1", ID_PUZZLE_ITEM12_COMBO1 },
{"PUZZLE_ITEM12_COMBO2", ID_PUZZLE_ITEM12_COMBO2 },
{"PUZZLE_ITEM13_COMBO1", ID_PUZZLE_ITEM13_COMBO1 },
{"PUZZLE_ITEM13_COMBO2", ID_PUZZLE_ITEM13_COMBO2 },
{"PUZZLE_ITEM14_COMBO1", ID_PUZZLE_ITEM14_COMBO1 },
{"PUZZLE_ITEM14_COMBO2", ID_PUZZLE_ITEM14_COMBO2 },
{"PUZZLE_ITEM15_COMBO1", ID_PUZZLE_ITEM15_COMBO1 },
{"PUZZLE_ITEM15_COMBO2", ID_PUZZLE_ITEM15_COMBO2 },
{"PUZZLE_ITEM16_COMBO1", ID_PUZZLE_ITEM16_COMBO1 },
{"PUZZLE_ITEM16_COMBO2", ID_PUZZLE_ITEM16_COMBO2 },
{"KEY_ITEM1", ID_KEY_ITEM1 },
{"KEY_ITEM2", ID_KEY_ITEM2 },
{"KEY_ITEM3", ID_KEY_ITEM3 },
{"KEY_ITEM4", ID_KEY_ITEM4 },
{"KEY_ITEM5", ID_KEY_ITEM5 },
{"KEY_ITEM6", ID_KEY_ITEM6 },
{"KEY_ITEM7", ID_KEY_ITEM7 },
{"KEY_ITEM8", ID_KEY_ITEM8 },
{"KEY_ITEM9", ID_KEY_ITEM9 },
{"KEY_ITEM10", ID_KEY_ITEM10 },
{"KEY_ITEM11", ID_KEY_ITEM11 },
{"KEY_ITEM12", ID_KEY_ITEM12 },
{"KEY_ITEM13", ID_KEY_ITEM13 },
{"KEY_ITEM14", ID_KEY_ITEM14 },
{"KEY_ITEM15", ID_KEY_ITEM15 },
{"KEY_ITEM16", ID_KEY_ITEM16 },
{"KEY_ITEM1_COMBO1", ID_KEY_ITEM1_COMBO1 },
{"KEY_ITEM1_COMBO2", ID_KEY_ITEM1_COMBO2 },
{"KEY_ITEM2_COMBO1", ID_KEY_ITEM2_COMBO1 },
{"KEY_ITEM2_COMBO2", ID_KEY_ITEM2_COMBO2 },
{"KEY_ITEM3_COMBO1", ID_KEY_ITEM3_COMBO1 },
{"KEY_ITEM3_COMBO2", ID_KEY_ITEM3_COMBO2 },
{"KEY_ITEM4_COMBO1", ID_KEY_ITEM4_COMBO1 },
{"KEY_ITEM4_COMBO2", ID_KEY_ITEM4_COMBO2 },
{"KEY_ITEM5_COMBO1", ID_KEY_ITEM5_COMBO1 },
{"KEY_ITEM5_COMBO2", ID_KEY_ITEM5_COMBO2 },
{"KEY_ITEM6_COMBO1", ID_KEY_ITEM6_COMBO1 },
{"KEY_ITEM6_COMBO2", ID_KEY_ITEM6_COMBO2 },
{"KEY_ITEM7_COMBO1", ID_KEY_ITEM7_COMBO1 },
{"KEY_ITEM7_COMBO2", ID_KEY_ITEM7_COMBO2 },
{"KEY_ITEM8_COMBO1", ID_KEY_ITEM8_COMBO1 },
{"KEY_ITEM8_COMBO2", ID_KEY_ITEM8_COMBO2 },
{"KEY_ITEM9_COMBO1", ID_KEY_ITEM9_COMBO1 },
{"KEY_ITEM9_COMBO2", ID_KEY_ITEM9_COMBO2 },
{"KEY_ITEM10_COMBO1", ID_KEY_ITEM10_COMBO1 },
{"KEY_ITEM10_COMBO2", ID_KEY_ITEM10_COMBO2 },
{"KEY_ITEM11_COMBO1", ID_KEY_ITEM11_COMBO1 },
{"KEY_ITEM11_COMBO2", ID_KEY_ITEM11_COMBO2 },
{"KEY_ITEM12_COMBO1", ID_KEY_ITEM12_COMBO1 },
{"KEY_ITEM12_COMBO2", ID_KEY_ITEM12_COMBO2 },
{"KEY_ITEM13_COMBO1", ID_KEY_ITEM13_COMBO1 },
{"KEY_ITEM13_COMBO2", ID_KEY_ITEM13_COMBO2 },
{"KEY_ITEM14_COMBO1", ID_KEY_ITEM14_COMBO1 },
{"KEY_ITEM14_COMBO2", ID_KEY_ITEM14_COMBO2 },
{"KEY_ITEM15_COMBO1", ID_KEY_ITEM15_COMBO1 },
{"KEY_ITEM15_COMBO2", ID_KEY_ITEM15_COMBO2 },
{"KEY_ITEM16_COMBO1", ID_KEY_ITEM16_COMBO1 },
{"KEY_ITEM16_COMBO2", ID_KEY_ITEM16_COMBO2 },
{"PICKUP_ITEM1", ID_PICKUP_ITEM1 },
{"PICKUP_ITEM2", ID_PICKUP_ITEM2 },
{"PICKUP_ITEM3", ID_PICKUP_ITEM3 },
{"PICKUP_ITEM4", ID_PICKUP_ITEM4 },
{"PICKUP_ITEM5", ID_PICKUP_ITEM5 },
{"PICKUP_ITEM6", ID_PICKUP_ITEM6 },
{"PICKUP_ITEM7", ID_PICKUP_ITEM7 },
{"PICKUP_ITEM8", ID_PICKUP_ITEM8 },
{"PICKUP_ITEM9", ID_PICKUP_ITEM9 },
{"PICKUP_ITEM10", ID_PICKUP_ITEM10 },
{"PICKUP_ITEM11", ID_PICKUP_ITEM11 },
{"PICKUP_ITEM12", ID_PICKUP_ITEM12 },
{"PICKUP_ITEM13", ID_PICKUP_ITEM13 },
{"PICKUP_ITEM14", ID_PICKUP_ITEM14 },
{"PICKUP_ITEM15", ID_PICKUP_ITEM15 },
{"PICKUP_ITEM16", ID_PICKUP_ITEM16 },
{"PICKUP_ITEM1_COMBO1", ID_PICKUP_ITEM1_COMBO1 },
{"PICKUP_ITEM1_COMBO2", ID_PICKUP_ITEM1_COMBO2 },
{"PICKUP_ITEM2_COMBO1", ID_PICKUP_ITEM2_COMBO1 },
{"PICKUP_ITEM2_COMBO2", ID_PICKUP_ITEM2_COMBO2 },
{"PICKUP_ITEM3_COMBO1", ID_PICKUP_ITEM3_COMBO1 },
{"PICKUP_ITEM3_COMBO2", ID_PICKUP_ITEM3_COMBO2 },
{"PICKUP_ITEM4_COMBO1", ID_PICKUP_ITEM4_COMBO1 },
{"PICKUP_ITEM4_COMBO2", ID_PICKUP_ITEM4_COMBO2 },
{"PICKUP_ITEM5_COMBO1", ID_PICKUP_ITEM5_COMBO1 },
{"PICKUP_ITEM5_COMBO2", ID_PICKUP_ITEM5_COMBO2 },
{"PICKUP_ITEM6_COMBO1", ID_PICKUP_ITEM6_COMBO1 },
{"PICKUP_ITEM6_COMBO2", ID_PICKUP_ITEM6_COMBO2 },
{"PICKUP_ITEM7_COMBO1", ID_PICKUP_ITEM7_COMBO1 },
{"PICKUP_ITEM7_COMBO2", ID_PICKUP_ITEM7_COMBO2 },
{"PICKUP_ITEM8_COMBO1", ID_PICKUP_ITEM8_COMBO1 },
{"PICKUP_ITEM8_COMBO2", ID_PICKUP_ITEM8_COMBO2 },
{"PICKUP_ITEM9_COMBO1", ID_PICKUP_ITEM9_COMBO1 },
{"PICKUP_ITEM9_COMBO2", ID_PICKUP_ITEM9_COMBO2 },
{"PICKUP_ITEM10_COMBO1", ID_PICKUP_ITEM10_COMBO1 },
{"PICKUP_ITEM10_COMBO2", ID_PICKUP_ITEM10_COMBO2 },
{"PICKUP_ITEM11_COMBO1", ID_PICKUP_ITEM11_COMBO1 },
{"PICKUP_ITEM11_COMBO2", ID_PICKUP_ITEM11_COMBO2 },
{"PICKUP_ITEM12_COMBO1", ID_PICKUP_ITEM12_COMBO1 },
{"PICKUP_ITEM12_COMBO2", ID_PICKUP_ITEM12_COMBO2 },
{"PICKUP_ITEM13_COMBO1", ID_PICKUP_ITEM13_COMBO1 },
{"PICKUP_ITEM13_COMBO2", ID_PICKUP_ITEM13_COMBO2 },
{"PICKUP_ITEM14_COMBO1", ID_PICKUP_ITEM14_COMBO1 },
{"PICKUP_ITEM14_COMBO2", ID_PICKUP_ITEM14_COMBO2 },
{"PICKUP_ITEM15_COMBO1", ID_PICKUP_ITEM15_COMBO1 },
{"PICKUP_ITEM15_COMBO2", ID_PICKUP_ITEM15_COMBO2 },
{"PICKUP_ITEM16_COMBO1", ID_PICKUP_ITEM16_COMBO1 },
{"PICKUP_ITEM16_COMBO2", ID_PICKUP_ITEM16_COMBO2 },
{"EXAMINE_ITEM1", ID_EXAMINE1 },
{"EXAMINE_ITEM2", ID_EXAMINE2 },
{"EXAMINE_ITEM3", ID_EXAMINE3 },
{"EXAMINE_ITEM4", ID_EXAMINE4 },
{"EXAMINE_ITEM5", ID_EXAMINE5 },
{"EXAMINE_ITEM6", ID_EXAMINE6 },
{"EXAMINE_ITEM7", ID_EXAMINE7 },
{"EXAMINE_ITEM8", ID_EXAMINE8 },
{"EXAMINE_ITEM1_COMBO1", ID_EXAMINE1_COMBO1 },
{"EXAMINE_ITEM1_COMBO2", ID_EXAMINE1_COMBO2 },
{"EXAMINE_ITEM2_COMBO1", ID_EXAMINE2_COMBO1 },
{"EXAMINE_ITEM2_COMBO2", ID_EXAMINE2_COMBO2 },
{"EXAMINE_ITEM3_COMBO1", ID_EXAMINE3_COMBO1 },
{"EXAMINE_ITEM3_COMBO2", ID_EXAMINE3_COMBO2 },
{"EXAMINE_ITEM4_COMBO1", ID_EXAMINE4_COMBO1 },
{"EXAMINE_ITEM4_COMBO2", ID_EXAMINE4_COMBO2 },
{"EXAMINE_ITEM5_COMBO1", ID_EXAMINE5_COMBO1 },
{"EXAMINE_ITEM5_COMBO2", ID_EXAMINE5_COMBO2 },
{"EXAMINE_ITEM6_COMBO1", ID_EXAMINE6_COMBO1 },
{"EXAMINE_ITEM6_COMBO2", ID_EXAMINE6_COMBO2 },
{"EXAMINE_ITEM7_COMBO1", ID_EXAMINE7_COMBO1 },
{"EXAMINE_ITEM7_COMBO2", ID_EXAMINE7_COMBO2 },
{"EXAMINE_ITEM8_COMBO1", ID_EXAMINE8_COMBO1 },
{"EXAMINE_ITEM8_COMBO2", ID_EXAMINE8_COMBO2 }
};

View file

@ -108,7 +108,7 @@ int LoadItems()
{
ITEM_INFO* item = &g_Level.Items[i];
item->objectNumber = ReadInt16();
item->objectNumber = from_underlying(ReadInt16());
item->roomNumber = ReadInt16();
item->pos.xPos = ReadInt32();
item->pos.yPos = ReadInt32();

View file

@ -62,7 +62,7 @@ struct ANIMATED_TEXTURES_SEQUENCE
struct AI_OBJECT
{
short objectNumber;
GAME_OBJECT_ID objectNumber;
short roomNumber;
int x;
int y;