Core/Loot: Allocate Loot separately from objects

This commit is contained in:
Shauren
2022-09-01 20:07:58 +02:00
parent 62e5b52d2b
commit 7957e2d380
24 changed files with 188 additions and 139 deletions
@@ -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"
+5 -2
View File
@@ -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;
+9 -13
View File
@@ -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);
+4 -2
View File
@@ -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();
@@ -17,6 +17,7 @@
#include "Trainer.h"
#include "BattlePetMgr.h"
#include "ConditionMgr.h"
#include "Creature.h"
#include "Log.h"
#include "NPCPackets.h"
@@ -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;
@@ -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
@@ -17,6 +17,7 @@
#include "AzeriteItem.h"
#include "AzeritePackets.h"
#include "ConditionMgr.h"
#include "DatabaseEnv.h"
#include "DB2Stores.h"
#include "GameObject.h"
+3 -2
View File
@@ -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());
}
+3 -2
View File
@@ -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; }
+3
View File
@@ -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();
+38 -35
View File
@@ -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;
loot = bones->GetLootForPlayer(this);
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));
}
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;
+14 -14
View File
@@ -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();
+2 -2
View File
@@ -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)
+71 -39
View File
@@ -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);
bool lootAllowed = creature && creature->IsAlive() == (player->GetClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING);
if (!lootAllowed || !creature->IsWithinDistInMap(_player, AELootCreatureCheck::LootDistance))
if (!creature)
{
player->SendLootError(req.Object, lguid, lootAllowed ? LOOT_ERROR_TOO_FAR : LOOT_ERROR_DIDNT_KILL);
player->SendLootError(req.Object, lguid, LOOT_ERROR_NO_LOOT);
continue;
}
loot = &creature->loot;
if (!creature->IsWithinDistInMap(player, AELootCreatureCheck::LootDistance))
{
player->SendLootError(req.Object, lguid, LOOT_ERROR_TOO_FAR);
continue;
}
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);
bool lootAllowed = creature && creature->IsAlive() == (player->GetClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING);
if (!lootAllowed || !creature->IsWithinDistInMap(_player, AELootCreatureCheck::LootDistance))
if (!creature)
return;
loot = &creature->loot;
if (loot->isLooted())
if (!creature->IsWithinDistInMap(player, AELootCreatureCheck::LootDistance))
return;
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)
@@ -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"
+1 -1
View File
@@ -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)
{
}
+1 -1
View File
@@ -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; }
+2 -1
View File
@@ -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
+1
View File
@@ -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"
@@ -17,6 +17,7 @@
#include "ItemPacketsCommon.h"
#include "Item.h"
#include "Loot.h"
#include "Player.h"
namespace WorldPackets
+3 -1
View File
@@ -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();
+16 -15
View File
@@ -32,6 +32,7 @@ EndScriptData */
#include "FollowMovementGenerator.h"
#include "GameTime.h"
#include "Language.h"
#include "Loot.h"
#include "Map.h"
#include "MotionMaster.h"
#include "MovementDefines.h"
@@ -1198,8 +1199,8 @@ public:
return false;
}
Loot const& loot = creatureTarget->loot;
if (!creatureTarget->isDead() || loot.empty())
Loot const* loot = creatureTarget->m_loot.get();
if (!creatureTarget->isDead() || !loot || loot->empty())
{
handler->PSendSysMessage(LANG_COMMAND_NOT_DEAD_OR_NO_LOOT, creatureTarget->GetName().c_str());
handler->SetSentErrorMessage(true);
@@ -1207,43 +1208,43 @@ public:
}
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_HEADER, creatureTarget->GetName().c_str(), creatureTarget->GetEntry());
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_MONEY, loot.gold / GOLD, (loot.gold%GOLD) / SILVER, loot.gold%SILVER);
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_MONEY, loot->gold / GOLD, (loot->gold % GOLD) / SILVER, loot->gold % SILVER);
if (!all)
{
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL, "Standard items", loot.items.size());
for (LootItem const& item : loot.items)
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL, "Standard items", loot->items.size());
for (LootItem const& item : loot->items)
if (!item.is_looted)
_ShowLootEntry(handler, item.itemid, item.count);
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL, "Quest items", loot.quest_items.size());
for (LootItem const& item : loot.quest_items)
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL, "Quest items", loot->quest_items.size());
for (LootItem const& item : loot->quest_items)
if (!item.is_looted)
_ShowLootEntry(handler, item.itemid, item.count);
}
else
{
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL, "Standard items", loot.items.size());
for (LootItem const& item : loot.items)
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL, "Standard items", loot->items.size());
for (LootItem const& item : loot->items)
if (!item.is_looted && !item.freeforall && item.conditions.empty())
_ShowLootEntry(handler, item.itemid, item.count);
if (!loot.GetPlayerQuestItems().empty())
if (!loot->GetPlayerQuestItems().empty())
{
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "Per-player quest items");
_IterateNotNormalLootMap(handler, loot.GetPlayerQuestItems(), loot.quest_items);
_IterateNotNormalLootMap(handler, loot->GetPlayerQuestItems(), loot->quest_items);
}
if (!loot.GetPlayerFFAItems().empty())
if (!loot->GetPlayerFFAItems().empty())
{
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "FFA items per allowed player");
_IterateNotNormalLootMap(handler, loot.GetPlayerFFAItems(), loot.items);
_IterateNotNormalLootMap(handler, loot->GetPlayerFFAItems(), loot->items);
}
if (!loot.GetPlayerNonQuestNonFFAConditionalItems().empty())
if (!loot->GetPlayerNonQuestNonFFAConditionalItems().empty())
{
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "Per-player conditional items");
_IterateNotNormalLootMap(handler, loot.GetPlayerNonQuestNonFFAConditionalItems(), loot.items);
_IterateNotNormalLootMap(handler, loot->GetPlayerNonQuestNonFFAConditionalItems(), loot->items);
}
}
@@ -27,6 +27,7 @@ npc_maghar_captive
EndContentData */
#include "ScriptMgr.h"
#include "ConditionMgr.h"
#include "GameObjectAI.h"
#include "MotionMaster.h"
#include "Player.h"