Core/Loot: Prevent looting bosses by players that have already completed that encounter

This commit is contained in:
Shauren
2022-10-06 18:33:44 +02:00
parent dc393e6ca6
commit 9ce47e6809
6 changed files with 56 additions and 18 deletions

View File

@@ -17888,7 +17888,7 @@ bool Player::isAllowedToLoot(const Creature* creature) const
Loot const* loot = creature->GetLootForPlayer(this);
if (!loot || loot->isLooted()) // nothing to loot or everything looted.
return false;
if (!loot->hasItemForAll() && !loot->hasItemFor(this)) // no loot in creature for this player
if (!loot->HasAllowedLooter(GetGUID()) || (!loot->hasItemForAll() && !loot->hasItemFor(this))) // no loot in creature for this player
return false;
if (loot->loot_type == LOOT_SKINNING)
@@ -20660,6 +20660,19 @@ void Player::SendResetInstanceFailed(ResetFailedReason reason, uint32 mapID) con
SendDirectMessage(data.Write());
}
bool Player::IsLockedToDungeonEncounter(uint32 dungeonEncounterId) const
{
DungeonEncounterEntry const* dungeonEncounter = sDungeonEncounterStore.LookupEntry(dungeonEncounterId);
if (!dungeonEncounter)
return false;
InstanceLock const* instanceLock = sInstanceLockMgr.FindActiveInstanceLock(GetGUID(), { GetMap()->GetEntry(), GetMap()->GetMapDifficulty() });
if (!instanceLock)
return false;
return (instanceLock->GetData()->CompletedEncountersMask & (1u << dungeonEncounter->Bit)) != 0;
}
/*********************************************************/
/*** Update timers ***/
/*********************************************************/
@@ -25434,7 +25447,7 @@ void Player::StoreLootItem(ObjectGuid lootWorldObjectGuid, uint8 lootSlot, Loot*
return;
}
if (!item->AllowedForPlayer(this))
if (!item->HasAllowedLooter(GetGUID()))
{
SendLootReleaseAll();
return;

View File

@@ -2091,6 +2091,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void SendResetInstanceSuccess(uint32 MapId) const;
void SendResetInstanceFailed(ResetFailedReason reason, uint32 mapID) const;
void SendResetFailedNotify(uint32 mapid) const;
bool IsLockedToDungeonEncounter(uint32 dungeonEncounterId) const;
bool UpdatePosition(float x, float y, float z, float orientation, bool teleport = false) override;
bool UpdatePosition(Position const& pos, bool teleport = false) override { return UpdatePosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), teleport); }

View File

