[11670] Implement in proper way ITEM_SPELLTRIGGER_ON_STORE (5)

Item spells with this trigger mode expected casted at item add
to inventory/equip slots (exclude bank), and spell auras (if any)
applied while item in like slots. Item expected destroyed at aura
remove by some reason.

Implementation note: because 2 step way item moves from slot to slot
related auras not removed at internal RemoveItem call and removed only
at directly item destroy/remove from player function calles, or at StoreItem
(if item added to not appropriate slot). So need in future careful with new
RemoveItem call cases.
This commit is contained in:
VladimirMangos 2011-06-24 22:26:50 +04:00
parent 3f840222ee
commit 04a469f393
6 changed files with 122 additions and 34 deletions

View file

@ -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
};

View file

@ -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)

View file

@ -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 )

View file

@ -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);

View file

@ -8565,12 +8565,15 @@ void SpellAuraHolder::_RemoveSpellAuraHolder()
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)

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__
#define __REVISION_NR_H__
#define REVISION_NR "11669"
#define REVISION_NR "11670"
#endif // __REVISION_NR_H__