From b349f922b23404fd9a41e0fa7c279117e7c7c607 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Fri, 17 Dec 2021 19:26:43 -0500 Subject: [PATCH] Core/Spells: don't allow spells with SPELL_EFFECT_CREATE_LOOT to be cast if there isn't enough space in inventory Port From (https://github.com/TrinityCore/TrinityCore/commit/59da957165ef7d7529db30f90a30dc401d682052) --- Source/Game/Entities/Player/Player.Items.cs | 91 +++++++++++++-------- Source/Game/Spells/Spell.cs | 50 +++++++---- Source/Game/Spells/SpellEffects.cs | 29 ++----- 3 files changed, 99 insertions(+), 71 deletions(-) diff --git a/Source/Game/Entities/Player/Player.Items.cs b/Source/Game/Entities/Player/Player.Items.cs index 58c1949ac..59c5625c5 100644 --- a/Source/Game/Entities/Player/Player.Items.cs +++ b/Source/Game/Entities/Player/Player.Items.cs @@ -17,6 +17,7 @@ using Framework.Constants; using Framework.Database; +using Game.BattleFields; using Game.BattleGrounds; using Game.DataStorage; using Game.Groups; @@ -30,7 +31,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; -using Game.BattleFields; namespace Game.Entities { @@ -1796,25 +1796,25 @@ namespace Game.Entities { case InventoryResult.CantEquipLevelI: case InventoryResult.PurchaseLevelTooLow: - { - failure.Level = (item1 ? item1.GetRequiredLevel() : 0); - break; - } + { + failure.Level = (item1 ? item1.GetRequiredLevel() : 0); + break; + } case InventoryResult.EventAutoequipBindConfirm: // no idea about this one... - { - //failure.SrcContainer - //failure.SrcSlot - //failure.DstContainer - break; - } + { + //failure.SrcContainer + //failure.SrcSlot + //failure.DstContainer + break; + } case InventoryResult.ItemMaxLimitCategoryCountExceededIs: case InventoryResult.ItemMaxLimitCategorySocketedExceededIs: case InventoryResult.ItemMaxLimitCategoryEquippedExceededIs: - { - ItemTemplate proto = item1 ? item1.GetTemplate() : Global.ObjectMgr.GetItemTemplate(itemId); - failure.LimitCategory = (int)(proto != null ? proto.GetItemLimitCategory() : 0u); - break; - } + { + ItemTemplate proto = item1 ? item1.GetTemplate() : Global.ObjectMgr.GetItemTemplate(itemId); + failure.LimitCategory = (int)(proto != null ? proto.GetItemLimitCategory() : 0u); + break; + } default: break; } @@ -3480,7 +3480,7 @@ namespace Game.Entities if (itemTemplate != null) if (itemTemplate.GetFlags().HasAnyFlag(ItemFlags.HasLoot)) Global.LootItemStorage.RemoveStoredLootForContainer(pItem.GetGUID().GetCounter()); - + pItem.SetState(ItemUpdateState.Removed, this); } } @@ -3566,7 +3566,7 @@ namespace Game.Entities ApplyItemEquipSpell(item, apply); if (updateItemAuras) - { + { ApplyItemDependentAuras(item, apply); WeaponAttackType attackType = Player.GetAttackBySlot(slot, item.GetTemplate().GetInventoryType()); if (attackType != WeaponAttackType.Max) @@ -3622,9 +3622,9 @@ namespace Game.Entities UpdateStatBuffMod(Stats.Intellect); break; //case ItemModType.Spirit: //modify spirit - //HandleStatModifier(UnitMods.StatSpirit, UnitModifierType.BaseValue, (float)val, apply); - //ApplyStatBuffMod(Stats.Spirit, MathFunctions.CalculatePct(val, GetModifierValue(UnitMods.StatSpirit, UnitModifierType.BasePCTExcludeCreate)), apply); - //break; + //HandleStatModifier(UnitMods.StatSpirit, UnitModifierType.BaseValue, (float)val, apply); + //ApplyStatBuffMod(Stats.Spirit, MathFunctions.CalculatePct(val, GetModifierValue(UnitMods.StatSpirit, UnitModifierType.BasePCTExcludeCreate)), apply); + //break; case ItemModType.Stamina: //modify stamina GtGenericMultByILvlRecord staminaMult = CliDB.StaminaMultByILvlGameTable.GetRow(itemLevel); if (staminaMult != null) @@ -4514,7 +4514,30 @@ namespace Game.Entities return freeSlotCount; } - + + public uint GetFreeInventorySpace() + { + uint freeSpace = 0; + + // Check backpack + for (byte slot = InventorySlots.ItemStart; slot < InventorySlots.ItemEnd; ++slot) + { + Item item = GetItemByPos(InventorySlots.Bag0, slot); + if (item == null) + freeSpace += 1; + } + + // Check bags + for (byte i = InventorySlots.BagStart; i < InventorySlots.BagEnd; i++) + { + Bag bag = GetBagByPos(i); + if (bag != null) + freeSpace += bag.GetFreeSlots(); + } + + return freeSpace; + } + //Bags public Bag GetBagByPos(byte bag) { @@ -4681,15 +4704,15 @@ namespace Game.Entities slots[0] = EquipmentSlot.Cloak; break; case InventoryType.Weapon: - { - slots[0] = EquipmentSlot.MainHand; + { + slots[0] = EquipmentSlot.MainHand; - // suggest offhand slot only if know dual wielding - // (this will be replace mainhand weapon at auto equip instead unwonted "you don't known dual wielding" ... - if (CanDualWield()) - slots[1] = EquipmentSlot.OffHand; - break; - } + // suggest offhand slot only if know dual wielding + // (this will be replace mainhand weapon at auto equip instead unwonted "you don't known dual wielding" ... + if (CanDualWield()) + slots[1] = EquipmentSlot.OffHand; + break; + } case InventoryType.Shield: slots[0] = EquipmentSlot.OffHand; break; @@ -5462,7 +5485,7 @@ namespace Game.Entities // equipment visual show SetVisibleItemSlot(slot, null); } - + m_items[slot] = null; } else if ((pBag = GetBagByPos(bag)) != null) @@ -5802,7 +5825,7 @@ namespace Game.Entities } } - public byte GetInventorySlotCount() { return m_activePlayerData.NumBackpackSlots; } + public byte GetInventorySlotCount() { return m_activePlayerData.NumBackpackSlots; } public void SetInventorySlotCount(byte slots) { //ASSERT(slots <= (INVENTORY_SLOT_ITEM_END - INVENTORY_SLOT_ITEM_START)); @@ -5951,7 +5974,7 @@ namespace Game.Entities } public Dictionary GetAELootView() { return m_AELootView; } - + /// /// if in a Battleground a player dies, and an enemy removes the insignia, the player's bones is lootable /// Called by remove insignia spell effect @@ -6559,7 +6582,7 @@ namespace Game.Entities { if (oldSlot >= SharedConst.VoidStorageMaxSlot || newSlot >= SharedConst.VoidStorageMaxSlot || oldSlot == newSlot) return false; - + _voidStorageItems.Swap(newSlot, oldSlot); return true; } diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index a7dd199a0..3d672c7fb 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -6094,33 +6094,49 @@ namespace Game.Spells case SpellEffectName.CreateLoot: { // m_targets.GetUnitTarget() means explicit cast, otherwise we dont check for possible equip error - Unit target = m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : player; - if (target.IsPlayer() && !IsTriggered() && spellEffectInfo.ItemType != 0) + Unit target = m_targets.GetUnitTarget() ?? player; + if (target.IsPlayer() && !IsTriggered()) { - List dest = new(); - InventoryResult msg = target.ToPlayer().CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, spellEffectInfo.ItemType, 1); - if (msg != InventoryResult.Ok) + + // SPELL_EFFECT_CREATE_ITEM_2 differs from SPELL_EFFECT_CREATE_ITEM in that it picks the random item to create from a pool of potential items, + // so we need to make sure there is at least one free space in the player's inventory + if (spellEffectInfo.Effect == SpellEffectName.CreateLoot) { - ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(spellEffectInfo.ItemType); - // @todo Needs review - if (itemTemplate != null && itemTemplate.GetItemLimitCategory() == 0) + if (target.ToPlayer().GetFreeInventorySpace() == 0) { - player.SendEquipError(msg, null, null, spellEffectInfo.ItemType); + player.SendEquipError(InventoryResult.InvFull, null, null, spellEffectInfo.ItemType); return SpellCastResult.DontReport; } - else + } + + if (spellEffectInfo.ItemType != 0) + { + List dest = new(); + InventoryResult msg = target.ToPlayer().CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, spellEffectInfo.ItemType, 1); + if (msg != InventoryResult.Ok) { - // Conjure Food/Water/Refreshment spells - if (!(m_spellInfo.SpellFamilyName == SpellFamilyNames.Mage && m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x40000000u))) - return SpellCastResult.TooManyOfItem; - else if (!target.ToPlayer().HasItemCount(spellEffectInfo.ItemType)) + ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(spellEffectInfo.ItemType); + /// @todo Needs review + if (itemTemplate != null && itemTemplate.GetItemLimitCategory() == 0) { player.SendEquipError(msg, null, null, spellEffectInfo.ItemType); return SpellCastResult.DontReport; } - else if (m_spellInfo.GetEffects().Count > 1) - player.CastSpell(m_caster, (uint)m_spellInfo.GetEffect(1).CalcValue(), new CastSpellExtraArgs().SetOriginalCastId(m_castId)); // move this to anywhere - return SpellCastResult.DontReport; + else + { + // Conjure Food/Water/Refreshment spells + if (m_spellInfo.SpellFamilyName != SpellFamilyNames.Mage || (!m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x40000000u))) + return SpellCastResult.TooManyOfItem; + else if (!(target.ToPlayer().HasItemCount(spellEffectInfo.ItemType))) + { + player.SendEquipError(msg, null, null, spellEffectInfo.ItemType); + return SpellCastResult.DontReport; + } + else if (m_spellInfo.GetEffects().Count > 1) + player.CastSpell(player, (uint)m_spellInfo.GetEffect(1).CalcValue(), new CastSpellExtraArgs() + .SetOriginalCastId(m_castId)); // move this to anywhere + return SpellCastResult.DontReport; + } } } } diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index 48456e296..daf7d6628 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -1098,32 +1098,21 @@ namespace Game.Spells Player player = unitTarget.ToPlayer(); - uint item_id = effectInfo.ItemType; ItemContext context = m_spellInfo.HasAttribute(SpellAttr0.Tradespell) ? ItemContext.TradeSkill : ItemContext.None; - if (item_id != 0) - DoCreateItem(item_id, context); - - // special case: fake item replaced by generate using spell_loot_template + // Pick a random item from spell_loot_template if (m_spellInfo.IsLootCrafting()) { - if (item_id != 0) - { - if (!player.HasItemCount(item_id)) - return; - - // remove reagent - uint count = 1; - player.DestroyItemCount(item_id, count, true); - - // create some random items - player.AutoStoreLoot(m_spellInfo.Id, LootStorage.Spell, context); - } - else - player.AutoStoreLoot(m_spellInfo.Id, LootStorage.Spell, context); // create some random items - + player.AutoStoreLoot(m_spellInfo.Id, LootStorage.Spell, context, false, true); player.UpdateCraftSkill(m_spellInfo.Id); } + else // If there's no random loot entries for this spell, pick the item associated with this spell + { + uint itemId = effectInfo.ItemType; + if (itemId != 0) + DoCreateItem(itemId, context); + } + // @todo ExecuteLogEffectCreateItem(i, GetEffect(i].ItemType); }