From 7bba4bdd7908173617f1042d53e05dfacc2e5bb8 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Fri, 2 Sep 2022 14:14:04 -0400 Subject: [PATCH] Core/Loot: Allocate Loot separately from objects Port From (https://github.com/TrinityCore/TrinityCore/commit/7957e2d380e08fa831765f610c0e29d2f3e11a04) --- Source/Game/Chat/Commands/NPCCommands.cs | 2 +- Source/Game/Entities/Corpse.cs | 6 +- .../Game/Entities/Creature/Creature.Fields.cs | 2 +- Source/Game/Entities/Creature/Creature.cs | 16 +-- Source/Game/Entities/GameObject/GameObject.cs | 14 +-- Source/Game/Entities/Item/Item.cs | 8 +- Source/Game/Entities/Object/WorldObject.cs | 4 +- Source/Game/Entities/Player/Player.Items.cs | 70 ++++++----- Source/Game/Entities/Player/Player.cs | 4 +- Source/Game/Entities/Unit/Unit.Combat.cs | 24 ++-- Source/Game/Groups/Group.cs | 8 +- Source/Game/Handlers/LootHandler.cs | 119 +++++++++++------- Source/Game/Loot/Loot.cs | 4 +- Source/Game/Loot/LootItemStorage.cs | 2 +- Source/Game/Spells/Spell.cs | 3 +- 15 files changed, 160 insertions(+), 126 deletions(-) diff --git a/Source/Game/Chat/Commands/NPCCommands.cs b/Source/Game/Chat/Commands/NPCCommands.cs index 5c6281d6c..1c87d6ca4 100644 --- a/Source/Game/Chat/Commands/NPCCommands.cs +++ b/Source/Game/Chat/Commands/NPCCommands.cs @@ -325,7 +325,7 @@ namespace Game.Chat } Loot loot = creatureTarget.loot; - if (!creatureTarget.IsDead() || loot.Empty()) + if (!creatureTarget.IsDead() || loot == null || loot.Empty()) { handler.SendSysMessage(CypherStrings.CommandNotDeadOrNoLoot, creatureTarget.GetName()); return false; diff --git a/Source/Game/Entities/Corpse.cs b/Source/Game/Entities/Corpse.cs index f8a36a072..4829fb46d 100644 --- a/Source/Game/Entities/Corpse.cs +++ b/Source/Game/Entities/Corpse.cs @@ -38,7 +38,7 @@ namespace Game.Entities m_updateFlag.Stationary = true; - m_corpseData = new CorpseData(); + m_corpseData = new(); m_time = GameTime.GetGameTime(); } @@ -323,9 +323,11 @@ namespace Game.Entities public CellCoord GetCellCoord() { return _cellCoord; } public void SetCellCoord(CellCoord cellCoord) { _cellCoord = cellCoord; } + public override Loot GetLootForPlayer(Player player) { return loot; } + public CorpseData m_corpseData; - public Loot loot = new(); + public Loot loot; public Player lootRecipient; CorpseType m_type; diff --git a/Source/Game/Entities/Creature/Creature.Fields.cs b/Source/Game/Entities/Creature/Creature.Fields.cs index 987b5cfcf..2b03f5d79 100644 --- a/Source/Game/Entities/Creature/Creature.Fields.cs +++ b/Source/Game/Entities/Creature/Creature.Fields.cs @@ -90,7 +90,7 @@ namespace Game.Entities // vendor items List m_vendorItemCounts = new(); - public Loot loot = new(); + public Loot loot; public uint m_groupLootTimer; // (msecs)timer used for group loot public ObjectGuid lootingGroupLowGUID; // used to find group which is looting corpse ObjectGuid m_lootRecipient; diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index 221b64b28..4f6181d3f 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -160,7 +160,7 @@ namespace Game.Entities SetDeathState(DeathState.Dead); RemoveAllAuras(); //DestroyForNearbyPlayers(); // old UpdateObjectVisibility() - loot.Clear(); + loot = null; uint respawnDelay = m_respawnDelay; CreatureAI ai = GetAI(); if (ai != null) @@ -256,10 +256,6 @@ namespace Game.Entities if (cInfo == null) 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 @@ -511,7 +507,7 @@ namespace Game.Entities if (IsEngaged()) AIUpdateTick(diff); - if (m_groupLootTimer != 0 && !lootingGroupLowGUID.IsEmpty()) + if (loot != null && m_groupLootTimer != 0 && !lootingGroupLowGUID.IsEmpty()) { if (m_groupLootTimer <= diff) { @@ -1941,7 +1937,7 @@ namespace Game.Entities Log.outDebug(LogFilter.Unit, "Respawning creature {0} ({1})", GetName(), GetGUID().ToString()); m_respawnTime = 0; ResetPickPocketRefillTimer(); - loot.Clear(); + loot = null; if (m_originalEntry != GetEntry()) UpdateEntry(m_originalEntry); @@ -2578,7 +2574,7 @@ namespace Game.Entities public void AllLootRemovedFromCorpse() { - if (loot.loot_type != LootType.Skinning && !IsPet() && GetCreatureTemplate().SkinLootId != 0 && HasLootRecipient()) + if ((loot == null || loot.loot_type != LootType.Skinning) && !IsPet() && GetCreatureTemplate().SkinLootId != 0 && HasLootRecipient()) if (LootStorage.Skinning.HaveLootFor(GetCreatureTemplate().SkinLootId)) SetUnitFlag(UnitFlags.Skinnable); @@ -2591,7 +2587,7 @@ namespace Game.Entities float decayRate = m_ignoreCorpseDecayRatio ? 1.0f : WorldConfig.GetFloatValue(WorldCfg.RateCorpseDecayLooted); // corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update - if (loot.loot_type == LootType.Skinning) + if (loot != null && loot.loot_type == LootType.Skinning) m_corpseRemoveTime = now; else m_corpseRemoveTime = now + (uint)(m_corpseDelay * decayRate); @@ -3280,8 +3276,6 @@ namespace Game.Entities // checked at creature_template loading 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; diff --git a/Source/Game/Entities/GameObject/GameObject.cs b/Source/Game/Entities/GameObject/GameObject.cs index 12099f86d..b7515f9c1 100644 --- a/Source/Game/Entities/GameObject/GameObject.cs +++ b/Source/Game/Entities/GameObject/GameObject.cs @@ -373,10 +373,6 @@ namespace Game.Entities LastUsedScriptID = GetGoInfo().ScriptId; AIM_Initialize(); - // Initialize loot duplicate count depending on raid difficulty - if (map.Is25ManRaid()) - loot.maxDuplicates = 3; - if (spawnid != 0) m_spawnId = spawnid; @@ -701,7 +697,7 @@ namespace Game.Entities } break; case GameObjectTypes.Chest: - if (m_groupLootTimer != 0) + if (loot != null && m_groupLootTimer != 0) { if (m_groupLootTimer <= diff) { @@ -805,7 +801,7 @@ namespace Game.Entities return; } - loot.Clear(); + loot = null; // 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() != null || GetSpellId() != 0) && m_respawnTime == 0; @@ -3314,7 +3310,9 @@ namespace Game.Entities uint GetUniqueUseCount() { return (uint)m_unique_users.Count; } bool HasLootRecipient() { return !m_lootRecipient.IsEmpty() || !m_lootRecipientGroup.IsEmpty(); } - + + public override Loot GetLootForPlayer(Player player) { return loot; } + public override uint GetLevelForTarget(WorldObject target) { Unit owner = GetOwner(); @@ -3441,7 +3439,7 @@ namespace Game.Entities Dictionary ChairListSlots = new(); List m_SkillupList = new(); - public Loot loot = new(); + public Loot loot; public GameObjectModel m_model; diff --git a/Source/Game/Entities/Item/Item.cs b/Source/Game/Entities/Item/Item.cs index 77dc2e53b..37edb6c9b 100644 --- a/Source/Game/Entities/Item/Item.cs +++ b/Source/Game/Entities/Item/Item.cs @@ -43,8 +43,6 @@ namespace Game.Entities uState = ItemUpdateState.New; uQueuePos = -1; m_lastPlayedTimeUpdate = GameTime.GetGameTime(); - - loot = new Loot(); } public virtual bool Create(ulong guidlow, uint itemId, ItemContext context, Player owner) @@ -386,7 +384,7 @@ namespace Game.Entities } // Delete the items if this is a container - if (!loot.IsLooted()) + if (loot != null && !loot.IsLooted()) Global.LootItemStorage.RemoveStoredLootForContainer(GetGUID().GetCounter()); Dispose(); @@ -689,7 +687,7 @@ namespace Game.Entities DeleteFromDB(trans, GetGUID().GetCounter()); // Delete the items if this is a container - if (!loot.IsLooted()) + if (loot != null && !loot.IsLooted()) Global.LootItemStorage.RemoveStoredLootForContainer(GetGUID().GetCounter()); } @@ -2667,6 +2665,8 @@ namespace Game.Entities public void SetChildItem(ObjectGuid childItem) { m_childItem = childItem; } public ItemEffectRecord[] GetEffects() { return _bonusData.Effects[0.._bonusData.EffectCount]; } + + public override Loot GetLootForPlayer(Player player) { return loot; } //Static public static bool ItemCanGoIntoBag(ItemTemplate pProto, ItemTemplate pBagProto) diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index 0920fdbba..dc8ca5573 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -20,6 +20,7 @@ using Framework.Dynamic; using Game.AI; using Game.BattleFields; using Game.DataStorage; +using Game.Loots; using Game.Maps; using Game.Movement; using Game.Networking; @@ -28,7 +29,6 @@ using Game.Scenarios; using Game.Spells; using System; using System.Collections.Generic; -using System.Linq; using System.Numerics; namespace Game.Entities @@ -779,6 +779,8 @@ namespace Game.Entities return $"{base.GetDebugInfo()}\n{GetGUID()} Entry: {GetEntry()}\nName: { GetName()}"; } + public virtual Loot GetLootForPlayer(Player player) { return null; } + public abstract void BuildValuesCreate(WorldPacket data, Player target); public abstract void BuildValuesUpdate(WorldPacket data, Player target); diff --git a/Source/Game/Entities/Player/Player.Items.cs b/Source/Game/Entities/Player/Player.Items.cs index 78a4d2b72..a7018a149 100644 --- a/Source/Game/Entities/Player/Player.Items.cs +++ b/Source/Game/Entities/Player/Player.Items.cs @@ -6105,9 +6105,23 @@ namespace Game.Entities // Now we must make bones lootable, and send player loot bones.SetCorpseDynamicFlag(CorpseDynFlags.Lootable); - // We store the level of our player in the gold field - // We retrieve this information at Player.SendLoot() - bones.loot.gold = GetLevel(); + bones.loot = new Loot(); + bones.loot.SetGUID(ObjectGuid.Create(HighGuid.LootObject, GetMapId(), 0, GetMap().GenerateLowGuid(HighGuid.LootObject))); + + // For AV Achievement + Battleground bg = GetBattleground(); + if (bg != null) + { + if (bg.GetTypeID(true) == BattlegroundTypeId.AV) + bones.loot.FillLoot(1, LootStorage.Creature, this, true); + } + // For wintergrasp Quests + else if (GetZoneId() == (uint)AreaId.Wintergrasp) + bones.loot.FillLoot(1, LootStorage.Creature, this, true); + + // It may need a better formula + // Now it works like this: lvl10: ~6copper, lvl70: ~9silver + bones.loot.gold = (uint)(RandomHelper.URand(50, 150) * 0.016f * Math.Pow((float)GetLevel() / 5.76f, 2.5f) * WorldConfig.GetFloatValue(WorldCfg.RateDropMoney)); bones.lootRecipient = looterPlr; looterPlr.SendLoot(bones.GetGUID(), LootType.Insignia); } @@ -6168,12 +6182,12 @@ namespace Game.Entities 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() == LootState.Activated && !go.loot.IsLooted() && !go.GetMap().Instanceable() && go.GetLootGenerationTime() + go.GetRespawnDelay() < GameTime.GetGameTime()) + if (go.IsSpawnedByDefault() && go.GetLootState() == LootState.Activated && (loot == null || loot.IsLooted()) && !go.GetMap().Instanceable() && go.GetLootGenerationTime() + go.GetRespawnDelay() < GameTime.GetGameTime()) go.SetLootState(LootState.Ready); if (go.GetLootState() == LootState.Ready) @@ -6189,6 +6203,13 @@ namespace Game.Entities } } + 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.loot = loot; + if (lootid != 0) { loot.Clear(); @@ -6204,7 +6225,7 @@ namespace Game.Entities go.SetLootGenerationTime(); // get next RR player (for next loot) - if (groupRules) + if (groupRules && !loot.Empty()) group.UpdateLooterGuid(go); } @@ -6277,14 +6298,16 @@ namespace Game.Entities permission = PermissionTypes.Owner; - 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 && !Global.LootItemStorage.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.loot = loot; switch (loot_type) { @@ -6320,29 +6343,9 @@ namespace Game.Entities return; } - loot = bones.loot; + loot = bones.GetLootForPlayer(this); - if (loot.loot_type == LootType.None) - { - uint pLevel = bones.loot.gold; - bones.loot.Clear(); - - // For AV Achievement - Battleground bg = GetBattleground(); - if (bg) - { - if (bg.GetTypeID(true) == BattlegroundTypeId.AV) - loot.FillLoot(SharedConst.PlayerCorpseLootEntry, LootStorage.Creature, this, true); - } - else if (GetZoneId() == (uint)AreaId.Wintergrasp) - loot.FillLoot(SharedConst.PlayerCorpseLootEntry, LootStorage.Creature, this, true); - - // It may need a better formula - // Now it works like this: lvl10: ~6copper, lvl70: ~9silver - bones.loot.gold = (uint)(RandomHelper.URand(50, 150) * 0.016f * Math.Pow(pLevel / 5.76f, 2.5f) * WorldConfig.GetFloatValue(WorldCfg.RateDropMoney)); - } - - if (bones.lootRecipient != this) + if (bones.lootRecipient != null && bones.lootRecipient != this) permission = PermissionTypes.None; else permission = PermissionTypes.Owner; @@ -6364,7 +6367,7 @@ namespace Game.Entities return; } - loot = creature.loot; + loot = creature.GetLootForPlayer(this); if (loot_type == LootType.Pickpocketing) { @@ -6491,7 +6494,8 @@ namespace Game.Entities } // need know merged fishing/corpse loot type for achievements - loot.loot_type = loot_type; + if (loot != null) + loot.loot_type = loot_type; if (permission != PermissionTypes.None) { @@ -6532,7 +6536,7 @@ namespace Game.Entities SetUnitFlag(UnitFlags.Looting); } else - SendLootError(loot.GetGUID(), guid, LootError.DidntKill); + SendLootError(loot != null ? loot.GetGUID() : ObjectGuid.Empty, guid, LootError.DidntKill); } public void SendLootError(ObjectGuid lootObj, ObjectGuid owner, LootError error) diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index cbc77c287..d368db6ea 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -3085,8 +3085,8 @@ namespace Game.Entities if (HasPendingBind()) return false; - Loot loot = creature.loot; - if (loot.IsLooted()) // nothing to loot or everything looted. + Loot loot = creature.GetLootForPlayer(this); + if (loot == null || 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/Source/Game/Entities/Unit/Unit.Combat.cs b/Source/Game/Entities/Unit/Unit.Combat.cs index 2fce6f825..51bb67662 100644 --- a/Source/Game/Entities/Unit/Unit.Combat.cs +++ b/Source/Game/Entities/Unit/Unit.Combat.cs @@ -772,23 +772,16 @@ namespace Game.Entities } } else - { player.SendPacket(partyKillLog); - if (creature != null) - { - LootList lootList = new(); - lootList.Owner = creature.GetGUID(); - lootList.LootObj = creature.loot.GetGUID(); - player.SendMessageToSet(lootList, true); - } - } - if (creature) { + creature.loot = new Loot(); Loot loot = creature.loot; + loot.SetGUID(ObjectGuid.Create(HighGuid.LootObject, creature.GetMapId(), 0, creature.GetMap().GenerateLowGuid(HighGuid.LootObject))); + if (creature.GetMap().Is25ManRaid()) + loot.maxDuplicates = 3; - loot.Clear(); uint lootid = creature.GetCreatureTemplate().LootId; if (lootid != 0) loot.FillLoot(lootid, LootStorage.Creature, looter, false, false, creature.GetLootMode(), creature.GetMap().GetDifficultyLootItemContext()); @@ -807,6 +800,13 @@ namespace Game.Entities if (!loot.Empty()) group.UpdateLooterGuid(creature); } + else + { + LootList lootList = new(); + lootList.Owner = creature.GetGUID(); + lootList.LootObj = creature.loot.GetGUID(); + player.SendMessageToSet(lootList, true); + } } player.RewardPlayerAndGroupAtKill(victim, false); @@ -907,7 +907,7 @@ namespace Game.Entities if (!creature.IsPet()) { // must be after setDeathState which resets dynamic flags - if (!creature.loot.IsLooted()) + if (creature.loot != null && !creature.loot.IsLooted()) creature.SetDynamicFlag(UnitDynFlags.Lootable); else creature.AllLootRemovedFromCorpse(); diff --git a/Source/Game/Groups/Group.cs b/Source/Game/Groups/Group.cs index 37273443c..1731cc063 100644 --- a/Source/Game/Groups/Group.cs +++ b/Source/Game/Groups/Group.cs @@ -112,7 +112,7 @@ namespace Game.Groups leader.SetPlayerFlag(PlayerFlags.GroupLeader); if (IsBGGroup() || IsBFGroup()) - { + { m_groupFlags = GroupFlags.MaskBgRaid; m_groupCategory = GroupCategory.Instance; } @@ -992,7 +992,7 @@ namespace Game.Groups return true; } - + public void GroupLoot(Loot loot, WorldObject lootedObject) { byte itemSlot = 0; @@ -2525,7 +2525,7 @@ namespace Game.Groups { return GetMemberFlags(guid).HasAnyFlag(GroupMemberFlags.Assistant); } - + public ObjectGuid GetMemberGUID(string name) { foreach (var member in m_memberSlots) @@ -2778,7 +2778,7 @@ namespace Game.Groups RaidMarker[] m_markers = new RaidMarker[MapConst.RaidMarkersCount]; uint m_activeMarkers; - public static implicit operator bool (Group group) + public static implicit operator bool(Group group) { return group != null; } diff --git a/Source/Game/Handlers/LootHandler.cs b/Source/Game/Handlers/LootHandler.cs index a49f6fdb0..19dece6c7 100644 --- a/Source/Game/Handlers/LootHandler.cs +++ b/Source/Game/Handlers/LootHandler.cs @@ -53,7 +53,7 @@ namespace Game continue; } - loot = go.loot; + loot = go.GetLootForPlayer(player); } else if (lguid.IsItem()) { @@ -65,7 +65,7 @@ namespace Game continue; } - loot = pItem.loot; + loot = pItem.GetLootForPlayer(player); } else if (lguid.IsCorpse()) { @@ -76,20 +76,35 @@ namespace Game continue; } - loot = bones.loot; + loot = bones.GetLootForPlayer(player); } else { Creature creature = player.GetMap().GetCreature(lguid); - - bool lootAllowed = creature && creature.IsAlive() == (player.GetClass() == Class.Rogue && creature.loot.loot_type == LootType.Pickpocketing); - if (!lootAllowed || !creature.IsWithinDistInMap(player, AELootCreatureCheck.LootDistance)) + if (creature == null) { - player.SendLootError(req.Object, lguid, lootAllowed ? LootError.TooFar : LootError.DidntKill); + player.SendLootError(req.Object, lguid, LootError.NoLoot); continue; } - loot = creature.loot; + if (!creature.IsWithinDistInMap(player, AELootCreatureCheck.LootDistance)) + { + player.SendLootError(req.Object, lguid, LootError.TooFar); + continue; + } + + loot = creature.GetLootForPlayer(player); + if (creature.IsAlive() != (loot != null && loot.loot_type == LootType.Pickpocketing)) + { + player.SendLootError(req.Object, lguid, LootError.DidntKill); + continue; + } + } + + if (loot == null) + { + player.SendLootRelease(lguid); + continue; } player.StoreLootItem(lguid, (byte)(req.LootListID - 1), loot, aeResult); @@ -132,9 +147,9 @@ namespace Game // 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; + break; } case HighGuid.Corpse: // remove insignia ONLY in BG { @@ -142,8 +157,8 @@ namespace Game if (bones && bones.IsWithinDistInMap(player, SharedConst.InteractionDistance)) { - loot = bones.loot; - shareMoney = false; + loot = bones.GetLootForPlayer(player); + shareMoney = false; } break; @@ -153,26 +168,38 @@ namespace Game Item item = player.GetItemByGuid(guid); if (item) { - loot = item.loot; - shareMoney = false; + loot = item.GetLootForPlayer(player); + shareMoney = false; } break; } case HighGuid.Creature: case HighGuid.Vehicle: + { + Creature creature = player.GetMap().GetCreature(guid); + if (creature == null) { - Creature creature = player.GetMap().GetCreature(guid); - bool lootAllowed = creature && creature.IsAlive() == (player.GetClass() == Class.Rogue && creature.loot.loot_type == LootType.Pickpocketing); - if (lootAllowed && creature.IsWithinDistInMap(player, AELootCreatureCheck.LootDistance)) - { - loot = creature.loot; - if (creature.IsAlive()) - shareMoney = false; - } - else - player.SendLootError(lootView.Key, guid, lootAllowed ? LootError.TooFar : LootError.DidntKill); - break; + player.SendLootError(lootView.Key, guid, LootError.NoLoot); + continue; } + + if (!creature.IsWithinDistInMap(player, AELootCreatureCheck.LootDistance)) + { + player.SendLootError(lootView.Key, guid, LootError.TooFar); + continue; + } + + loot = creature.GetLootForPlayer(player); + if (creature.IsAlive() != (loot != null && loot.loot_type == LootType.Pickpocketing)) + { + player.SendLootError(lootView.Key, guid, LootError.DidntKill); + continue; + } + + if (loot != null && loot.loot_type != LootType.Corpse) + shareMoney = false; + break; + } default: continue; // unlootable type } @@ -336,7 +363,7 @@ namespace Game if (!go || ((go.GetOwnerGUID() != player.GetGUID() && go.GetGoType() != GameObjectTypes.FishingHole) && !go.IsWithinDistInMap(player))) return; - loot = go.loot; + loot = go.GetLootForPlayer(player); if (go.GetGoType() == GameObjectTypes.Door) { @@ -374,9 +401,9 @@ namespace Game if (!corpse || !corpse.IsWithinDistInMap(player, SharedConst.InteractionDistance)) return; - loot = corpse.loot; + loot = corpse.GetLootForPlayer(player); - if (loot.IsLooted()) + if (loot != null && loot.IsLooted()) { loot.Clear(); corpse.RemoveCorpseDynamicFlag(CorpseDynFlags.Lootable); @@ -390,11 +417,13 @@ namespace Game ItemTemplate proto = pItem.GetTemplate(); + loot = pItem.GetLootForPlayer(player); + // destroy only 5 items from stack in case prospecting and milling - if (pItem.loot.loot_type == LootType.Prospecting || pItem.loot.loot_type == LootType.Milling) + if (loot != null && (loot.loot_type == LootType.Prospecting || loot.loot_type == LootType.Milling)) { pItem.m_lootGenerated = false; - pItem.loot.Clear(); + pItem.loot = null; uint count = pItem.GetCount(); @@ -406,7 +435,8 @@ namespace Game } else { - if (pItem.loot.IsLooted() || !proto.HasFlag(ItemFlags.HasLoot)) // Only delete item if no loot or money (unlooted loot is saved to db) + // Only delete item if no loot or money (unlooted loot is saved to db) or if it isn't an openable item + if ((loot != null && loot.IsLooted()) || !proto.HasFlag(ItemFlags.HasLoot)) player.DestroyItem(pItem.GetBagSlot(), pItem.GetSlot(), true); } return; // item can be looted only single player @@ -414,21 +444,23 @@ namespace Game else { Creature creature = player.GetMap().GetCreature(lguid); - - bool lootAllowed = creature && creature.IsAlive() == (player.GetClass() == Class.Rogue && creature.loot.loot_type == LootType.Pickpocketing); - if (!lootAllowed || !creature.IsWithinDistInMap(player, AELootCreatureCheck.LootDistance)) + if (creature == null) return; - loot = creature.loot; - if (loot.IsLooted()) + if (!creature.IsWithinDistInMap(player, AELootCreatureCheck.LootDistance)) + return; + + loot = creature.GetLootForPlayer(player); + if (creature.IsAlive() != (loot != null && loot.loot_type == LootType.Pickpocketing)) + return; + + if (loot == null || loot.IsLooted()) { creature.RemoveDynamicFlag(UnitDynFlags.Lootable); // skip pickpocketing loot for speed, skinning timer reduction is no-op in fact if (!creature.IsAlive()) creature.AllLootRemovedFromCorpse(); - - loot.Clear(); } else { @@ -451,7 +483,8 @@ namespace Game } //Player is not looking at loot list, he doesn't need to see updates on the loot list - loot.RemoveLooter(player.GetGUID()); + if (loot != null) + loot.RemoveLooter(player.GetGUID()); } public void DoLootReleaseAll() @@ -498,15 +531,15 @@ namespace Game 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 == null) diff --git a/Source/Game/Loot/Loot.cs b/Source/Game/Loot/Loot.cs index 0c7974e63..9bdf0710e 100644 --- a/Source/Game/Loot/Loot.cs +++ b/Source/Game/Loot/Loot.cs @@ -165,9 +165,9 @@ namespace Game.Loots public class Loot { - public Loot(uint _gold = 0) + public Loot() { - gold = _gold; + gold = 0; unlootedCount = 0; loot_type = LootType.None; maxDuplicates = 1; diff --git a/Source/Game/Loot/LootItemStorage.cs b/Source/Game/Loot/LootItemStorage.cs index 7e06dc229..19d78310c 100644 --- a/Source/Game/Loot/LootItemStorage.cs +++ b/Source/Game/Loot/LootItemStorage.cs @@ -99,7 +99,7 @@ namespace Game.Loots public bool LoadStoredLoot(Item item, Player player) { - Loot loot = item.loot; + Loot loot = item.GetLootForPlayer(player); if (!_lootItemStorage.ContainsKey(item.GetGUID().GetCounter())) return false; diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index e088ac8b1..3e013ca3e 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -5187,7 +5187,8 @@ namespace Game.Spells return SpellCastResult.TargetUnskinnable; Creature creature = m_targets.GetUnitTarget().ToCreature(); - if (!creature.IsCritter() && !creature.loot.IsLooted()) + Loot loot = creature.GetLootForPlayer(m_caster.ToPlayer()); + if (loot != null && !loot.IsLooted()) return SpellCastResult.TargetNotLooted; SkillType skill = creature.GetCreatureTemplate().GetRequiredLootSkill();