diff options
author | Shauren <shauren.trinity@gmail.com> | 2022-09-01 20:07:58 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2022-09-01 20:07:58 +0200 |
commit | 7957e2d380e08fa831765f610c0e29d2f3e11a04 (patch) | |
tree | 55632f7f94edb3096360c983abbdd448d8dc79d7 /src/server/game | |
parent | 62e5b52d2b91832889f02edc7bbd83ad474923e3 (diff) |
Core/Loot: Allocate Loot separately from objects
Diffstat (limited to 'src/server/game')
22 files changed, 169 insertions, 122 deletions
diff --git a/src/server/game/Entities/Corpse/Corpse.cpp b/src/server/game/Entities/Corpse/Corpse.cpp index c0e267c6b28..e5085e73849 100644 --- a/src/server/game/Entities/Corpse/Corpse.cpp +++ b/src/server/game/Entities/Corpse/Corpse.cpp @@ -22,6 +22,7 @@ #include "DB2Stores.h" #include "GameTime.h" #include "Log.h" +#include "Loot.h" #include "Map.h" #include "PhasingHandler.h" #include "Player.h" diff --git a/src/server/game/Entities/Corpse/Corpse.h b/src/server/game/Entities/Corpse/Corpse.h index 5d65e858ae3..b54742c3e9c 100644 --- a/src/server/game/Entities/Corpse/Corpse.h +++ b/src/server/game/Entities/Corpse/Corpse.h @@ -23,7 +23,8 @@ #include "DatabaseEnvFwd.h" #include "GridDefines.h" #include "IteratorPair.h" -#include "Loot.h" + +struct Loot; enum CorpseType { @@ -124,7 +125,9 @@ class TC_GAME_API Corpse : public WorldObject, public GridObject<Corpse> CellCoord const& GetCellCoord() const { return _cellCoord; } void SetCellCoord(CellCoord const& cellCoord) { _cellCoord = cellCoord; } - Loot loot; // remove insignia ONLY at BG + std::unique_ptr<Loot> m_loot; + Loot* GetLootForPlayer(Player const* /*player*/) const override { return m_loot.get(); } + Player* lootRecipient; bool IsExpired(time_t t) const; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index e0bd742ca5c..ea00b2c8e6d 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -304,7 +304,7 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) return true; } -Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), m_groupLootTimer(0), m_PlayerDamageReq(0), _pickpocketLootRestore(0), +Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), m_PlayerDamageReq(0), _pickpocketLootRestore(0), m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_ignoreCorpseDecayRatio(false), m_wanderDistance(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(UI64LIT(0)), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_cannotReachTarget(false), m_cannotReachTimer(0), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), _waypointPathId(0), _currentWaypointNodeInfo(0, 0), @@ -325,6 +325,8 @@ Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), m_grou m_isTempWorldObject = false; } +Creature::~Creature() = default; + void Creature::AddToWorld() { ///- Register the creature for guid lookup @@ -421,7 +423,7 @@ void Creature::RemoveCorpse(bool setSpawnTime, bool destroyForNearbyPlayers) m_corpseRemoveTime = GameTime::GetGameTime(); setDeathState(DEAD); RemoveAllAuras(); - loot.clear(); + m_loot = nullptr; uint32 respawnDelay = m_respawnDelay; if (CreatureAI* ai = AI()) ai->CorpseRemoved(respawnDelay); @@ -516,10 +518,6 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/) if (!cinfo) cinfo = normalInfo; - // Initialize loot duplicate count depending on raid difficulty - if (GetMap()->Is25ManRaid()) - loot.maxDuplicates = 3; - SetEntry(entry); // normal entry always m_creatureInfo = cinfo; // map mode related always @@ -777,12 +775,12 @@ void Creature::Update(uint32 diff) if (IsEngaged()) Unit::AIUpdateTick(diff); - if (m_groupLootTimer && !lootingGroupLowGUID.IsEmpty()) + if (m_loot && m_groupLootTimer && !lootingGroupLowGUID.IsEmpty()) { if (m_groupLootTimer <= diff) { if (Group* group = sGroupMgr->GetGroupByGUID(lootingGroupLowGUID)) - group->EndRoll(&loot, GetMap()); + group->EndRoll(m_loot.get(), GetMap()); m_groupLootTimer = 0; lootingGroupLowGUID.Clear(); @@ -1817,8 +1815,6 @@ bool Creature::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, // checked at creature_template loading m_defaultMovementType = MovementGeneratorType(data->movementType); - loot.SetGUID(ObjectGuid::Create<HighGuid::LootObject>(GetMapId(), data->id, GetMap()->GenerateLowGuid<HighGuid::LootObject>())); - if (addToMap && !GetMap()->AddToMap(this)) return false; return true; @@ -2209,7 +2205,7 @@ void Creature::Respawn(bool force) TC_LOG_DEBUG("entities.unit", "Respawning creature %s (%s)", GetName().c_str(), GetGUID().ToString().c_str()); m_respawnTime = 0; ResetPickPocketRefillTimer(); - loot.clear(); + m_loot = nullptr; if (m_originalEntry != GetEntry()) UpdateEntry(m_originalEntry); @@ -2849,7 +2845,7 @@ void Creature::RefreshCanSwimFlag(bool recheck) void Creature::AllLootRemovedFromCorpse() { - if (loot.loot_type != LOOT_SKINNING && !IsPet() && GetCreatureTemplate()->SkinLootId && hasLootRecipient()) + if ((!m_loot || m_loot->loot_type != LOOT_SKINNING) && !IsPet() && GetCreatureTemplate()->SkinLootId && hasLootRecipient()) if (LootTemplates_Skinning.HaveLootFor(GetCreatureTemplate()->SkinLootId)) SetUnitFlag(UNIT_FLAG_SKINNABLE); @@ -2862,7 +2858,7 @@ 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 (loot.loot_type == LOOT_SKINNING) + if (m_loot && m_loot->loot_type == LOOT_SKINNING) 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 0f5e4882ced..6caabee9ad0 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -23,7 +23,6 @@ #include "CreatureData.h" #include "DatabaseEnvFwd.h" #include "Duration.h" -#include "Loot.h" #include "GridObject.h" #include "MapObject.h" #include <list> @@ -35,6 +34,7 @@ class Quest; class Player; class SpellInfo; class WorldSession; +struct Loot; enum MovementGeneratorType : uint8; @@ -70,6 +70,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma { public: explicit Creature(bool isWorldObject = false); + ~Creature(); void AddToWorld() override; void RemoveFromWorld() override; @@ -223,7 +224,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma virtual void SaveToDB(uint32 mapid, std::vector<Difficulty> const& spawnDifficulties); static bool DeleteFromDB(ObjectGuid::LowType spawnId); - Loot loot; + std::unique_ptr<Loot> m_loot; void StartPickPocketRefillTimer(); void ResetPickPocketRefillTimer() { _pickpocketLootRestore = 0; } bool CanGeneratePickPocketLoot() const; @@ -232,6 +233,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma Group* GetLootRecipientGroup() const; bool hasLootRecipient() const { return !m_lootRecipient.IsEmpty() || !m_lootRecipientGroup.IsEmpty(); } 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(); } void SetLootRecipient (Unit* unit, bool withGroup = true); void AllLootRemovedFromCorpse(); diff --git a/src/server/game/Entities/Creature/Trainer.cpp b/src/server/game/Entities/Creature/Trainer.cpp index 11c7bfc57f5..24a75bfd1d3 100644 --- a/src/server/game/Entities/Creature/Trainer.cpp +++ b/src/server/game/Entities/Creature/Trainer.cpp @@ -17,6 +17,7 @@ #include "Trainer.h" #include "BattlePetMgr.h" +#include "ConditionMgr.h" #include "Creature.h" #include "Log.h" #include "NPCPackets.h" diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index a3c199a4a1a..c789fc6f64b 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -825,10 +825,6 @@ bool GameObject::Create(uint32 entry, Map* map, Position const& pos, QuaternionD LastUsedScriptID = GetGOInfo()->ScriptId; AIM_Initialize(); - // Initialize loot duplicate count depending on raid difficulty - if (map->Is25ManRaid()) - loot.maxDuplicates = 3; - if (spawnid) m_spawnId = spawnid; @@ -1177,12 +1173,12 @@ void GameObject::Update(uint32 diff) } break; case GAMEOBJECT_TYPE_CHEST: - if (m_groupLootTimer) + if (m_loot && m_groupLootTimer) { if (m_groupLootTimer <= diff) { if (Group* group = sGroupMgr->GetGroupByGUID(lootingGroupLowGUID)) - group->EndRoll(&loot, GetMap()); + group->EndRoll(m_loot.get(), GetMap()); m_groupLootTimer = 0; lootingGroupLowGUID.Clear(); @@ -1269,7 +1265,7 @@ void GameObject::Update(uint32 diff) return; } - loot.clear(); + m_loot = nullptr; // Do not delete chests or goobers that are not consumed on loot, while still allowing them to despawn when they expire if summoned bool isSummonedAndExpired = (GetOwner() || GetSpellId()) && m_respawnTime == 0; diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index e345f9a147d..133c1c4e460 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -21,7 +21,6 @@ #include "Object.h" #include "GridObject.h" #include "GameObjectData.h" -#include "Loot.h" #include "MapObject.h" #include "SharedDefines.h" @@ -33,6 +32,7 @@ class OPvPCapturePoint; class Transport; class TransportBase; class Unit; +struct Loot; struct TransportAnimation; enum TriggerCastFlags : uint32; @@ -279,13 +279,14 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> void SaveRespawnTime(uint32 forceDelay = 0); - Loot loot; + std::unique_ptr<Loot> m_loot; Player* GetLootRecipient() const; Group* GetLootRecipientGroup() const; void SetLootRecipient(Unit* unit, Group* group = nullptr); bool IsLootAllowedFor(Player const* player) const; bool HasLootRecipient() const { return !m_lootRecipient.IsEmpty() || !m_lootRecipientGroup.IsEmpty(); } + Loot* GetLootForPlayer(Player const* /*player*/) const override { return m_loot.get(); } uint32 m_groupLootTimer; // (msecs)timer used for group loot ObjectGuid lootingGroupLowGUID; // used to find group which is looting diff --git a/src/server/game/Entities/Item/AzeriteItem/AzeriteItem.cpp b/src/server/game/Entities/Item/AzeriteItem/AzeriteItem.cpp index 21662ee9d96..ae86e0ab97e 100644 --- a/src/server/game/Entities/Item/AzeriteItem/AzeriteItem.cpp +++ b/src/server/game/Entities/Item/AzeriteItem/AzeriteItem.cpp @@ -17,6 +17,7 @@ #include "AzeriteItem.h" #include "AzeritePackets.h" +#include "ConditionMgr.h" #include "DatabaseEnv.h" #include "DB2Stores.h" #include "GameObject.h" diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index d52519fa164..464706d5538 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -31,6 +31,7 @@ #include "ItemEnchantmentMgr.h" #include "ItemPackets.h" #include "Log.h" +#include "Loot.h" #include "LootItemStorage.h" #include "LootMgr.h" #include "Map.h" @@ -805,7 +806,7 @@ void Item::SaveToDB(CharacterDatabaseTransaction trans) CharacterDatabase.CommitTransaction(trans); // Delete the items if this is a container - if (!loot.isLooted()) + if (m_loot && !m_loot->isLooted()) sLootItemStorage->RemoveStoredLootForContainer(GetGUID().GetCounter()); delete this; @@ -1117,7 +1118,7 @@ void Item::DeleteFromDB(CharacterDatabaseTransaction trans) DeleteFromDB(trans, GetGUID().GetCounter()); // Delete the items if this is a container - if (!loot.isLooted()) + if (m_loot && !m_loot->isLooted()) sLootItemStorage->RemoveStoredLootForContainer(GetGUID().GetCounter()); } diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index 8e7aebc7660..6a778e0fe65 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -25,11 +25,11 @@ #include "ItemEnchantmentMgr.h" #include "ItemTemplate.h" #include "IteratorPair.h" -#include "Loot.h" class SpellInfo; class Bag; class Unit; +struct Loot; namespace WorldPackets { namespace Item @@ -313,8 +313,9 @@ class TC_GAME_API Item : public Object int32 GetSpellCharges(uint8 index/*0..5*/ = 0) const { return m_itemData->SpellCharges[index]; } void SetSpellCharges(uint8 index/*0..5*/, int32 value) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::SpellCharges, index), value); } - Loot loot; + std::unique_ptr<Loot> m_loot; bool m_lootGenerated; + Loot* GetLootForPlayer(Player const* /*player*/) const override { return m_loot.get(); } // Update States ItemUpdateState GetState() const { return uState; } diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 46b3f7b8a03..85a5c829498 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -61,6 +61,7 @@ class WorldObject; class WorldPacket; class ZoneScript; struct FactionTemplateEntry; +struct Loot; struct PositionFullTerrainStatus; struct QuaternionData; enum ZLiquidStatus : uint32; @@ -259,6 +260,8 @@ class TC_GAME_API Object virtual std::string GetDebugInfo() const; + virtual Loot* GetLootForPlayer([[maybe_unused]] Player const* player) const { return nullptr; } + protected: Object(); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index c645129b356..5c97a5a5261 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -8755,9 +8755,22 @@ void Player::RemovedInsignia(Player* looterPlr) // Now we must make bones lootable, and send player loot bones->SetCorpseDynamicFlag(CORPSE_DYNFLAG_LOOTABLE); - // We store the level of our player in the gold field - // We retrieve this information at Player::SendLoot() - bones->loot.gold = GetLevel(); + bones->m_loot.reset(new Loot()); + bones->m_loot->SetGUID(ObjectGuid::Create<HighGuid::LootObject>(GetMapId(), 0, GetMap()->GenerateLowGuid<HighGuid::LootObject>())); + + // For AV Achievement + if (Battleground* bg = GetBattleground()) + { + if (bg->GetTypeID(true) == BATTLEGROUND_AV) + bones->m_loot->FillLoot(PLAYER_CORPSE_LOOT_ENTRY, LootTemplates_Creature, this, true); + } + // For wintergrasp Quests + else if (GetZoneId() == AREA_WINTERGRASP) + bones->m_loot->FillLoot(PLAYER_CORPSE_LOOT_ENTRY, LootTemplates_Creature, this, true); + + // It may need a better formula + // Now it works like this: lvl10: ~6copper, lvl70: ~9silver + bones->m_loot->gold = uint32(urand(50, 150) * 0.016f * std::pow(float(GetLevel()) / 5.76f, 2.5f) * sWorld->getRate(RATE_DROP_MONEY)); bones->lootRecipient = looterPlr; looterPlr->SendLoot(bones->GetGUID(), LOOT_INSIGNIA); } @@ -8819,12 +8832,12 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa return; } - loot = &go->loot; + loot = go->GetLootForPlayer(this); // loot was generated and respawntime has passed since then, allow to recreate loot // to avoid bugs, this rule covers spawned gameobjects only // Don't allow to regenerate chest loot inside instances and raids, to avoid exploits with duplicate boss loot being given for some encounters - if (go->isSpawnedByDefault() && go->getLootState() == GO_ACTIVATED && !go->loot.isLooted() && !go->GetMap()->Instanceable() && go->GetLootGenerationTime() + go->GetRespawnDelay() < GameTime::GetGameTime()) + if (go->isSpawnedByDefault() && go->getLootState() == GO_ACTIVATED && (!loot || !loot->isLooted()) && !go->GetMap()->Instanceable() && go->GetLootGenerationTime() + go->GetRespawnDelay() < GameTime::GetGameTime()) go->SetLootState(GO_READY); if (go->getLootState() == GO_READY) @@ -8837,6 +8850,13 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa return; } + loot = new Loot(); + loot->SetGUID(ObjectGuid::Create<HighGuid::LootObject>(go->GetMapId(), 0, go->GetMap()->GenerateLowGuid<HighGuid::LootObject>())); + if (go->GetMap()->Is25ManRaid()) + loot->maxDuplicates = 3; + + go->m_loot.reset(loot); + if (lootid) { loot->clear(); @@ -8852,7 +8872,7 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa go->SetLootGenerationTime(); // get next RR player (for next loot) - if (groupRules && !go->loot.empty()) + if (groupRules && !loot->empty()) group->UpdateLooterGuid(go); } @@ -8920,14 +8940,16 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa permission = OWNER_PERMISSION; - loot = &item->loot; + loot = item->GetLootForPlayer(this); // If item doesn't already have loot, attempt to load it. If that // fails then this is first time opening, generate loot if (!item->m_lootGenerated && !sLootItemStorage->LoadStoredLoot(item, this)) { item->m_lootGenerated = true; - loot->clear(); + loot = new Loot(); + loot->SetGUID(ObjectGuid::Create<HighGuid::LootObject>(GetMapId(), 0, GetMap()->GenerateLowGuid<HighGuid::LootObject>())); + item->m_loot.reset(loot); switch (loot_type) { @@ -8963,29 +8985,9 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa return; } - loot = &bones->loot; - - if (loot->loot_type == LOOT_NONE) - { - uint32 pLevel = bones->loot.gold; - bones->loot.clear(); - - // For AV Achievement - if (Battleground* bg = GetBattleground()) - { - if (bg->GetTypeID(true) == BATTLEGROUND_AV) - loot->FillLoot(PLAYER_CORPSE_LOOT_ENTRY, LootTemplates_Creature, this, true); - } - // For wintergrasp Quests - else if (GetZoneId() == AREA_WINTERGRASP) - loot->FillLoot(PLAYER_CORPSE_LOOT_ENTRY, LootTemplates_Creature, this, true); - - // It may need a better formula - // Now it works like this: lvl10: ~6copper, lvl70: ~9silver - bones->loot.gold = uint32(urand(50, 150) * 0.016f * std::pow(float(pLevel) / 5.76f, 2.5f) * sWorld->getRate(RATE_DROP_MONEY)); - } + loot = bones->GetLootForPlayer(this); - if (bones->lootRecipient != this) + if (bones->lootRecipient && bones->lootRecipient != this) permission = NONE_PERMISSION; else permission = OWNER_PERMISSION; @@ -9007,7 +9009,7 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa return; } - loot = &creature->loot; + loot = creature->GetLootForPlayer(this); if (loot_type == LOOT_PICKPOCKETING) { @@ -9129,7 +9131,8 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa } // need know merged fishing/corpse loot type for achievements - loot->loot_type = loot_type; + if (loot) + loot->loot_type = loot_type; if (permission != NONE_PERMISSION) { @@ -9167,7 +9170,7 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa SetUnitFlag(UNIT_FLAG_LOOTING); } else - SendLootError(loot->GetGUID(), guid, LOOT_ERROR_DIDNT_KILL); + SendLootError(loot ? loot->GetGUID() : ObjectGuid::Empty, guid, LOOT_ERROR_DIDNT_KILL); } void Player::SendLootError(ObjectGuid const& lootObj, ObjectGuid const& owner, LootError error) const @@ -18186,8 +18189,8 @@ bool Player::isAllowedToLoot(const Creature* creature) const if (HasPendingBind()) return false; - Loot const* loot = &creature->loot; - if (loot->isLooted()) // nothing to loot or everything looted. + Loot const* loot = creature->GetLootForPlayer(this); + if (!loot || loot->isLooted()) // nothing to loot or everything looted. return false; if (!loot->hasItemForAll() && !loot->hasItemFor(this)) // no loot in creature for this player return false; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 65827ede11b..993295b668f 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -10658,24 +10658,17 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId) } } else - { player->SendDirectMessage(partyKillLog.Write()); - if (creature) - { - WorldPackets::Loot::LootList lootList; - lootList.Owner = creature->GetGUID(); - lootList.LootObj = creature->loot.GetGUID(); - - player->SendMessageToSet(lootList.Write(), true); - } - } - // Generate loot before updating looter if (creature) { - Loot* loot = &creature->loot; - loot->clear(); + creature->m_loot.reset(new Loot()); + Loot* loot = creature->m_loot.get(); + loot->SetGUID(ObjectGuid::Create<HighGuid::LootObject>(creature->GetMapId(), 0, creature->GetMap()->GenerateLowGuid<HighGuid::LootObject>())); + if (creature->GetMap()->Is25ManRaid()) + loot->maxDuplicates = 3; + if (uint32 lootid = creature->GetCreatureTemplate()->lootid) loot->FillLoot(lootid, LootTemplates_Creature, looter, false, false, creature->GetLootMode(), creature->GetMap()->GetDifficultyLootItemContext()); @@ -10693,6 +10686,13 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId) if (!loot->empty()) group->UpdateLooterGuid(creature); } + else + { + WorldPackets::Loot::LootList lootList; + lootList.Owner = creature->GetGUID(); + lootList.LootObj = creature->m_loot->GetGUID(); + player->SendMessageToSet(lootList.Write(), true); + } } player->RewardPlayerAndGroupAtKill(victim, false); @@ -10785,7 +10785,7 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId) if (!creature->IsPet()) { // must be after setDeathState which resets dynamic flags - if (!creature->loot.isLooted()) + if (creature->m_loot && !creature->m_loot->isLooted()) creature->SetDynamicFlag(UNIT_DYNFLAG_LOOTABLE); else creature->AllLootRemovedFromCorpse(); diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 272b21c5366..e371053dfac 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -1024,9 +1024,9 @@ void Group::SendLooter(Creature* creature, Player* groupLooter) WorldPackets::Loot::LootList lootList; lootList.Owner = creature->GetGUID(); - lootList.LootObj = creature->loot.GetGUID(); + lootList.LootObj = creature->m_loot->GetGUID(); - if (GetLootMethod() == MASTER_LOOT && creature->loot.hasOverThresholdItem()) + if (GetLootMethod() == MASTER_LOOT && creature->m_loot->hasOverThresholdItem()) lootList.Master = GetMasterLooterGuid(); if (groupLooter) diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index 615f574d08e..2d161ab7694 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -78,13 +78,13 @@ void WorldSession::HandleAutostoreLootItemOpcode(WorldPackets::Loot::LootItem& p GameObject* go = player->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO - if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player))) + if (!go || ((go->GetOwnerGUID() != player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(player))) { player->SendLootRelease(lguid); continue; } - loot = &go->loot; + loot = go->GetLootForPlayer(player); } else if (lguid.IsItem()) { @@ -96,7 +96,7 @@ void WorldSession::HandleAutostoreLootItemOpcode(WorldPackets::Loot::LootItem& p continue; } - loot = &pItem->loot; + loot = pItem->GetLootForPlayer(player); } else if (lguid.IsCorpse()) { @@ -107,20 +107,35 @@ void WorldSession::HandleAutostoreLootItemOpcode(WorldPackets::Loot::LootItem& p continue; } - loot = &bones->loot; + loot = bones->GetLootForPlayer(player); } else { Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid); + if (!creature) + { + player->SendLootError(req.Object, lguid, LOOT_ERROR_NO_LOOT); + continue; + } - bool lootAllowed = creature && creature->IsAlive() == (player->GetClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING); - if (!lootAllowed || !creature->IsWithinDistInMap(_player, AELootCreatureCheck::LootDistance)) + if (!creature->IsWithinDistInMap(player, AELootCreatureCheck::LootDistance)) { - player->SendLootError(req.Object, lguid, lootAllowed ? LOOT_ERROR_TOO_FAR : LOOT_ERROR_DIDNT_KILL); + player->SendLootError(req.Object, lguid, LOOT_ERROR_TOO_FAR); continue; } - loot = &creature->loot; + loot = creature->GetLootForPlayer(player); + if (creature->IsAlive() != (loot && loot->loot_type == LOOT_PICKPOCKETING)) + { + player->SendLootError(req.Object, lguid, LOOT_ERROR_DIDNT_KILL); + continue; + } + } + + if (!loot) + { + player->SendLootRelease(lguid); + continue; } player->StoreLootItem(lguid, req.LootListID - 1, loot, aeResultPtr); @@ -161,7 +176,7 @@ void WorldSession::HandleLootMoneyOpcode(WorldPackets::Loot::LootMoney& /*packet // 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)))) - loot = &go->loot; + loot = go->GetLootForPlayer(player); break; } @@ -171,7 +186,7 @@ void WorldSession::HandleLootMoneyOpcode(WorldPackets::Loot::LootMoney& /*packet if (bones && bones->IsWithinDistInMap(player, INTERACTION_DISTANCE)) { - loot = &bones->loot; + loot = bones->GetLootForPlayer(player); shareMoney = false; } @@ -181,7 +196,7 @@ void WorldSession::HandleLootMoneyOpcode(WorldPackets::Loot::LootMoney& /*packet { if (Item* item = player->GetItemByGuid(guid)) { - loot = &item->loot; + loot = item->GetLootForPlayer(player); shareMoney = false; } break; @@ -190,15 +205,27 @@ void WorldSession::HandleLootMoneyOpcode(WorldPackets::Loot::LootMoney& /*packet 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, AELootCreatureCheck::LootDistance)) + if (!creature) { - loot = &creature->loot; - if (creature->IsAlive()) - shareMoney = false; + player->SendLootError(lootView.first, guid, LOOT_ERROR_NO_LOOT); + continue; } - else - player->SendLootError(lootView.first, lootView.second, lootAllowed ? LOOT_ERROR_TOO_FAR : LOOT_ERROR_DIDNT_KILL); + + if (!creature->IsWithinDistInMap(player, AELootCreatureCheck::LootDistance)) + { + player->SendLootError(lootView.first, guid, LOOT_ERROR_TOO_FAR); + continue; + } + + loot = creature->GetLootForPlayer(player); + if (creature->IsAlive() != (loot && loot->loot_type == LOOT_PICKPOCKETING)) + { + player->SendLootError(lootView.first, guid, LOOT_ERROR_DIDNT_KILL); + continue; + } + + if (loot && loot->loot_type != LOOT_CORPSE) + shareMoney = false; break; } default: @@ -330,10 +357,10 @@ void WorldSession::DoLootRelease(ObjectGuid lguid) GameObject* go = GetPlayer()->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO - if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player))) + if (!go || ((go->GetOwnerGUID() != player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(player))) return; - loot = &go->loot; + loot = go->GetLootForPlayer(player); if (go->GetGoType() == GAMEOBJECT_TYPE_DOOR) { @@ -368,12 +395,12 @@ void WorldSession::DoLootRelease(ObjectGuid lguid) else if (lguid.IsCorpse()) // ONLY remove insignia at BG { Corpse* corpse = ObjectAccessor::GetCorpse(*player, lguid); - if (!corpse || !corpse->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) + if (!corpse || !corpse->IsWithinDistInMap(player, INTERACTION_DISTANCE)) return; - loot = &corpse->loot; + loot = corpse->GetLootForPlayer(player); - if (loot->isLooted()) + if (loot && loot->isLooted()) { loot->clear(); corpse->RemoveCorpseDynamicFlag(CORPSE_DYNFLAG_LOOTABLE); @@ -387,11 +414,13 @@ void WorldSession::DoLootRelease(ObjectGuid lguid) ItemTemplate const* proto = pItem->GetTemplate(); + loot = pItem->GetLootForPlayer(player); + // destroy only 5 items from stack in case prospecting and milling - if (pItem->loot.loot_type == LOOT_PROSPECTING || pItem->loot.loot_type == LOOT_MILLING) + if (loot && (loot->loot_type == LOOT_PROSPECTING || loot->loot_type == LOOT_MILLING)) { pItem->m_lootGenerated = false; - pItem->loot.clear(); + pItem->m_loot.reset(); uint32 count = pItem->GetCount(); @@ -404,7 +433,7 @@ void WorldSession::DoLootRelease(ObjectGuid lguid) else { // Only delete item if no loot or money (unlooted loot is saved to db) or if it isn't an openable item - if (pItem->loot.isLooted() || !proto->HasFlag(ITEM_FLAG_HAS_LOOT)) + if ((loot && loot->isLooted()) || !proto->HasFlag(ITEM_FLAG_HAS_LOOT)) player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true); } return; // item can be looted only single player @@ -412,21 +441,23 @@ void WorldSession::DoLootRelease(ObjectGuid lguid) else { Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid); + if (!creature) + return; - bool lootAllowed = creature && creature->IsAlive() == (player->GetClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING); - if (!lootAllowed || !creature->IsWithinDistInMap(_player, AELootCreatureCheck::LootDistance)) + if (!creature->IsWithinDistInMap(player, AELootCreatureCheck::LootDistance)) return; - loot = &creature->loot; - if (loot->isLooted()) + loot = creature->GetLootForPlayer(player); + if (creature->IsAlive() != (loot && loot->loot_type == LOOT_PICKPOCKETING)) + return; + + if (!loot || loot->isLooted()) { creature->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE); // skip pickpocketing loot for speed, skinning timer reduction is no-op in fact if (!creature->IsAlive()) creature->AllLootRemovedFromCorpse(); - - loot->clear(); } else { @@ -444,7 +475,8 @@ 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()); + if (loot) + loot->RemoveLooter(player->GetGUID()); } void WorldSession::DoLootReleaseAll() @@ -492,15 +524,15 @@ void WorldSession::HandleLootMasterGiveOpcode(WorldPackets::Loot::MasterLootItem if (!creature) return; - loot = &creature->loot; + loot = creature->GetLootForPlayer(_player); } else if (GetPlayer()->GetLootGUID().IsGameObject()) { - GameObject* pGO = GetPlayer()->GetMap()->GetGameObject(lootguid); - if (!pGO) + GameObject* go = GetPlayer()->GetMap()->GetGameObject(lootguid); + if (!go) return; - loot = &pGO->loot; + loot = go->GetLootForPlayer(_player); } if (!loot) diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index a189a8022b1..824676b8967 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -26,6 +26,7 @@ #include "GuildMgr.h" #include "Item.h" #include "Log.h" +#include "Loot.h" #include "Map.h" #include "Player.h" #include "ObjectAccessor.h" diff --git a/src/server/game/Loot/Loot.cpp b/src/server/game/Loot/Loot.cpp index b05156c1ba9..7445d96dc71 100644 --- a/src/server/game/Loot/Loot.cpp +++ b/src/server/game/Loot/Loot.cpp @@ -125,7 +125,7 @@ void LootItem::AddAllowedLooter(const Player* player) // --------- Loot --------- // -Loot::Loot(uint32 _gold /*= 0*/) : gold(_gold), unlootedCount(0), roundRobinPlayer(), loot_type(LOOT_NONE), maxDuplicates(1), _itemContext(ItemContext::NONE) +Loot::Loot() : gold(0), unlootedCount(0), roundRobinPlayer(), loot_type(LOOT_NONE), maxDuplicates(1), _itemContext(ItemContext::NONE) { } diff --git a/src/server/game/Loot/Loot.h b/src/server/game/Loot/Loot.h index d5d1f771f08..ae96d14393d 100644 --- a/src/server/game/Loot/Loot.h +++ b/src/server/game/Loot/Loot.h @@ -220,7 +220,7 @@ struct TC_GAME_API Loot 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) - Loot(uint32 _gold = 0); + Loot(); ~Loot(); ObjectGuid const& GetGUID() const { return _GUID; } diff --git a/src/server/game/Loot/LootItemStorage.cpp b/src/server/game/Loot/LootItemStorage.cpp index e1cf342fd4d..69b68acde70 100644 --- a/src/server/game/Loot/LootItemStorage.cpp +++ b/src/server/game/Loot/LootItemStorage.cpp @@ -19,6 +19,7 @@ #include "Item.h" #include "ItemTemplate.h" #include "Log.h" +#include "Loot.h" #include "LootItemStorage.h" #include "LootMgr.h" #include "ObjectMgr.h" @@ -136,7 +137,7 @@ void LootItemStorage::LoadStorageFromDB() bool LootItemStorage::LoadStoredLoot(Item* item, Player* player) { - Loot* loot = &item->loot; + Loot* loot = item->GetLootForPlayer(player); StoredLootContainer const* container = nullptr; // read diff --git a/src/server/game/Mails/Mail.cpp b/src/server/game/Mails/Mail.cpp index dfdba81f51e..7f633f5c49c 100644 --- a/src/server/game/Mails/Mail.cpp +++ b/src/server/game/Mails/Mail.cpp @@ -24,6 +24,7 @@ #include "GameTime.h" #include "Item.h" #include "Log.h" +#include "Loot.h" #include "LootMgr.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" diff --git a/src/server/game/Server/Packets/ItemPacketsCommon.cpp b/src/server/game/Server/Packets/ItemPacketsCommon.cpp index 6c12a6dde1e..a022a0b3783 100644 --- a/src/server/game/Server/Packets/ItemPacketsCommon.cpp +++ b/src/server/game/Server/Packets/ItemPacketsCommon.cpp @@ -17,6 +17,7 @@ #include "ItemPacketsCommon.h" #include "Item.h" +#include "Loot.h" #include "Player.h" namespace WorldPackets diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index d461c4298f7..f49f0495339 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -37,6 +37,7 @@ #include "InstanceScript.h" #include "Item.h" #include "Log.h" +#include "Loot.h" #include "LootMgr.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" @@ -5996,7 +5997,8 @@ SpellCastResult Spell::CheckCast(bool strict, int32* param1 /*= nullptr*/, int32 return SPELL_FAILED_TARGET_UNSKINNABLE; Creature* creature = m_targets.GetUnitTarget()->ToCreature(); - if (!creature->IsCritter() && !creature->loot.isLooted()) + Loot* loot = creature->GetLootForPlayer(m_caster->ToPlayer()); + if (loot && !loot->isLooted()) return SPELL_FAILED_TARGET_NOT_LOOTED; uint32 skill = creature->GetCreatureTemplate()->GetRequiredLootSkill(); |