fix(Core/Loot): (#7452)

- Players that did not participate in killing dungeon boss are not eligible to get loot.
- Players that are too far away from the looted object are not eligible to get loot.
- Players that released spirit and were outside the dungeon when the loot has been released are eligible to get loot.
- Players that have pending bind are not eligible to get loot.
- Properly get loot recipient for some chests in dungeons.
- All above fixes should work in any loot mode (group loot, master loot, etc.)
- Closes #2104.
This commit is contained in:
UltraNix
2021-08-24 23:48:22 +02:00
committed by GitHub
parent a594bf5b29
commit 1b7d3708a6
19 changed files with 197 additions and 97 deletions

View File

@@ -1139,22 +1139,48 @@ void Creature::SetLootRecipient(Unit* unit, bool withGroup)
m_lootRecipient.Clear();
m_lootRecipientGroup = 0;
RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE | UNIT_DYNFLAG_TAPPED);
ResetAllowedLooters();
return;
}
//if (unit->GetTypeId() != TYPEID_PLAYER && !unit->IsVehicle())
// return;
Player* player = unit->GetCharmerOrOwnerPlayerOrPlayerItself();
if (!player) // normal creature, no player involved
return;
m_lootRecipient = player->GetGUID();
Map* map = GetMap();
if (map && map->IsDungeon() && (isWorldBoss() || IsDungeonBoss()))
{
AddAllowedLooter(m_lootRecipient);
}
if (withGroup)
{
if (Group* group = player->GetGroup())
{
m_lootRecipientGroup = group->GetGUID().GetCounter();
if (map && map->IsDungeon() && (isWorldBoss() || IsDungeonBoss()))
{
Map::PlayerList const& PlayerList = map->GetPlayers();
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
{
if (Player* groupMember = i->GetSource())
{
if (groupMember->IsGameMaster() || groupMember->IsSpectator())
{
continue;
}
if (groupMember->GetGroup() == group)
{
AddAllowedLooter(groupMember->GetGUID());
}
}
}
}
}
}
else
m_lootRecipientGroup = 0;

View File

@@ -198,6 +198,7 @@ public:
Loot loot;
[[nodiscard]] ObjectGuid GetLootRecipientGUID() const { return m_lootRecipient; }
[[nodiscard]] Player* GetLootRecipient() const;
[[nodiscard]] ObjectGuid::LowType GetLootRecipientGroupGUID() const { return m_lootRecipientGroup; }
[[nodiscard]] Group* GetLootRecipientGroup() const;
[[nodiscard]] bool hasLootRecipient() const { return m_lootRecipient || m_lootRecipientGroup; }
bool isTappedBy(Player const* player) const; // return true if the creature is tapped by the player or a member of his party.
@@ -374,7 +375,7 @@ protected:
static float _GetHealthMod(int32 Rank);
ObjectGuid m_lootRecipient;
uint32 m_lootRecipientGroup;
ObjectGuid::LowType m_lootRecipientGroup;
/// Timers
time_t m_corpseRemoveTime; // (msecs)timer for death or corpse disappearance

View File

@@ -2305,29 +2305,60 @@ Group* GameObject::GetLootRecipientGroup() const
return sGroupMgr->GetGroupByGUID(m_lootRecipientGroup);
}
void GameObject::SetLootRecipient(Unit* unit)
void GameObject::SetLootRecipient(Creature* creature)
{
// set the player whose group should receive the right
// to loot the creature after it dies
// should be set to nullptr after the loot disappears
if (!unit)
if (!creature)
{
m_lootRecipient.Clear();
m_lootRecipientGroup = 0;
ResetAllowedLooters();
return;
}
if (unit->GetTypeId() != TYPEID_PLAYER && !unit->IsVehicle())
return;
m_lootRecipient = creature->GetLootRecipientGUID();
m_lootRecipientGroup = creature->GetLootRecipientGroupGUID();
SetAllowedLooters(creature->GetAllowedLooters());
}
Player* player = unit->GetCharmerOrOwnerPlayerOrPlayerItself();
if (!player) // normal creature, no player involved
return;
void GameObject::SetLootRecipient(Map* map)
{
Group* group = nullptr;
Map::PlayerList const& PlayerList = map->GetPlayers();
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
{
if (Player* groupMember = i->GetSource())
{
if (groupMember->IsGameMaster() || groupMember->IsSpectator())
{
continue;
}
m_lootRecipient = player->GetGUID();
if (Group* group = player->GetGroup())
m_lootRecipientGroup = group->GetGUID().GetCounter();
if (!m_lootRecipient)
{
m_lootRecipient = groupMember->GetGUID();
}
Group* memberGroup = groupMember->GetGroup();
if (memberGroup && !group)
{
group = memberGroup;
m_lootRecipientGroup = group->GetGUID().GetCounter();
}
if (memberGroup == group)
{
AddAllowedLooter(groupMember->GetGUID());
}
}
}
if (!group)
{
AddAllowedLooter(m_lootRecipient);
}
}
bool GameObject::IsLootAllowedFor(Player const* player) const

View File

