diff --git a/src/game/ItemPrototype.h b/src/game/ItemPrototype.h index debdf9fb7..4be8a4fe2 100644 --- a/src/game/ItemPrototype.h +++ b/src/game/ItemPrototype.h @@ -77,13 +77,7 @@ enum ItemSpelltriggerType ITEM_SPELLTRIGGER_ON_EQUIP = 1, ITEM_SPELLTRIGGER_CHANCE_ON_HIT = 2, ITEM_SPELLTRIGGER_SOULSTONE = 4, - /* - * ItemSpelltriggerType 5 might have changed on 2.4.3/3.0.3: Such auras - * will be applied on item pickup and removed on item loss - maybe on the - * other hand the item is destroyed if the aura is removed ("removed on - * death" of spell 57348 makes me think so) - */ - ITEM_SPELLTRIGGER_ON_NO_DELAY_USE = 5, // no equip cooldown + ITEM_SPELLTRIGGER_ON_STORE = 5, // casted at add item to inventory/equip, applied aura removed at remove item, item deleted at aura cancel/expire/etc ITEM_SPELLTRIGGER_LEARN_SPELL_ID = 6 // used in item_template.spell_2 with spell_id with SPELL_GENERIC_LEARN in spell_1 }; diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 006a76557..1f34284fe 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -2513,8 +2513,7 @@ void ObjectMgr::LoadItemRequiredTarget() { if (SpellEntry const* pSpellInfo = sSpellStore.LookupEntry(pItemProto->Spells[i].SpellId)) { - if (pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE || - pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) + if (pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE) { SpellScriptTargetBounds bounds = sSpellMgr.GetSpellScriptTargetBounds(pSpellInfo->Id); if (bounds.first != bounds.second) diff --git a/src/game/Player.cpp b/src/game/Player.cpp index bc11b747d..ef1a18509 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -7541,6 +7541,78 @@ void Player::UpdateEquipSpellsAtFormChange() } } +/** + * (un-)Apply item spells triggered at adding item to inventory ITEM_SPELLTRIGGER_ON_STORE + * + * @param item added/removed item to/from inventory + * @param apply (un-)apply spell affects. + * + * Note: item moved from slot to slot in 2 steps RemoveItem and StoreItem/EquipItem + * In result function not called in RemoveItem for prevent unexpected re-apply auras from related spells + * with duration reset and etc. Instead unapply done in StoreItem/EquipItem and in specialized + * functions for item final remove/destroy from inventory. If new RemoveItem calls added need be sure that + * function will call after it in some way if need. + */ + +void Player::ApplyItemOnStoreSpell(Item *item, bool apply) +{ + if (!item) + return; + + ItemPrototype const *proto = item->GetProto(); + if (!proto) + return; + + for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + _Spell const& spellData = proto->Spells[i]; + + // no spell + if (!spellData.SpellId) + continue; + + // apply/unapply only at-store spells + if (spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_STORE) + continue; + + if (apply) + { + // can be attempt re-applied at move in inventory slots + if (!HasAura(spellData.SpellId)) + CastSpell(this, spellData.SpellId, true, item); + } + else + RemoveAurasDueToItemSpell(item, spellData.SpellId); + } +} + +void Player::DestroyItemWithOnStoreSpell(Item* item) +{ + if (!item) + return; + + ItemPrototype const *proto = item->GetProto(); + if (!proto) + return; + + for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + _Spell const& spellData = proto->Spells[i]; + + // no spell + if (!spellData.SpellId) + continue; + + // apply/unapply only at-store spells + if (spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_STORE) + continue; + + DestroyItem(item->GetBagSlot(), item->GetSlot(), true); + break; + } +} + + /// handles unique effect of Deadly Poison: apply poison of the other weapon when already at max. stack void Player::_HandleDeadlyPoison(Unit* Target, WeaponAttackType attType, SpellEntry const *spellInfo) { @@ -7685,7 +7757,7 @@ void Player::CastItemUseSpell(Item *item,SpellCastTargets const& targets,uint8 c { ItemPrototype const* proto = item->GetProto(); // special learning case - if(proto->Spells[0].SpellId==SPELL_ID_GENERIC_LEARN || proto->Spells[0].SpellId==SPELL_ID_GENERIC_LEARN_PET) + if (proto->Spells[0].SpellId==SPELL_ID_GENERIC_LEARN || proto->Spells[0].SpellId==SPELL_ID_GENERIC_LEARN_PET) { uint32 learn_spell_id = proto->Spells[0].SpellId; uint32 learning_spell_id = proto->Spells[1].SpellId; @@ -7715,15 +7787,15 @@ void Player::CastItemUseSpell(Item *item,SpellCastTargets const& targets,uint8 c _Spell const& spellData = proto->Spells[i]; // no spell - if(!spellData.SpellId) + if (!spellData.SpellId) continue; // wrong triggering type - if( spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE && spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) + if (spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE) continue; SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellData.SpellId); - if(!spellInfo) + if (!spellInfo) { sLog.outError("Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring",proto->ItemId, spellData.SpellId); continue; @@ -7739,14 +7811,16 @@ void Player::CastItemUseSpell(Item *item,SpellCastTargets const& targets,uint8 c } // Item enchantments spells casted at use - for(int e_slot = 0; e_slot < MAX_ENCHANTMENT_SLOT; ++e_slot) + for (int e_slot = 0; e_slot < MAX_ENCHANTMENT_SLOT; ++e_slot) { uint32 enchant_id = item->GetEnchantmentId(EnchantmentSlot(e_slot)); SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if(!pEnchant) continue; + if (!pEnchant) + continue; + for (int s = 0; s < 3; ++s) { - if(pEnchant->type[s]!=ITEM_ENCHANTMENT_TYPE_USE_SPELL) + if (pEnchant->type[s]!=ITEM_ENCHANTMENT_TYPE_USE_SPELL) continue; SpellEntry const *spellInfo = sSpellStore.LookupEntry(pEnchant->spellid[s]); @@ -11143,6 +11217,9 @@ Item* Player::_StoreItem( uint16 pos, Item *pItem, uint32 count, bool clone, boo AddEnchantmentDurations(pItem); AddItemDurations(pItem); + // at place into not appropriate slot (bank, for example) remove aura + ApplyItemOnStoreSpell(pItem, IsEquipmentPos(pItem->GetBagSlot(), pItem->GetSlot()) || IsInventoryPos(pItem->GetBagSlot(), pItem->GetSlot())); + return pItem; } else @@ -11150,7 +11227,7 @@ Item* Player::_StoreItem( uint16 pos, Item *pItem, uint32 count, bool clone, boo if (pItem2->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem2->GetProto()->Bonding == BIND_QUEST_ITEM || (pItem2->GetProto()->Bonding == BIND_WHEN_EQUIPPED && IsBagPos(pos))) - pItem2->SetBinding( true ); + pItem2->SetBinding(true); pItem2->SetCount( pItem2->GetCount() + count ); if (IsInWorld() && update) @@ -11201,22 +11278,23 @@ Item* Player::EquipItem( uint16 pos, Item *pItem, bool update ) uint8 bag = pos >> 8; uint8 slot = pos & 255; - Item *pItem2 = GetItemByPos( bag, slot ); - - if( !pItem2 ) + Item *pItem2 = GetItemByPos(bag, slot); + if (!pItem2) { VisualizeItem( slot, pItem); - if(isAlive()) + if (isAlive()) { ItemPrototype const *pProto = pItem->GetProto(); // item set bonuses applied only at equip and removed at unequip, and still active for broken items - if(pProto && pProto->ItemSet) + if (pProto && pProto->ItemSet) AddItemsSetItem(this, pItem); _ApplyItemMods(pItem, slot, true); + ApplyItemOnStoreSpell(pItem, true); + // Weapons and also Totem/Relic/Sigil/etc if (pProto && isInCombat() && (pProto->Class == ITEM_CLASS_WEAPON || pProto->InventoryType == INVTYPE_RELIC) && m_weaponChangeTimer == 0) { @@ -11304,6 +11382,7 @@ void Player::QuickEquipItem( uint16 pos, Item *pItem) { AddEnchantmentDurations(pItem); AddItemDurations(pItem); + ApplyItemOnStoreSpell(pItem, true); uint8 slot = pos & 255; VisualizeItem( slot, pItem); @@ -11368,8 +11447,7 @@ void Player::RemoveItem( uint8 bag, uint8 slot, bool update ) // note2: if removeitem is to be used for delinking // the item must be removed from the player's updatequeue - Item *pItem = GetItemByPos( bag, slot ); - if( pItem ) + if (Item *pItem = GetItemByPos(bag, slot)) { DEBUG_LOG( "STORAGE: RemoveItem bag = %u, slot = %u, item = %u", bag, slot, pItem->GetEntry()); @@ -11441,7 +11519,10 @@ void Player::RemoveItem( uint8 bag, uint8 slot, bool update ) pItem->SetGuidValue(ITEM_FIELD_CONTAINED, ObjectGuid()); // pItem->SetGuidValue(ITEM_FIELD_OWNER, ObjectGuid()); not clear owner at remove (it will be set at store). This used in mail and auction code pItem->SetSlot( NULL_SLOT ); - if( IsInWorld() && update ) + + //ApplyItemOnStoreSpell, for avoid re-apply will remove at _adding_ to not appropriate slot + + if (IsInWorld() && update) pItem->SendCreateUpdateToPlayer( this ); } } @@ -11453,6 +11534,11 @@ void Player::MoveItemFromInventory(uint8 bag, uint8 slot, bool update) { ItemRemovedQuestCheck(it->GetEntry(), it->GetCount()); RemoveItem(bag, slot, update); + + // item atStore spell not removed in RemoveItem (for avoid reappaly in slots changes), so do it directly + if (IsEquipmentPos(bag, slot) || IsInventoryPos(bag, slot)) + ApplyItemOnStoreSpell(it, false); + it->RemoveFromUpdateQueueOf(this); if(it->IsInWorld()) { @@ -11510,6 +11596,9 @@ void Player::DestroyItem( uint8 bag, uint8 slot, bool update ) RemoveEnchantmentDurations(pItem); RemoveItemDurations(pItem); + if (IsEquipmentPos(bag, slot) || IsInventoryPos(bag, slot)) + ApplyItemOnStoreSpell(pItem, false); + ItemRemovedQuestCheck( pItem->GetEntry(), pItem->GetCount() ); if( bag == INVENTORY_SLOT_BAG_0 ) diff --git a/src/game/Player.h b/src/game/Player.h index 280aadb7d..742a3f19b 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1259,8 +1259,8 @@ class MANGOS_DLL_SPEC Player : public Unit { return StoreItem( dest, pItem, update); } - Item* BankItem( uint16 pos, Item *pItem, bool update ); - void RemoveItem( uint8 bag, uint8 slot, bool update ); + Item* BankItem(uint16 pos, Item *pItem, bool update); + void RemoveItem(uint8 bag, uint8 slot, bool update);// see ApplyItemOnStoreSpell notes void MoveItemFromInventory(uint8 bag, uint8 slot, bool update); // in trade, auction, guild bank, mail.... void MoveItemToInventory(ItemPosCountVec const& dest, Item* pItem, bool update, bool in_characterInventoryDB = false); @@ -2019,6 +2019,9 @@ class MANGOS_DLL_SPEC Player : public Unit void CastItemCombatSpell(Unit* Target, WeaponAttackType attType); void CastItemUseSpell(Item *item,SpellCastTargets const& targets,uint8 cast_count, uint32 glyphIndex); + void ApplyItemOnStoreSpell(Item *item, bool apply); + void DestroyItemWithOnStoreSpell(Item* item); + void SendEquipmentSetList(); void SetEquipmentSet(uint32 index, EquipmentSet eqset); void DeleteEquipmentSet(uint64 setGuid); diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index d5c25d272..45734bc93 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -8558,19 +8558,22 @@ void SpellAuraHolder::_AddSpellAuraHolder() void SpellAuraHolder::_RemoveSpellAuraHolder() { - // Remove all triggered by aura spells vs unlimited duration + // Remove all triggered by aura spells vs unlimited duration // except same aura replace case if(m_removeMode!=AURA_REMOVE_BY_STACK) CleanupTriggeredSpells(); Unit* caster = GetCaster(); - if(caster && IsPersistent()) - { - DynamicObject *dynObj = caster->GetDynObject(GetId()); - if (dynObj) + if (caster && IsPersistent()) + if (DynamicObject *dynObj = caster->GetDynObject(GetId())) dynObj->RemoveAffected(m_target); - } + + // remove at-store spell cast items (for all remove modes?) + if (caster && caster->GetTypeId() == TYPEID_PLAYER && m_removeMode != AURA_REMOVE_BY_DEFAULT && m_removeMode != AURA_REMOVE_BY_DELETE) + if (ObjectGuid castItemGuid = GetCastItemGuid()) + if (Item* castItem = ((Player*)m_target)->GetItemByGuid(castItemGuid)) + ((Player*)caster)->DestroyItemWithOnStoreSpell(castItem); //passive auras do not get put in slots - said who? ;) // Note: but totem can be not accessible for aura target in time remove (to far for find in grid) diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 9b4095a63..11c977ffc 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "11669" + #define REVISION_NR "11670" #endif // __REVISION_NR_H__