aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/game/AI/CreatureAI.cpp2
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.cpp2
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp2
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp29
-rw-r--r--src/server/game/Battlegrounds/Battleground.cpp6
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp97
-rw-r--r--src/server/game/Entities/Creature/Creature.h20
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp95
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h11
-rw-r--r--src/server/game/Entities/Object/Updates/UpdateFields.cpp4
-rw-r--r--src/server/game/Entities/Object/Updates/UpdateFields.h1
-rw-r--r--src/server/game/Entities/Object/Updates/ViewerDependentValues.h21
-rw-r--r--src/server/game/Entities/Player/KillRewarder.cpp89
-rw-r--r--src/server/game/Entities/Player/KillRewarder.h12
-rw-r--r--src/server/game/Entities/Player/Player.cpp17
-rw-r--r--src/server/game/Entities/Player/Player.h1
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp145
-rw-r--r--src/server/game/Handlers/LootHandler.cpp15
-rw-r--r--src/server/game/Loot/Loot.cpp21
-rw-r--r--src/server/game/Loot/Loot.h4
-rw-r--r--src/server/game/Loot/LootMgr.cpp10
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h2
-rw-r--r--src/server/game/Spells/Spell.cpp9
-rw-r--r--src/server/game/Spells/SpellEffects.cpp10
-rw-r--r--src/server/scripts/Commands/cs_debug.cpp12
-rw-r--r--src/server/scripts/Commands/cs_npc.cpp2
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp2
-rw-r--r--src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp4
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp2
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp3
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp3
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp4
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp2
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp4
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_assembly_of_iron.cpp6
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp2
36 files changed, 321 insertions, 350 deletions
diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp
index 4d5be9811b9..d5d9cf7406e 100644
--- a/src/server/game/AI/CreatureAI.cpp
+++ b/src/server/game/AI/CreatureAI.cpp
@@ -311,7 +311,7 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/)
me->RemoveAurasOnEvade();
me->CombatStop(true);
- me->SetLootRecipient(nullptr);
+ me->SetTappedBy(nullptr);
me->ResetPlayerDamageReq();
me->SetLastDamagedTime(0);
me->SetCannotReachTarget(false);
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
index 95c23c38904..c3da81c6d30 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
@@ -250,7 +250,7 @@ void ScriptedAI::ForceCombatStop(Creature* who, bool reset /*= true*/)
if (reset)
{
who->LoadCreaturesAddon();
- who->SetLootRecipient(nullptr);
+ who->SetTappedBy(nullptr);
who->ResetPlayerDamageReq();
who->SetLastDamagedTime(0);
who->SetCannotReachTarget(false);
diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
index c219d459cb1..0db25b9498e 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
@@ -94,7 +94,7 @@ void EscortAI::EnterEvadeMode(EvadeReason /*why*/)
{
me->RemoveAllAuras();
me->CombatStop(true);
- me->SetLootRecipient(nullptr);
+ me->SetTappedBy(nullptr);
EngagementOver();
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp
index 014475d0d4e..c045eda5288 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -933,11 +933,14 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (!me)
break;
- if (Player* player = me->GetLootRecipient())
+ for (ObjectGuid tapperGuid : me->GetTapList())
{
- player->RewardPlayerAndGroupAtEvent(e.action.killedMonster.creature, player);
- TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_CALL_KILLEDMONSTER: Player %s, Killcredit: %u",
- player->GetGUID().ToString().c_str(), e.action.killedMonster.creature);
+ if (Player* tapper = ObjectAccessor::GetPlayer(*me, tapperGuid))
+ {
+ tapper->KilledMonsterCredit(e.action.killedMonster.creature, me->GetGUID());
+ TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_CALL_KILLEDMONSTER: Player %s, Killcredit: %u",
+ tapper->GetGUID().ToString().c_str(), e.action.killedMonster.creature);
+ }
}
}
else // Specific target type
@@ -2947,20 +2950,10 @@ void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e,
case SMART_TARGET_LOOT_RECIPIENTS:
{
if (me)
- {
- if (Group* lootGroup = me->GetLootRecipientGroup())
- {
- for (GroupReference* it = lootGroup->GetFirstMember(); it != nullptr; it = it->next())
- if (Player* recipient = it->GetSource())
- if (recipient->IsInMap(me))
- targets.push_back(recipient);
- }
- else
- {
- if (Player* recipient = me->GetLootRecipient())
- targets.push_back(recipient);
- }
- }
+ for (ObjectGuid tapperGuid : me->GetTapList())
+ if (Player* tapper = ObjectAccessor::GetPlayer(*me, tapperGuid))
+ targets.push_back(tapper);
+
break;
}
case SMART_TARGET_VEHICLE_PASSENGER:
diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp
index 1e84e026689..93db259b128 100644
--- a/src/server/game/Battlegrounds/Battleground.cpp
+++ b/src/server/game/Battlegrounds/Battleground.cpp
@@ -31,6 +31,7 @@
#include "Group.h"
#include "Guild.h"
#include "GuildMgr.h"
+#include "KillRewarder.h"
#include "Language.h"
#include "Log.h"
#include "Map.h"
@@ -1944,7 +1945,10 @@ void Battleground::SetBracket(PVPDifficultyEntry const* bracketEntry)
void Battleground::RewardXPAtKill(Player* killer, Player* victim)
{
if (sWorld->getBoolConfig(CONFIG_BG_XP_FOR_KILL) && killer && victim)
- killer->RewardPlayerAndGroupAtKill(victim, true);
+ {
+ Player* killers[] = { killer };
+ KillRewarder(Trinity::IteratorPair(std::begin(killers), std::end(killers)), victim, true).Reward();
+ }
}
uint32 Battleground::GetTeamScore(uint32 teamId) const
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 34302dcbce9..a30c7d1129f 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -30,7 +30,6 @@
#include "GameTime.h"
#include "GridNotifiersImpl.h"
#include "Group.h"
-#include "GroupMgr.h"
#include "ItemTemplate.h"
#include "Log.h"
#include "Loot.h"
@@ -778,6 +777,9 @@ void Creature::Update(uint32 diff)
if (m_loot)
m_loot->Update();
+ for (auto&& [playerOwner, loot] : m_personalLoot)
+ loot->Update();
+
if (m_corpseRemoveTime <= GameTime::GetGameTime())
{
RemoveCorpse(false);
@@ -1299,23 +1301,7 @@ bool Creature::CanResetTalents(Player* player) const
&& player->GetClass() == GetCreatureTemplate()->trainer_class;
}
-Player* Creature::GetLootRecipient() const
-{
- if (!m_lootRecipient)
- return nullptr;
-
- return ObjectAccessor::FindConnectedPlayer(m_lootRecipient);
-}
-
-Group* Creature::GetLootRecipientGroup() const
-{
- if (m_lootRecipientGroup.IsEmpty())
- return nullptr;
-
- return sGroupMgr->GetGroupByGUID(m_lootRecipientGroup);
-}
-
-void Creature::SetLootRecipient(Unit* unit, bool withGroup)
+void Creature::SetTappedBy(Unit const* unit, bool withGroup)
{
// set the player whose group should receive the right
// to loot the creature after it dies
@@ -1323,12 +1309,14 @@ void Creature::SetLootRecipient(Unit* unit, bool withGroup)
if (!unit)
{
- m_lootRecipient.Clear();
- m_lootRecipientGroup.Clear();
+ m_tapList.clear();
RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE | UNIT_DYNFLAG_TAPPED);
return;
}
+ if (m_tapList.size() >= CREATURE_TAPPERS_SOFT_CAP)
+ return;
+
if (unit->GetTypeId() != TYPEID_PLAYER && !unit->IsVehicle())
return;
@@ -1336,31 +1324,54 @@ void Creature::SetLootRecipient(Unit* unit, bool withGroup)
if (!player) // normal creature, no player involved
return;
- m_lootRecipient = player->GetGUID();
+ m_tapList.insert(player->GetGUID());
if (withGroup)
- {
- if (Group* group = player->GetGroup())
- m_lootRecipientGroup = group->GetGUID();
- }
- else
- m_lootRecipientGroup = ObjectGuid::Empty;
+ if (Group const* group = player->GetGroup())
+ for (auto const* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
+ if (GetMap()->IsRaid() || group->SameSubGroup(player, itr->GetSource()))
+ m_tapList.insert(itr->GetSource()->GetGUID());
- SetDynamicFlag(UNIT_DYNFLAG_TAPPED);
+ if (m_tapList.size() >= CREATURE_TAPPERS_SOFT_CAP)
+ SetDynamicFlag(UNIT_DYNFLAG_TAPPED);
}
// return true if this creature is tapped by the player or by a member of his group.
bool Creature::isTappedBy(Player const* player) const
{
- if (player->GetGUID() == m_lootRecipient)
- return true;
+ return m_tapList.find(player->GetGUID()) != m_tapList.end();
+}
- Group const* playerGroup = player->GetGroup();
- if (!playerGroup || playerGroup != GetLootRecipientGroup()) // if we dont have a group we arent the recipient
- return false; // if creature doesnt have group bound it means it was solo killed by someone else
+Loot* Creature::GetLootForPlayer(Player const* player) const
+{
+ if (m_personalLoot.empty())
+ return m_loot.get();
+
+ if (std::unique_ptr<Loot> const* loot = Trinity::Containers::MapGetValuePtr(m_personalLoot, player->GetGUID()))
+ return loot->get();
+
+ return nullptr;
+}
+
+bool Creature::IsFullyLooted() const
+{
+ if (m_loot && !m_loot->isLooted())
+ return false;
+
+ for (auto const& [_, loot] : m_personalLoot)
+ if (!loot->isLooted())
+ return false;
return true;
}
+bool Creature::IsSkinnedBy(Player const* player) const
+{
+ if (Loot* loot = GetLootForPlayer(player))
+ return loot->loot_type == LOOT_SKINNING;
+
+ return false;
+}
+
void Creature::SaveToDB()
{
// this should only be used when the creature has already been loaded
@@ -2136,7 +2147,7 @@ void Creature::setDeathState(DeathState s)
else
SetSpawnHealth();
- SetLootRecipient(nullptr);
+ SetTappedBy(nullptr);
ResetPlayerDamageReq();
SetCannotReachTarget(false);
@@ -2836,10 +2847,6 @@ void Creature::RefreshCanSwimFlag(bool recheck)
void Creature::AllLootRemovedFromCorpse()
{
- if ((!m_loot || m_loot->loot_type != LOOT_SKINNING) && !IsPet() && GetCreatureTemplate()->SkinLootId && hasLootRecipient())
- if (LootTemplates_Skinning.HaveLootFor(GetCreatureTemplate()->SkinLootId))
- SetUnitFlag(UNIT_FLAG_SKINNABLE);
-
time_t now = GameTime::GetGameTime();
// Do not reset corpse remove time if corpse is already removed
if (m_corpseRemoveTime <= now)
@@ -2849,7 +2856,19 @@ void Creature::AllLootRemovedFromCorpse()
float decayRate = m_ignoreCorpseDecayRatio ? 1.f : sWorld->getRate(RATE_CORPSE_DECAY_LOOTED);
// corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update
- if (m_loot && m_loot->loot_type == LOOT_SKINNING)
+ bool isFullySkinned = [&]() -> bool
+ {
+ if (m_loot && m_loot->loot_type == LOOT_SKINNING && m_loot->isLooted())
+ return true;
+
+ for (auto const& [_, loot] : m_personalLoot)
+ if (loot->loot_type != LOOT_SKINNING || !loot->isLooted())
+ return false;
+
+ return true;
+ }();
+
+ if (isFullySkinned)
m_corpseRemoveTime = now;
else
m_corpseRemoveTime = now + uint32(m_corpseDelay * decayRate);
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index 4b14899c0f7..beb74bb7526 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -29,7 +29,6 @@
class CreatureAI;
class CreatureGroup;
-class Group;
class Quest;
class Player;
class SpellInfo;
@@ -61,6 +60,7 @@ enum class VendorInventoryReason : uint8
};
static constexpr uint8 WILD_BATTLE_PET_DEFAULT_LEVEL = 1;
+static constexpr size_t CREATURE_TAPPERS_SOFT_CAP = 5;
//used for handling non-repeatable random texts
typedef std::vector<uint8> CreatureTextRepeatIds;
@@ -225,17 +225,19 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
static bool DeleteFromDB(ObjectGuid::LowType spawnId);
std::unique_ptr<Loot> m_loot;
+ std::unordered_map<ObjectGuid, std::unique_ptr<Loot>> m_personalLoot;
void StartPickPocketRefillTimer();
void ResetPickPocketRefillTimer() { _pickpocketLootRestore = 0; }
bool CanGeneratePickPocketLoot() const;
- ObjectGuid GetLootRecipientGUID() const { return m_lootRecipient; }
- Player* GetLootRecipient() const;
- Group* GetLootRecipientGroup() const;
- bool hasLootRecipient() const { return !m_lootRecipient.IsEmpty() || !m_lootRecipientGroup.IsEmpty(); }
+ GuidUnorderedSet const& GetTapList() const { return m_tapList; }
+ void SetTapList(GuidUnorderedSet tapList) { m_tapList = std::move(tapList); }
+ bool hasLootRecipient() const { return !m_tapList.empty(); }
bool isTappedBy(Player const* player) const; // return true if the creature is tapped by the player or a member of his party.
- Loot* GetLootForPlayer(Player const* /*player*/) const override { return m_loot.get(); }
+ Loot* GetLootForPlayer(Player const* player) const override;
+ bool IsFullyLooted() const;
+ bool IsSkinnedBy(Player const* player) const;
- void SetLootRecipient (Unit* unit, bool withGroup = true);
+ void SetTappedBy(Unit const* unit, bool withGroup = true);
void AllLootRemovedFromCorpse();
uint16 GetLootMode() const { return m_LootMode; }
@@ -334,7 +336,6 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
void SetDisableReputationGain(bool disable) { DisableReputationGain = disable; }
bool IsReputationGainDisabled() const { return DisableReputationGain; }
- bool IsDamageEnoughForLootingAndReward() const { return (m_creatureInfo->flags_extra & CREATURE_FLAG_EXTRA_NO_PLAYER_DAMAGE_REQ) || (m_PlayerDamageReq == 0); }
void LowerPlayerDamageReq(uint64 unDamage);
void ResetPlayerDamageReq() { m_PlayerDamageReq = GetHealth() / 2; }
uint64 m_PlayerDamageReq;
@@ -395,8 +396,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
static float _GetHealthMod(int32 Rank);
- ObjectGuid m_lootRecipient;
- ObjectGuid m_lootRecipientGroup;
+ GuidUnorderedSet m_tapList;
/// Timers
time_t _pickpocketLootRestore;
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index ed2dc526fba..4dffcf2652e 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -34,7 +34,6 @@
#include "GossipDef.h"
#include "GridNotifiersImpl.h"
#include "Group.h"
-#include "GroupMgr.h"
#include "Item.h"
#include "Log.h"
#include "Loot.h"
@@ -1419,45 +1418,45 @@ void GameObject::SendGameObjectDespawn()
Loot* GameObject::GetFishLoot(Player* lootOwner)
{
- uint32 zone, subzone;
uint32 defaultzone = 1;
- GetZoneAndAreaId(zone, subzone);
Loot* fishLoot = new Loot(GetMap(), GetGUID(), LOOT_FISHING, nullptr);
- // if subzone loot exist use it
- fishLoot->FillLoot(subzone, LootTemplates_Fishing, lootOwner, true, true);
- if (fishLoot->empty()) //use this becase if zone or subzone has set LOOT_MODE_JUNK_FISH,Even if no normal drop, fishloot->FillLoot return true. it wrong.
+ uint32 areaId = GetAreaId();
+ while (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId))
{
- //subzone no result,use zone loot
- fishLoot->FillLoot(zone, LootTemplates_Fishing, lootOwner, true, true);
- //use zone 1 as default, somewhere fishing got nothing,becase subzone and zone not set, like Off the coast of Storm Peaks.
- if (fishLoot->empty())
- fishLoot->FillLoot(defaultzone, LootTemplates_Fishing, lootOwner, true, true);
+ fishLoot->FillLoot(areaId, LootTemplates_Fishing, lootOwner, true, true);
+ if (!fishLoot->isLooted())
+ break;
+
+ areaId = areaEntry->ParentAreaID;
}
+ if (fishLoot->isLooted())
+ fishLoot->FillLoot(defaultzone, LootTemplates_Fishing, lootOwner, true, true);
+
return fishLoot;
}
Loot* GameObject::GetFishLootJunk(Player* lootOwner)
{
- uint32 zone, subzone;
uint32 defaultzone = 1;
- GetZoneAndAreaId(zone, subzone);
Loot* fishLoot = new Loot(GetMap(), GetGUID(), LOOT_FISHING_JUNK, nullptr);
- // if subzone loot exist use it
- fishLoot->FillLoot(subzone, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_JUNK_FISH);
- if (fishLoot->empty()) //use this becase if zone or subzone has normal mask drop, then fishloot->FillLoot return true.
+ uint32 areaId = GetAreaId();
+ while (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId))
{
- //use zone loot
- fishLoot->FillLoot(zone, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_JUNK_FISH);
- if (fishLoot->empty())
- //use zone 1 as default
- fishLoot->FillLoot(defaultzone, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_JUNK_FISH);
+ fishLoot->FillLoot(areaId, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_JUNK_FISH);
+ if (!fishLoot->isLooted())
+ break;
+
+ areaId = areaEntry->ParentAreaID;
}
+ if (fishLoot->isLooted())
+ fishLoot->FillLoot(defaultzone, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_JUNK_FISH);
+
return fishLoot;
}
@@ -3279,49 +3278,6 @@ void GameObject::UpdateModel()
GetMap()->InsertGameObjectModel(*m_model);
}
-Player* GameObject::GetLootRecipient() const
-{
- if (!m_lootRecipient)
- return nullptr;
- return ObjectAccessor::FindConnectedPlayer(m_lootRecipient);
-}
-
-Group* GameObject::GetLootRecipientGroup() const
-{
- if (!m_lootRecipientGroup)
- return nullptr;
- return sGroupMgr->GetGroupByGUID(m_lootRecipientGroup);
-}
-
-void GameObject::SetLootRecipient(Unit* unit, Group* group)
-{
- // 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)
- {
- m_lootRecipient.Clear();
- m_lootRecipientGroup = group ? group->GetGUID() : ObjectGuid::Empty;
- 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();
-
- // either get the group from the passed parameter or from unit's one
- if (group)
- m_lootRecipientGroup = group->GetGUID();
- else if (Group* unitGroup = player->GetGroup())
- m_lootRecipientGroup = unitGroup->GetGUID();
-}
-
bool GameObject::IsLootAllowedFor(Player const* player) const
{
if (Loot const* loot = GetLootForPlayer(player)) // check only if loot was already generated
@@ -3332,15 +3288,8 @@ bool GameObject::IsLootAllowedFor(Player const* player) const
return false;
}
- if (!m_lootRecipient && !m_lootRecipientGroup)
- return true;
-
- if (player->GetGUID() == m_lootRecipient)
- return true;
-
- Group const* playerGroup = player->GetGroup();
- if (!playerGroup || playerGroup != GetLootRecipientGroup()) // if we dont have a group we arent the recipient
- return false; // if go doesnt have group bound it means it was solo killed by someone else
+ if (HasLootRecipient())
+ return m_tapList.find(player->GetGUID()) != m_tapList.end();
return true;
}
diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h
index 55d86f1e5f8..7d7d0563532 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -27,7 +27,6 @@
class GameObject;
class GameObjectAI;
class GameObjectModel;
-class Group;
class OPvPCapturePoint;
class Transport;
class TransportBase;
@@ -281,11 +280,10 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
std::unique_ptr<Loot> m_loot;
std::unordered_map<ObjectGuid, std::unique_ptr<Loot>> m_personalLoot;
- Player* GetLootRecipient() const;
- Group* GetLootRecipientGroup() const;
- void SetLootRecipient(Unit* unit, Group* group = nullptr);
+ GuidUnorderedSet const& GetTapList() const { return m_tapList; }
+ void SetTapList(GuidUnorderedSet tapList) { m_tapList = std::move(tapList); }
bool IsLootAllowedFor(Player const* player) const;
- bool HasLootRecipient() const { return !m_lootRecipient.IsEmpty() || !m_lootRecipientGroup.IsEmpty(); }
+ bool HasLootRecipient() const { return !m_tapList.empty(); }
Loot* GetLootForPlayer(Player const* /*player*/) const override;
GameObject* GetLinkedTrap();
@@ -427,8 +425,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
QuaternionData m_localRotation;
Position m_stationaryPosition;
- ObjectGuid m_lootRecipient;
- ObjectGuid m_lootRecipientGroup;
+ GuidUnorderedSet m_tapList;
uint16 m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable
ObjectGuid m_linkedTrap;
diff --git a/src/server/game/Entities/Object/Updates/UpdateFields.cpp b/src/server/game/Entities/Object/Updates/UpdateFields.cpp
index b12c585a788..c9f7d991d67 100644
--- a/src/server/game/Entities/Object/Updates/UpdateFields.cpp
+++ b/src/server/game/Entities/Object/Updates/UpdateFields.cpp
@@ -1006,7 +1006,7 @@ void UnitData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisi
}
data << uint32(ViewerDependentValue<FlagsTag>::GetValue(this, owner, receiver));
data << uint32(Flags2);
- data << uint32(Flags3);
+ data << uint32(ViewerDependentValue<Flags3Tag>::GetValue(this, owner, receiver));
data << uint32(ViewerDependentValue<AuraStateTag>::GetValue(this, owner, receiver));
for (uint32 i = 0; i < 2; ++i)
{
@@ -1397,7 +1397,7 @@ void UnitData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignor
}
if (changesMask[45])
{
- data << uint32(Flags3);
+ data << uint32(ViewerDependentValue<Flags3Tag>::GetValue(this, owner, receiver));
}
if (changesMask[46])
{
diff --git a/src/server/game/Entities/Object/Updates/UpdateFields.h b/src/server/game/Entities/Object/Updates/UpdateFields.h
index b19fdebafb0..41f47ffa22f 100644
--- a/src/server/game/Entities/Object/Updates/UpdateFields.h
+++ b/src/server/game/Entities/Object/Updates/UpdateFields.h
@@ -303,6 +303,7 @@ struct UnitData : public IsUpdateFieldStructureTag, public HasChangesMask<194>
struct FlagsTag : ViewerDependentValueTag<uint32> {};
UpdateField<uint32, 32, 44> Flags2;
UpdateField<uint32, 32, 45> Flags3;
+ struct Flags3Tag : ViewerDependentValueTag<uint32> {};
UpdateField<uint32, 32, 46> AuraState;
struct AuraStateTag : ViewerDependentValueTag<uint32> {};
UpdateField<uint32, 32, 47> RangedAttackRoundBaseTime;
diff --git a/src/server/game/Entities/Object/Updates/ViewerDependentValues.h b/src/server/game/Entities/Object/Updates/ViewerDependentValues.h
index 028fdd91a26..3ddef2b2db8 100644
--- a/src/server/game/Entities/Object/Updates/ViewerDependentValues.h
+++ b/src/server/game/Entities/Object/Updates/ViewerDependentValues.h
@@ -67,8 +67,6 @@ public:
value_type dynamicFlags = objectData->DynamicFlags;
if (Unit const* unit = object->ToUnit())
{
- dynamicFlags &= ~UNIT_DYNFLAG_TAPPED;
-
if (Creature const* creature = object->ToCreature())
{
if (creature->hasLootRecipient() && !creature->isTappedBy(receiver))
@@ -76,6 +74,9 @@ public:
if (!receiver->isAllowedToLoot(creature))
dynamicFlags &= ~UNIT_DYNFLAG_LOOTABLE;
+
+ if (dynamicFlags & UNIT_DYNFLAG_CAN_SKIN && creature->IsSkinnedBy(receiver))
+ dynamicFlags &= ~UNIT_DYNFLAG_CAN_SKIN;
}
// unit UNIT_DYNFLAG_TRACK_UNIT should only be sent to caster of SPELL_AURA_MOD_STALKED auras
@@ -218,6 +219,22 @@ public:
};
template<>
+class ViewerDependentValue<UF::UnitData::Flags3Tag>
+{
+public:
+ using value_type = UF::UnitData::Flags3Tag::value_type;
+
+ static value_type GetValue(UF::UnitData const* unitData, Unit const* unit, Player const* receiver)
+ {
+ value_type flags = unitData->Flags3;
+ if (flags & UNIT_FLAG3_ALREADY_SKINNED && unit->IsCreature() && !unit->ToCreature()->IsSkinnedBy(receiver))
+ flags &= ~UNIT_FLAG3_ALREADY_SKINNED;
+
+ return flags;
+ }
+};
+
+template<>
class ViewerDependentValue<UF::UnitData::AuraStateTag>
{
public:
diff --git a/src/server/game/Entities/Player/KillRewarder.cpp b/src/server/game/Entities/Player/KillRewarder.cpp
index cc6d813b50d..e2acc7eb515 100644
--- a/src/server/game/Entities/Player/KillRewarder.cpp
+++ b/src/server/game/Entities/Player/KillRewarder.cpp
@@ -27,6 +27,8 @@
#include "Player.h"
#include "Scenario.h"
#include "SpellAuraEffects.h"
+#include <boost/container/flat_set.hpp>
+#include <boost/container/small_vector.hpp>
// == KillRewarder ====================================================
// KillRewarder encapsulates logic of rewarding player upon kill with:
@@ -71,9 +73,9 @@
// 6. Update guild achievements.
// 7. Scenario credit
-KillRewarder::KillRewarder(Player* killer, Unit* victim, bool isBattleGround) :
+KillRewarder::KillRewarder(Trinity::IteratorPair<Player**> killers, Unit* victim, bool isBattleGround) :
// 1. Initialize internal variables to default values.
- _killer(killer), _victim(victim), _group(killer->GetGroup()),
+ _killers(killers), _victim(victim),
_groupRate(1.0f), _maxNotGrayMember(nullptr), _count(0), _sumLevel(0), _xp(0),
_isFullXP(false), _maxLevel(0), _isBattleGround(isBattleGround), _isPvP(false)
{
@@ -83,18 +85,18 @@ KillRewarder::KillRewarder(Player* killer, Unit* victim, bool isBattleGround) :
// or if its owned by player and its not a vehicle
else if (victim->GetCharmerOrOwnerGUID().IsPlayer())
_isPvP = !victim->IsVehicle();
-
- _InitGroupData();
}
-inline void KillRewarder::_InitGroupData()
+inline void KillRewarder::_InitGroupData(Player const* killer)
{
- if (_group)
+ if (Group const* group = killer->GetGroup())
{
// 2. In case when player is in group, initialize variables necessary for group calculations:
- for (GroupReference* itr = _group->GetFirstMember(); itr != nullptr; itr = itr->next())
+ for (GroupReference const* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
+ {
if (Player* member = itr->GetSource())
- if (_killer == member || (member->IsAtGroupRewardDistance(_victim) && member->IsAlive()))
+ {
+ if (killer == member || (member->IsAtGroupRewardDistance(_victim) && member->IsAlive()))
{
const uint8 lvl = member->GetLevel();
// 2.1. _count - number of alive group members within reward distance;
@@ -110,6 +112,8 @@ inline void KillRewarder::_InitGroupData()
if (_victim->GetLevelForTarget(member) > grayLevel && (!_maxNotGrayMember || _maxNotGrayMember->GetLevel() < lvl))
_maxNotGrayMember = member;
}
+ }
+ }
// 2.5. _isFullXP - flag identifying that for all group members victim is not gray,
// so 100% XP will be rewarded (50% otherwise).
_isFullXP = _maxNotGrayMember && (_maxLevel == _maxNotGrayMember->GetLevel());
@@ -118,14 +122,14 @@ inline void KillRewarder::_InitGroupData()
_count = 1;
}
-inline void KillRewarder::_InitXP(Player* player)
+inline void KillRewarder::_InitXP(Player* player, Player const* killer)
{
// Get initial value of XP for kill.
// XP is given:
// * on battlegrounds;
// * otherwise, not in PvP;
// * not if killer is on vehicle.
- if (_isBattleGround || (!_isPvP && !_killer->GetVehicle()))
+ if (_isBattleGround || (!_isPvP && !killer->GetVehicle()))
_xp = Trinity::XP::Gain(player, _victim, _isBattleGround);
}
@@ -139,7 +143,7 @@ inline void KillRewarder::_RewardHonor(Player* player)
inline void KillRewarder::_RewardXP(Player* player, float rate)
{
uint32 xp(_xp);
- if (_group)
+ if (player->GetGroup())
{
// 4.2.1. If player is in group, adjust XP:
// * set to 0 if player's level is more than maximum level of not gray member;
@@ -162,7 +166,7 @@ inline void KillRewarder::_RewardXP(Player* player, float rate)
player->GiveXP(xp, _victim, _groupRate);
if (Pet* pet = player->GetPet())
// 4.2.4. If player has pet, reward pet with XP (100% for single player, 50% for group case).
- pet->GivePetXP(_group ? xp / 2 : xp);
+ pet->GivePetXP(player->GetGroup() ? xp / 2 : xp);
}
}
@@ -176,12 +180,14 @@ inline void KillRewarder::_RewardReputation(Player* player, float rate)
inline void KillRewarder::_RewardKillCredit(Player* player)
{
// 4.4. Give kill credit (player must not be in group, or he must be alive or without corpse).
- if (!_group || player->IsAlive() || !player->GetCorpse())
+ if (!player->GetGroup() || player->IsAlive() || !player->GetCorpse())
+ {
if (Creature* target = _victim->ToCreature())
{
player->KilledMonster(target->GetCreatureTemplate(), target->GetGUID());
player->UpdateCriteria(CriteriaType::KillAnyCreature, target->GetCreatureType(), 1, 0, target);
}
+ }
}
void KillRewarder::_RewardPlayer(Player* player, bool isDungeon)
@@ -199,7 +205,7 @@ void KillRewarder::_RewardPlayer(Player* player, bool isDungeon)
// Give reputation and kill credit only in PvE.
if (!_isPvP || _isBattleGround)
{
- float const rate = _group ?
+ float const rate = player->GetGroup() ?
_groupRate * float(player->GetLevel()) / _sumLevel : // Group rate depends on summary level.
1.0f; // Personal rate is 100%.
if (_xp)
@@ -214,34 +220,34 @@ void KillRewarder::_RewardPlayer(Player* player, bool isDungeon)
}
}
-void KillRewarder::_RewardGroup()
+void KillRewarder::_RewardGroup(Group const* group, Player const* killer)
{
if (_maxLevel)
{
if (_maxNotGrayMember)
// 3.1.1. Initialize initial XP amount based on maximum level of group member,
// for whom victim is not gray.
- _InitXP(_maxNotGrayMember);
+ _InitXP(_maxNotGrayMember, killer);
// To avoid unnecessary calculations and calls,
// proceed only if XP is not ZERO or player is not on battleground
// (battleground rewards only XP, that's why).
if (!_isBattleGround || _xp)
{
- bool const isDungeon = !_isPvP && sMapStore.LookupEntry(_killer->GetMapId())->IsDungeon();
+ bool const isDungeon = !_isPvP && sMapStore.LookupEntry(killer->GetMapId())->IsDungeon();
if (!_isBattleGround)
{
// 3.1.2. Alter group rate if group is in raid (not for battlegrounds).
- bool const isRaid = !_isPvP && sMapStore.LookupEntry(_killer->GetMapId())->IsRaid() && _group->isRaidGroup();
+ bool const isRaid = !_isPvP && sMapStore.LookupEntry(killer->GetMapId())->IsRaid() && group->isRaidGroup();
_groupRate = Trinity::XP::xp_in_group_rate(_count, isRaid);
}
// 3.1.3. Reward each group member (even dead or corpse) within reward distance.
- for (GroupReference* itr = _group->GetFirstMember(); itr != nullptr; itr = itr->next())
+ for (GroupReference const* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
{
if (Player* member = itr->GetSource())
{
// Killer may not be at reward distance, check directly
- if (_killer == member || member->IsAtGroupRewardDistance(_victim))
+ if (killer == member || member->IsAtGroupRewardDistance(_victim))
{
_RewardPlayer(member, isDungeon);
}
@@ -253,21 +259,32 @@ void KillRewarder::_RewardGroup()
void KillRewarder::Reward()
{
- // 3. Reward killer (and group, if necessary).
- if (_group)
- // 3.1. If killer is in group, reward group.
- _RewardGroup();
- else
+ boost::container::flat_set<Group const*, std::less<>, boost::container::small_vector<Group const*, 3>> processedGroups;
+ for (Player* killer : _killers)
{
- // 3.2. Reward single killer (not group case).
- // 3.2.1. Initialize initial XP amount based on killer's level.
- _InitXP(_killer);
- // To avoid unnecessary calculations and calls,
- // proceed only if XP is not ZERO or player is not on battleground
- // (battleground rewards only XP, that's why).
- if (!_isBattleGround || _xp)
- // 3.2.2. Reward killer.
- _RewardPlayer(_killer, false);
+ _InitGroupData(killer);
+
+ // 3. Reward killer (and group, if necessary).
+ if (Group* group = killer->GetGroup())
+ {
+ if (!processedGroups.insert(group).second)
+ continue;
+
+ // 3.1. If killer is in group, reward group.
+ _RewardGroup(group, killer);
+ }
+ else
+ {
+ // 3.2. Reward single killer (not group case).
+ // 3.2.1. Initialize initial XP amount based on killer's level.
+ _InitXP(killer, killer);
+ // To avoid unnecessary calculations and calls,
+ // proceed only if XP is not ZERO or player is not on battleground
+ // (battleground rewards only XP, that's why).
+ if (!_isBattleGround || _xp)
+ // 3.2.2. Reward killer.
+ _RewardPlayer(killer, false);
+ }
}
// 5. Credit instance encounter.
@@ -281,9 +298,9 @@ void KillRewarder::Reward()
if (ObjectGuid::LowType guildId = victim->GetMap()->GetOwnerGuildId())
if (Guild* guild = sGuildMgr->GetGuildById(guildId))
- guild->UpdateCriteria(CriteriaType::KillCreature, victim->GetEntry(), 1, 0, victim, _killer);
+ guild->UpdateCriteria(CriteriaType::KillCreature, victim->GetEntry(), 1, 0, victim, *_killers.begin());
if (Scenario* scenario = victim->GetScenario())
- scenario->UpdateCriteria(CriteriaType::KillCreature, victim->GetEntry(), 1, 0, victim, _killer);
+ scenario->UpdateCriteria(CriteriaType::KillCreature, victim->GetEntry(), 1, 0, victim, *_killers.begin());
}
}
diff --git a/src/server/game/Entities/Player/KillRewarder.h b/src/server/game/Entities/Player/KillRewarder.h
index 662f8c6b0e1..6abf0aae464 100644
--- a/src/server/game/Entities/Player/KillRewarder.h
+++ b/src/server/game/Entities/Player/KillRewarder.h
@@ -19,6 +19,7 @@
#define KillRewarder_h__
#include "Define.h"
+#include "IteratorPair.h"
class Player;
class Unit;
@@ -27,24 +28,23 @@ class Group;
class TC_GAME_API KillRewarder
{
public:
- KillRewarder(Player* killer, Unit* victim, bool isBattleGround);
+ KillRewarder(Trinity::IteratorPair<Player**> killers, Unit* victim, bool isBattleGround);
void Reward();
private:
- void _InitXP(Player* player);
- void _InitGroupData();
+ void _InitXP(Player* player, Player const* killer);
+ void _InitGroupData(Player const* killer);
void _RewardHonor(Player* player);
void _RewardXP(Player* player, float rate);
void _RewardReputation(Player* player, float rate);
void _RewardKillCredit(Player* player);
void _RewardPlayer(Player* player, bool isDungeon);
- void _RewardGroup();
+ void _RewardGroup(Group const* group, Player const* killer);
- Player* _killer;
+ Trinity::IteratorPair<Player**> _killers;
Unit* _victim;
- Group* _group;
float _groupRate;
Player* _maxNotGrayMember;
uint32 _count;
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 8dd3ad58eb9..b581bd07799 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -71,7 +71,6 @@
#include "InstancePackets.h"
#include "InstanceScript.h"
#include "ItemPackets.h"
-#include "KillRewarder.h"
#include "Language.h"
#include "LanguageMgr.h"
#include "LFGMgr.h"
@@ -17853,7 +17852,7 @@ void Player::_LoadCUFProfiles(PreparedQueryResult result)
bool Player::isAllowedToLoot(const Creature* creature) const
{
- if (!creature->isDead() || !creature->IsDamageEnoughForLootingAndReward())
+ if (!creature->isDead())
return false;
if (HasPendingBind())
@@ -17865,15 +17864,6 @@ bool Player::isAllowedToLoot(const Creature* creature) const
if (!loot->HasAllowedLooter(GetGUID()) || (!loot->hasItemForAll() && !loot->hasItemFor(this))) // no loot in creature for this player
return false;
- if (loot->loot_type == LOOT_SKINNING)
- return creature->GetLootRecipientGUID() == GetGUID();
-
- Group const* thisGroup = GetGroup();
- if (!thisGroup)
- return this == creature->GetLootRecipient();
- else if (thisGroup != creature->GetLootRecipientGroup())
- return false;
-
switch (loot->GetLootMethod())
{
case PERSONAL_LOOT: /// @todo implement personal loot (http://wow.gamepedia.com/Loot#Personal_Loot)
@@ -24636,11 +24626,6 @@ bool Player::GetsRecruitAFriendBonus(bool forXP)
return recruitAFriend;
}
-void Player::RewardPlayerAndGroupAtKill(Unit* victim, bool isBattleGround)
-{
- KillRewarder(this, victim, isBattleGround).Reward();
-}
-
void Player::RewardPlayerAndGroupAtEvent(uint32 creature_id, WorldObject* pRewardSource)
{
if (!pRewardSource)
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 221d9c08218..335da76cb5c 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -2167,7 +2167,6 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
bool IsAtGroupRewardDistance(WorldObject const* pRewardSource) const;
bool IsAtRecruitAFriendDistance(WorldObject const* pOther) const;
- void RewardPlayerAndGroupAtKill(Unit* victim, bool isBattleGround);
void RewardPlayerAndGroupAtEvent(uint32 creature_id, WorldObject* pRewardSource);
bool isHonorOrXPTarget(Unit const* victim) const;
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index e029077780b..db3caadc21c 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -44,6 +44,7 @@
#include "Group.h"
#include "InstanceScript.h"
#include "Item.h"
+#include "KillRewarder.h"
#include "Log.h"
#include "Loot.h"
#include "LootMgr.h"
@@ -873,8 +874,7 @@ bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) cons
if (victim->GetTypeId() != TYPEID_PLAYER && (!victim->IsControlledByPlayer() || victim->IsVehicle()))
{
- if (!victim->ToCreature()->hasLootRecipient())
- victim->ToCreature()->SetLootRecipient(attacker);
+ victim->ToCreature()->SetTappedBy(attacker);
if (!attacker || attacker->IsControlledByPlayer())
victim->ToCreature()->LowerPlayerDamageReq(health < damage ? health : damage);
@@ -10585,36 +10585,13 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId)
bool isRewardAllowed = true;
if (creature)
- {
- isRewardAllowed = creature->IsDamageEnoughForLootingAndReward();
- if (!isRewardAllowed)
- creature->SetLootRecipient(nullptr);
- }
+ isRewardAllowed = !creature->GetTapList().empty();
+ std::vector<Player*> tappers;
if (isRewardAllowed && creature)
- {
- if (Player* lootRecipient = creature->GetLootRecipient())
- {
- // Loot recipient can be in a different map
- if (!creature->IsInMap(lootRecipient))
- {
- if (Group* group = creature->GetLootRecipientGroup())
- {
- for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
- {
- Player* member = itr->GetSource();
- if (!member || !creature->IsInMap(member))
- continue;
-
- player = member;
- break;
- }
- }
- }
- else
- player = creature->GetLootRecipient();
- }
- }
+ for (ObjectGuid tapperGuid : creature->GetTapList())
+ if (Player* tapper = ObjectAccessor::GetPlayer(*creature, tapperGuid))
+ tappers.push_back(tapper);
// Exploit fix
if (creature && creature->IsPet() && creature->GetOwnerGUID().IsPlayer())
@@ -10622,59 +10599,87 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId)
// Reward player, his pets, and group/raid members
// call kill spell proc event (before real die and combat stop to triggering auras removed at death/combat stop)
- if (isRewardAllowed && player && player != victim)
+ if (isRewardAllowed)
{
WorldPackets::Party::PartyKillLog partyKillLog;
partyKillLog.Player = player->GetGUID();
partyKillLog.Victim = victim->GetGUID();
+ partyKillLog.Write();
- Player* looter = player;
- Group* group = player->GetGroup();
-
- if (group)
+ std::unordered_set<Group*> groups;
+ for (Player* tapper : tappers)
{
- group->BroadcastPacket(partyKillLog.Write(), group->GetMemberGroup(player->GetGUID()) != 0);
-
- if (creature)
+ if (Group* tapperGroup = tapper->GetGroup())
{
- group->UpdateLooterGuid(creature, true);
- if (!group->GetLooterGuid().IsEmpty())
+ if (groups.insert(tapperGroup).second)
{
- looter = ObjectAccessor::FindPlayer(group->GetLooterGuid());
- if (looter)
- creature->SetLootRecipient(looter); // update creature loot recipient to the allowed looter.
+ tapperGroup->BroadcastPacket(partyKillLog.GetRawPacket(), tapperGroup->GetMemberGroup(tapper->GetGUID()) != 0);
+
+ if (creature)
+ tapperGroup->UpdateLooterGuid(creature, true);
}
}
+ else
+ tapper->SendDirectMessage(partyKillLog.GetRawPacket());
}
- else
- player->SendDirectMessage(partyKillLog.Write());
// Generate loot before updating looter
if (creature)
{
- creature->m_loot.reset(new Loot(creature->GetMap(), creature->GetGUID(), LOOT_CORPSE, group));
- Loot* loot = creature->m_loot.get();
- if (creature->GetMap()->Is25ManRaid())
- loot->maxDuplicates = 3;
-
+ DungeonEncounterEntry const* dungeonEncounter = nullptr;
if (InstanceScript const* instance = creature->GetInstanceScript())
- if (DungeonEncounterEntry const* dungeonEncounter = instance->GetBossDungeonEncounter(creature))
+ dungeonEncounter = instance->GetBossDungeonEncounter(creature);
+
+ if (creature->GetMap()->IsDungeon())
+ {
+ Group* group = !groups.empty() ? *groups.begin() : nullptr;
+ Player* looter = group ? ASSERT_NOTNULL(ObjectAccessor::GetPlayer(*creature, group->GetLooterGuid())) : tappers[0];
+
+ Loot* loot = new Loot(creature->GetMap(), creature->GetGUID(), LOOT_CORPSE, dungeonEncounter ? group : nullptr);
+
+ if (dungeonEncounter)
loot->SetDungeonEncounterId(dungeonEncounter->ID);
- if (uint32 lootid = creature->GetCreatureTemplate()->lootid)
- loot->FillLoot(lootid, LootTemplates_Creature, looter, false, false, creature->GetLootMode(), creature->GetMap()->GetDifficultyLootItemContext());
+ if (uint32 lootid = creature->GetCreatureTemplate()->lootid)
+ loot->FillLoot(lootid, LootTemplates_Creature, looter, dungeonEncounter != nullptr, false, creature->GetLootMode(), creature->GetMap()->GetDifficultyLootItemContext());
+
+ if (creature->GetLootMode() > 0)
+ loot->generateMoneyLoot(creature->GetCreatureTemplate()->mingold, creature->GetCreatureTemplate()->maxgold);
- if (creature->GetLootMode() > 0)
- loot->generateMoneyLoot(creature->GetCreatureTemplate()->mingold, creature->GetCreatureTemplate()->maxgold);
+ if (group)
+ loot->NotifyLootList(creature->GetMap());
- loot->NotifyLootList(creature->GetMap());
+ if (dungeonEncounter || groups.empty())
+ creature->m_loot.reset(loot); // TODO: personal boss loot
+ else
+ creature->m_personalLoot[looter->GetGUID()].reset(loot); // trash mob loot is personal, generated with round robin rules
+
+ // Update round robin looter only if the creature had loot
+ if (!loot->isLooted())
+ for (Group* tapperGroup : groups)
+ tapperGroup->UpdateLooterGuid(creature);
+ }
+ else
+ {
+ for (Player* tapper : tappers)
+ {
+ Loot* loot = new Loot(creature->GetMap(), creature->GetGUID(), LOOT_CORPSE, nullptr);
- // Update round robin looter only if the creature had loot
- if (group && !loot->empty())
- group->UpdateLooterGuid(creature);
+ if (dungeonEncounter)
+ loot->SetDungeonEncounterId(dungeonEncounter->ID);
+
+ if (uint32 lootid = creature->GetCreatureTemplate()->lootid)
+ loot->FillLoot(lootid, LootTemplates_Creature, tapper, true, false, creature->GetLootMode(), creature->GetMap()->GetDifficultyLootItemContext());
+
+ if (creature->GetLootMode() > 0)
+ loot->generateMoneyLoot(creature->GetCreatureTemplate()->mingold, creature->GetCreatureTemplate()->maxgold);
+
+ creature->m_personalLoot[tapper->GetGUID()].reset(loot);
+ }
+ }
}
- player->RewardPlayerAndGroupAtKill(victim, false);
+ KillRewarder(Trinity::IteratorPair(tappers.data(), tappers.data() + tappers.size()), victim, false).Reward();
}
// Do KILL and KILLED procs. KILL proc is called only for the unit who landed the killing blow (and its owner - for pets and totems) regardless of who tapped the victim
@@ -10689,11 +10694,9 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId)
{
Unit::ProcSkillsAndAuras(attacker, victim, PROC_FLAG_KILL, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr);
- if (player && player->GetGroup())
- for (GroupReference* itr = player->GetGroup()->GetFirstMember(); itr != nullptr; itr = itr->next())
- if (Player* member = itr->GetSource())
- if (member->IsAtGroupRewardDistance(victim))
- Unit::ProcSkillsAndAuras(member, victim, { PROC_FLAG_NONE, PROC_FLAG_2_TARGET_DIES }, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr);
+ for (Player* tapper : tappers)
+ if (tapper->IsAtGroupRewardDistance(victim))
+ Unit::ProcSkillsAndAuras(tapper, victim, { PROC_FLAG_NONE, PROC_FLAG_2_TARGET_DIES }, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr);
}
// Proc auras on death - must be before aura/combat remove
@@ -10714,9 +10717,9 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId)
// Inform pets (if any) when player kills target)
// MUST come after victim->setDeathState(JUST_DIED); or pet next target
// selection will get stuck on same target and break pet react state
- if (player)
+ for (Player* tapper : tappers)
{
- Pet* pet = player->GetPet();
+ Pet* pet = tapper->GetPet();
if (pet && pet->IsAlive() && pet->isControlled())
{
if (pet->IsAIEnabled())
@@ -10764,10 +10767,16 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId)
if (!creature->IsPet())
{
// must be after setDeathState which resets dynamic flags
- if (creature->m_loot && !creature->m_loot->isLooted())
+ if (!creature->IsFullyLooted())
creature->SetDynamicFlag(UNIT_DYNFLAG_LOOTABLE);
else
creature->AllLootRemovedFromCorpse();
+
+ if (LootTemplates_Skinning.HaveLootFor(creature->GetCreatureTemplate()->SkinLootId))
+ {
+ creature->SetDynamicFlag(UNIT_DYNFLAG_CAN_SKIN);
+ creature->SetUnitFlag(UNIT_FLAG_SKINNABLE);
+ }
}
// Call KilledUnit for creatures, this needs to be called after the lootable flag is set
diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp
index 8fe3ecff2b5..fdaf064dd74 100644
--- a/src/server/game/Handlers/LootHandler.cpp
+++ b/src/server/game/Handlers/LootHandler.cpp
@@ -354,11 +354,14 @@ void WorldSession::DoLootRelease(Loot* loot)
if (loot->isLooted())
{
- creature->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE);
+ if (creature->IsFullyLooted())
+ {
+ creature->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE);
- // skip pickpocketing loot for speed, skinning timer reduction is no-op in fact
- if (!creature->IsAlive())
- creature->AllLootRemovedFromCorpse();
+ // skip pickpocketing loot for speed, skinning timer reduction is no-op in fact
+ if (!creature->IsAlive())
+ creature->AllLootRemovedFromCorpse();
+ }
}
else
{
@@ -368,9 +371,9 @@ void WorldSession::DoLootRelease(Loot* loot)
loot->roundRobinPlayer.Clear();
loot->NotifyLootList(creature->GetMap());
}
- // force dynflag update to update looter and lootable info
- creature->ForceUpdateFieldChange(creature->m_values.ModifyValue(&Object::m_objectData).ModifyValue(&UF::ObjectData::DynamicFlags));
}
+ // force dynflag update to update looter and lootable info
+ creature->ForceUpdateFieldChange(creature->m_values.ModifyValue(&Object::m_objectData).ModifyValue(&UF::ObjectData::DynamicFlags));
}
}
diff --git a/src/server/game/Loot/Loot.cpp b/src/server/game/Loot/Loot.cpp
index d67f9ea55d9..6c5dd9286d0 100644
--- a/src/server/game/Loot/Loot.cpp
+++ b/src/server/game/Loot/Loot.cpp
@@ -613,7 +613,7 @@ void LootRoll::Finish(RollVoteMap::const_iterator winnerItr)
// --------- Loot ---------
//
-Loot::Loot(Map* map, ObjectGuid owner, LootType type, Group const* group) : gold(0), unlootedCount(0), loot_type(type), maxDuplicates(1),
+Loot::Loot(Map* map, ObjectGuid owner, LootType type, Group const* group) : gold(0), unlootedCount(0), loot_type(type),
_guid(map ? ObjectGuid::Create<HighGuid::LootObject>(map->GetId(), 0, map->GenerateLowGuid<HighGuid::LootObject>()) : ObjectGuid::Empty),
_owner(owner), _itemContext(ItemContext::NONE), _lootMethod(group ? group->GetLootMethod() : FREE_FOR_ALL),
_lootMaster(group ? group->GetMasterLooterGuid() : ObjectGuid::Empty), _wasOpened(false), _dungeonEncounterId(0)
@@ -622,25 +622,10 @@ Loot::Loot(Map* map, ObjectGuid owner, LootType type, Group const* group) : gold
Loot::~Loot()
{
- clear();
-}
-
-void Loot::clear()
-{
- PlayerFFAItems.clear();
-
- for (ObjectGuid playerGuid : PlayersLooting)
+ GuidSet activeLooters = std::move(PlayersLooting);
+ for (ObjectGuid playerGuid : activeLooters)
if (Player* player = ObjectAccessor::FindConnectedPlayer(playerGuid))
player->GetSession()->DoLootRelease(this);
- PlayersLooting.clear();
-
- items.clear();
- gold = 0;
- unlootedCount = 0;
- roundRobinPlayer.Clear();
- loot_type = LOOT_NONE;
- _itemContext = ItemContext::NONE;
- _rolls.clear();
}
void Loot::NotifyLootList(Map const* map) const
diff --git a/src/server/game/Loot/Loot.h b/src/server/game/Loot/Loot.h
index 12f3d559f05..8f1e717d8bd 100644
--- a/src/server/game/Loot/Loot.h
+++ b/src/server/game/Loot/Loot.h
@@ -271,7 +271,6 @@ struct TC_GAME_API Loot
uint8 unlootedCount;
ObjectGuid roundRobinPlayer; // GUID of the player having the Round-Robin ownership for the loot. If 0, round robin owner has released.
LootType loot_type; // required for achievement system
- uint8 maxDuplicates; // Max amount of items with the same entry that can drop (default is 1; on 25 man raid mode 3)
explicit Loot(Map* map, ObjectGuid owner, LootType type, Group const* group);
~Loot();
@@ -288,9 +287,6 @@ struct TC_GAME_API Loot
uint32 GetDungeonEncounterId() const { return _dungeonEncounterId; }
void SetDungeonEncounterId(uint32 dungeonEncounterId) { _dungeonEncounterId = dungeonEncounterId; }
- void clear();
-
- bool empty() const { return items.empty() && gold == 0; }
bool isLooted() const { return gold == 0 && unlootedCount == 0; }
void NotifyLootList(Map const* map) const;
diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp
index 9e04a32edd4..7ea8bd2b1d0 100644
--- a/src/server/game/Loot/LootMgr.cpp
+++ b/src/server/game/Loot/LootMgr.cpp
@@ -56,24 +56,18 @@ LootStore LootTemplates_Spell("spell_loot_template", "spell id (
// Selects invalid loot items to be removed from group possible entries (before rolling)
struct LootGroupInvalidSelector
{
- explicit LootGroupInvalidSelector(Loot const& loot, uint16 lootMode) : _loot(loot), _lootMode(lootMode) { }
+ explicit LootGroupInvalidSelector(Loot const& /*loot*/, uint16 lootMode) : /*_loot(loot),*/ _lootMode(lootMode) { }
bool operator()(LootStoreItem* item) const
{
if (!(item->lootmode & _lootMode))
return true;
- uint8 foundDuplicates = 0;
- for (std::vector<LootItem>::const_iterator itr = _loot.items.begin(); itr != _loot.items.end(); ++itr)
- if (itr->itemid == item->itemid)
- if (++foundDuplicates == _loot.maxDuplicates)
- return true;
-
return false;
}
private:
- Loot const& _loot;
+ //Loot const& _loot;
uint16 _lootMode;
};
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 85b7ff04715..651a88ddd43 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -5667,7 +5667,7 @@ enum UnitDynFlags
UNIT_DYNFLAG_TRACK_UNIT = 0x0008,
UNIT_DYNFLAG_TAPPED = 0x0010, // Lua_UnitIsTapped
UNIT_DYNFLAG_SPECIALINFO = 0x0020,
- UNIT_DYNFLAG_UNUSED = 0x0040, // previously UNIT_DYNFLAG_DEAD
+ UNIT_DYNFLAG_CAN_SKIN = 0x0040,
UNIT_DYNFLAG_REFER_A_FRIEND = 0x0080
};
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index d6f252695c3..fe7a1e12e06 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -1659,7 +1659,8 @@ void Spell::SelectImplicitCasterObjectTargets(SpellEffectInfo const& spellEffect
break;
case TARGET_UNIT_TARGET_TAP_LIST:
if (Creature* creatureCaster = m_caster->ToCreature())
- target = creatureCaster->GetLootRecipient();
+ if (!creatureCaster->GetTapList().empty())
+ target = ObjectAccessor::GetWorldObject(*creatureCaster, Trinity::Containers::SelectRandomContainerElement(creatureCaster->GetTapList()));
break;
case TARGET_UNIT_OWN_CRITTER:
if (Unit const* unitCaster = m_caster->ToUnit())
@@ -2857,8 +2858,8 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell)
if (spell->m_spellInfo->HasAttribute(SPELL_ATTR6_TAPS_IMMEDIATELY))
if (Creature* targetCreature = unit->ToCreature())
- if (!targetCreature->hasLootRecipient() && unitCaster->IsPlayer())
- targetCreature->SetLootRecipient(unitCaster);
+ if (unitCaster->IsPlayer())
+ targetCreature->SetTappedBy(unitCaster);
}
if (!spell->m_spellInfo->HasAttribute(SPELL_ATTR3_DO_NOT_TRIGGER_TARGET_STAND) && !unit->IsStandState())
@@ -5998,7 +5999,7 @@ SpellCastResult Spell::CheckCast(bool strict, int32* param1 /*= nullptr*/, int32
Creature* creature = m_targets.GetUnitTarget()->ToCreature();
Loot* loot = creature->GetLootForPlayer(m_caster->ToPlayer());
- if (loot && !loot->isLooted())
+ if (loot && (!loot->isLooted() || loot->loot_type == LOOT_SKINNING))
return SPELL_FAILED_TARGET_NOT_LOOTED;
uint32 skill = creature->GetCreatureTemplate()->GetRequiredLootSkill();
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index e4e9179dc37..b09841cac2a 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -3733,12 +3733,12 @@ void Spell::EffectSkinning()
uint32 skill = creature->GetCreatureTemplate()->GetRequiredLootSkill();
- creature->RemoveUnitFlag(UNIT_FLAG_SKINNABLE);
+ creature->SetUnitFlag3(UNIT_FLAG3_ALREADY_SKINNED);
creature->SetDynamicFlag(UNIT_DYNFLAG_LOOTABLE);
- creature->m_loot.reset(new Loot(creature->GetMap(), creature->GetGUID(), LOOT_SKINNING, nullptr));
- creature->m_loot->FillLoot(creature->GetCreatureTemplate()->SkinLootId, LootTemplates_Skinning, player, true);
- creature->SetLootRecipient(player, false);
- player->SendLoot(*creature->m_loot);
+ Loot* loot = new Loot(creature->GetMap(), creature->GetGUID(), LOOT_SKINNING, nullptr);
+ creature->m_personalLoot[player->GetGUID()].reset(loot);
+ loot->FillLoot(creature->GetCreatureTemplate()->SkinLootId, LootTemplates_Skinning, player, true);
+ player->SendLoot(*loot);
if (skill == SKILL_SKINNING)
{
diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp
index c242f96ae94..26e2baa9c81 100644
--- a/src/server/scripts/Commands/cs_debug.cpp
+++ b/src/server/scripts/Commands/cs_debug.cpp
@@ -546,9 +546,15 @@ public:
if (!target)
return false;
- handler->PSendSysMessage("Loot recipient for creature %s (%s, SpawnID " UI64FMTD ") is %s",
- target->GetName().c_str(), target->GetGUID().ToString().c_str(), target->GetSpawnId(),
- target->hasLootRecipient() ? (target->GetLootRecipient() ? target->GetLootRecipient()->GetName().c_str() : "offline") : "no loot recipient");
+ handler->PSendSysMessage("Loot recipients for creature %s (%s, SpawnID " UI64FMTD ") are:",
+ target->GetName().c_str(), target->GetGUID().ToString().c_str(), target->GetSpawnId());
+
+ for (ObjectGuid tapperGuid : target->GetTapList())
+ {
+ Player* tapper = ObjectAccessor::GetPlayer(*target, tapperGuid);
+ handler->PSendSysMessage("* %s", tapper ? tapper->GetName().c_str() : "offline");
+ }
+
return true;
}
diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp
index 9340b0f292f..4305ba8b10e 100644
--- a/src/server/scripts/Commands/cs_npc.cpp
+++ b/src/server/scripts/Commands/cs_npc.cpp
@@ -1200,7 +1200,7 @@ public:
}
Loot const* loot = creatureTarget->m_loot.get();
- if (!creatureTarget->isDead() || !loot || loot->empty())
+ if (!creatureTarget->isDead() || !loot || loot->isLooted())
{
handler->PSendSysMessage(LANG_COMMAND_NOT_DEAD_OR_NO_LOOT, creatureTarget->GetName().c_str());
handler->SetSentErrorMessage(true);
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp
index a0e404c3d09..6247dbf990b 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp
@@ -433,7 +433,7 @@ void hyjalAI::EnterEvadeMode(EvadeReason /*why*/)
if (me->IsAlive())
me->GetMotionMaster()->MoveTargetedHome();
- me->SetLootRecipient(nullptr);
+ me->SetTappedBy(nullptr);
}
void hyjalAI::JustEngagedWith(Unit* /*who*/)
diff --git a/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp b/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp
index 0b8f48a26e0..670f503a717 100644
--- a/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp
+++ b/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp
@@ -200,10 +200,8 @@ public:
if (Creature* legoso = me->FindNearestCreature(NPC_LEGOSO, SIZE_OF_GRIDS))
{
- Group* group = me->GetLootRecipientGroup();
-
if (killer->GetGUID() == legoso->GetGUID() ||
- (group && group->IsMember(killer->GetGUID())) ||
+ (killer->IsPlayer() && me->isTappedBy(killer->ToPlayer())) ||
killer->GetGUID() == legoso->AI()->GetGUID(DATA_EVENT_STARTER_GUID))
legoso->AI()->DoAction(ACTION_LEGOSO_SIRONAS_KILLED);
}
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp
index 7b791385242..c5f211c538b 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp
@@ -323,7 +323,7 @@ struct dummy_dragonAI : public ScriptedAI
void JustDied(Unit* /*killer*/) override
{
if (!_canLoot)
- me->SetLootRecipient(nullptr);
+ me->SetTappedBy(nullptr);
uint32 spellId = 0;
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
index 347a874d2d4..e0223f674be 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
@@ -452,8 +452,7 @@ class boss_twilight_halion : public CreatureScript
if (Creature* halion = instance->GetCreature(DATA_HALION))
{
// Ensure looting
- if (me->IsDamageEnoughForLootingAndReward())
- halion->LowerPlayerDamageReq(halion->GetMaxHealth());
+ halion->LowerPlayerDamageReq(halion->GetMaxHealth());
if (halion->IsAlive())
Unit::Kill(killer, halion);
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp
index a4567be2607..c4043240376 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp
@@ -327,8 +327,7 @@ struct boss_blood_council_controller : public BossAI
if (Creature* prince = ObjectAccessor::GetCreature(*me, _invocationOrder[_invocationStage].guid))
{
// Make sure looting is allowed
- if (me->IsDamageEnoughForLootingAndReward())
- prince->LowerPlayerDamageReq(prince->GetMaxHealth());
+ prince->LowerPlayerDamageReq(prince->GetMaxHealth());
Unit::Kill(killer, prince);
}
}
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
index 4e428ab041c..5211071b1ef 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
@@ -523,7 +523,7 @@ struct boss_the_lich_king : public BossAI
me->SummonCreature(NPC_HIGHLORD_TIRION_FORDRING_LK, TirionSpawn, TEMPSUMMON_MANUAL_DESPAWN);
}
- void JustDied(Unit* /*killer*/) override
+ void JustDied(Unit* killer) override
{
_JustDied();
DoCastAOE(SPELL_PLAY_MOVIE, false);
@@ -535,7 +535,7 @@ struct boss_the_lich_king : public BossAI
me->GetMap()->SetZoneWeather(AREA_ICECROWN_CITADEL, WEATHER_STATE_FOG, 0.0f);
if (Is25ManRaid())
- if (Player* player = me->GetLootRecipient())
+ if (Player* player = Object::ToPlayer(killer))
player->RewardPlayerAndGroupAtEvent(NPC_THE_LICH_KING_QUEST, player);
}
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp
index 6b08bf1717d..a1dd29b48d1 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp
@@ -318,7 +318,7 @@ struct boss_valithria_dreamwalker : public ScriptedAI
void HealReceived(Unit* healer, uint32& heal) override
{
if (!me->hasLootRecipient())
- me->SetLootRecipient(healer);
+ me->SetTappedBy(healer);
me->LowerPlayerDamageReq(heal);
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
index 132980fae3f..2c243261b3d 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
@@ -609,7 +609,7 @@ class instance_icecrown_citadel : public InstanceMapScript
case GO_CACHE_OF_THE_DREAMWALKER_10H:
case GO_CACHE_OF_THE_DREAMWALKER_25H:
if (Creature* valithria = instance->GetCreature(ValithriaDreamwalkerGUID))
- go->SetLootRecipient(valithria->GetLootRecipient(), valithria->GetLootRecipientGroup());
+ go->SetTapList(valithria->GetTapList());
go->RemoveFlag(GO_FLAG_LOCKED | GO_FLAG_NOT_SELECTABLE | GO_FLAG_NODESPAWN);
break;
case GO_ARTHAS_PLATFORM:
@@ -843,7 +843,7 @@ class instance_icecrown_citadel : public InstanceMapScript
if (GameObject* loot = instance->GetGameObject(DeathbringersCacheGUID))
{
if (Creature* deathbringer = instance->GetCreature(DeathbringerSaurfangGUID))
- loot->SetLootRecipient(deathbringer->GetLootRecipient(), deathbringer->GetLootRecipientGroup());
+ loot->SetTapList(deathbringer->GetTapList());
loot->RemoveFlag(GO_FLAG_LOCKED | GO_FLAG_NOT_SELECTABLE | GO_FLAG_NODESPAWN);
}
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_assembly_of_iron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_assembly_of_iron.cpp
index b4dbadd51f9..3e4451164c3 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_assembly_of_iron.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_assembly_of_iron.cpp
@@ -207,7 +207,7 @@ class boss_steelbreaker : public CreatureScript
}
else
{
- me->SetLootRecipient(nullptr);
+ me->SetTappedBy(nullptr);
Talk(SAY_STEELBREAKER_DEATH);
//DoCastAOE(SPELL_SUPERCHARGE, true);
@@ -355,7 +355,7 @@ class boss_runemaster_molgeim : public CreatureScript
}
else
{
- me->SetLootRecipient(nullptr);
+ me->SetTappedBy(nullptr);
Talk(SAY_MOLGEIM_DEATH);
//DoCastAOE(SPELL_SUPERCHARGE, true);
@@ -534,7 +534,7 @@ class boss_stormcaller_brundir : public CreatureScript
}
else
{
- me->SetLootRecipient(nullptr);
+ me->SetTappedBy(nullptr);
Talk(SAY_BRUNDIR_DEATH);
//DoCastAOE(SPELL_SUPERCHARGE, true);
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp
index e7dd5c1cf2f..02825349ad5 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp
@@ -674,7 +674,7 @@ class instance_ulduar : public InstanceMapScript
{
if (GameObject* cache = instance->GetGameObject(thorim->AI()->GetData(DATA_THORIM_HARDMODE) ? CacheOfStormsHardmodeGUID : CacheOfStormsGUID))
{
- cache->SetLootRecipient(thorim->GetLootRecipient());
+ cache->SetTapList(thorim->GetTapList());
cache->SetRespawnTime(cache->GetRespawnDelay());
cache->RemoveFlag(GO_FLAG_LOCKED);
cache->RemoveFlag(GO_FLAG_NOT_SELECTABLE);