@@ -863,7 +863,8 @@ public:
[[nodiscard]] Player* GetLootRecipient() const;
[[nodiscard]] Group* GetLootRecipientGroup() const;
void SetLootRecipient(Unit* unit);
void SetLootRecipient(Creature* creature);
void SetLootRecipient(Map* map);
bool IsLootAllowedFor(Player const* player) const;
[[nodiscard]] bool HasLootRecipient() const { return m_lootRecipient || m_lootRecipientGroup; }
uint32 m_groupLootTimer; // (msecs)timer used for group loot
@@ -982,7 +983,7 @@ protected:
Position m_stationaryPosition;
ObjectGuid m_lootRecipient;
uint32 m_lootRecipientGroup;
ObjectGuid::LowType m_lootRecipientGroup;
uint16 m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable
uint32 m_lootGenerationTime;
private:

View File

@@ -3036,3 +3036,33 @@ float WorldObject::GetFloorZ() const
return std::max<float>(m_staticFloorZ, GetMap()->GetGameObjectFloor(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ() + std::max(GetCollisionHeight(), Z_OFFSET_FIND_HEIGHT)));
}
void WorldObject::AddAllowedLooter(ObjectGuid guid)
{
_allowedLooters.insert(guid);
}
void WorldObject::SetAllowedLooters(GuidUnorderedSet const looters)
{
_allowedLooters = looters;
}
void WorldObject::ResetAllowedLooters()
{
_allowedLooters.clear();
}
bool WorldObject::HasAllowedLooter(ObjectGuid guid) const
{
if (_allowedLooters.empty())
{
return true;
}
return _allowedLooters.find(guid) != _allowedLooters.end();
}
GuidUnorderedSet const& WorldObject::GetAllowedLooters() const
{
return _allowedLooters;
}

View File

@@ -1047,6 +1047,12 @@ public:
[[nodiscard]] virtual float GetCollisionWidth() const { return GetObjectSize(); }
[[nodiscard]] virtual float GetCollisionRadius() const { return GetObjectSize() / 2; }
void AddAllowedLooter(ObjectGuid guid);
void ResetAllowedLooters();
void SetAllowedLooters(GuidUnorderedSet const looters);
[[nodiscard]] bool HasAllowedLooter(ObjectGuid guid) const;
[[nodiscard]] GuidUnorderedSet const& GetAllowedLooters() const;
protected:
std::string m_name;
bool m_isActive;
@@ -1091,6 +1097,8 @@ private:
bool CanDetectInvisibilityOf(WorldObject const* obj) const;
//bool CanDetectStealthOf(WorldObject const* obj) const;
bool CanDetectStealthOf(WorldObject const* obj, bool checkAlert = false) const;
GuidUnorderedSet _allowedLooters;
};
namespace Acore

View File

@@ -7560,7 +7560,7 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type)
if (groupRules)
group->UpdateLooterGuid(go, true);
loot->FillLoot(lootid, LootTemplates_Gameobject, this, !groupRules, false, go->GetLootMode());
loot->FillLoot(lootid, LootTemplates_Gameobject, this, !groupRules, false, go->GetLootMode(), go);
go->SetLootGenerationTime();
// get next RR player (for next loot)
@@ -11846,18 +11846,40 @@ void Player::RewardPlayerAndGroupAtEvent(uint32 creature_id, WorldObject* pRewar
bool Player::IsAtGroupRewardDistance(WorldObject const* pRewardSource) const
{
if (!pRewardSource || !IsInMap(pRewardSource))
return false;
const WorldObject* player = GetCorpse();
WorldObject const* player = GetCorpse();
if (!player || IsAlive())
{
player = this;
}
if (!pRewardSource || !player->IsInMap(pRewardSource))
{
return false;
}
if (pRewardSource->GetMap()->IsDungeon())
{
return true;
}
return pRewardSource->GetDistance(player) <= sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE);
}
bool Player::IsAtLootRewardDistance(WorldObject const* pRewardSource) const
{
if (!IsAtGroupRewardDistance(pRewardSource))
{
return false;
}
if (HasPendingBind())
{
return false;
}
return pRewardSource->HasAllowedLooter(GetGUID());
}
bool Player::IsAtRecruitAFriendDistance(WorldObject const* pOther) const
{
if (!pOther)

View File

@@ -1996,6 +1996,7 @@ public:
void InitDisplayIds();
bool IsAtGroupRewardDistance(WorldObject const* pRewardSource) const;
bool IsAtLootRewardDistance(WorldObject const* pRewardSource) const;
bool IsAtRecruitAFriendDistance(WorldObject const* pOther) const;
void RewardPlayerAndGroupAtKill(Unit* victim, bool isBattleGround);
void RewardPlayerAndGroupAtEvent(uint32 creature_id, WorldObject* pRewardSource);

View File

@@ -16935,7 +16935,7 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp
else if (Group* lrg = creature->GetLootRecipientGroup())
for (GroupReference* itr = lrg->GetFirstMember(); itr != nullptr; itr = itr->next())
if (Player* member = itr->GetSource())
if (member->IsAtGroupRewardDistance(creature))
if (member->IsAtLootRewardDistance(creature))
{
player = member;
break;
@@ -16997,7 +16997,7 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp
loot->clear();
if (uint32 lootid = creature->GetCreatureTemplate()->lootid)
loot->FillLoot(lootid, LootTemplates_Creature, looter, false, false, creature->GetLootMode());
loot->FillLoot(lootid, LootTemplates_Creature, looter, false, false, creature->GetLootMode(), creature);
if (creature->GetLootMode())
loot->generateMoneyLoot(creature->GetCreatureTemplate()->mingold, creature->GetCreatureTemplate()->maxgold);