aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Entities/Player/Player.cpp70
-rw-r--r--src/server/game/Entities/Player/Player.h13
-rw-r--r--src/server/game/Handlers/LootHandler.cpp181
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp5
-rw-r--r--src/server/game/Loot/LootMgr.cpp30
-rw-r--r--src/server/game/Loot/LootMgr.h23
-rw-r--r--src/server/game/Server/Packets/LootPackets.cpp9
-rw-r--r--src/server/game/Server/Packets/LootPackets.h26
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp6
-rw-r--r--src/server/game/Server/WorldSession.cpp5
-rw-r--r--src/server/game/Server/WorldSession.h1
11 files changed, 280 insertions, 89 deletions
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 52e4369123a..8ea901380a2 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -8019,6 +8019,28 @@ void Player::_ApplyAllLevelScaleItemMods(bool apply)
}
}
+ObjectGuid Player::GetLootWorldObjectGUID(ObjectGuid const& lootObjectGuid) const
+{
+ auto itr = m_AELootView.find(lootObjectGuid);
+ if (itr != m_AELootView.end())
+ return itr->second;
+
+ return ObjectGuid::Empty;
+}
+
+void Player::RemoveAELootedObject(ObjectGuid const& lootObjectGuid)
+{
+ m_AELootView.erase(lootObjectGuid);
+}
+
+bool Player::HasLootWorldObjectGUID(ObjectGuid const& lootWorldObjectGuid) const
+{
+ return m_AELootView.end() != std::find_if(m_AELootView.begin(), m_AELootView.end(), [&lootWorldObjectGuid](std::pair<ObjectGuid, ObjectGuid> const& lootView)
+ {
+ return lootView.second == lootWorldObjectGuid;
+ });
+}
+
/* If in a battleground a player dies, and an enemy removes the insignia, the player's bones is lootable
Called by remove insignia spell effect */
void Player::RemovedInsignia(Player* looterPlr)
@@ -8060,10 +8082,15 @@ void Player::SendLootRelease(ObjectGuid guid) const
SendDirectMessage(packet.Write());
}
-void Player::SendLoot(ObjectGuid guid, LootType loot_type)
+void Player::SendLootReleaseAll() const
+{
+ SendDirectMessage(WorldPackets::Loot::LootReleaseAll().Write());
+}
+
+void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = false*/)
{
ObjectGuid currentLootGuid = GetLootGUID();
- if (!currentLootGuid.IsEmpty())
+ if (!currentLootGuid.IsEmpty() && !aeLooting)
m_session->DoLootRelease(currentLootGuid);
Loot* loot;
@@ -8244,7 +8271,7 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type)
Creature* creature = GetMap()->GetCreature(guid);
// must be in range and creature must be alive for pickpocket and must be dead for another loot
- if (!creature || creature->IsAlive() != (loot_type == LOOT_PICKPOCKETING) || !creature->IsWithinDistInMap(this, INTERACTION_DISTANCE))
+ if (!creature || creature->IsAlive() != (loot_type == LOOT_PICKPOCKETING) || (!aeLooting && !creature->IsWithinDistInMap(this, INTERACTION_DISTANCE)))
{
SendLootRelease(guid);
return;
@@ -8381,19 +8408,22 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type)
}
}
- SetLootGUID(guid);
+ if (!aeLooting)
+ SetLootGUID(guid);
WorldPackets::Loot::LootResponse packet;
- packet.LootObj = guid;
- packet.Owner = loot->GetGUID();
+ packet.Owner = guid;
+ packet.LootObj = loot->GetGUID();
packet._LootMethod = _lootMethod;
packet.AcquireReason = loot_type;
packet.Acquired = true; // false == No Loot (this too^^)
+ packet.AELooting = aeLooting;
loot->BuildLootResponse(packet, this, permission);
SendDirectMessage(packet.Write());
// add 'this' player as one of the players that are looting 'loot'
loot->AddLooter(GetGUID());
+ m_AELootView[loot->GetGUID()] = guid;
}
else
SendLootError(GetLootGUID(), LOOT_ERROR_DIDNT_KILL);
@@ -8418,10 +8448,10 @@ void Player::SendNotifyLootMoneyRemoved(ObjectGuid lootObj) const
SendDirectMessage(packet.Write());
}
-void Player::SendNotifyLootItemRemoved(ObjectGuid owner, ObjectGuid lootObj, uint8 lootSlot) const
+void Player::SendNotifyLootItemRemoved(ObjectGuid lootObj, uint8 lootSlot) const
{
WorldPackets::Loot::LootRemoved packet;
- packet.Owner = owner;
+ packet.Owner = GetLootWorldObjectGUID(lootObj);
packet.LootObj = lootObj;
packet.LootListID = lootSlot + 1;
GetSession()->SendPacket(packet.Write());
@@ -25106,7 +25136,7 @@ void Player::AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore cons
}
}
-void Player::StoreLootItem(uint8 lootSlot, Loot* loot)
+void Player::StoreLootItem(uint8 lootSlot, Loot* loot, AELootResult* aeResult/* = nullptr*/)
{
QuestItem* qitem = nullptr;
QuestItem* ffaitem = nullptr;
@@ -25122,14 +25152,14 @@ void Player::StoreLootItem(uint8 lootSlot, Loot* loot)
if (!item->AllowedForPlayer(this))
{
- SendLootRelease(GetLootGUID());
+ SendLootReleaseAll();
return;
}
// questitems use the blocked field for other purposes
if (!qitem && item->is_blocked)
{
- SendLootRelease(GetLootGUID());
+ SendLootReleaseAll();
return;
}
@@ -25144,7 +25174,7 @@ void Player::StoreLootItem(uint8 lootSlot, Loot* loot)
qitem->is_looted = true;
//freeforall is 1 if everyone's supposed to get the quest item.
if (item->freeforall || loot->GetPlayerQuestItems().size() == 1)
- SendNotifyLootItemRemoved(GetLootGUID(), loot->GetGUID(), lootSlot);
+ SendNotifyLootItemRemoved(loot->GetGUID(), lootSlot);
else
loot->NotifyQuestItemRemoved(qitem->index);
}
@@ -25154,7 +25184,7 @@ void Player::StoreLootItem(uint8 lootSlot, Loot* loot)
{
//freeforall case, notify only one player of the removal
ffaitem->is_looted = true;
- SendNotifyLootItemRemoved(GetLootGUID(), loot->GetGUID(), lootSlot);
+ SendNotifyLootItemRemoved(loot->GetGUID(), lootSlot);
}
else
{
@@ -25176,10 +25206,16 @@ void Player::StoreLootItem(uint8 lootSlot, Loot* loot)
if (Guild* guild = GetGuild())
guild->AddGuildNews(GUILD_NEWS_ITEM_LOOTED, GetGUID(), 0, item->itemid);
- SendNewItem(newitem, uint32(item->count), false, false, true);
- UpdateCriteria(CRITERIA_TYPE_LOOT_ITEM, item->itemid, item->count);
- UpdateCriteria(CRITERIA_TYPE_LOOT_TYPE, item->itemid, item->count, loot->loot_type);
- UpdateCriteria(CRITERIA_TYPE_LOOT_EPIC_ITEM, item->itemid, item->count);
+ // if aeLooting then we must delay sending out item so that it appears properly stacked in chat
+ if (!aeResult)
+ {
+ SendNewItem(newitem, uint32(item->count), false, false, true);
+ UpdateCriteria(CRITERIA_TYPE_LOOT_ITEM, item->itemid, item->count);
+ UpdateCriteria(CRITERIA_TYPE_LOOT_TYPE, item->itemid, item->count, loot->loot_type);
+ UpdateCriteria(CRITERIA_TYPE_LOOT_EPIC_ITEM, item->itemid, item->count);
+ }
+ else
+ aeResult->Add(newitem, item->count, loot->loot_type);
// LootItem is being removed (looted) from the container, delete it from the DB.
if (!loot->containerID.IsEmpty())
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index ee23fb76cdc..c4f229de191 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -1394,7 +1394,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
bool StoreNewItemInBestSlots(uint32 item_id, uint32 item_count);
void AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore const& store, bool broadcast = false);
void AutoStoreLoot(uint32 loot_id, LootStore const& store, bool broadcast = false) { AutoStoreLoot(NULL_BAG, NULL_SLOT, loot_id, store, broadcast); }
- void StoreLootItem(uint8 lootSlot, Loot* loot);
+ void StoreLootItem(uint8 lootSlot, Loot* loot, AELootResult* aeResult = nullptr);
InventoryResult CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count = nullptr, uint32* offendingItemId = nullptr) const;
InventoryResult CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 entry, uint32 count, Item* pItem = nullptr, bool swap = false, uint32* no_space_count = nullptr) const;
@@ -1987,6 +1987,10 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
ObjectGuid const& GetLootGUID() const { return GetGuidValue(PLAYER_LOOT_TARGET_GUID); }
void SetLootGUID(ObjectGuid const& guid) { SetGuidValue(PLAYER_LOOT_TARGET_GUID, guid); }
+ ObjectGuid GetLootWorldObjectGUID(ObjectGuid const& lootObjectGuid) const;
+ void RemoveAELootedObject(ObjectGuid const& lootObjectGuid);
+ bool HasLootWorldObjectGUID(ObjectGuid const& lootWorldObjectGuid) const;
+ std::unordered_map<ObjectGuid, ObjectGuid> const& GetAELootView() const { return m_AELootView; }
void RemovedInsignia(Player* looterPlr);
@@ -2193,10 +2197,11 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
PlayerMenu* PlayerTalkClass;
std::vector<ItemSetEffect*> ItemSetEff;
- void SendLoot(ObjectGuid guid, LootType loot_type);
+ void SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting = false);
void SendLootError(ObjectGuid guid, LootError error) const;
void SendLootRelease(ObjectGuid guid) const;
- void SendNotifyLootItemRemoved(ObjectGuid owner, ObjectGuid lootObj, uint8 lootSlot) const;
+ void SendLootReleaseAll() const;
+ void SendNotifyLootItemRemoved(ObjectGuid lootObj, uint8 lootSlot) const;
void SendNotifyLootMoneyRemoved(ObjectGuid lootObj) const;
/*********************************************************/
@@ -2837,6 +2842,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
WorldLocation _corpseLocation;
SceneMgr m_sceneMgr;
+
+ std::unordered_map<ObjectGuid /*LootObject*/, ObjectGuid /*world object*/> m_AELootView;
};
TC_GAME_API void AddItemsSetItem(Player* player, Item* item);
diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp
index 9b6a8923615..ac5c0a92474 100644
--- a/src/server/game/Handlers/LootHandler.cpp
+++ b/src/server/game/Handlers/LootHandler.cpp
@@ -21,6 +21,8 @@
#include "Corpse.h"
#include "Creature.h"
#include "GameObject.h"
+#include "CellImpl.h"
+#include "GridNotifiersImpl.h"
#include "Group.h"
#include "GuildMgr.h"
#include "LootMgr.h"
@@ -34,12 +36,14 @@
void WorldSession::HandleAutostoreLootItemOpcode(WorldPackets::Loot::LootItem& packet)
{
Player* player = GetPlayer();
- ObjectGuid lguid = player->GetLootGUID();
+ AELootResult aeResult;
+ AELootResult* aeResultPtr = player->GetAELootView().size() > 1 ? &aeResult : nullptr;
/// @todo Implement looting by LootObject guid
for (WorldPackets::Loot::LootRequest const& req : packet.Loot)
{
- Loot* loot = NULL;
+ Loot* loot = nullptr;
+ ObjectGuid lguid = player->GetLootWorldObjectGUID(req.Object);
if (lguid.IsGameObject())
{
@@ -91,78 +95,89 @@ void WorldSession::HandleAutostoreLootItemOpcode(WorldPackets::Loot::LootItem& p
loot = &creature->loot;
}
- player->StoreLootItem(req.LootListID - 1, loot);
+ player->StoreLootItem(req.LootListID - 1, loot, aeResultPtr);
// If player is removing the last LootItem, delete the empty container.
if (loot->isLooted() && lguid.IsItem())
player->GetSession()->DoLootRelease(lguid);
}
+
+ if (aeResultPtr)
+ {
+ for (AELootResult::ResultValue const& resultValue : aeResult)
+ {
+ player->SendNewItem(resultValue.item, resultValue.count, false, false, true);
+ player->UpdateCriteria(CRITERIA_TYPE_LOOT_ITEM, resultValue.item->GetEntry(), resultValue.count);
+ player->UpdateCriteria(CRITERIA_TYPE_LOOT_TYPE, resultValue.item->GetEntry(), resultValue.count, resultValue.lootType);
+ player->UpdateCriteria(CRITERIA_TYPE_LOOT_EPIC_ITEM, resultValue.item->GetEntry(), resultValue.count);
+ }
+ }
}
void WorldSession::HandleLootMoneyOpcode(WorldPackets::Loot::LootMoney& /*packet*/)
{
Player* player = GetPlayer();
- ObjectGuid guid = player->GetLootGUID();
- if (!guid)
- return;
-
- Loot* loot = NULL;
- bool shareMoney = true;
-
- switch (guid.GetHigh())
+ for (std::pair<ObjectGuid, ObjectGuid> const& lootView : player->GetAELootView())
{
- case HighGuid::GameObject:
- {
- GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid);
+ ObjectGuid guid = lootView.second;
+ Loot* loot = nullptr;
+ bool shareMoney = true;
- // do not check distance for GO if player is the owner of it (ex. fishing bobber)
- if (go && ((go->GetOwnerGUID() == player->GetGUID() || go->IsWithinDistInMap(player, INTERACTION_DISTANCE))))
- loot = &go->loot;
-
- break;
- }
- case HighGuid::Corpse: // remove insignia ONLY in BG
+ switch (guid.GetHigh())
{
- Corpse* bones = ObjectAccessor::GetCorpse(*player, guid);
-
- if (bones && bones->IsWithinDistInMap(player, INTERACTION_DISTANCE))
+ case HighGuid::GameObject:
{
- loot = &bones->loot;
- shareMoney = false;
- }
+ GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid);
- break;
- }
- case HighGuid::Item:
- {
- if (Item* item = player->GetItemByGuid(guid))
+ // do not check distance for GO if player is the owner of it (ex. fishing bobber)
+ if (go && ((go->GetOwnerGUID() == player->GetGUID() || go->IsWithinDistInMap(player, INTERACTION_DISTANCE))))
+ loot = &go->loot;
+
+ break;
+ }
+ case HighGuid::Corpse: // remove insignia ONLY in BG
{
- loot = &item->loot;
- shareMoney = false;
+ Corpse* bones = ObjectAccessor::GetCorpse(*player, guid);
+
+ if (bones && bones->IsWithinDistInMap(player, INTERACTION_DISTANCE))
+ {
+ loot = &bones->loot;
+ shareMoney = false;
+ }
+
+ break;
}
- break;
- }
- case HighGuid::Creature:
- case HighGuid::Vehicle:
- {
- Creature* creature = player->GetMap()->GetCreature(guid);
- bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING);
- if (lootAllowed && creature->IsWithinDistInMap(player, INTERACTION_DISTANCE))
+ case HighGuid::Item:
{
- loot = &creature->loot;
- if (creature->IsAlive())
+ if (Item* item = player->GetItemByGuid(guid))
+ {
+ loot = &item->loot;
shareMoney = false;
+ }
+ break;
}
- else
- player->SendLootError(guid, lootAllowed ? LOOT_ERROR_TOO_FAR : LOOT_ERROR_DIDNT_KILL);
- break;
+ case HighGuid::Creature:
+ case HighGuid::Vehicle:
+ {
+ Creature* creature = player->GetMap()->GetCreature(guid);
+ bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING);
+ if (lootAllowed && creature->IsWithinDistInMap(player, INTERACTION_DISTANCE))
+ {
+ loot = &creature->loot;
+ if (creature->IsAlive())
+ shareMoney = false;
+ }
+ else
+ player->SendLootError(guid, lootAllowed ? LOOT_ERROR_TOO_FAR : LOOT_ERROR_DIDNT_KILL);
+ break;
+ }
+ default:
+ continue; // unlootable type
}
- default:
- return; // unlootable type
- }
- if (loot)
- {
+ if (!loot)
+ continue;
+
loot->NotifyMoneyRemoved();
if (shareMoney && player->GetGroup()) //item, pickpocket and players can be looted only single player
{
@@ -223,14 +238,59 @@ void WorldSession::HandleLootMoneyOpcode(WorldPackets::Loot::LootMoney& /*packet
}
}
+class AELootCreatureCheck
+{
+public:
+ static float constexpr LootDistance = 30.0f;
+
+ AELootCreatureCheck(Player* looter, ObjectGuid mainLootTarget) : _looter(looter), _mainLootTarget(mainLootTarget) { }
+
+ bool operator()(Creature* creature) const
+ {
+ if (creature->IsAlive())
+ return false;
+
+ if (creature->GetGUID() == _mainLootTarget)
+ return false;
+
+ if (!_looter->IsWithinDist(creature, LootDistance))
+ return false;
+
+ return _looter->isAllowedToLoot(creature);
+ }
+
+ Player* _looter;
+ ObjectGuid _mainLootTarget;
+};
+
void WorldSession::HandleLootOpcode(WorldPackets::Loot::LootUnit& packet)
{
// Check possible cheat
if (!GetPlayer()->IsAlive() || !packet.Unit.IsCreatureOrVehicle())
return;
+ std::list<Creature*> corpses;
+ AELootCreatureCheck check(_player, packet.Unit);
+ Trinity::CreatureListSearcher<AELootCreatureCheck> searcher(_player, corpses, check);
+ _player->VisitNearbyGridObject(AELootCreatureCheck::LootDistance, searcher);
+
+ if (!corpses.empty())
+ SendPacket(WorldPackets::Loot::AELootTargets(uint32(corpses.size() + 1)).Write());
+
GetPlayer()->SendLoot(packet.Unit, LOOT_CORPSE);
+ if (!corpses.empty())
+ {
+ // main target
+ SendPacket(WorldPackets::Loot::AELootTargetsAck().Write());
+
+ for (Creature* creature : corpses)
+ {
+ GetPlayer()->SendLoot(creature->GetGUID(), LOOT_CORPSE, true);
+ SendPacket(WorldPackets::Loot::AELootTargetsAck().Write());
+ }
+ }
+
// interrupt cast
if (GetPlayer()->IsNonMeleeSpellCast(false))
GetPlayer()->InterruptNonMeleeSpells(false);
@@ -240,10 +300,8 @@ void WorldSession::HandleLootReleaseOpcode(WorldPackets::Loot::LootRelease& pack
{
// cheaters can modify lguid to prevent correct apply loot release code and re-loot
// use internal stored guid
- ObjectGuid lguid = GetPlayer()->GetLootGUID();
- if (!lguid.IsEmpty())
- if (lguid == packet.Unit)
- DoLootRelease(lguid);
+ if (GetPlayer()->HasLootWorldObjectGUID(packet.Unit))
+ DoLootRelease(packet.Unit);
}
void WorldSession::DoLootRelease(ObjectGuid lguid)
@@ -251,7 +309,8 @@ void WorldSession::DoLootRelease(ObjectGuid lguid)
Player *player = GetPlayer();
Loot *loot;
- player->SetLootGUID(ObjectGuid::Empty);
+ if (player->GetLootGUID() == lguid)
+ player->SetLootGUID(ObjectGuid::Empty);
player->SendLootRelease(lguid);
player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING);
@@ -382,6 +441,14 @@ void WorldSession::DoLootRelease(ObjectGuid lguid)
//Player is not looking at loot list, he doesn't need to see updates on the loot list
loot->RemoveLooter(player->GetGUID());
+ player->RemoveAELootedObject(loot->GetGUID());
+}
+
+void WorldSession::DoLootReleaseAll()
+{
+ std::unordered_map<ObjectGuid, ObjectGuid> lootView = _player->GetAELootView();
+ for (std::pair<ObjectGuid, ObjectGuid> lootPair : lootView)
+ DoLootRelease(lootPair.second);
}
void WorldSession::HandleLootMasterGiveOpcode(WorldPacket& recvData)
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index 499f11e0d75..1ec1564d9ed 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -247,9 +247,8 @@ void WorldSession::HandleWhoOpcode(WorldPackets::Who::WhoRequestPkt& whoRequest)
void WorldSession::HandleLogoutRequestOpcode(WorldPackets::Character::LogoutRequest& /*logoutRequest*/)
{
- ObjectGuid lguid = GetPlayer()->GetLootGUID();
- if (!lguid.IsEmpty())
- DoLootRelease(lguid);
+ if (!GetPlayer()->GetLootGUID().IsEmpty())
+ GetPlayer()->SendLootReleaseAll();
bool instantLogout = (GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) && !GetPlayer()->IsInCombat()) ||
GetPlayer()->IsInFlight() || HasPermission(rbac::RBAC_PERM_INSTANT_LOGOUT);
diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp
index e4a9ef76a1e..0bde9c54458 100644
--- a/src/server/game/Loot/LootMgr.cpp
+++ b/src/server/game/Loot/LootMgr.cpp
@@ -649,7 +649,7 @@ void Loot::NotifyItemRemoved(uint8 lootIndex)
i_next = i;
++i_next;
if (Player* player = ObjectAccessor::FindPlayer(*i))
- player->SendNotifyLootItemRemoved(player->GetLootGUID(), GetGUID(), lootIndex);
+ player->SendNotifyLootItemRemoved(GetGUID(), lootIndex);
else
PlayersLooting.erase(i);
}
@@ -696,7 +696,7 @@ void Loot::NotifyQuestItemRemoved(uint8 questIndex)
break;
if (j < pql.size())
- player->SendNotifyLootItemRemoved(player->GetLootGUID(), GetGUID(), items.size()+j);
+ player->SendNotifyLootItemRemoved(GetGUID(), items.size()+j);
}
}
else
@@ -1884,3 +1884,29 @@ void LoadLootTables()
LoadLootTemplates_Reference();
}
+
+void AELootResult::Add(Item* item, uint8 count, LootType lootType)
+{
+ auto itr = _byItem.find(item);
+ if (itr != _byItem.end())
+ _byOrder[itr->second].count += count;
+ else
+ {
+ _byItem[item] = _byOrder.size();
+ ResultValue value;
+ value.item = item;
+ value.count = count;
+ value.lootType = lootType;
+ _byOrder.push_back(value);
+ }
+}
+
+AELootResult::OrderedStorage::const_iterator AELootResult::begin() const
+{
+ return _byOrder.begin();
+}
+
+AELootResult::OrderedStorage::const_iterator AELootResult::end() const
+{
+ return _byOrder.end();
+}
diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h
index e64661aa337..c1c9a65854b 100644
--- a/src/server/game/Loot/LootMgr.h
+++ b/src/server/game/Loot/LootMgr.h
@@ -29,6 +29,8 @@
#include <vector>
#include <list>
+class Item;
+
namespace WorldPackets
{
namespace Loot
@@ -413,6 +415,27 @@ private:
uint32 _difficultyBonusTreeMod;
};
+class TC_GAME_API AELootResult
+{
+public:
+ struct ResultValue
+ {
+ Item* item;
+ uint8 count;
+ LootType lootType;
+ };
+
+ typedef std::vector<ResultValue> OrderedStorage;
+
+ void Add(Item* item, uint8 count, LootType lootType);
+
+ OrderedStorage::const_iterator begin() const;
+ OrderedStorage::const_iterator end() const;
+
+ OrderedStorage _byOrder;
+ std::unordered_map<Item*, OrderedStorage::size_type> _byItem;
+};
+
TC_GAME_API extern LootStore LootTemplates_Creature;
TC_GAME_API extern LootStore LootTemplates_Fishing;
TC_GAME_API extern LootStore LootTemplates_Gameobject;
diff --git a/src/server/game/Server/Packets/LootPackets.cpp b/src/server/game/Server/Packets/LootPackets.cpp
index ff61790e2fe..685a2d7dbc3 100644
--- a/src/server/game/Server/Packets/LootPackets.cpp
+++ b/src/server/game/Server/Packets/LootPackets.cpp
@@ -37,8 +37,8 @@ void WorldPackets::Loot::LootUnit::Read()
WorldPacket const* WorldPackets::Loot::LootResponse::Write()
{
- _worldPacket << LootObj;
_worldPacket << Owner;
+ _worldPacket << LootObj;
_worldPacket << uint8(FailureReason);
_worldPacket << uint8(AcquireReason);
_worldPacket << uint8(_LootMethod);
@@ -200,3 +200,10 @@ WorldPacket const* WorldPackets::Loot::LootRollsComplete::Write()
return &_worldPacket;
}
+
+WorldPacket const* WorldPackets::Loot::AELootTargets::Write()
+{
+ _worldPacket << uint32(Count);
+
+ return &_worldPacket;
+}
diff --git a/src/server/game/Server/Packets/LootPackets.h b/src/server/game/Server/Packets/LootPackets.h
index 9074b58bab1..0507b62e48b 100644
--- a/src/server/game/Server/Packets/LootPackets.h
+++ b/src/server/game/Server/Packets/LootPackets.h
@@ -167,6 +167,14 @@ namespace WorldPackets
ObjectGuid Owner;
};
+ class LootReleaseAll final : public ServerPacket
+ {
+ public:
+ LootReleaseAll() : ServerPacket(SMSG_LOOT_RELEASE_ALL, 0) { }
+
+ WorldPacket const* Write() override { return &_worldPacket; }
+ };
+
class LootList final : public ServerPacket
{
public:
@@ -255,6 +263,24 @@ namespace WorldPackets
ObjectGuid LootObj;
uint8 LootListID = 0;
};
+
+ class AELootTargets : public ServerPacket
+ {
+ public:
+ AELootTargets(uint32 count) : ServerPacket(SMSG_AE_LOOT_TARGETS, 4), Count(count) { }
+
+ WorldPacket const* Write() override;
+
+ uint32 Count;
+ };
+
+ class AELootTargetsAck : public ServerPacket
+ {
+ public:
+ AELootTargetsAck() : ServerPacket(SMSG_AE_LOOT_TARGET_ACK, 0) { }
+
+ WorldPacket const* Write() override { return &_worldPacket; }
+ };
}
}
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index 74d59b96e78..3bf5d8ed66a 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -848,8 +848,8 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ADD_LOSS_OF_CONTROL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ADD_RUNE_POWER, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ADJUST_SPLINE_DURATION, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_AE_LOOT_TARGETS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_AE_LOOT_TARGET_ACK, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_AE_LOOT_TARGETS, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_AE_LOOT_TARGET_ACK, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_AI_REACTION, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ALL_ACCOUNT_CRITERIA, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ALL_ACHIEVEMENT_DATA, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
@@ -1326,7 +1326,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_LOOT_LIST, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_LOOT_MONEY_NOTIFY, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_LOOT_RELEASE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_LOOT_RELEASE_ALL, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_LOOT_RELEASE_ALL, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_LOOT_REMOVED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_LOOT_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_LOOT_ROLL, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index 72750f97ade..b61971c1f45 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -510,9 +510,8 @@ void WorldSession::LogoutPlayer(bool save)
if (_player)
{
- ObjectGuid lguid = _player->GetLootGUID();
- if (!lguid.IsEmpty())
- DoLootRelease(lguid);
+ if (!_player->GetLootGUID().IsEmpty())
+ DoLootReleaseAll();
///- If the player just died before logging out, make him appear as a ghost
if (_player->GetDeathTimer())
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 44c133db4c1..4526d756d8b 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -1077,6 +1077,7 @@ class TC_GAME_API WorldSession
void SendPetitionShowList(ObjectGuid guid);
void DoLootRelease(ObjectGuid lguid);
+ void DoLootReleaseAll();
// Account mute time
time_t m_muteTime;