diff --git a/zone/corpse.cpp b/zone/corpse.cpp index e0e1d1992..9bdedc492 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -38,6 +38,7 @@ Child of the Mob class. #include "corpse.h" #include "entity.h" +#include "expedition.h" #include "groups.h" #include "mob.h" #include "raids.h" @@ -1275,6 +1276,20 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) return; } + if (zone && zone->GetInstanceID() != 0) + { + // expeditions may prevent looting based on client's lockouts + auto expedition = Expedition::FindCachedExpeditionByInstanceID(zone->GetInstanceID()); + if (expedition && !expedition->CanClientLootCorpse(client, GetNPCTypeID(), GetID())) + { + client->MessageString(Chat::Red, LOOT_NOT_ALLOWED, inst->GetItem()->Name); + client->QueuePacket(app); + SendEndLootErrorPacket(client); + ResetLooter(); + delete inst; + return; + } + } // do we want this to have a fail option too? parse->EventItem(EVENT_LOOT, client, inst, this, buf, 0); diff --git a/zone/expedition.cpp b/zone/expedition.cpp index 3ad0ed113..3ed57a099 100644 --- a/zone/expedition.cpp +++ b/zone/expedition.cpp @@ -363,13 +363,17 @@ Expedition* Expedition::FindCachedExpeditionByID(uint32_t expedition_id) return nullptr; } -Expedition* Expedition::FindExpeditionByInstanceID(uint32_t instance_id) +Expedition* Expedition::FindCachedExpeditionByInstanceID(uint32_t instance_id) { - if (instance_id) + if (instance_id && zone) { - // ask database since it may have expired - auto expedition_id = ExpeditionDatabase::GetExpeditionIDFromInstanceID(instance_id); - return Expedition::FindCachedExpeditionByID(expedition_id); + for (const auto& cached_expedition : zone->expedition_cache) + { + if (cached_expedition.second->GetInstanceID() == instance_id) + { + return cached_expedition.second.get(); + } + } } return nullptr; } @@ -1891,3 +1895,82 @@ void Expedition::SetDzZoneInLocation(float x, float y, float z, float heading, b SendWorldDzLocationUpdate(ServerOP_ExpeditionDzZoneIn, location); } } + +bool Expedition::CanClientLootCorpse(Client* client, uint32_t npc_type_id, uint32_t spawn_id) +{ + if (client && m_dynamiczone.IsCurrentZoneDzInstance()) + { + // entity id takes priority, falls back to checking by npc type if not set + std::string event_name = GetLootEventBySpawnID(spawn_id); + if (event_name.empty()) + { + event_name = GetLootEventByNPCTypeID(npc_type_id); + } + + if (!event_name.empty()) + { + auto client_lockout = client->GetExpeditionLockout(GetName(), event_name); + if (!client_lockout || client_lockout->GetExpeditionUUID() != GetUUID()) + { + // client lockout not received in this expedition, prevent looting + LogExpeditions( + "Character [{}] denied looting npc [{}] spawn [{}] for lockout event [{}]", + client->CharacterID(), npc_type_id, spawn_id, event_name + ); + return false; + } + } + } + + return true; +} + +void Expedition::SetLootEventByNPCTypeID(uint32_t npc_type_id, const std::string& event_name) +{ + if (npc_type_id && m_dynamiczone.IsCurrentZoneDzInstance()) + { + LogExpeditions("Setting loot event [{}] for npc type id [{}]", event_name, npc_type_id); + m_npc_loot_events[npc_type_id] = event_name; + } +} + +void Expedition::SetLootEventBySpawnID(uint32_t spawn_id, const std::string& event_name) +{ + if (spawn_id && m_dynamiczone.IsCurrentZoneDzInstance()) + { + LogExpeditions("Setting loot event [{}] for entity id [{}]", event_name, spawn_id); + m_spawn_loot_events[spawn_id] = event_name; + } +} + +std::string Expedition::GetLootEventByNPCTypeID(uint32_t npc_type_id) +{ + std::string event_name; + + if (npc_type_id && m_dynamiczone.IsCurrentZoneDzInstance()) + { + auto it = m_npc_loot_events.find(npc_type_id); + if (it != m_npc_loot_events.end()) + { + event_name = it->second; + } + } + + return event_name; +} + +std::string Expedition::GetLootEventBySpawnID(uint32_t spawn_id) +{ + std::string event_name; + + if (spawn_id && m_dynamiczone.IsCurrentZoneDzInstance()) + { + auto it = m_spawn_loot_events.find(spawn_id); + if (it != m_spawn_loot_events.end()) + { + event_name = it->second; + } + } + + return event_name; +} diff --git a/zone/expedition.h b/zone/expedition.h index cbd8fe896..53cc32306 100644 --- a/zone/expedition.h +++ b/zone/expedition.h @@ -76,7 +76,7 @@ public: static Expedition* FindCachedExpeditionByCharacterID(uint32_t character_id); static Expedition* FindCachedExpeditionByCharacterName(const std::string& char_name); static Expedition* FindCachedExpeditionByID(uint32_t expedition_id); - static Expedition* FindExpeditionByInstanceID(uint32_t instance_id); + static Expedition* FindCachedExpeditionByInstanceID(uint32_t instance_id); static void RemoveCharacterLockouts(std::string character_name, std::string expedition_name = {}, std::string event_name = {}); static void HandleWorldMessage(ServerPacket* pack); @@ -110,6 +110,12 @@ public: void RemoveLockout(const std::string& event_name); void SetReplayLockoutOnMemberJoin(bool add_on_join, bool update_db = false); + bool CanClientLootCorpse(Client* client, uint32_t npc_type_id, uint32_t spawn_id); + std::string GetLootEventByNPCTypeID(uint32_t npc_id); + std::string GetLootEventBySpawnID(uint32_t spawn_id); + void SetLootEventByNPCTypeID(uint32_t npc_type_id, const std::string& event_name); + void SetLootEventBySpawnID(uint32_t spawn_id, const std::string& event_name); + void SendClientExpeditionInfo(Client* client); void SendWorldPendingInvite(const ExpeditionInvite& invite, const std::string& add_name); @@ -184,6 +190,8 @@ private: ExpeditionMember m_leader; std::vector m_members; std::unordered_map m_lockouts; + std::unordered_map m_npc_loot_events; // only valid inside dz zone + std::unordered_map m_spawn_loot_events; // only valid inside dz zone }; #endif diff --git a/zone/expedition_database.cpp b/zone/expedition_database.cpp index d69e36263..9f77d537c 100644 --- a/zone/expedition_database.cpp +++ b/zone/expedition_database.cpp @@ -429,24 +429,6 @@ uint32_t ExpeditionDatabase::GetExpeditionIDFromCharacterID(uint32_t character_i return expedition_id; } -uint32_t ExpeditionDatabase::GetExpeditionIDFromInstanceID(uint32_t instance_id) -{ - LogExpeditionsDetail("Getting expedition id for instance [{}]", instance_id); - - uint32_t expedition_id = 0; - auto query = fmt::format( - "SELECT id FROM expedition_details WHERE instance_id = {};", instance_id - ); - - auto results = database.QueryDatabase(query); - if (results.Success() && results.RowCount() > 0) - { - auto row = results.begin(); - expedition_id = std::strtoul(row[0], nullptr, 10); - } - return expedition_id; -} - ExpeditionMember ExpeditionDatabase::GetExpeditionLeader(uint32_t expedition_id) { LogExpeditionsDetail("Getting expedition leader for expedition [{}]", expedition_id); diff --git a/zone/expedition_database.h b/zone/expedition_database.h index a3c283351..184fdb1d7 100644 --- a/zone/expedition_database.h +++ b/zone/expedition_database.h @@ -59,7 +59,6 @@ namespace ExpeditionDatabase void DeletePendingLockouts(uint32_t character_id); void DeleteAllMembersPendingLockouts(const std::vector& members); uint32_t GetExpeditionIDFromCharacterID(uint32_t character_id); - uint32_t GetExpeditionIDFromInstanceID(uint32_t instance_id); ExpeditionMember GetExpeditionLeader(uint32_t expedition_id); void InsertCharacterLockouts( uint32_t character_id, const std::vector& lockouts, diff --git a/zone/lua_expedition.cpp b/zone/lua_expedition.cpp index c07379410..c819b4092 100644 --- a/zone/lua_expedition.cpp +++ b/zone/lua_expedition.cpp @@ -66,6 +66,16 @@ luabind::object Lua_Expedition::GetLockouts(lua_State* L) { return lua_table; } +std::string Lua_Expedition::GetLootEventByNPCTypeID(uint32_t npc_type_id) { + Lua_Safe_Call_String(); + return self->GetLootEventByNPCTypeID(npc_type_id); +} + +std::string Lua_Expedition::GetLootEventBySpawnID(uint32_t spawn_id) { + Lua_Safe_Call_String(); + return self->GetLootEventBySpawnID(spawn_id); +} + uint32_t Lua_Expedition::GetMemberCount() { Lua_Safe_Call_Int(); return self->GetMemberCount(); @@ -140,6 +150,16 @@ void Lua_Expedition::SetLocked(bool lock_expedition) { self->SetLocked(lock_expedition, true); } +void Lua_Expedition::SetLootEventByNPCTypeID(uint32_t npc_type_id, std::string event_name) { + Lua_Safe_Call_Void(); + self->SetLootEventByNPCTypeID(npc_type_id, event_name); +} + +void Lua_Expedition::SetLootEventBySpawnID(uint32_t spawn_id, std::string event_name) { + Lua_Safe_Call_Void(); + self->SetLootEventBySpawnID(spawn_id, event_name); +} + void Lua_Expedition::SetReplayLockoutOnMemberJoin(bool enable) { Lua_Safe_Call_Void(); self->SetReplayLockoutOnMemberJoin(enable, true); @@ -171,6 +191,8 @@ luabind::scope lua_register_expedition() { .def("GetInstanceID", (int(Lua_Expedition::*)(void))&Lua_Expedition::GetInstanceID) .def("GetLeaderName", (std::string(Lua_Expedition::*)(void))&Lua_Expedition::GetLeaderName) .def("GetLockouts", &Lua_Expedition::GetLockouts) + .def("GetLootEventByNPCTypeID", (std::string(Lua_Expedition::*)(uint32_t))&Lua_Expedition::GetLootEventByNPCTypeID) + .def("GetLootEventBySpawnID", (std::string(Lua_Expedition::*)(uint32_t))&Lua_Expedition::GetLootEventBySpawnID) .def("GetMemberCount", (uint32_t(Lua_Expedition::*)(void))&Lua_Expedition::GetMemberCount) .def("GetMembers", &Lua_Expedition::GetMembers) .def("GetName", (std::string(Lua_Expedition::*)(void))&Lua_Expedition::GetName) @@ -184,6 +206,8 @@ luabind::scope lua_register_expedition() { .def("SetCompass", (void(Lua_Expedition::*)(uint32_t, float, float, float))&Lua_Expedition::SetCompass) .def("SetCompass", (void(Lua_Expedition::*)(std::string, float, float, float))&Lua_Expedition::SetCompass) .def("SetLocked", (void(Lua_Expedition::*)(bool))&Lua_Expedition::SetLocked) + .def("SetLootEventByNPCTypeID", (void(Lua_Expedition::*)(uint32_t, std::string))&Lua_Expedition::SetLootEventByNPCTypeID) + .def("SetLootEventBySpawnID", (void(Lua_Expedition::*)(uint32_t, std::string))&Lua_Expedition::SetLootEventBySpawnID) .def("SetReplayLockoutOnMemberJoin", (void(Lua_Expedition::*)(bool))&Lua_Expedition::SetReplayLockoutOnMemberJoin) .def("SetSafeReturn", (void(Lua_Expedition::*)(uint32_t, float, float, float, float))&Lua_Expedition::SetSafeReturn) .def("SetSafeReturn", (void(Lua_Expedition::*)(std::string, float, float, float, float))&Lua_Expedition::SetSafeReturn) diff --git a/zone/lua_expedition.h b/zone/lua_expedition.h index 09d09be97..09881bf0f 100644 --- a/zone/lua_expedition.h +++ b/zone/lua_expedition.h @@ -58,6 +58,8 @@ public: int GetInstanceID(); std::string GetLeaderName(); luabind::object GetLockouts(lua_State* L); + std::string GetLootEventByNPCTypeID(uint32_t npc_type_id); + std::string GetLootEventBySpawnID(uint32_t spawn_id); uint32_t GetMemberCount(); luabind::object GetMembers(lua_State* L); std::string GetName(); @@ -71,6 +73,8 @@ public: void SetCompass(uint32_t zone_id, float x, float y, float z); void SetCompass(std::string zone_name, float x, float y, float z); void SetLocked(bool lock_expedition); + void SetLootEventByNPCTypeID(uint32_t npc_type_id, std::string event_name); + void SetLootEventBySpawnID(uint32_t spawn_id, std::string event_name); void SetReplayLockoutOnMemberJoin(bool enable); void SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading); void SetSafeReturn(std::string zone_name, float x, float y, float z, float heading); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index a255d110f..0529bced0 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -4336,14 +4336,14 @@ Expedition* QuestManager::GetExpeditionByCharID(uint32 char_id) Expedition* QuestManager::GetExpeditionByInstanceID(uint32 instance_id) { - return Expedition::FindExpeditionByInstanceID(instance_id); + return Expedition::FindCachedExpeditionByInstanceID(instance_id); } Expedition* QuestManager::GetExpeditionForCurrentInstance() { if (zone && zone->GetInstanceID() != 0) { - return Expedition::FindExpeditionByInstanceID(zone->GetInstanceID()); + return Expedition::FindCachedExpeditionByInstanceID(zone->GetInstanceID()); } return nullptr; } diff --git a/zone/zone.cpp b/zone/zone.cpp index da2555785..0630dff50 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1492,7 +1492,7 @@ bool Zone::Process() { if(Instance_Timer->Check()) { // if this is a dynamic zone instance notify system associated with it - Expedition* expedition = Expedition::FindExpeditionByInstanceID(GetInstanceID()); + Expedition* expedition = Expedition::FindCachedExpeditionByInstanceID(GetInstanceID()); if (expedition) { expedition->RemoveAllMembers(false); // entity list will teleport clients out immediately