2021-06-28 18:23:26 +01:00
|
|
|
#include "framework.h"
|
|
|
|
#include "GameScriptItemInfo.h"
|
|
|
|
#include "items.h"
|
|
|
|
#include "objectslist.h"
|
|
|
|
#include "level.h"
|
2021-07-01 19:27:57 +01:00
|
|
|
#include "setup.h"
|
|
|
|
#include "lot.h"
|
2021-06-30 14:08:12 +01:00
|
|
|
#include "GameScriptPosition.h"
|
|
|
|
#include "GameScriptRotation.h"
|
2021-07-03 23:14:09 +01:00
|
|
|
#include "trmath.h"
|
2021-06-28 18:23:26 +01:00
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
extern bool const WarningsAsErrors;
|
|
|
|
|
2021-07-04 14:37:50 +01:00
|
|
|
constexpr auto LUA_CLASS_NAME{ "ItemInfo" };
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
GameScriptItemInfo::GameScriptItemInfo(short num) : m_item{ &g_Level.Items[num]}, m_num { num }
|
|
|
|
{};
|
|
|
|
|
|
|
|
GameScriptItemInfo::GameScriptItemInfo(GameScriptItemInfo&& other) noexcept : m_item { std::exchange(other.m_item, nullptr) }, m_num{ std::exchange(other.m_num, -1) } {};
|
|
|
|
|
|
|
|
// todo.. how to check if item is killed outside of script?
|
|
|
|
GameScriptItemInfo::~GameScriptItemInfo() {
|
|
|
|
// todo.. see if there's a better default state than -1
|
|
|
|
if (m_num > -1)
|
|
|
|
{
|
|
|
|
KillItem(m_num);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-04 14:37:50 +01:00
|
|
|
static void index_error(GameScriptItemInfo & item, sol::object key)
|
|
|
|
{
|
|
|
|
std::string err = "Attempted to read non-existant var \"" + key.as<std::string>() + "\" from " + LUA_CLASS_NAME;
|
|
|
|
if (WarningsAsErrors)
|
|
|
|
{
|
|
|
|
throw std::runtime_error(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GameScriptItemInfo::Register(sol::state* state)
|
2021-06-28 18:23:26 +01:00
|
|
|
{
|
2021-07-04 14:37:50 +01:00
|
|
|
state->new_usertype<GameScriptItemInfo>(LUA_CLASS_NAME,
|
2021-06-30 14:08:12 +01:00
|
|
|
"new", sol::overload(&GameScriptItemInfo::Create, &GameScriptItemInfo::CreateEmpty),
|
2021-07-04 14:37:50 +01:00
|
|
|
sol::meta_function::index, &index_error,
|
2021-06-30 14:08:12 +01:00
|
|
|
"Init", &GameScriptItemInfo::Init,
|
2021-07-03 23:14:58 +01:00
|
|
|
"currentAnim", sol::property(&GameScriptItemInfo::GetCurrentAnim, &GameScriptItemInfo::SetCurrentAnim),
|
2021-07-03 23:14:09 +01:00
|
|
|
"requiredAnim", sol::property(&GameScriptItemInfo::GetRequiredAnim, &GameScriptItemInfo::SetRequiredAnim),
|
|
|
|
"goalAnim", sol::property(&GameScriptItemInfo::GetGoalAnim, &GameScriptItemInfo::SetGoalAnim),
|
|
|
|
"HP", sol::property(&GameScriptItemInfo::GetHP, &GameScriptItemInfo::SetHP),
|
|
|
|
"OCB", sol::property(&GameScriptItemInfo::GetOCB, &GameScriptItemInfo::SetOCB),
|
|
|
|
"itemFlags", sol::property(&GameScriptItemInfo::GetItemFlags, &GameScriptItemInfo::SetItemFlags),
|
|
|
|
"AIBits", sol::property(&GameScriptItemInfo::GetAIBits, &GameScriptItemInfo::SetAIBits),
|
|
|
|
"status", sol::property(&GameScriptItemInfo::GetStatus, &GameScriptItemInfo::SetStatus),
|
|
|
|
"hitStatus", sol::property(&GameScriptItemInfo::GetHitStatus, &GameScriptItemInfo::SetHitStatus),
|
|
|
|
"active", sol::property(&GameScriptItemInfo::GetActive, &GameScriptItemInfo::SetActive),
|
|
|
|
"room", sol::property(&GameScriptItemInfo::GetRoom, &GameScriptItemInfo::SetRoom),
|
|
|
|
"pos", sol::property(&GameScriptItemInfo::GetPos, &GameScriptItemInfo::SetPos),
|
|
|
|
"rot", sol::property(&GameScriptItemInfo::GetRot, &GameScriptItemInfo::SetRot),
|
2021-07-01 19:27:57 +01:00
|
|
|
"Enable", &GameScriptItemInfo::EnableItem,
|
|
|
|
"Disable", &GameScriptItemInfo::DisableItem);
|
|
|
|
}
|
2021-06-30 14:08:12 +01:00
|
|
|
|
|
|
|
std::unique_ptr<GameScriptItemInfo> GameScriptItemInfo::CreateEmpty()
|
|
|
|
{
|
|
|
|
short num = CreateItem();
|
|
|
|
ITEM_INFO * item = &g_Level.Items[num];
|
|
|
|
item->objectNumber = ID_SMALLMEDI_ITEM;
|
|
|
|
return std::make_unique<GameScriptItemInfo>(num);
|
2021-06-28 18:23:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<GameScriptItemInfo> GameScriptItemInfo::Create(
|
2021-06-30 14:08:12 +01:00
|
|
|
GameScriptPosition pos,
|
|
|
|
GameScriptRotation rot,
|
2021-07-01 19:27:57 +01:00
|
|
|
short room,
|
2021-06-28 18:23:26 +01:00
|
|
|
short currentAnim,
|
2021-06-30 14:08:12 +01:00
|
|
|
short requiredAnim,
|
2021-07-01 19:27:57 +01:00
|
|
|
short goalAnim,
|
2021-06-30 14:08:12 +01:00
|
|
|
short hp,
|
2021-06-28 18:23:26 +01:00
|
|
|
short ocb,
|
2021-06-30 14:08:12 +01:00
|
|
|
sol::as_table_t<std::array<short, 8>> itemFlags,
|
2021-06-28 18:23:26 +01:00
|
|
|
byte aiBits,
|
|
|
|
short status,
|
|
|
|
bool active,
|
2021-06-30 14:08:12 +01:00
|
|
|
bool hitStatus
|
|
|
|
)
|
2021-06-28 18:23:26 +01:00
|
|
|
{
|
2021-07-01 19:27:57 +01:00
|
|
|
short num = CreateItem();
|
|
|
|
auto ptr = std::make_unique<GameScriptItemInfo>(num);
|
2021-06-28 18:23:26 +01:00
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
ITEM_INFO * item = &g_Level.Items[num];
|
|
|
|
ptr->SetPos(pos);
|
|
|
|
ptr->SetRot(rot);
|
|
|
|
ptr->SetRoom(room);
|
2021-06-28 18:23:26 +01:00
|
|
|
//make it a big medipack by default for now
|
|
|
|
item->objectNumber = ID_BIGMEDI_ITEM;
|
|
|
|
InitialiseItem(num);
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
ptr->SetCurrentAnim(currentAnim);
|
|
|
|
ptr->SetRequiredAnim(requiredAnim);
|
|
|
|
ptr->SetGoalAnim(requiredAnim);
|
|
|
|
ptr->SetHP(hp);
|
|
|
|
ptr->SetOCB(ocb);
|
|
|
|
ptr->SetItemFlags(itemFlags);
|
|
|
|
ptr->SetAIBits(aiBits);
|
|
|
|
ptr->SetStatus(status);
|
|
|
|
ptr->SetActive(active);
|
|
|
|
ptr->SetHitStatus(hitStatus);
|
|
|
|
|
|
|
|
return ptr;
|
2021-06-28 18:23:26 +01:00
|
|
|
}
|
|
|
|
|
2021-06-30 14:08:12 +01:00
|
|
|
void GameScriptItemInfo::Init()
|
|
|
|
{
|
|
|
|
InitialiseItem(m_num);
|
|
|
|
}
|
|
|
|
|
|
|
|
GameScriptPosition GameScriptItemInfo::GetPos() const
|
|
|
|
{
|
2021-07-03 23:14:09 +01:00
|
|
|
return GameScriptPosition( m_item->pos );
|
2021-07-01 19:27:57 +01:00
|
|
|
}
|
2021-07-03 23:14:09 +01:00
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
void GameScriptItemInfo::SetPos(GameScriptPosition const& pos)
|
|
|
|
{
|
2021-07-03 23:14:09 +01:00
|
|
|
pos.StoreInPHDPos(m_item->pos);
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-03 23:14:09 +01:00
|
|
|
// This does not guarantee that the returned value will be identical
|
|
|
|
// to a value written in via SetRot - only that the angle measures
|
|
|
|
// will be mathematically equal
|
|
|
|
// (e.g. 90 degrees = -270 degrees = 450 degrees)
|
2021-06-30 14:08:12 +01:00
|
|
|
GameScriptRotation GameScriptItemInfo::GetRot() const
|
|
|
|
{
|
2021-07-03 23:14:09 +01:00
|
|
|
return GameScriptRotation( int(TO_DEGREES(m_item->pos.xRot)) % 360,
|
|
|
|
int(TO_DEGREES(m_item->pos.yRot)) % 360,
|
|
|
|
int(TO_DEGREES(m_item->pos.zRot)) % 360);
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
void GameScriptItemInfo::SetRot(GameScriptRotation const& rot)
|
2021-06-30 14:08:12 +01:00
|
|
|
{
|
2021-07-03 23:14:09 +01:00
|
|
|
m_item->pos.xRot = FROM_DEGREES(rot.x);
|
|
|
|
m_item->pos.yRot = FROM_DEGREES(rot.y);
|
|
|
|
m_item->pos.zRot = FROM_DEGREES(rot.z);
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
short GameScriptItemInfo::GetGoalAnim() const
|
2021-06-30 14:08:12 +01:00
|
|
|
{
|
2021-07-01 19:27:57 +01:00
|
|
|
return m_item->goalAnimState;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
void GameScriptItemInfo::SetGoalAnim(short state)
|
2021-06-30 14:08:12 +01:00
|
|
|
{
|
2021-07-01 19:27:57 +01:00
|
|
|
m_item->goalAnimState = state;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
short GameScriptItemInfo::GetHP() const
|
|
|
|
{
|
|
|
|
return(m_item->hitPoints);
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
void GameScriptItemInfo::SetHP(short hp)
|
2021-06-30 14:08:12 +01:00
|
|
|
{
|
2021-07-04 14:36:01 +01:00
|
|
|
if(Objects[m_item->objectNumber].intelligent &&
|
|
|
|
(hp < 0 || hp > Objects[m_item->objectNumber].hitPoints))
|
2021-07-01 19:27:57 +01:00
|
|
|
{
|
|
|
|
if (WarningsAsErrors)
|
|
|
|
throw std::runtime_error("invalid HP");
|
|
|
|
if (hp < 0)
|
|
|
|
{
|
|
|
|
hp = 0;
|
|
|
|
}
|
|
|
|
else if (hp > Objects[m_item->objectNumber].hitPoints)
|
|
|
|
{
|
|
|
|
hp = Objects[m_item->objectNumber].hitPoints;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_item->hitPoints = hp;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
short GameScriptItemInfo::GetOCB() const
|
2021-06-30 14:08:12 +01:00
|
|
|
{
|
2021-07-01 19:27:57 +01:00
|
|
|
return m_item->triggerFlags;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
void GameScriptItemInfo::SetOCB(short ocb)
|
2021-06-30 14:08:12 +01:00
|
|
|
{
|
2021-07-01 19:27:57 +01:00
|
|
|
m_item->triggerFlags = ocb;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
byte GameScriptItemInfo::GetAIBits() const
|
2021-06-30 14:08:12 +01:00
|
|
|
{
|
2021-07-01 19:27:57 +01:00
|
|
|
return m_item->aiBits;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
void GameScriptItemInfo::SetAIBits(byte bits)
|
2021-06-30 14:08:12 +01:00
|
|
|
{
|
2021-07-01 19:27:57 +01:00
|
|
|
m_item->aiBits = bits;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
sol::as_table_t<std::array<short, 8>> GameScriptItemInfo::GetItemFlags() const
|
|
|
|
{
|
|
|
|
std::array<short, 8> ret{};
|
|
|
|
memcpy(ret.data(), m_item->itemFlags, sizeof(m_item->itemFlags));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GameScriptItemInfo::SetItemFlags(sol::as_table_t<std::array<short, 8>> const& arr)
|
|
|
|
{
|
|
|
|
memcpy(m_item->itemFlags, arr.value().data(), sizeof(m_item->itemFlags));
|
|
|
|
}
|
|
|
|
|
|
|
|
short GameScriptItemInfo::GetCurrentAnim() const
|
2021-06-30 14:08:12 +01:00
|
|
|
{
|
2021-07-01 19:27:57 +01:00
|
|
|
return m_item->currentAnimState;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
void GameScriptItemInfo::SetCurrentAnim(short anim)
|
2021-06-30 14:08:12 +01:00
|
|
|
{
|
2021-07-01 19:27:57 +01:00
|
|
|
m_item->currentAnimState = anim;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
short GameScriptItemInfo::GetRequiredAnim() const
|
2021-06-30 14:08:12 +01:00
|
|
|
{
|
2021-07-01 19:27:57 +01:00
|
|
|
return m_item->requiredAnimState;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
void GameScriptItemInfo::SetRequiredAnim(short anim)
|
2021-06-30 14:08:12 +01:00
|
|
|
{
|
2021-07-01 19:27:57 +01:00
|
|
|
m_item->requiredAnimState = anim;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
short GameScriptItemInfo::GetStatus() const
|
2021-06-30 14:08:12 +01:00
|
|
|
{
|
2021-07-01 19:27:57 +01:00
|
|
|
return m_item->status;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
void GameScriptItemInfo::SetStatus(short status)
|
|
|
|
{
|
|
|
|
m_item->status = status;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
bool GameScriptItemInfo::GetActive() const
|
2021-06-30 14:08:12 +01:00
|
|
|
{
|
2021-07-01 19:27:57 +01:00
|
|
|
return m_item->active;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
void GameScriptItemInfo::SetActive(bool active)
|
2021-06-30 14:08:12 +01:00
|
|
|
{
|
2021-07-01 19:27:57 +01:00
|
|
|
m_item->active = active;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
bool GameScriptItemInfo::GetHitStatus() const
|
2021-06-30 14:08:12 +01:00
|
|
|
{
|
2021-07-01 19:27:57 +01:00
|
|
|
return m_item->hitStatus;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void GameScriptItemInfo::SetHitStatus(bool hitStatus)
|
|
|
|
{
|
2021-07-01 19:27:57 +01:00
|
|
|
m_item->hitStatus = hitStatus;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
short GameScriptItemInfo::GetRoom() const
|
2021-06-30 14:08:12 +01:00
|
|
|
{
|
2021-07-01 19:27:57 +01:00
|
|
|
return m_item->roomNumber;
|
2021-06-30 14:08:12 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 19:27:57 +01:00
|
|
|
void GameScriptItemInfo::SetRoom(short room)
|
|
|
|
{
|
|
|
|
if (room < 0 || room >= g_Level.Rooms.size())
|
|
|
|
{
|
|
|
|
if (WarningsAsErrors)
|
|
|
|
throw std::runtime_error("invalid room number");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_item->roomNumber = room;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GameScriptItemInfo::EnableItem()
|
|
|
|
{
|
|
|
|
if (!m_item->active)
|
|
|
|
{
|
|
|
|
if (Objects[m_item->objectNumber].intelligent)
|
|
|
|
{
|
|
|
|
if (m_item->status == ITEM_DEACTIVATED)
|
|
|
|
{
|
|
|
|
m_item->touchBits = 0;
|
|
|
|
m_item->status = ITEM_ACTIVE;
|
|
|
|
AddActiveItem(m_num);
|
|
|
|
EnableBaddieAI(m_num, 1);
|
|
|
|
}
|
|
|
|
else if (m_item->status == ITEM_INVISIBLE)
|
|
|
|
{
|
|
|
|
m_item->touchBits = 0;
|
|
|
|
if (EnableBaddieAI(m_num, 0))
|
|
|
|
m_item->status = ITEM_ACTIVE;
|
|
|
|
else
|
|
|
|
m_item->status = ITEM_INVISIBLE;
|
|
|
|
AddActiveItem(m_num);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_item->touchBits = 0;
|
|
|
|
AddActiveItem(m_num);
|
|
|
|
m_item->status = ITEM_ACTIVE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GameScriptItemInfo::DisableItem()
|
|
|
|
{
|
|
|
|
if (m_item->active)
|
|
|
|
{
|
|
|
|
if (Objects[m_item->objectNumber].intelligent)
|
|
|
|
{
|
|
|
|
if (m_item->status == ITEM_ACTIVE)
|
|
|
|
{
|
|
|
|
m_item->touchBits = 0;
|
|
|
|
m_item->status = ITEM_DEACTIVATED;
|
|
|
|
RemoveActiveItem(m_num);
|
|
|
|
DisableBaddieAI(m_num);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_item->touchBits = 0;
|
|
|
|
RemoveActiveItem(m_num);
|
|
|
|
m_item->status = ITEM_DEACTIVATED;
|
|
|
|
}
|
|
|
|
}
|
2021-07-05 16:38:22 +02:00
|
|
|
}
|