diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 6c5e10606..48ff211d4 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -284,6 +284,8 @@ void Creature::AddToWorld() GetZoneScript()->OnCreatureCreate(this); } + loot.sourceWorldObjectGUID = GetGUID(); + sScriptMgr->OnCreatureAddWorld(this); } } diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index bde7bf453..58900172e 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -159,6 +159,8 @@ void GameObject::AddToWorld() WorldObject::AddToWorld(); + loot.sourceWorldObjectGUID = GetGUID(); + sScriptMgr->OnGameObjectAddWorld(this); } } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 92cd8817a..f8f73de79 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -13180,7 +13180,7 @@ void Player::StoreLootItem(uint8 lootSlot, Loot* loot) return; } - if (!item->AllowedForPlayer(this)) + if (!item->AllowedForPlayer(this, loot->sourceWorldObjectGUID)) { SendLootRelease(GetLootGUID()); return; diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 9a4d2a4d3..d93c476b3 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -1015,7 +1015,7 @@ void Group::GroupLoot(Loot* loot, WorldObject* pLootedObject) continue; if (member->IsAtLootRewardDistance(pLootedObject)) { - if (i->AllowedForPlayer(member)) + if (i->AllowedForPlayer(member, loot->sourceWorldObjectGUID)) { r->totalPlayersRolling++; @@ -1099,7 +1099,7 @@ void Group::GroupLoot(Loot* loot, WorldObject* pLootedObject) if (member->IsAtLootRewardDistance(pLootedObject)) { - if (i->AllowedForPlayer(member)) + if (i->AllowedForPlayer(member, loot->sourceWorldObjectGUID)) { r->totalPlayersRolling++; r->playerVote[member->GetGUID()] = NOT_EMITED_YET; @@ -1157,7 +1157,7 @@ void Group::NeedBeforeGreed(Loot* loot, WorldObject* lootedObject) if (!playerToRoll) continue; - if (i->AllowedForPlayer(playerToRoll) && playerToRoll->IsAtLootRewardDistance(lootedObject)) + if (i->AllowedForPlayer(playerToRoll, loot->sourceWorldObjectGUID) && playerToRoll->IsAtLootRewardDistance(lootedObject)) { r->totalPlayersRolling++; if (playerToRoll->GetPassOnGroupLoot()) @@ -1231,7 +1231,7 @@ void Group::NeedBeforeGreed(Loot* loot, WorldObject* lootedObject) if (!playerToRoll) continue; - if (i->AllowedForPlayer(playerToRoll) && playerToRoll->IsAtLootRewardDistance(lootedObject)) + if (i->AllowedForPlayer(playerToRoll, loot->sourceWorldObjectGUID) && playerToRoll->IsAtLootRewardDistance(lootedObject)) { r->totalPlayersRolling++; r->playerVote[playerToRoll->GetGUID()] = NOT_EMITED_YET; diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index bf6ca83d4..5b8ec44d3 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -469,7 +469,7 @@ void WorldSession::HandleLootMasterGiveOpcode(WorldPacket& recvData) ItemPosCountVec dest; InventoryResult msg = target->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item.itemid, item.count); - if (!item.AllowedForPlayer(target, true)) + if (!item.AllowedForPlayer(target, loot->sourceWorldObjectGUID)) msg = EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM; if (msg != EQUIP_ERR_OK) { diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 7e3b6d94f..25cc841f4 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -404,7 +404,7 @@ LootItem::LootItem(LootStoreItem const& li) } // Basic checks for player/item compatibility - if false no chance to see the item in the loot -bool LootItem::AllowedForPlayer(Player const* player, bool isGivenByMasterLooter /*= false*/, bool allowQuestLoot /*= true*/) const +bool LootItem::AllowedForPlayer(Player const* player, bool isGivenByMasterLooter /*= false*/, bool allowQuestLoot /*= true*/, ObjectGuid source) const { ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(itemid); if (!pProto) @@ -472,6 +472,11 @@ bool LootItem::AllowedForPlayer(Player const* player, bool isGivenByMasterLooter return false; } + if (!sScriptMgr->OnAllowedForPlayerLootCheck(player, source)) + { + return false; + } + return true; } @@ -515,7 +520,7 @@ void Loot::AddItem(LootStoreItem const& item) { if (auto member = itr->GetSource()) { - if (generatedLoot.AllowedForPlayer(member)) + if (generatedLoot.AllowedForPlayer(member, sourceWorldObjectGUID)) { canSeeItemInLootWindow = true; break; @@ -523,7 +528,7 @@ void Loot::AddItem(LootStoreItem const& item) } } } - else if (generatedLoot.AllowedForPlayer(player)) + else if (generatedLoot.AllowedForPlayer(player, sourceWorldObjectGUID)) { canSeeItemInLootWindow = true; } @@ -626,7 +631,7 @@ void Loot::FillNotNormalLootFor(Player* player) else item = &quest_items[i - itemsSize]; - if (!item->is_looted && item->freeforall && item->AllowedForPlayer(player)) + if (!item->is_looted && item->freeforall && item->AllowedForPlayer(player, sourceWorldObjectGUID)) if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item->itemid)) if (proto->IsCurrencyToken()) player->StoreLootItem(i, this); @@ -640,7 +645,7 @@ QuestItemList* Loot::FillFFALoot(Player* player) for (uint8 i = 0; i < items.size(); ++i) { LootItem& item = items[i]; - if (!item.is_looted && item.freeforall && item.AllowedForPlayer(player)) + if (!item.is_looted && item.freeforall && item.AllowedForPlayer(player, containerGUID)) { ql->push_back(QuestItem(i)); ++unlootedCount; @@ -701,7 +706,7 @@ QuestItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player) for (uint8 i = 0; i < items.size(); ++i) { LootItem& item = items[i]; - if (!item.is_looted && !item.freeforall && (item.AllowedForPlayer(player) || (item.follow_loot_rules && player->GetGroup() && ((player->GetGroup()->GetLootMethod() == MASTER_LOOT && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID()) || player->GetGroup()->GetLootMethod() != MASTER_LOOT )))) + if (!item.is_looted && !item.freeforall && (item.AllowedForPlayer(player, sourceWorldObjectGUID) || (item.follow_loot_rules && player->GetGroup() && ((player->GetGroup()->GetLootMethod() == MASTER_LOOT && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID()) || player->GetGroup()->GetLootMethod() != MASTER_LOOT )))) { item.AddAllowedLooter(player); @@ -820,7 +825,7 @@ LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem * *qit if (qitem) *qitem = qitem2; item = &quest_items[qitem2->index]; - if (item->follow_loot_rules && !item->AllowedForPlayer(player)) // pussywizard: such items (follow_loot_rules) are added to every player, but not everyone is allowed, check it here + if (item->follow_loot_rules && !item->AllowedForPlayer(player, sourceWorldObjectGUID)) // pussywizard: such items (follow_loot_rules) are added to every player, but not everyone is allowed, check it here return nullptr; is_looted = qitem2->is_looted; } @@ -989,7 +994,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) // blocked rolled items and quest items, and !ffa items for (uint8 i = 0; i < l.items.size(); ++i) { - if (!l.items[i].is_looted && !l.items[i].freeforall && (l.items[i].conditions.empty() || isMasterLooter) && l.items[i].AllowedForPlayer(lv.viewer)) + if (!l.items[i].is_looted && !l.items[i].freeforall && (l.items[i].conditions.empty() || isMasterLooter) && l.items[i].AllowedForPlayer(lv.viewer, l.sourceWorldObjectGUID)) { uint8 slot_type = 0; @@ -1047,7 +1052,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) { for (uint8 i = 0; i < l.items.size(); ++i) { - if (!l.items[i].is_looted && !l.items[i].freeforall && l.items[i].conditions.empty() && l.items[i].AllowedForPlayer(lv.viewer)) + if (!l.items[i].is_looted && !l.items[i].freeforall && l.items[i].conditions.empty() && l.items[i].AllowedForPlayer(lv.viewer, l.sourceWorldObjectGUID)) { if (l.roundRobinPlayer && lv.viewer->GetGUID() != l.roundRobinPlayer) // item shall not be displayed. @@ -1066,7 +1071,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) uint8 slot_type = lv.permission == OWNER_PERMISSION ? LOOT_SLOT_TYPE_OWNER : LOOT_SLOT_TYPE_ALLOW_LOOT; for (uint8 i = 0; i < l.items.size(); ++i) { - if (!l.items[i].is_looted && !l.items[i].freeforall && l.items[i].conditions.empty() && l.items[i].AllowedForPlayer(lv.viewer)) + if (!l.items[i].is_looted && !l.items[i].freeforall && l.items[i].conditions.empty() && l.items[i].AllowedForPlayer(lv.viewer, l.sourceWorldObjectGUID)) { b << uint8(i) << l.items[i]; b << uint8(slot_type); diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h index 10147178f..bd1633abf 100644 --- a/src/server/game/Loot/LootMgr.h +++ b/src/server/game/Loot/LootMgr.h @@ -176,8 +176,8 @@ struct LootItem LootItem() = default; // Basic checks for player/item compatibility - if false no chance to see the item in the loot - bool AllowedForPlayer(Player const* player, bool isGivenByMasterLooter = false, bool allowQuestLoot = true) const; - + bool AllowedForPlayer(Player const* player, bool isGivenByMasterLooter = false, bool allowQuestLoot = true, ObjectGuid source = ObjectGuid::Empty) const; + bool AllowedForPlayer(Player const* player, ObjectGuid source) { return AllowedForPlayer(player, false, true, source); }; void AddAllowedLooter(Player const* player); [[nodiscard]] const AllowedLooterSet& GetAllowedLooters() const { return allowedGUIDs; } }; @@ -327,6 +327,7 @@ struct Loot // GUID of container that holds this loot (item_instance.entry), set for items that can be looted ObjectGuid containerGUID; + ObjectGuid sourceWorldObjectGUID; GameObject* sourceGameObject{nullptr}; Loot(uint32 _gold = 0) : gold(_gold) { } diff --git a/src/server/game/Scripting/ScriptDefines/GlobalScript.cpp b/src/server/game/Scripting/ScriptDefines/GlobalScript.cpp index c72d7eaa6..ed9214a30 100644 --- a/src/server/game/Scripting/ScriptDefines/GlobalScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/GlobalScript.cpp @@ -161,3 +161,18 @@ void ScriptMgr::OnLoadSpellCustomAttr(SpellInfo* spell) script->OnLoadSpellCustomAttr(spell); }); } + +bool ScriptMgr::OnAllowedForPlayerLootCheck(Player const* player, ObjectGuid source) +{ + auto ret = IsValidBoolScript([&](GlobalScript* script) + { + return script->OnAllowedForPlayerLootCheck(player, source); + }); + + if (ret && *ret) + { + return false; + } + + return true; +} diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index d5e480509..ed213f619 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -1549,6 +1549,9 @@ public: // Called after loading spell dbc corrections virtual void OnLoadSpellCustomAttr(SpellInfo* /*spell*/) { } + + // Called when checking if a player can see the creature loot + virtual bool OnAllowedForPlayerLootCheck(Player const* /*player*/, ObjectGuid /*source*/) { return false; }; }; class BGScript : public ScriptObject @@ -2371,6 +2374,7 @@ public: /* GlobalScript */ bool OnIsAffectedBySpellModCheck(SpellInfo const* affectSpell, SpellInfo const* checkSpell, SpellModifier const* mod); bool OnSpellHealingBonusTakenNegativeModifiers(Unit const* target, Unit const* caster, SpellInfo const* spellInfo, float& val); void OnLoadSpellCustomAttr(SpellInfo* spell); + bool OnAllowedForPlayerLootCheck(Player const* player, ObjectGuid source); public: /* Scheduled scripts */ uint32 IncreaseScheduledScriptsCount() { return ++_scheduledScripts; }