@@ -159,6 +159,9 @@ void WorldSession::HandleLootMoneyOpcode(WorldPackets::Loot::LootMoney& /*packet
if (!member)
continue;
if (!loot->HasAllowedLooter(member->GetGUID()))
continue;
if (player->IsAtGroupRewardDistance(member))
playersNear.push_back(member);
}
@@ -404,15 +407,21 @@ void WorldSession::HandleLootMasterGiveOpcode(WorldPackets::Loot::MasterLootItem
{
Loot* loot = Trinity::Containers::MapGetValuePtr(_player->GetAELootView(), req.Object);
if (!loot || loot->GetLootMethod() != MASTER_LOOT)
return;
if (!_player->IsInRaidWith(target) || !_player->IsInMap(target))
{
_player->SendLootError(req.Object, ObjectGuid::Empty, LOOT_ERROR_MASTER_OTHER);
_player->SendLootError(req.Object, loot->GetOwnerGUID(), LOOT_ERROR_MASTER_OTHER);
TC_LOG_INFO("entities.player.cheat", "MasterLootItem: Player %s tried to give an item to ineligible player %s !", GetPlayer()->GetName().c_str(), target->GetName().c_str());
return;
}
if (!loot || loot->GetLootMethod() != MASTER_LOOT)
if (!loot->HasAllowedLooter(masterLootItem.Target))
{
_player->SendLootError(req.Object, loot->GetOwnerGUID(), LOOT_ERROR_MASTER_OTHER);
return;
}
if (req.LootListID >= loot->items.size())
{
@@ -425,16 +434,16 @@ void WorldSession::HandleLootMasterGiveOpcode(WorldPackets::Loot::MasterLootItem
ItemPosCountVec dest;
InventoryResult msg = target->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item.itemid, item.count);
if (!item.AllowedForPlayer(target, true))
if (!item.HasAllowedLooter(target->GetGUID()))
msg = EQUIP_ERR_CANT_EQUIP_EVER;
if (msg != EQUIP_ERR_OK)
{
if (msg == EQUIP_ERR_ITEM_MAX_COUNT)
_player->SendLootError(req.Object, ObjectGuid::Empty, LOOT_ERROR_MASTER_UNIQUE_ITEM);
_player->SendLootError(req.Object, loot->GetOwnerGUID(), LOOT_ERROR_MASTER_UNIQUE_ITEM);
else if (msg == EQUIP_ERR_INV_FULL)
_player->SendLootError(req.Object, ObjectGuid::Empty, LOOT_ERROR_MASTER_INV_FULL);
_player->SendLootError(req.Object, loot->GetOwnerGUID(), LOOT_ERROR_MASTER_INV_FULL);
else
_player->SendLootError(req.Object, ObjectGuid::Empty, LOOT_ERROR_MASTER_OTHER);
_player->SendLootError(req.Object, loot->GetOwnerGUID(), LOOT_ERROR_MASTER_OTHER);
return;
}

View File

@@ -68,7 +68,7 @@ LootItem& LootItem::operator=(LootItem&&) noexcept = default;
LootItem::~LootItem() = default;
// 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) const
bool LootItem::AllowedForPlayer(Player const* player, Loot const& loot) const
{
// DB conditions check
if (!sConditionMgr->IsObjectMeetToConditions(const_cast<Player*>(player), conditions))
@@ -86,10 +86,8 @@ bool LootItem::AllowedForPlayer(Player const* player, bool isGivenByMasterLooter
return false;
// Master looter can see all items even if the character can't loot them
if (!isGivenByMasterLooter && player->GetGroup() && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID())
{
if (loot.GetLootMethod() == MASTER_LOOT && follow_loot_rules && player->GetGroup() && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID())
return true;
}
// Don't allow loot for players without profession or those who already know the recipe
if (pProto->HasFlag(ITEM_FLAG_HIDE_UNUSABLE_RECIPE))
@@ -132,6 +130,11 @@ void LootItem::AddAllowedLooter(const Player* player)
allowedGUIDs.insert(player->GetGUID());
}
bool LootItem::HasAllowedLooter(ObjectGuid const& looter) const
{
return allowedGUIDs.find(looter) != allowedGUIDs.end();
}
Optional<LootSlotType> LootItem::GetUiTypeForPlayer(Player const* player, Loot const& loot) const
{
if (is_looted)
@@ -405,7 +408,7 @@ bool LootRoll::TryToStart(Map* map, Loot& loot, uint32 lootListId, uint16 enchan
for (ObjectGuid allowedLooter : m_lootItem->GetAllowedLooters())
{
Player* plr = ObjectAccessor::GetPlayer(m_map, allowedLooter);
if (!plr || !m_lootItem->AllowedForPlayer(plr)) // check if player meet the condition to be able to roll this item
if (!plr || !m_lootItem->HasAllowedLooter(plr->GetGUID())) // check if player meet the condition to be able to roll this item
{
m_rollVoteMap[allowedLooter].Vote = RollVote::NotValid;
continue;
@@ -739,6 +742,11 @@ void Loot::OnLootOpened(Map* map, ObjectGuid looter)
}
}
bool Loot::HasAllowedLooter(ObjectGuid const& looter) const
{
return _allowedLooters.find(looter) != _allowedLooters.end();
}
void Loot::generateMoneyLoot(uint32 minAmount, uint32 maxAmount)
{
if (maxAmount > 0)
@@ -858,7 +866,7 @@ bool Loot::AutoStore(Player* player, uint8 bag, uint8 slot, bool broadcast, bool
if (!lootItem || lootItem->is_looted)
continue;
if (!lootItem->AllowedForPlayer(player))
if (!lootItem->HasAllowedLooter(player->GetGUID()))
continue;
if (lootItem->is_blocked)
@@ -1012,6 +1020,10 @@ void Loot::Update()
void Loot::FillNotNormalLootFor(Player const* player)
{
if (_dungeonEncounterId)
if (player->IsLockedToDungeonEncounter(_dungeonEncounterId))
return;
ObjectGuid plguid = player->GetGUID();
_allowedLooters.insert(plguid);
@@ -1019,7 +1031,7 @@ void Loot::FillNotNormalLootFor(Player const* player)
for (LootItem& item : items)
{
if (!item.AllowedForPlayer(player))
if (!item.AllowedForPlayer(player, *this))
continue;
item.AddAllowedLooter(player);

View File

@@ -192,10 +192,11 @@ struct TC_GAME_API LootItem
LootItem& operator=(LootItem&&) noexcept;
~LootItem();
// 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) const;
// Basic checks for player/item compatibility - if false no chance to see the item in the loot - used only for loot generation
bool AllowedForPlayer(Player const* player, Loot const& loot) const;
void AddAllowedLooter(Player const* player);
GuidSet const& GetAllowedLooters() const { return allowedGUIDs; }
bool HasAllowedLooter(ObjectGuid const& looter) const;
Optional<LootSlotType> GetUiTypeForPlayer(Player const* player, Loot const& loot) const;
};
@@ -299,6 +300,8 @@ struct TC_GAME_API Loot
void AddLooter(ObjectGuid GUID) { PlayersLooting.insert(GUID); }
void RemoveLooter(ObjectGuid GUID) { PlayersLooting.erase(GUID); }
bool HasAllowedLooter(ObjectGuid const& looter) const;
void generateMoneyLoot(uint32 minAmount, uint32 maxAmount);
bool FillLoot(uint32 lootId, LootStore const& store, Player* lootOwner, bool personal, bool noEmptyError = false, uint16 lootMode = LOOT_MODE_DEFAULT, ItemContext context = ItemContext::NONE);

View File

@@ -272,7 +272,7 @@ void LootItemStorage::AddNewStoredLoot(uint64 containerId, Loot* loot, Player* p
// saved to the DB that the player never should have gotten. This check prevents that, so that only
// items that the player should get in loot are in the DB.
// IE: Horde items are not saved to the DB for Ally players.
if (!li.AllowedForPlayer(player))
if (!li.AllowedForPlayer(player, *loot))
continue;
// Don't save currency tokens