From 37c723895ba5670a2343e92d2f1d0c91ab329844 Mon Sep 17 00:00:00 2001 From: sogladev Date: Sun, 19 Jul 2026 00:57:27 +0200 Subject: [PATCH] fix(Core/Player): Loot Chest eligibility and re-send loot rolls (#26469) --- src/server/game/Entities/Player/Player.cpp | 16 +++++ src/server/game/Groups/Group.cpp | 70 ++++++++++++++++++++-- src/server/game/Groups/Group.h | 1 + 3 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 0bd855652..bd68be918 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -7896,6 +7896,15 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) // get next RR player (for next loot) if (groupRules && !go->loot.empty()) group->UpdateLooterGuid(go); + + if (groupRules) + { + GuidUnorderedSet const& allowedLooters = go->GetAllowedLooters(); + if (!allowedLooters.empty()) + for (ObjectGuid const& allowedGuid : allowedLooters) + if (Player* allowedPlayer = ObjectAccessor::FindPlayer(allowedGuid)) + loot->FillNotNormalLootFor(allowedPlayer); + } } if (GameObjectTemplateAddon const* addon = go->GetTemplateAddon()) loot->generateMoneyLoot(addon->mingold, addon->maxgold); @@ -11761,6 +11770,13 @@ void Player::SendInitialPacketsAfterAddToMap() } else if (GetRaidDifficulty() != GetStoredRaidDifficulty()) SendRaidDifficulty(GetGroup() != nullptr); + + // Re-send any pending group loot rolls to this player when they enter a dungeon/raid. + if (Map* map = GetMap()) + if (map->IsDungeon() || map->IsRaid()) + if (Group* group = GetGroup()) + group->SendPendingRollsToPlayer(this, map); + } void Player::SendUpdateToOutOfRangeGroupMembers() diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 23470aded..30167c3db 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -880,6 +880,50 @@ void Group::SendLootStartRoll(uint32 CountDown, uint32 mapid, const Roll& r) } } +void Group::SendPendingRollsToPlayer(Player* player, Map* map) +{ + for (Roll* roll : RollId) + { + auto itr = roll->playerVote.find(player->GetGUID()); + if (itr == roll->playerVote.end()) + continue; + + if (itr->second != NOT_EMITED_YET) + continue; + + Loot* loot = roll->getLoot(); + if (!loot) + continue; + + // Get remaining time from the loot source object + uint32 remainingTime = 60000; + WorldObject* lootedObject = nullptr; + if (GameObject* go = loot->sourceGameObject) + { + remainingTime = go->m_groupLootTimer; + lootedObject = go; + } + else if (!loot->sourceWorldObjectGUID.IsEmpty()) + { + if (Creature* creature = map->GetCreature(loot->sourceWorldObjectGUID)) + { + remainingTime = creature->m_groupLootTimer; + lootedObject = creature; + } + } + + bool canNeed = true; + if (GetLootMethod() == NEED_BEFORE_GREED) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(roll->itemid); + if (proto && lootedObject) + canNeed = (player->CanRollForItemInLFG(proto, lootedObject) == EQUIP_ERR_OK); + } + + SendLootStartRollToPlayer(remainingTime, map->GetId(), player, canNeed, *roll); + } +} + void Group::SendLootStartRollToPlayer(uint32 countDown, uint32 mapId, Player* p, bool canNeed, Roll const& r) { if (!p) @@ -1036,7 +1080,12 @@ void Group::GroupLoot(Loot* loot, WorldObject* pLootedObject) Player* member = itr->GetSource(); if (!member || !member->GetSession()) continue; - if (member->IsAtLootRewardDistance(pLootedObject)) + + bool canLoot = member->IsAtLootRewardDistance(pLootedObject); + if (!canLoot && !pLootedObject->GetAllowedLooters().empty()) + canLoot = pLootedObject->HasAllowedLooter(member->GetGUID()); + + if (canLoot) { r->totalPlayersRolling++; @@ -1122,7 +1171,11 @@ void Group::GroupLoot(Loot* loot, WorldObject* pLootedObject) if (!member || !member->GetSession()) continue; - if (member->IsAtLootRewardDistance(pLootedObject)) + bool canLoot = member->IsAtLootRewardDistance(pLootedObject); + if (!canLoot && !pLootedObject->GetAllowedLooters().empty()) + canLoot = pLootedObject->HasAllowedLooter(member->GetGUID()); + + if (canLoot) { r->totalPlayersRolling++; @@ -1186,7 +1239,11 @@ void Group::NeedBeforeGreed(Loot* loot, WorldObject* lootedObject) if (!playerToRoll || !playerToRoll->GetSession()) continue; - if (playerToRoll->IsAtGroupRewardDistance(lootedObject)) + bool canLoot = playerToRoll->IsAtGroupRewardDistance(lootedObject); + if (!canLoot && !lootedObject->GetAllowedLooters().empty()) + canLoot = lootedObject->HasAllowedLooter(playerToRoll->GetGUID()); + + if (canLoot) { r->totalPlayersRolling++; @@ -1335,6 +1392,7 @@ void Group::MasterLoot(Loot* loot, WorldObject* pLootedObject) } std::vector looters; + bool hasAllowedLooters = !pLootedObject->GetAllowedLooters().empty(); for (GroupReference* itr = GetFirstMember(); itr != nullptr; itr = itr->next()) { Player* looter = itr->GetSource(); @@ -1343,7 +1401,11 @@ void Group::MasterLoot(Loot* loot, WorldObject* pLootedObject) continue; } - if (looter->IsAtLootRewardDistance(pLootedObject)) + bool canLoot = looter->IsAtLootRewardDistance(pLootedObject); + if (!canLoot && hasAllowedLooters) + canLoot = pLootedObject->HasAllowedLooter(looter->GetGUID()); + + if (canLoot) { looters.push_back(looter); } diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h index a59327485..8b020cb60 100644 --- a/src/server/game/Groups/Group.h +++ b/src/server/game/Groups/Group.h @@ -295,6 +295,7 @@ public: bool isRollLootActive() const; void SendLootStartRoll(uint32 CountDown, uint32 mapid, const Roll& r); void SendLootStartRollToPlayer(uint32 countDown, uint32 mapId, Player* p, bool canNeed, Roll const& r); + void SendPendingRollsToPlayer(Player* player, Map* map); void SendLootRoll(ObjectGuid SourceGuid, ObjectGuid TargetGuid, uint8 RollNumber, uint8 RollType, const Roll& r, bool autoPass = false); void SendLootRollWon(ObjectGuid SourceGuid, ObjectGuid TargetGuid, uint8 RollNumber, uint8 RollType, const Roll& r); void SendLootAllPassed(Roll const& roll);