From 6c7991c28fbdcf7ca0b452357bc47c0bc763b90e Mon Sep 17 00:00:00 2001 From: hondacrx Date: Sun, 18 Sep 2022 22:13:40 -0400 Subject: [PATCH] Core/Loot: Move loot generation out of Player::SendLoot Port From (https://github.com/TrinityCore/TrinityCore/commit/8c20f620d7b070b13b40803095e5fa97f9349512) --- Source/Game/Entities/GameObject/GameObject.cs | 68 +++++-- Source/Game/Entities/Player/Player.Items.cs | 185 +----------------- Source/Game/Handlers/LootHandler.cs | 41 ++-- Source/Game/Handlers/SpellHandler.cs | 23 ++- Source/Game/Loot/Loot.cs | 3 +- Source/Game/Spells/SpellEffects.cs | 95 ++++++++- 6 files changed, 185 insertions(+), 230 deletions(-) diff --git a/Source/Game/Entities/GameObject/GameObject.cs b/Source/Game/Entities/GameObject/GameObject.cs index a66c1c3db..866ba9a03 100644 --- a/Source/Game/Entities/GameObject/GameObject.cs +++ b/Source/Game/Entities/GameObject/GameObject.cs @@ -480,6 +480,8 @@ namespace Game.Entities // If there is no restock timer, or if the restock timer passed, the chest becomes ready to loot m_restockTime = 0; m_lootState = LootState.Ready; + loot = null; + m_personalLoot.Clear(); AddToObjectUpdateIfNeeded(); break; default: @@ -699,11 +701,16 @@ namespace Game.Entities case GameObjectTypes.Chest: loot?.Update(); + foreach (var (_, loot) in m_personalLoot) + loot.Update(); + // Non-consumable chest was partially looted and restock time passed, restock all loot now - if (GetGoInfo().Chest.consumable == 0 && GameTime.GetGameTime() >= m_restockTime) + if (GetGoInfo().Chest.consumable == 0 && GetGoInfo().Chest.chestRestockTime != 0 && GameTime.GetGameTime() >= m_restockTime) { m_restockTime = 0; m_lootState = LootState.Ready; + loot = null; + m_personalLoot.Clear(); AddToObjectUpdateIfNeeded(); } break; @@ -789,6 +796,7 @@ namespace Game.Entities } loot = null; + m_personalLoot.Clear(); // 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; @@ -940,44 +948,48 @@ namespace Game.Entities SendMessageToSet(packet, true); } - public void GetFishLoot(Loot fishloot, Player loot_owner) + public Loot GetFishLoot(Player lootOwner) { - fishloot.Clear(); - uint zone, subzone; uint defaultzone = 1; GetZoneAndAreaId(out zone, out subzone); + Loot fishLoot = new(GetMap(), GetGUID(), LootType.Fishing, null); + // if subzone loot exist use it - fishloot.FillLoot(subzone, LootStorage.Fishing, loot_owner, true, true); - if (fishloot.Empty()) + fishLoot.FillLoot(subzone, LootStorage.Fishing, lootOwner, true, true); + if (fishLoot.Empty()) { //subzone no result,use zone loot - fishloot.FillLoot(zone, LootStorage.Fishing, loot_owner, true); + fishLoot.FillLoot(zone, LootStorage.Fishing, lootOwner, true); //use zone 1 as default, somewhere fishing got nothing,becase subzone and zone not set, like Off the coast of Storm Peaks. - if (fishloot.Empty()) - fishloot.FillLoot(defaultzone, LootStorage.Fishing, loot_owner, true, true); + if (fishLoot.Empty()) + fishLoot.FillLoot(defaultzone, LootStorage.Fishing, lootOwner, true, true); } + + return fishLoot; } - public void GetFishLootJunk(Loot fishloot, Player loot_owner) + public Loot GetFishLootJunk(Player lootOwner) { - fishloot.Clear(); - uint zone, subzone; uint defaultzone = 1; GetZoneAndAreaId(out zone, out subzone); + Loot fishLoot = new(GetMap(), GetGUID(), LootType.FishingJunk, null); + // if subzone loot exist use it - fishloot.FillLoot(subzone, LootStorage.Fishing, loot_owner, true, true, LootModes.JunkFish); - if (fishloot.Empty()) //use this becase if zone or subzone has normal mask drop, then fishloot.FillLoot return true. + fishLoot.FillLoot(subzone, LootStorage.Fishing, lootOwner, true, true, LootModes.JunkFish); + if (fishLoot.Empty()) //use this becase if zone or subzone has normal mask drop, then fishloot.FillLoot return true. { //use zone loot - fishloot.FillLoot(zone, LootStorage.Fishing, loot_owner, true, true, LootModes.JunkFish); - if (fishloot.Empty()) + fishLoot.FillLoot(zone, LootStorage.Fishing, lootOwner, true, true, LootModes.JunkFish); + if (fishLoot.Empty()) //use zone 1 as default - fishloot.FillLoot(defaultzone, LootStorage.Fishing, loot_owner, true, true, LootModes.JunkFish); + fishLoot.FillLoot(defaultzone, LootStorage.Fishing, lootOwner, true, true, LootModes.JunkFish); } + + return fishLoot; } public void SaveToDB() @@ -1886,10 +1898,16 @@ namespace Game.Entities SetLootState(LootState.JustDeactivated); } else + { + loot = GetFishLoot(player); player.SendLoot(GetGUID(), LootType.Fishing); + } } else// If fishing skill is too low, send junk loot. + { + loot = GetFishLootJunk(player); player.SendLoot(GetGUID(), LootType.FishingJunk); + } break; } case LootState.JustDeactivated: // nothing to do, will be deleted at next update @@ -2099,6 +2117,10 @@ namespace Game.Entities Player player = user.ToPlayer(); + Loot loot = new Loot(GetMap(), GetGUID(), LootType.Fishinghole, null); + loot.FillLoot(GetGoInfo().GetLootId(), LootStorage.Gameobject, player, true); + m_personalLoot[player.GetGUID()] = loot; + player.SendLoot(GetGUID(), LootType.Fishinghole); player.UpdateCriteria(CriteriaType.CatchFishInFishingHole, GetGoInfo().entry); return; @@ -2814,7 +2836,16 @@ namespace Game.Entities return true; } + public override Loot GetLootForPlayer(Player player) + { + if (m_personalLoot.Empty()) + return loot; + + return m_personalLoot.LookupByKey(player.GetGUID()); + } + public void SetLinkedTrap(GameObject linkedTrap) { m_linkedTrap = linkedTrap.GetGUID(); } + public GameObject GetLinkedTrap() { return ObjectAccessor.GetGameObject(this, m_linkedTrap); @@ -3298,8 +3329,6 @@ namespace Game.Entities 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(); @@ -3425,6 +3454,7 @@ namespace Game.Entities List m_SkillupList = new(); public Loot loot; + Dictionary m_personalLoot = new(); public GameObjectModel m_model; diff --git a/Source/Game/Entities/Player/Player.Items.cs b/Source/Game/Entities/Player/Player.Items.cs index 254136815..d29af2a3a 100644 --- a/Source/Game/Entities/Player/Player.Items.cs +++ b/Source/Game/Entities/Player/Player.Items.cs @@ -6109,95 +6109,13 @@ namespace Game.Entities if (guid.IsGameObject()) { GameObject go = GetMap().GetGameObject(guid); - bool shouldLootRelease(GameObject go, LootType lootType) - { - // not check distance for GO in case owned GO (fishing bobber case, for example) - // And permit out of range GO with no owner in case fishing hole - if (!go) - return true; - - switch (lootType) - { - case LootType.Fishing: - case LootType.FishingJunk: - if (go.GetOwnerGUID() != GetGUID()) - return true; - break; - case LootType.Fishinghole: - break; - default: - if (!go.IsWithinDistInMap(this)) - return true; - break; - } - - return false; - } - - if (shouldLootRelease(go, loot_type)) + if (go == null) { SendLootRelease(guid); return; } 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 && (loot == null || loot.IsLooted()) && !go.GetMap().Instanceable() && go.GetLootGenerationTime() + go.GetRespawnDelay() < GameTime.GetGameTime()) - go.SetLootState(LootState.Ready); - - if (go.GetLootState() == LootState.Ready) - { - uint lootid = go.GetGoInfo().GetLootId(); - Battleground bg = GetBattleground(); - if (bg) - { - if (!bg.CanActivateGO((int)go.GetEntry(), (uint)bg.GetPlayerTeam(GetGUID()))) - { - SendLootRelease(guid); - return; - } - } - - Group group = GetGroup(); - bool groupRules = (group != null && go.GetGoInfo().type == GameObjectTypes.Chest && go.GetGoInfo().Chest.usegrouplootrules != 0); - - loot = new Loot(GetMap(), guid, loot_type, groupRules ? group : null); - if (go.GetMap().Is25ManRaid()) - loot.maxDuplicates = 3; - - go.loot = loot; - - if (lootid != 0) - { - // check current RR player and get next if necessary - if (groupRules) - group.UpdateLooterGuid(go, true); - - loot.FillLoot(lootid, LootStorage.Gameobject, this, !groupRules, false, go.GetLootMode(), GetMap().GetDifficultyLootItemContext()); - go.SetLootGenerationTime(); - - // get next RR player (for next loot) - if (groupRules && !loot.Empty()) - group.UpdateLooterGuid(go); - } - - if (go.GetLootMode() > 0) - { - GameObjectTemplateAddon addon = go.GetTemplateAddon(); - if (addon != null) - loot.GenerateMoneyLoot(addon.Mingold, addon.Maxgold); - } - - if (loot_type == LootType.Fishing) - go.GetFishLoot(loot, this); - else if (loot_type == LootType.FishingJunk) - go.GetFishLootJunk(loot, this); - - go.SetLootState(LootState.Activated, this); - } } else if (guid.IsItem()) { @@ -6210,44 +6128,11 @@ namespace Game.Entities } 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 = new Loot(GetMap(), guid, loot_type, null); - item.loot = loot; - - switch (loot_type) - { - case LootType.Disenchanting: - loot.FillLoot(item.GetDisenchantLoot(this).Id, LootStorage.Disenchant, this, true); - break; - case LootType.Prospecting: - loot.FillLoot(item.GetEntry(), LootStorage.Prospecting, this, true); - break; - case LootType.Milling: - loot.FillLoot(item.GetEntry(), LootStorage.Milling, this, true); - break; - default: - loot.GenerateMoneyLoot(item.GetTemplate().MinMoneyLoot, item.GetTemplate().MaxMoneyLoot); - loot.FillLoot(item.GetEntry(), LootStorage.Items, this, true, loot.gold != 0); - - // Force save the loot and money items that were just rolled - // Also saves the container item ID in Loot struct (not to DB) - if (loot.gold > 0 || loot.unlootedCount > 0) - Global.LootItemStorage.AddNewStoredLoot(item.GetGUID().GetCounter(), loot, this); - - break; - } - } } else if (guid.IsCorpse()) // remove insignia { Corpse bones = ObjectAccessor.GetCorpse(this, guid); - - if (bones == null || !(loot_type == LootType.Corpse || loot_type == LootType.Insignia) || bones.GetCorpseType() != CorpseType.Bones) + if (bones == null) { SendLootRelease(guid); return; @@ -6260,77 +6145,13 @@ namespace Game.Entities Creature creature = GetMap().GetCreature(guid); // must be in range and creature must be alive for pickpocket and must be dead for another loot - if (creature == null || creature.IsAlive() != (loot_type == LootType.Pickpocketing) || (!aeLooting && !creature.IsWithinDistInMap(this, SharedConst.InteractionDistance))) - { - SendLootRelease(guid); - return; - } - - if (loot_type == LootType.Pickpocketing && IsFriendlyTo(creature)) + if (creature == null) { SendLootRelease(guid); return; } loot = creature.GetLootForPlayer(this); - - if (loot_type == LootType.Pickpocketing) - { - if (loot == null || loot.loot_type != LootType.Pickpocketing) - { - if (creature.CanGeneratePickPocketLoot()) - { - creature.StartPickPocketRefillTimer(); - - loot = new Loot(GetMap(), creature.GetGUID(), LootType.Pickpocketing, null); - creature.loot = loot; - uint lootid = creature.GetCreatureTemplate().PickPocketId; - if (lootid != 0) - loot.FillLoot(lootid, LootStorage.Pickpocketing, this, true); - - // Generate extra money for pick pocket loot - uint a = RandomHelper.URand(0, creature.GetLevel() / 2); - uint b = RandomHelper.URand(0, GetLevel() / 2); - loot.gold = (uint)(10 * (a + b) * WorldConfig.GetFloatValue(WorldCfg.RateDropMoney)); - } - else - { - SendLootError(loot.GetGUID(), guid, LootError.AlreadPickPocketed); - return; - } - } - } - else - { - // exploit fix - if (!creature.HasDynamicFlag(UnitDynFlags.Lootable)) - { - SendLootError(loot.GetGUID(), guid, LootError.DidntKill); - return; - } - - // the player whose group may loot the corpse - Player recipient = creature.GetLootRecipient(); - Group recipientGroup = creature.GetLootRecipientGroup(); - if (!recipient && !recipientGroup) - { - SendLootError(loot.GetGUID(), guid, LootError.DidntKill); - return; - } - - if (loot.loot_type == LootType.Skinning) - { - loot_type = LootType.Skinning; - } - else if (loot_type == LootType.Skinning) - { - loot.Clear(); - loot.FillLoot(creature.GetCreatureTemplate().SkinLootId, LootStorage.Skinning, this, true); - - // Set new loot recipient - creature.SetLootRecipient(this, false); - } - } } diff --git a/Source/Game/Handlers/LootHandler.cs b/Source/Game/Handlers/LootHandler.cs index 8436d660f..b05bc353f 100644 --- a/Source/Game/Handlers/LootHandler.cs +++ b/Source/Game/Handlers/LootHandler.cs @@ -176,6 +176,11 @@ namespace Game } public bool Invoke(Creature creature) + { + return IsValidAELootTarget(creature); + } + + public bool IsValidLootTarget(Creature creature) { if (creature.IsAlive()) return false; @@ -189,6 +194,14 @@ namespace Game return _looter.IsAllowedToLoot(creature); } + bool IsValidAELootTarget(Creature creature) + { + if (creature.GetGUID() == _mainLootTarget) + return false; + + return IsValidLootTarget(creature); + } + Player _looter; ObjectGuid _mainLootTarget; } @@ -200,6 +213,14 @@ namespace Game if (!GetPlayer().IsAlive() || !packet.Unit.IsCreatureOrVehicle()) return; + Creature lootTarget = ObjectAccessor.GetCreature(GetPlayer(), packet.Unit); + if (!lootTarget) + return; + + AELootCreatureCheck check = new(_player, packet.Unit); + if (!check.IsValidLootTarget(lootTarget)) + return; + // interrupt cast if (GetPlayer().IsNonMeleeSpellCast(false)) GetPlayer().InterruptNonMeleeSpells(false); @@ -207,7 +228,6 @@ namespace Game GetPlayer().RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Looting); List corpses = new(); - AELootCreatureCheck check = new(_player, packet.Unit); CreatureListSearcher searcher = new(_player, corpses, check); Cell.VisitGridObjects(_player, searcher, AELootCreatureCheck.LootDistance); @@ -247,6 +267,8 @@ namespace Game if (player.GetLootGUID() == lguid) player.SetLootGUID(ObjectGuid.Empty); + //Player is not looking at loot list, he doesn't need to see updates on the loot list + loot.RemoveLooter(player.GetGUID()); player.SendLootRelease(lguid); player.GetAELootView().Remove(loot.GetGUID()); @@ -264,12 +286,7 @@ namespace Game if (!go || ((go.GetOwnerGUID() != player.GetGUID() && go.GetGoType() != GameObjectTypes.FishingHole) && !go.IsWithinDistInMap(player))) return; - if (go.GetGoType() == GameObjectTypes.Door) - { - // locked doors are opened with spelleffect openlock, prevent remove its as looted - go.UseDoorOrButton(); - } - else if (loot.IsLooted() || go.GetGoType() == GameObjectTypes.FishingNode) + if (loot.IsLooted() || go.GetGoType() == GameObjectTypes.FishingNode || go.GetGoType() == GameObjectTypes.FishingHole) { if (go.GetGoType() == GameObjectTypes.FishingHole) { // The fishing hole used once more @@ -288,10 +305,6 @@ namespace Game { // not fully looted object go.SetLootState(LootState.Activated, player); - - // if the round robin player release, reset it. - if (player.GetGUID() == loot.roundRobinPlayer) - loot.roundRobinPlayer.Clear(); } } else if (lguid.IsCorpse()) // ONLY remove insignia at BG @@ -342,12 +355,6 @@ namespace Game if (creature == null) return; - if (!creature.IsWithinDistInMap(player, AELootCreatureCheck.LootDistance)) - return; - - if (creature.IsAlive() != (loot != null && loot.loot_type == LootType.Pickpocketing)) - return; - if (loot.IsLooted()) { creature.RemoveDynamicFlag(UnitDynFlags.Lootable); diff --git a/Source/Game/Handlers/SpellHandler.cs b/Source/Game/Handlers/SpellHandler.cs index c112dafea..1b51f4ea5 100644 --- a/Source/Game/Handlers/SpellHandler.cs +++ b/Source/Game/Handlers/SpellHandler.cs @@ -20,10 +20,10 @@ using Framework.Database; using Game.DataStorage; using Game.Entities; using Game.Guilds; +using Game.Loots; using Game.Networking; using Game.Networking.Packets; using Game.Spells; -using System; using System.Collections.Generic; using System.Linq; @@ -190,11 +190,30 @@ namespace Game { PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_GIFT_BY_ITEM); stmt.AddValue(0, item.GetGUID().GetCounter()); + + var pos = item.GetPos(); + var itemGuid = item.GetGUID(); _queryProcessor.AddCallback(DB.Characters.AsyncQuery(stmt) - .WithCallback(result => HandleOpenWrappedItemCallback(item.GetPos(), item.GetGUID(), result))); + .WithCallback(result => HandleOpenWrappedItemCallback(pos, itemGuid, result))); } else + { + // 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, player)) + { + Loot loot = new(player.GetMap(), item.GetGUID(), LootType.Item, null); + item.loot = loot; + loot.GenerateMoneyLoot(item.GetTemplate().MinMoneyLoot, item.GetTemplate().MaxMoneyLoot); + loot.FillLoot(item.GetEntry(), LootStorage.Items, player, true, loot.gold != 0); + + // Force save the loot and money items that were just rolled + // Also saves the container item ID in Loot struct (not to DB) + if (loot.gold > 0 || loot.unlootedCount > 0) + Global.LootItemStorage.AddNewStoredLoot(item.GetGUID().GetCounter(), loot, player); + } player.SendLoot(item.GetGUID(), LootType.Item); + } } void HandleOpenWrappedItemCallback(ushort pos, ObjectGuid itemGuid, SQLResult result) diff --git a/Source/Game/Loot/Loot.cs b/Source/Game/Loot/Loot.cs index 32a5ac73f..bf19a2a58 100644 --- a/Source/Game/Loot/Loot.cs +++ b/Source/Game/Loot/Loot.cs @@ -762,7 +762,8 @@ namespace Game.Loots Group group = lootOwner.GetGroup(); if (!personal && group != null) { - roundRobinPlayer = lootOwner.GetGUID(); + if (loot_type == LootType.Corpse) + roundRobinPlayer = lootOwner.GetGUID(); for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) { diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index 664d4fbab..b18094799 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -1225,24 +1225,65 @@ namespace Game.Spells return; case GameObjectTypes.SpellFocus: + { // triggering linked GO uint trapEntry = gameObjTarget.GetGoInfo().SpellFocus.linkedTrap; if (trapEntry != 0) gameObjTarget.TriggeringLinkedGameObject(trapEntry, player); return; + } case GameObjectTypes.Chest: + { // @todo possible must be moved to loot release (in different from linked triggering) - if (gameObjTarget.GetGoInfo().Chest.triggeredEvent != 0) + var bg = player.GetBattleground(); + if (bg != null) { - Log.outDebug(LogFilter.Spells, "Chest ScriptStart id {0} for GO {1}", gameObjTarget.GetGoInfo().Chest.triggeredEvent, gameObjTarget.GetSpawnId()); - player.GetMap().ScriptsStart(ScriptsType.Event, gameObjTarget.GetGoInfo().Chest.triggeredEvent, player, gameObjTarget); + if (!bg.CanActivateGO((int)gameObjTarget.GetEntry(), (uint)bg.GetPlayerTeam(player.GetGUID()))) + { + player.SendLootRelease(guid); + return; + } } - // triggering linked GO - uint _trapEntry = gameObjTarget.GetGoInfo().Chest.linkedTrap; - if (_trapEntry != 0) - gameObjTarget.TriggeringLinkedGameObject(_trapEntry, player); + if (gameObjTarget.GetLootState() == LootState.Ready) + { + uint lootId = gameObjTarget.GetGoInfo().GetLootId(); + if (lootId != 0) + { + gameObjTarget.SetLootGenerationTime(); + + Group group = player.GetGroup(); + bool groupRules = group != null && gameObjTarget.GetGoInfo().Chest.usegrouplootrules != 0; + + Loot loot = new(gameObjTarget.GetMap(), guid, loottype, groupRules ? group : null); + gameObjTarget.loot = loot; + + loot.FillLoot(lootId, LootStorage.Gameobject, player, !groupRules, false, gameObjTarget.GetLootMode(), gameObjTarget.GetMap().GetDifficultyLootItemContext()); + + if (gameObjTarget.GetLootMode() > 0) + { + var addon = gameObjTarget.GetTemplateAddon(); + if (addon != null) + loot.GenerateMoneyLoot(addon.Mingold, addon.Maxgold); + } + } + + /// @todo possible must be moved to loot release (in different from linked triggering) + if (gameObjTarget.GetGoInfo().Chest.triggeredEvent != 0) + { + Log.outDebug(LogFilter.Spells, $"Chest ScriptStart id {gameObjTarget.GetGoInfo().Chest.triggeredEvent} for GO {gameObjTarget.GetSpawnId()}"); + GameEvents.Trigger(gameObjTarget.GetGoInfo().Chest.triggeredEvent, player, gameObjTarget); + } + + // triggering linked GO + uint trapEntry = gameObjTarget.GetGoInfo().Chest.linkedTrap; + if (trapEntry != 0) + gameObjTarget.TriggeringLinkedGameObject(trapEntry, player); + + gameObjTarget.SetLootState(LootState.Activated, player); + } break; + } // Don't return, let loots been taken default: break; @@ -1909,10 +1950,37 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.HitTarget) return; - if (!m_caster.IsTypeId(TypeId.Player)) + Player player = m_caster.ToPlayer(); + if (player == null) return; - m_caster.ToPlayer().SendLoot(unitTarget.GetGUID(), LootType.Pickpocketing); + Creature creature = unitTarget?.ToCreature(); + if (creature == null) + return; + + if (creature.CanGeneratePickPocketLoot()) + { + creature.StartPickPocketRefillTimer(); + + creature.loot = new Loot(creature.GetMap(), creature.GetGUID(), LootType.Pickpocketing, null); + uint lootid = creature.GetCreatureTemplate().PickPocketId; + if (lootid != 0) + creature.loot.FillLoot(lootid, LootStorage.Pickpocketing, player, true); + + // Generate extra money for pick pocket loot + uint a = RandomHelper.URand(0, creature.GetLevel() / 2); + uint b = RandomHelper.URand(0, player.GetLevel() / 2); + creature.loot.gold = (uint)(10 * (a + b) * WorldConfig.GetFloatValue(WorldCfg.RateDropMoney)); + } + else if (creature.loot != null) + { + if (creature.loot.loot_type == LootType.Pickpocketing && creature.loot.IsLooted()) + player.SendLootError(creature.loot.GetGUID(), creature.GetGUID(), LootError.AlreadPickPocketed); + + return; + } + + player.SendLoot(unitTarget.GetGUID(), LootType.Pickpocketing); } [SpellEffectHandler(SpellEffectName.AddFarsight)] @@ -3170,6 +3238,8 @@ namespace Game.Spells if (caster != null) { caster.UpdateCraftSkill(m_spellInfo); + itemTarget.loot = new Loot(caster.GetMap(), itemTarget.GetGUID(), LootType.Disenchanting, null); + itemTarget.loot.FillLoot(itemTarget.GetDisenchantLoot(caster).Id, LootStorage.Disenchant, caster, true); caster.SendLoot(itemTarget.GetGUID(), LootType.Disenchanting); } @@ -3543,6 +3613,9 @@ namespace Game.Spells creature.RemoveUnitFlag(UnitFlags.Skinnable); creature.SetDynamicFlag(UnitDynFlags.Lootable); + creature.loot = new Loot(creature.GetMap(), creature.GetGUID(), LootType.Skinning, null); + creature.loot.FillLoot(creature.GetCreatureTemplate().SkinLootId, LootStorage.Skinning, player, true); + creature.SetLootRecipient(player, false); player.SendLoot(creature.GetGUID(), LootType.Skinning); if (skill == SkillType.Skinning) @@ -4254,6 +4327,8 @@ namespace Game.Spells player.UpdateGatherSkill(SkillType.Jewelcrafting, SkillValue, reqSkillValue); } + itemTarget.loot = new Loot(player.GetMap(), itemTarget.GetGUID(), LootType.Prospecting, null); + itemTarget.loot.FillLoot(itemTarget.GetEntry(), LootStorage.Prospecting, player, true); player.SendLoot(itemTarget.GetGUID(), LootType.Prospecting); } @@ -4280,6 +4355,8 @@ namespace Game.Spells player.UpdateGatherSkill(SkillType.Inscription, SkillValue, reqSkillValue); } + itemTarget.loot = new Loot(player.GetMap(), itemTarget.GetGUID(), LootType.Milling, null); + itemTarget.loot.FillLoot(itemTarget.GetEntry(), LootStorage.Milling, player, true); player.SendLoot(itemTarget.GetGUID(), LootType.Milling); }