mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Core/Loot: Prevent looting bosses by players that have already completed that encounter
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user