From 26299354b69e0fedcd09ba9e573d77acfe10270b Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 31 Aug 2021 01:42:08 -0400 Subject: [PATCH] [Quest API] Adds new methods to NPCs and Corpses (#1510) - Add $npc->HasItem(item_id) to Perl. - Add $npc->CountItem(item_id) to Perl. - Add $npc->GetItemIDBySlot(loot_slot) to Perl. - Add $npc->GetFirstSlotByItemID(item_id) to Perl. - Add $corpse->HasItem(item_id) to Perl. - Add $corpse->CountItem(item_id) to Perl. - Add $corpse->GetItemIDBySlot(loot_slot) to Perl. - Add $corpse->GetFirstSlotByItemID(item_id) to Perl. - Add npc:HasItem(item_id) to Lua. - Add npc:CountItem(item_id) to Lua. - Add npc:GetItemIDBySlot(loot_slot) to Lua. - Add npc:GetFirstSlotByItemID(item_id) to Lua. - Add corpse:HasItem(item_id) to Lua. - Add corpse:CountItem(item_id) to Lua. - Add corpse:GetItemIDBySlot(loot_slot) to Lua. - Add corpse:GetFirstSlotByItemID(item_id) to Lua. These methods will allow server operators to view the loot a current has in a slot, the first slot found by item ID, count the item by ID, and see if the NPC has the item. With that functionality you could build a custom loot system and modify loot more dynamically. --- zone/corpse.cpp | 69 ++++++++++++++++++++++++++++++++++ zone/corpse.h | 4 ++ zone/lua_corpse.cpp | 26 ++++++++++++- zone/lua_corpse.h | 4 ++ zone/lua_npc.cpp | 30 ++++++++++++++- zone/lua_npc.h | 4 ++ zone/npc.cpp | 69 ++++++++++++++++++++++++++++++++++ zone/npc.h | 4 ++ zone/perl_npc.cpp | 75 +++++++++++++++++++++++++++++++++++++ zone/perl_player_corpse.cpp | 75 +++++++++++++++++++++++++++++++++++++ 10 files changed, 358 insertions(+), 2 deletions(-) diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 5113b6fdf..ae123e2da 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1454,6 +1454,75 @@ void Corpse::QueryLoot(Client* to) { } } +bool Corpse::HasItem(uint32 item_id) { + if (!database.GetItem(item_id)) { + return false; + } + + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (!loot_item) { + LogError("NPC::CountItem() - ItemList error, null item"); + continue; + } + + if (!loot_item->item_id || !database.GetItem(loot_item->item_id)) { + LogError("NPC::CountItem() - Database error, invalid item"); + continue; + } + + if (loot_item->item_id == item_id) { + return true; + } + } + return false; +} + +uint16 Corpse::CountItem(uint32 item_id) { + uint16 item_count = 0; + if (!database.GetItem(item_id)) { + return item_count; + } + + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (!loot_item) { + LogError("NPC::CountItem() - ItemList error, null item"); + continue; + } + + if (!loot_item->item_id || !database.GetItem(loot_item->item_id)) { + LogError("NPC::CountItem() - Database error, invalid item"); + continue; + } + + if (loot_item->item_id == item_id) { + item_count += loot_item->charges; + } + } + return item_count; +} + +uint32 Corpse::GetItemIDBySlot(uint16 loot_slot) { + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (loot_item->lootslot == loot_slot) { + return loot_item->item_id; + } + } + return 0; +} + +uint16 Corpse::GetFirstSlotByItemID(uint32 item_id) { + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (loot_item->item_id == item_id) { + return loot_item->lootslot; + } + } + return 0; +} + bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) { uint32 dist2 = 10000; // pow(100, 2); if (!spell) { diff --git a/zone/corpse.h b/zone/corpse.h index cd564e80d..80d3b4e8f 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -113,6 +113,10 @@ class Corpse : public Mob { /* Corpse: Loot */ void QueryLoot(Client* to); + bool HasItem(uint32 item_id); + uint16 CountItem(uint32 item_id); + uint32 GetItemIDBySlot(uint16 loot_slot); + uint16 GetFirstSlotByItemID(uint32 item_id); void LootItem(Client* client, const EQApplicationPacket* app); void EndLoot(Client* client, const EQApplicationPacket* app); void MakeLootRequestPackets(Client* client, const EQApplicationPacket* app); diff --git a/zone/lua_corpse.cpp b/zone/lua_corpse.cpp index 789555a54..85e4150f7 100644 --- a/zone/lua_corpse.cpp +++ b/zone/lua_corpse.cpp @@ -152,6 +152,26 @@ void Lua_Corpse::AddLooter(Lua_Mob who) { self->AddLooter(who); } +bool Lua_Corpse::HasItem(uint32 item_id) { + Lua_Safe_Call_Bool(); + return self->HasItem(item_id); +} + +uint16 Lua_Corpse::CountItem(uint32 item_id) { + Lua_Safe_Call_Int(); + return self->CountItem(item_id); +} + +uint32 Lua_Corpse::GetItemIDBySlot(uint16 loot_slot) { + Lua_Safe_Call_Int(); + return self->GetItemIDBySlot(loot_slot); +} + +uint16 Lua_Corpse::GetFirstSlotByItemID(uint32 item_id) { + Lua_Safe_Call_Int(); + return self->GetFirstSlotByItemID(item_id); +} + luabind::scope lua_register_corpse() { return luabind::class_("Corpse") .def(luabind::constructor<>()) @@ -185,7 +205,11 @@ luabind::scope lua_register_corpse() { .def("GetSilver", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetSilver) .def("GetGold", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetGold) .def("GetPlatinum", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetPlatinum) - .def("AddLooter", (void(Lua_Corpse::*)(Lua_Mob))&Lua_Corpse::AddLooter); + .def("AddLooter", (void(Lua_Corpse::*)(Lua_Mob))&Lua_Corpse::AddLooter) + .def("HasItem", (bool(Lua_Corpse::*)(uint32))&Lua_Corpse::HasItem) + .def("CountItem", (uint16(Lua_Corpse::*)(uint32))&Lua_Corpse::CountItem) + .def("GetItemIDBySlot", (uint32(Lua_Corpse::*)(uint16))&Lua_Corpse::GetItemIDBySlot) + .def("GetFirstSlotByItemID", (uint16(Lua_Corpse::*)(uint32))&Lua_Corpse::GetFirstSlotByItemID); } #endif diff --git a/zone/lua_corpse.h b/zone/lua_corpse.h index dfb3b4b6d..679fe4948 100644 --- a/zone/lua_corpse.h +++ b/zone/lua_corpse.h @@ -54,6 +54,10 @@ public: uint32 GetGold(); uint32 GetPlatinum(); void AddLooter(Lua_Mob who); + bool HasItem(uint32 item_id); + uint16 CountItem(uint32 item_id); + uint32 GetItemIDBySlot(uint16 loot_slot); + uint16 GetFirstSlotByItemID(uint32 item_id); }; #endif diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index d7dbe06e8..af975b913 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -582,6 +582,30 @@ void Lua_NPC::ClearLastName() self->ClearLastName(); } +bool Lua_NPC::HasItem(uint32 item_id) +{ + Lua_Safe_Call_Bool(); + return self->HasItem(item_id); +} + +uint16 Lua_NPC::CountItem(uint32 item_id) +{ + Lua_Safe_Call_Int(); + return self->CountItem(item_id); +} + +uint32 Lua_NPC::GetItemIDBySlot(uint16 loot_slot) +{ + Lua_Safe_Call_Int(); + return self->GetItemIDBySlot(loot_slot); +} + +uint16 Lua_NPC::GetFirstSlotByItemID(uint32 item_id) +{ + Lua_Safe_Call_Int(); + return self->GetFirstSlotByItemID(item_id); +} + luabind::scope lua_register_npc() { return luabind::class_("NPC") .def(luabind::constructor<>()) @@ -698,7 +722,11 @@ luabind::scope lua_register_npc() { .def("ScaleNPC", (void(Lua_NPC::*)(uint8))&Lua_NPC::ScaleNPC) .def("IsRaidTarget", (bool(Lua_NPC::*)(void))&Lua_NPC::IsRaidTarget) .def("ChangeLastName", (void(Lua_NPC::*)(const char*))&Lua_NPC::ChangeLastName) - .def("ClearLastName", (void(Lua_NPC::*)(void))&Lua_NPC::ClearLastName); + .def("ClearLastName", (void(Lua_NPC::*)(void))&Lua_NPC::ClearLastName) + .def("HasItem", (bool(Lua_NPC::*)(uint32))&Lua_NPC::HasItem) + .def("CountItem", (uint16(Lua_NPC::*)(uint32))&Lua_NPC::CountItem) + .def("GetItemIDBySlot", (uint32(Lua_NPC::*)(uint16))&Lua_NPC::GetItemIDBySlot) + .def("GetFirstSlotByItemID", (uint16(Lua_NPC::*)(uint32))&Lua_NPC::GetFirstSlotByItemID); } #endif diff --git a/zone/lua_npc.h b/zone/lua_npc.h index 2860366ef..e4773636f 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -140,6 +140,10 @@ public: bool IsRaidTarget(); void ChangeLastName(const char *lastname); void ClearLastName(); + bool HasItem(uint32 item_id); + uint16 CountItem(uint32 item_id); + uint32 GetItemIDBySlot(uint16 slot_id); + uint16 GetFirstSlotByItemID(uint32 item_id); }; #endif diff --git a/zone/npc.cpp b/zone/npc.cpp index 3d65eed93..d7e4aa796 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -674,6 +674,75 @@ void NPC::QueryLoot(Client* to) to->Message(Chat::White, "| %i Platinum %i Gold %i Silver %i Copper", platinum, gold, silver, copper); } +bool NPC::HasItem(uint32 item_id) { + if (!database.GetItem(item_id)) { + return false; + } + + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (!loot_item) { + LogError("NPC::CountItem() - ItemList error, null item"); + continue; + } + + if (!loot_item->item_id || !database.GetItem(loot_item->item_id)) { + LogError("NPC::CountItem() - Database error, invalid item"); + continue; + } + + if (loot_item->item_id == item_id) { + return true; + } + } + return false; +} + +uint16 NPC::CountItem(uint32 item_id) { + uint16 item_count = 0; + if (!database.GetItem(item_id)) { + return item_count; + } + + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (!loot_item) { + LogError("NPC::CountItem() - ItemList error, null item"); + continue; + } + + if (!loot_item->item_id || !database.GetItem(loot_item->item_id)) { + LogError("NPC::CountItem() - Database error, invalid item"); + continue; + } + + if (loot_item->item_id == item_id) { + item_count += loot_item->charges; + } + } + return item_count; +} + +uint32 NPC::GetItemIDBySlot(uint16 loot_slot) { + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (loot_item->lootslot == loot_slot) { + return loot_item->item_id; + } + } + return 0; +} + +uint16 NPC::GetFirstSlotByItemID(uint32 item_id) { + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (loot_item->item_id == item_id) { + return loot_item->lootslot; + } + } + return 0; +} + void NPC::AddCash(uint16 in_copper, uint16 in_silver, uint16 in_gold, uint16 in_platinum) { if(in_copper >= 0) copper = in_copper; diff --git a/zone/npc.h b/zone/npc.h index f28394b9b..1521eb4de 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -204,6 +204,10 @@ public: void AddCash(); void RemoveCash(); void QueryLoot(Client* to); + bool HasItem(uint32 item_id); + uint16 CountItem(uint32 item_id); + uint32 GetItemIDBySlot(uint16 loot_slot); + uint16 GetFirstSlotByItemID(uint32 item_id); uint32 CountLoot(); inline uint32 GetLoottableID() const { return loottable_id; } virtual void UpdateEquipmentLight(); diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 99cd83b86..2a5061bd0 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -1741,6 +1741,77 @@ XS(XS_NPC_IsRaidTarget) { XSRETURN(1); } +XS(XS_NPC_HasItem); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_HasItem) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::HasItem(THIS, uint32 item_id)"); // @categories Script Utility + { + NPC *THIS; + bool has_item = false; + uint32 item_id = (uint32) SvUV(ST(1)); + VALIDATE_THIS_IS_NPC; + has_item = THIS->HasItem(item_id); + ST(0) = boolSV(has_item); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_NPC_CountItem); +XS(XS_NPC_CountItem) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::CountItem(THIS, uint32 item_id)"); // @categories Script Utility + { + NPC *THIS; + uint16 item_count = 0; + uint32 item_id = (uint32) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_NPC; + item_count = THIS->CountItem(item_id); + XSprePUSH; + PUSHu((UV) item_count); + } + XSRETURN(1); +} + +XS(XS_NPC_GetItemIDBySlot); +XS(XS_NPC_GetItemIDBySlot) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::GetItemIDBySlot(THIS, uint16 loot_slot)"); // @categories Script Utility + { + NPC *THIS; + uint32 item_id = 0; + uint16 loot_slot = (uint16) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_NPC; + item_id = THIS->GetItemIDBySlot(loot_slot); + XSprePUSH; + PUSHu((UV) item_id); + } + XSRETURN(1); +} + +XS(XS_NPC_GetFirstSlotByItemID); +XS(XS_NPC_GetFirstSlotByItemID) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::GetFirstSlotByItemID(THIS, uint32 item_id)"); // @categories Script Utility + { + NPC *THIS; + uint16 loot_slot = 0; + uint32 item_id = (uint32) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_NPC; + loot_slot = THIS->GetFirstSlotByItemID(item_id); + XSprePUSH; + PUSHu((UV) loot_slot); + } + XSRETURN(1); +} + #ifdef __cplusplus extern "C" #endif @@ -1859,6 +1930,10 @@ XS(boot_NPC) { newXSproto(strcpy(buf, "RecalculateSkills"), XS_NPC_RecalculateSkills, file, "$"); newXSproto(strcpy(buf, "ScaleNPC"), XS_NPC_ScaleNPC, file, "$$"); newXSproto(strcpy(buf, "IsRaidTarget"), XS_NPC_IsRaidTarget, file, "$"); + newXSproto(strcpy(buf, "HasItem"), XS_NPC_HasItem, file, "$$"); + newXSproto(strcpy(buf, "CountItem"), XS_NPC_CountItem, file, "$$"); + newXSproto(strcpy(buf, "GetItemIDBySlot"), XS_NPC_GetItemIDBySlot, file, "$$"); + newXSproto(strcpy(buf, "GetFirstSlotByItemID"), XS_NPC_GetFirstSlotByItemID, file, "$$"); XSRETURN_YES; } diff --git a/zone/perl_player_corpse.cpp b/zone/perl_player_corpse.cpp index 8979d2102..dc6aaeb8b 100644 --- a/zone/perl_player_corpse.cpp +++ b/zone/perl_player_corpse.cpp @@ -528,6 +528,77 @@ XS(XS_Corpse_IsRezzed) { XSRETURN(1); } +XS(XS_Corpse_HasItem); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_HasItem) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Corpse::HasItem(THIS, uint32 item_id)"); // @categories Script Utility + { + Corpse *THIS; + bool has_item = false; + uint32 item_id = (uint32) SvUV(ST(1)); + VALIDATE_THIS_IS_CORPSE; + has_item = THIS->HasItem(item_id); + ST(0) = boolSV(has_item); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Corpse_CountItem); +XS(XS_Corpse_CountItem) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Corpse::CountItem(THIS, uint32 item_id)"); // @categories Script Utility + { + Corpse *THIS; + uint16 item_count = 0; + uint32 item_id = (uint32) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_CORPSE; + item_count = THIS->CountItem(item_id); + XSprePUSH; + PUSHu((UV) item_count); + } + XSRETURN(1); +} + +XS(XS_Corpse_GetItemIDBySlot); +XS(XS_Corpse_GetItemIDBySlot) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Corpse::GetItemIDBySlot(THIS, uint16 loot_slot)"); // @categories Script Utility + { + Corpse *THIS; + uint32 item_id = 0; + uint16 loot_slot = (uint16) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_CORPSE; + item_id = THIS->GetItemIDBySlot(loot_slot); + XSprePUSH; + PUSHu((UV) item_id); + } + XSRETURN(1); +} + +XS(XS_Corpse_GetFirstSlotByItemID); +XS(XS_Corpse_GetFirstSlotByItemID) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Corpse::GetFirstSlotByItemID(THIS, uint32 item_id)"); // @categories Script Utility + { + Corpse *THIS; + uint16 loot_slot = 0; + uint32 item_id = (uint32) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_CORPSE; + loot_slot = THIS->GetFirstSlotByItemID(item_id); + XSprePUSH; + PUSHu((UV) loot_slot); + } + XSRETURN(1); +} + #ifdef __cplusplus extern "C" #endif @@ -574,6 +645,10 @@ XS(boot_Corpse) { newXSproto(strcpy(buf, "AllowMobLoot"), XS_Corpse_AllowMobLoot, file, "$$$"); newXSproto(strcpy(buf, "AddLooter"), XS_Corpse_AddLooter, file, "$$"); newXSproto(strcpy(buf, "IsRezzed"), XS_Corpse_IsRezzed, file, "$"); + newXSproto(strcpy(buf, "HasItem"), XS_Corpse_HasItem, file, "$$"); + newXSproto(strcpy(buf, "CountItem"), XS_Corpse_CountItem, file, "$$"); + newXSproto(strcpy(buf, "GetItemIDBySlot"), XS_Corpse_GetItemIDBySlot, file, "$$"); + newXSproto(strcpy(buf, "GetFirstSlotByItemID"), XS_Corpse_GetFirstSlotByItemID, file, "$$"); XSRETURN_YES; }