diff options
Diffstat (limited to 'src/game/Spell.cpp')
-rw-r--r-- | src/game/Spell.cpp | 3614 |
1 files changed, 2251 insertions, 1363 deletions
diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index 94065b8357f..17ff750452a 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,9 +33,9 @@ #include "Player.h" #include "Pet.h" #include "Unit.h" +#include "Totem.h" #include "Spell.h" #include "DynamicObject.h" -#include "SpellAuras.h" #include "Group.h" #include "UpdateData.h" #include "MapManager.h" @@ -43,14 +43,14 @@ #include "CellImpl.h" #include "Policies/SingletonImp.h" #include "SharedDefines.h" -#include "Tools.h" #include "LootMgr.h" #include "VMapFactory.h" #include "BattleGround.h" #include "Util.h" #include "TemporarySummon.h" +#include "Vehicle.h" -#define SPELL_CHANNEL_UPDATE_INTERVAL 1000 +#define SPELL_CHANNEL_UPDATE_INTERVAL (1 * IN_MILISECONDS) extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS]; @@ -63,6 +63,55 @@ bool IsQuestTameSpell(uint32 spellId) && spellproto->Effect[1] == SPELL_EFFECT_APPLY_AURA && spellproto->EffectApplyAuraName[1] == SPELL_AURA_DUMMY; } +class PrioritizeManaUnitWraper +{ + public: + explicit PrioritizeManaUnitWraper(Unit* unit) : i_unit(unit) + { + uint32 maxmana = unit->GetMaxPower(POWER_MANA); + i_percent = maxmana ? unit->GetPower(POWER_MANA) * 100 / maxmana : 101; + } + Unit* getUnit() const { return i_unit; } + uint32 getPercent() const { return i_percent; } + private: + Unit* i_unit; + uint32 i_percent; +}; + +struct PrioritizeMana +{ + int operator()( PrioritizeManaUnitWraper const& x, PrioritizeManaUnitWraper const& y ) const + { + return x.getPercent() < y.getPercent(); + } +}; + +typedef std::priority_queue<PrioritizeManaUnitWraper, std::vector<PrioritizeManaUnitWraper>, PrioritizeMana> PrioritizeManaUnitQueue; + +class PrioritizeHealthUnitWraper +{ +public: + explicit PrioritizeHealthUnitWraper(Unit* unit) : i_unit(unit) + { + i_percent = unit->GetHealth() * 100 / unit->GetMaxHealth(); + } + Unit* getUnit() const { return i_unit; } + uint32 getPercent() const { return i_percent; } +private: + Unit* i_unit; + uint32 i_percent; +}; + +struct PrioritizeHealth +{ + int operator()( PrioritizeHealthUnitWraper const& x, PrioritizeHealthUnitWraper const& y ) const + { + return x.getPercent() < y.getPercent(); + } +}; + +typedef std::priority_queue<PrioritizeHealthUnitWraper, std::vector<PrioritizeHealthUnitWraper>, PrioritizeHealth> PrioritizeHealthUnitQueue; + SpellCastTargets::SpellCastTargets() { m_unitTarget = NULL; @@ -130,6 +179,14 @@ void SpellCastTargets::setDestination(WorldObject *target) m_targetMask |= TARGET_FLAG_DEST_LOCATION; } +/*void SpellCastTargets::setSource(float x, float y, float z) +{ + m_srcX = x; + m_srcY = y; + m_srcZ = z; + m_targetMask |= TARGET_FLAG_SOURCE_LOCATION; +}*/ + void SpellCastTargets::setGOTarget(GameObject *target) { m_GOTarget = target; @@ -155,13 +212,13 @@ void SpellCastTargets::setCorpseTarget(Corpse* corpse) void SpellCastTargets::Update(Unit* caster) { - m_GOTarget = m_GOTargetGUID ? ObjectAccessor::GetGameObject(*caster,m_GOTargetGUID) : NULL; + m_GOTarget = m_GOTargetGUID ? caster->GetMap()->GetGameObject(m_GOTargetGUID) : NULL; m_unitTarget = m_unitTargetGUID ? - ( m_unitTargetGUID==caster->GetGUID() ? caster : ObjectAccessor::GetUnit(*caster, m_unitTargetGUID) ) : + ( m_unitTargetGUID == caster->GetGUID() ? caster : ObjectAccessor::GetUnit(*caster, m_unitTargetGUID) ) : NULL; m_itemTarget = NULL; - if(caster->GetTypeId()==TYPEID_PLAYER) + if(caster->GetTypeId() == TYPEID_PLAYER) { if(m_targetMask & TARGET_FLAG_ITEM) m_itemTarget = ((Player*)caster)->GetItemByGuid(m_itemTargetGUID); @@ -179,31 +236,37 @@ void SpellCastTargets::Update(Unit* caster) bool SpellCastTargets::read ( WorldPacket * data, Unit *caster ) { - if(data->rpos()+4 > data->size()) + if(data->rpos() + 4 > data->size()) return false; + //data->hexlike(); + *data >> m_targetMask; - sLog.outDebug("Spell read, target mask = %u", m_targetMask); + //sLog.outDebug("Spell read, target mask = %u", m_targetMask); if(m_targetMask == TARGET_FLAG_SELF) return true; // TARGET_FLAG_UNK2 is used for non-combat pets, maybe other? - if( m_targetMask & (TARGET_FLAG_UNIT|TARGET_FLAG_UNK2) ) - if(!readGUID(*data, m_unitTargetGUID)) + if( m_targetMask & ( TARGET_FLAG_UNIT | TARGET_FLAG_UNK2 )) + if(!data->readPackGUID(m_unitTargetGUID)) return false; - if( m_targetMask & ( TARGET_FLAG_OBJECT | TARGET_FLAG_OBJECT_UNK )) - if(!readGUID(*data, m_GOTargetGUID)) + if( m_targetMask & ( TARGET_FLAG_OBJECT )) + if(!data->readPackGUID(m_GOTargetGUID)) return false; if(( m_targetMask & ( TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM )) && caster->GetTypeId() == TYPEID_PLAYER) - if(!readGUID(*data, m_itemTargetGUID)) + if(!data->readPackGUID(m_itemTargetGUID)) + return false; + + if( m_targetMask & (TARGET_FLAG_CORPSE | TARGET_FLAG_PVP_CORPSE ) ) + if(!data->readPackGUID(m_CorpseTargetGUID)) return false; if( m_targetMask & TARGET_FLAG_SOURCE_LOCATION ) { - if(data->rpos()+4+4+4 > data->size()) + if(data->rpos() + 4 + 4 + 4 > data->size()) return false; *data >> m_srcX >> m_srcY >> m_srcZ; @@ -213,7 +276,10 @@ bool SpellCastTargets::read ( WorldPacket * data, Unit *caster ) if( m_targetMask & TARGET_FLAG_DEST_LOCATION ) { - if(data->rpos()+4+4+4 > data->size()) + if(data->rpos() + 1 + 4 + 4 + 4 > data->size()) + return false; + + if(!data->readPackGUID(m_unitTargetGUID)) return false; *data >> m_destX >> m_destY >> m_destZ; @@ -223,16 +289,12 @@ bool SpellCastTargets::read ( WorldPacket * data, Unit *caster ) if( m_targetMask & TARGET_FLAG_STRING ) { - if(data->rpos()+1 > data->size()) + if(data->rpos() + 1 > data->size()) return false; *data >> m_strTarget; } - if( m_targetMask & (TARGET_FLAG_CORPSE | TARGET_FLAG_PVP_CORPSE ) ) - if(!readGUID(*data, m_CorpseTargetGUID)) - return false; - // find real units/GOs Update(caster); return true; @@ -241,7 +303,7 @@ bool SpellCastTargets::read ( WorldPacket * data, Unit *caster ) void SpellCastTargets::write ( WorldPacket * data ) { *data << uint32(m_targetMask); - sLog.outDebug("Spell write, target mask = %u", m_targetMask); + //sLog.outDebug("Spell write, target mask = %u", m_targetMask); if( m_targetMask & ( TARGET_FLAG_UNIT | TARGET_FLAG_PVP_CORPSE | TARGET_FLAG_OBJECT | TARGET_FLAG_CORPSE | TARGET_FLAG_UNK2 ) ) { @@ -252,7 +314,7 @@ void SpellCastTargets::write ( WorldPacket * data ) else *data << uint8(0); } - else if( m_targetMask & ( TARGET_FLAG_OBJECT | TARGET_FLAG_OBJECT_UNK ) ) + else if( m_targetMask & TARGET_FLAG_OBJECT ) { if(m_GOTarget) data->append(m_GOTarget->GetPackGUID()); @@ -277,7 +339,14 @@ void SpellCastTargets::write ( WorldPacket * data ) *data << m_srcX << m_srcY << m_srcZ; if( m_targetMask & TARGET_FLAG_DEST_LOCATION ) + { + if(m_unitTarget) + data->append(m_unitTarget->GetPackGUID()); + else + *data << uint8(0); + *data << m_destX << m_destY << m_destZ; + } if( m_targetMask & TARGET_FLAG_STRING ) *data << m_strTarget; @@ -293,10 +362,15 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi m_triggeringContainer = triggeringContainer; m_referencedFromCurrentSpell = false; m_executedCurrently = false; + m_needComboPoints = NeedsComboPoints(m_spellInfo); + m_comboPointGain = 0; m_delayStart = 0; m_delayAtDamageCount = 0; + m_canTrigger=true; + m_applyMultiplierMask = 0; + m_effectMask = 0; // Get data for type of attack switch (m_spellInfo->DmgClass) @@ -308,11 +382,11 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi m_attackType = BASE_ATTACK; break; case SPELL_DAMAGE_CLASS_RANGED: - m_attackType = RANGED_ATTACK; + m_attackType = IsRangedWeaponSpell(m_spellInfo) ? RANGED_ATTACK : BASE_ATTACK; break; default: // Wands - if (m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_REQ_WAND) + if (m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG) m_attackType = RANGED_ATTACK; else m_attackType = BASE_ATTACK; @@ -324,10 +398,10 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi if(m_attackType == RANGED_ATTACK) { // wand case - if((m_caster->getClassMask() & CLASSMASK_WAND_USERS) != 0 && m_caster->GetTypeId()==TYPEID_PLAYER) + if((m_caster->getClassMask() & CLASSMASK_WAND_USERS) != 0 && m_caster->GetTypeId() == TYPEID_PLAYER) { if(Item* pItem = ((Player*)m_caster)->GetWeaponForAttack(RANGED_ATTACK)) - m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetProto()->Damage->DamageType); + m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetProto()->Damage[0].DamageType); } } // Set health leech amount to zero @@ -338,11 +412,11 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi else m_originalCasterGUID = m_caster->GetGUID(); - if(m_originalCasterGUID==m_caster->GetGUID()) + if(m_originalCasterGUID == m_caster->GetGUID()) m_originalCaster = m_caster; else { - m_originalCaster = ObjectAccessor::GetUnit(*m_caster,m_originalCasterGUID); + m_originalCaster = ObjectAccessor::GetUnit(*m_caster, m_originalCasterGUID); if(m_originalCaster && !m_originalCaster->IsInWorld()) m_originalCaster = NULL; } @@ -351,7 +425,6 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi m_spellState = SPELL_STATE_NULL; - m_castPositionX = m_castPositionY = m_castPositionZ = 0; m_TriggerSpells.clear(); m_IsTriggeredSpell = triggered; //m_AreaAura = false; @@ -362,14 +435,15 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi gameObjTarget = NULL; focusObject = NULL; m_cast_count = 0; + m_glyphIndex = 0; + m_preCastSpell = 0; m_triggeredByAuraSpell = NULL; + m_spellAura = NULL; - //Auto Shot & Shoot - if( m_spellInfo->AttributesEx2 == 0x000020 && !triggered ) - m_autoRepeat = true; - else - m_autoRepeat = false; + //Auto Shot & Shoot (wand) + m_autoRepeat = IsAutoRepeatRangedSpell(m_spellInfo); + m_runesState = 0; m_powerCost = 0; // setup to correct value in Spell::prepare, don't must be used before. m_casttime = 0; // setup to correct value in Spell::prepare, don't must be used before. m_timer = 0; // will set to castime in prepare @@ -379,14 +453,14 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi // determine reflection m_canReflect = false; - if(m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && !IsAreaOfEffectSpell(m_spellInfo) && (m_spellInfo->AttributesEx2 & 0x4)==0) + if(m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && !IsAreaOfEffectSpell(m_spellInfo) && !(m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_CANT_REFLECTED)) { - for(int j=0;j<3;j++) + for(int j = 0;j < 3; ++j) { - if (m_spellInfo->Effect[j]==0) + if (m_spellInfo->Effect[j] == 0) continue; - if(!IsPositiveTarget(m_spellInfo->EffectImplicitTargetA[j],m_spellInfo->EffectImplicitTargetB[j])) + if(!IsPositiveTarget(m_spellInfo->EffectImplicitTargetA[j], m_spellInfo->EffectImplicitTargetB[j])) m_canReflect = true; else m_canReflect = (m_spellInfo->AttributesEx & SPELL_ATTR_EX_NEGATIVE) ? true : false; @@ -406,6 +480,35 @@ Spell::~Spell() delete m_spellValue; } +template<typename T> +WorldObject* Spell::FindCorpseUsing() +{ + // non-standard target selection + float max_range = GetSpellMaxRange(m_spellInfo, false); + + CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + WorldObject* result = NULL; + + T u_check(m_caster, max_range); + MaNGOS::WorldObjectSearcher<T> searcher(m_caster, result, u_check); + + TypeContainerVisitor<MaNGOS::WorldObjectSearcher<T>, GridTypeMapContainer > grid_searcher(searcher); + CellLock<GridReadGuard> cell_lock(cell, p); + cell_lock->Visit(cell_lock, grid_searcher, *m_caster->GetMap()); + + if (!result) + { + TypeContainerVisitor<MaNGOS::WorldObjectSearcher<T>, WorldTypeMapContainer > world_searcher(searcher); + cell_lock->Visit(cell_lock, world_searcher, *m_caster->GetMap()); + } + + return result; +} + void Spell::FillTargetMap() { for(uint32 i = 0; i < 3; ++i) @@ -441,8 +544,20 @@ void Spell::FillTargetMap() continue; } + if(/*tmpUnitMap.empty() && */m_spellInfo->Targets & TARGET_FLAG_CASTER) + { + AddUnitTarget(m_caster, i); + continue; + } + if(!targetA && !targetB) { + if(!GetSpellMaxRangeForFriend(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex))) + { + AddUnitTarget(m_caster, i); + continue; + } + // add here custom effects that need default target. // FOR EVERY TARGET TYPE THERE IS A DIFFERENT FILL!! switch(m_spellInfo->Effect[i]) @@ -453,17 +568,7 @@ void Spell::FillTargetMap() { case 20577: // Cannibalize { - // non-standard target selection - SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex); - float max_range = GetSpellMaxRange(srange); - WorldObject* result = NULL; - - Trinity::CannibalizeObjectCheck u_check(m_caster, max_range); - Trinity::WorldObjectSearcher<Trinity::CannibalizeObjectCheck > searcher(result, u_check); - m_caster->VisitNearbyGridObject(max_range, searcher); - if(!result) - m_caster->VisitNearbyWorldObject(max_range, searcher); - + WorldObject* result = FindCorpseUsing<MaNGOS::CannibalizeObjectCheck> (); if(result) { @@ -483,16 +588,8 @@ void Spell::FillTargetMap() else { // clear cooldown at fail - if(m_caster->GetTypeId()==TYPEID_PLAYER) - { - ((Player*)m_caster)->RemoveSpellCooldown(m_spellInfo->Id); - - WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8)); - data << uint32(m_spellInfo->Id); - data << uint64(m_caster->GetGUID()); - ((Player*)m_caster)->GetSession()->SendPacket(&data); - } - + if(m_caster->GetTypeId() == TYPEID_PLAYER) + ((Player*)m_caster)->RemoveSpellCooldown(m_spellInfo->Id, true); SendCastResult(SPELL_FAILED_NO_EDIBLE_CORPSES); finish(false); } @@ -501,6 +598,8 @@ void Spell::FillTargetMap() default: if(m_targets.getUnitTarget()) AddUnitTarget(m_targets.getUnitTarget(), i); + else + AddUnitTarget(m_caster, i); break; } break; @@ -509,12 +608,16 @@ void Spell::FillTargetMap() case SPELL_EFFECT_CREATE_ITEM: case SPELL_EFFECT_TRIGGER_SPELL: case SPELL_EFFECT_SKILL_STEP: + case SPELL_EFFECT_PROFICIENCY: + case SPELL_EFFECT_SUMMON_OBJECT_WILD: case SPELL_EFFECT_SELF_RESURRECT: case SPELL_EFFECT_REPUTATION: case SPELL_EFFECT_LEARN_SPELL: + case SPELL_EFFECT_SEND_TAXI: if(m_targets.getUnitTarget()) AddUnitTarget(m_targets.getUnitTarget(), i); - else + // Triggered spells have additional spell targets - cast them even if no explicit unit target is given (required for spell 50516 for example) + else if(m_spellInfo->Effect[i] == SPELL_EFFECT_TRIGGER_SPELL) AddUnitTarget(m_caster, i); break; case SPELL_EFFECT_SUMMON_PLAYER: @@ -530,7 +633,7 @@ void Spell::FillTargetMap() AddUnitTarget(m_targets.getUnitTarget(), i); if(m_targets.getCorpseTargetGUID()) { - Corpse *corpse = ObjectAccessor::GetCorpse(*m_caster,m_targets.getCorpseTargetGUID()); + Corpse *corpse = ObjectAccessor::GetCorpse(*m_caster, m_targets.getCorpseTargetGUID()); if(corpse) { Player* owner = ObjectAccessor::FindPlayer(corpse->GetOwnerGUID()); @@ -541,19 +644,23 @@ void Spell::FillTargetMap() break; case SPELL_EFFECT_SUMMON_CHANGE_ITEM: case SPELL_EFFECT_ADD_FARSIGHT: + case SPELL_EFFECT_APPLY_GLYPH: case SPELL_EFFECT_STUCK: + case SPELL_EFFECT_FEED_PET: case SPELL_EFFECT_DESTROY_ALL_TOTEMS: + case SPELL_EFFECT_KILL_CREDIT2: // only one spell: 42793 AddUnitTarget(m_caster, i); break; case SPELL_EFFECT_LEARN_PET_SPELL: - if(Pet* pet = m_caster->GetPet()) + if(Guardian* pet = m_caster->GetGuardianPet()) AddUnitTarget(pet, i); break; /*case SPELL_EFFECT_ENCHANT_ITEM: case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY: + case SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC: case SPELL_EFFECT_DISENCHANT: - case SPELL_EFFECT_FEED_PET: case SPELL_EFFECT_PROSPECTING: + case SPELL_EFFECT_MILLING: if(m_targets.getItemTarget()) AddItemTarget(m_targets.getItemTarget(), i); break;*/ @@ -590,6 +697,7 @@ void Spell::FillTargetMap() } break; default: + AddUnitTarget(m_caster, i); break; } } @@ -619,45 +727,14 @@ void Spell::FillTargetMap() } } -void Spell::prepareDataForTriggerSystem() +void Spell::prepareDataForTriggerSystem(AuraEffect * triggeredByAura) { //========================================================================================== // Now fill data for trigger system, need know: - // Ņan spell trigger another or not ( m_canTrigger ) - // Create base triggers flags for Attacker and Victim ( m_procAttacker and m_procVictim) + // can spell trigger another or not ( m_canTrigger ) + // Create base triggers flags for Attacker and Victim ( m_procAttacker, m_procVictim and m_procEx) //========================================================================================== - // Fill flag can spell trigger or not - if (!m_IsTriggeredSpell) - m_canTrigger = true; // Normal cast - can trigger - else if (!m_triggeredByAuraSpell) - m_canTrigger = true; // Triggered from SPELL_EFFECT_TRIGGER_SPELL - can trigger - else // Exceptions (some periodic triggers) - { - m_canTrigger = false; // Triggered spells can`t trigger another - switch (m_spellInfo->SpellFamilyName) - { - case SPELLFAMILY_MAGE: // Arcane Missles / Blizzard triggers need do it - if (m_spellInfo->SpellFamilyFlags & 0x0000000000200080LL) m_canTrigger = true; - break; - case SPELLFAMILY_WARLOCK: // For Hellfire Effect / Rain of Fire / Seed of Corruption triggers need do it - if (m_spellInfo->SpellFamilyFlags & 0x0000800000000060LL) m_canTrigger = true; - break; - case SPELLFAMILY_HUNTER: // Hunter Explosive Trap Effect/Immolation Trap Effect/Frost Trap Aura/Snake Trap Effect - if (m_spellInfo->SpellFamilyFlags & 0x0000200000000014LL) m_canTrigger = true; - break; - case SPELLFAMILY_PALADIN: // For Holy Shock triggers need do it - if (m_spellInfo->SpellFamilyFlags & 0x0001000000200000LL) m_canTrigger = true; - break; - case SPELLFAMILY_ROGUE: // mutilate mainhand + offhand - if (m_spellInfo->SpellFamilyFlags & 0x600000000LL) m_canTrigger = true; - break; - } - } - // Do not trigger from item cast spell - if (m_CastItem) - m_canTrigger = false; - // Get data for type of attack and fill base info for trigger switch (m_spellInfo->DmgClass) { @@ -666,31 +743,92 @@ void Spell::prepareDataForTriggerSystem() m_procVictim = PROC_FLAG_TAKEN_MELEE_SPELL_HIT; break; case SPELL_DAMAGE_CLASS_RANGED: - m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT; - m_procVictim = PROC_FLAG_TAKEN_RANGED_SPELL_HIT; - break; - default: - if (IsPositiveSpell(m_spellInfo->Id)) // Check for positive spell + // Auto attack + if (m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG) { - m_procAttacker = PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL; - m_procVictim = PROC_FLAG_TAKEN_POSITIVE_SPELL; + m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_HIT; + m_procVictim = PROC_FLAG_TAKEN_RANGED_HIT; } - else if (m_spellInfo->Id == 5019) // Wands + else // Ranged spell attack { m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT; m_procVictim = PROC_FLAG_TAKEN_RANGED_SPELL_HIT; } - else + break; + default: + if (IsPositiveSpell(m_spellInfo->Id)) // Check for positive spell + { + if(m_customAttr & SPELL_ATTR_CU_DIRECT_DAMAGE) + { + m_procAttacker = PROC_FLAG_SUCCESSFUL_HEALING_SPELL; + m_procVictim = PROC_FLAG_TAKEN_HEALING_SPELL; + } + else + { + m_procAttacker = PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL; + m_procVictim = PROC_FLAG_TAKEN_POSITIVE_SPELL; + } + } + else if (m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG) // Wands auto attack { - m_procAttacker = PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT; - m_procVictim = PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT; + m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_HIT; + m_procVictim = PROC_FLAG_TAKEN_RANGED_HIT; + } + else // Negative spell + { + if(m_customAttr & SPELL_ATTR_CU_DIRECT_DAMAGE) + { + m_procAttacker = PROC_FLAG_SUCCESSFUL_DAMAGING_SPELL_HIT; + m_procVictim = PROC_FLAG_TAKEN_DAMAGING_SPELL_HIT; + } + else + { + m_procAttacker = PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT; + m_procVictim = PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT; + } } - break; } + m_procEx= PROC_EX_NONE; + // Hunter traps spells (for Entrapment trigger) // Gives your Immolation Trap, Frost Trap, Explosive Trap, and Snake Trap .... - if (m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && m_spellInfo->SpellFamilyFlags & 0x0000200000000014LL) + if (m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && (m_spellInfo->SpellFamilyFlags[1] & 0x00002000 || m_spellInfo->SpellFamilyFlags[0] & 0x1C)) + { m_procAttacker |= PROC_FLAG_ON_TRAP_ACTIVATION; + } + else + { + /* + Effects which are result of aura proc from triggered spell cannot proc + to prevent chain proc of these spells + */ + if ((triggeredByAura && !triggeredByAura->GetParentAura()->GetTarget()->CanProc()) || !m_caster->CanProc()) + { + m_canTrigger=false; + } + + if (m_IsTriggeredSpell && + (m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_TRIGGERED_CAN_TRIGGER || + m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_TRIGGERED_CAN_TRIGGER_2)) + m_procEx |= PROC_EX_INTERNAL_TRIGGERED; + + // Totem casts require spellfamilymask defined in spell_proc_event to proc + if (m_originalCaster && m_caster != m_originalCaster && m_caster->GetTypeId()==TYPEID_UNIT && ((Creature*)m_caster)->isTotem() && m_caster->IsControlledByPlayer()) + { + m_procEx |= PROC_EX_INTERNAL_REQ_FAMILY; + } + // Check done for judgements to make them not trigger seal effects + else if (m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_UNK1) + { + // Rogue poisons + if (m_spellInfo->SpellFamilyName && m_spellInfo->SpellFamilyFlags) + m_procEx |= PROC_EX_INTERNAL_REQ_FAMILY; + else + m_canTrigger=false; + } + } + if (m_IsTriggeredSpell || triggeredByAura) + m_procEx |= PROC_EX_INTERNAL_CANT_PROC; } void Spell::CleanupTargetList() @@ -698,27 +836,29 @@ void Spell::CleanupTargetList() m_UniqueTargetInfo.clear(); m_UniqueGOTargetInfo.clear(); m_UniqueItemInfo.clear(); - m_countOfHit = 0; - m_countOfMiss = 0; m_delayMoment = 0; } void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex) { - if( m_spellInfo->Effect[effIndex]==0 ) + if( m_spellInfo->Effect[effIndex] == 0 ) return; if(!CheckTarget(pVictim, effIndex)) return; + // Check for effect immune skip if immuned + bool immuned = pVictim->IsImmunedToSpellEffect(m_spellInfo, effIndex); + uint64 targetGUID = pVictim->GetGUID(); // Lookup target in already in list - for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) + for(std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) { if (targetGUID == ihit->targetGUID) // Found in list { - ihit->effectMask |= 1<<effIndex; // Add only effect mask + if (!immuned) + ihit->effectMask |= 1 << effIndex; // Add only effect mask if not immuned return; } } @@ -728,8 +868,9 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex) // Get spell hit result on target TargetInfo target; target.targetGUID = targetGUID; // Store target GUID - target.effectMask = 1<<effIndex; // Store index of effect + target.effectMask = immuned ? 0 : 1 << effIndex; // Store index of effect if not immuned target.processed = false; // Effects not apply on target + target.alive = pVictim->isAlive(); target.damage = 0; target.crit = false; @@ -743,11 +884,6 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex) else target.missCondition = SPELL_MISS_EVADE; //SPELL_MISS_NONE; - if (target.missCondition == SPELL_MISS_NONE) - ++m_countOfHit; - else - ++m_countOfMiss; - // Spell have speed - need calculate incoming time if (m_spellInfo->speed > 0.0f) { @@ -759,14 +895,14 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex) target.timeDelay = (uint64) floor(dist / m_spellInfo->speed * 1000.0f); // Calculate minimum incoming time - if (m_delayMoment==0 || m_delayMoment>target.timeDelay) + if (m_delayMoment == 0 || m_delayMoment>target.timeDelay) m_delayMoment = target.timeDelay; } else target.timeDelay = 0LL; // If target reflect spell back to caster - if (target.missCondition==SPELL_MISS_REFLECT) + if (target.missCondition == SPELL_MISS_REFLECT) { // Calculate reflected spell result on caster target.reflectResult = m_caster->SpellHitResult(m_caster, m_spellInfo, m_canReflect); @@ -775,7 +911,7 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex) target.reflectResult = SPELL_MISS_PARRY; // Increase time interval for reflected spells by 1.5 - target.timeDelay+=target.timeDelay>>1; + target.timeDelay += target.timeDelay >> 1; } else target.reflectResult = SPELL_MISS_NONE; @@ -786,24 +922,24 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex) void Spell::AddUnitTarget(uint64 unitGUID, uint32 effIndex) { - Unit* unit = m_caster->GetGUID()==unitGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, unitGUID); + Unit* unit = m_caster->GetGUID() == unitGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, unitGUID); if (unit) AddUnitTarget(unit, effIndex); } void Spell::AddGOTarget(GameObject* pVictim, uint32 effIndex) { - if( m_spellInfo->Effect[effIndex]==0 ) + if( m_spellInfo->Effect[effIndex] == 0 ) return; uint64 targetGUID = pVictim->GetGUID(); // Lookup target in already in list - for(std::list<GOTargetInfo>::iterator ihit= m_UniqueGOTargetInfo.begin();ihit != m_UniqueGOTargetInfo.end();++ihit) + for(std::list<GOTargetInfo>::iterator ihit = m_UniqueGOTargetInfo.begin(); ihit != m_UniqueGOTargetInfo.end(); ++ihit) { if (targetGUID == ihit->targetGUID) // Found in list { - ihit->effectMask |= 1<<effIndex; // Add only effect mask + ihit->effectMask |= 1 << effIndex; // Add only effect mask return; } } @@ -812,7 +948,7 @@ void Spell::AddGOTarget(GameObject* pVictim, uint32 effIndex) GOTargetInfo target; target.targetGUID = targetGUID; - target.effectMask = 1<<effIndex; + target.effectMask = 1 << effIndex; target.processed = false; // Effects not apply on target // Spell have speed - need calculate incoming time @@ -828,26 +964,24 @@ void Spell::AddGOTarget(GameObject* pVictim, uint32 effIndex) else target.timeDelay = 0LL; - ++m_countOfHit; - // Add target to list m_UniqueGOTargetInfo.push_back(target); } void Spell::AddGOTarget(uint64 goGUID, uint32 effIndex) { - GameObject* go = ObjectAccessor::GetGameObject(*m_caster, goGUID); + GameObject* go = m_caster->GetMap()->GetGameObject(goGUID); if (go) AddGOTarget(go, effIndex); } void Spell::AddItemTarget(Item* pitem, uint32 effIndex) { - if( m_spellInfo->Effect[effIndex]==0 ) + if( m_spellInfo->Effect[effIndex] == 0 ) return; // Lookup target in already in list - for(std::list<ItemTargetInfo>::iterator ihit= m_UniqueItemInfo.begin();ihit != m_UniqueItemInfo.end();++ihit) + for(std::list<ItemTargetInfo>::iterator ihit = m_UniqueItemInfo.begin(); ihit != m_UniqueItemInfo.end(); ++ihit) { if (pitem == ihit->item) // Found in list { @@ -860,86 +994,9 @@ void Spell::AddItemTarget(Item* pitem, uint32 effIndex) ItemTargetInfo target; target.item = pitem; - target.effectMask = 1<<effIndex; + target.effectMask = 1 << effIndex; m_UniqueItemInfo.push_back(target); } -/* -void Spell::doTriggers(SpellMissInfo missInfo, uint32 damage, SpellSchoolMask damageSchoolMask, uint32 block, uint32 absorb, bool crit) -{ - // Do triggers depends from hit result (triggers on hit do in effects) - // Set aura states depends from hit result - if (missInfo!=SPELL_MISS_NONE) - { - // Miss/dodge/parry/block only for melee based spells - // Resist only for magic based spells - switch (missInfo) - { - case SPELL_MISS_MISS: - if(m_caster->GetTypeId()== TYPEID_PLAYER) - ((Player*)m_caster)->UpdateWeaponSkill(BASE_ATTACK); - - m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_MISS, m_spellInfo, m_IsTriggeredSpell); - break; - case SPELL_MISS_RESIST: - m_caster->ProcDamageAndSpell(unitTarget, PROC_FLAG_TARGET_RESISTS, PROC_FLAG_RESIST_SPELL, 0, damageSchoolMask, m_spellInfo, m_IsTriggeredSpell); - break; - case SPELL_MISS_DODGE: - if(unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->UpdateDefense(); - - // Overpower - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->getClass() == CLASS_WARRIOR) - { - ((Player*) m_caster)->AddComboPoints(unitTarget, 1); - m_caster->StartReactiveTimer( REACTIVE_OVERPOWER ); - } - - // Riposte - if (unitTarget->getClass() != CLASS_ROGUE) - { - unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true); - unitTarget->StartReactiveTimer( REACTIVE_DEFENSE ); - } - - m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_DODGE, m_spellInfo, m_IsTriggeredSpell); - break; - case SPELL_MISS_PARRY: - // Update victim defense ? - if(unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->UpdateDefense(); - // Mongoose bite - set only Counterattack here - if (unitTarget->getClass() == CLASS_HUNTER) - { - unitTarget->ModifyAuraState(AURA_STATE_HUNTER_PARRY,true); - unitTarget->StartReactiveTimer( REACTIVE_HUNTER_PARRY ); - } - else - { - unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true); - unitTarget->StartReactiveTimer( REACTIVE_DEFENSE ); - } - m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_PARRY, m_spellInfo, m_IsTriggeredSpell); - break; - case SPELL_MISS_BLOCK: - unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true); - unitTarget->StartReactiveTimer( REACTIVE_DEFENSE ); - - m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_BLOCK, m_spellInfo, m_IsTriggeredSpell); - break; - // Trigger from this events not supported - case SPELL_MISS_EVADE: - case SPELL_MISS_IMMUNE: - case SPELL_MISS_IMMUNE2: - case SPELL_MISS_DEFLECT: - case SPELL_MISS_ABSORB: - // Trigger from reflects need do after get reflect result - case SPELL_MISS_REFLECT: - break; - default: - break; - } - } -}*/ void Spell::DoAllEffectOnTarget(TargetInfo *target) { @@ -949,15 +1006,16 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) // Get mask of effects for target uint32 mask = target->effectMask; - if (mask == 0) // No effects - return; Unit* unit = m_caster->GetGUID()==target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster,target->targetGUID); if (!unit) return; + if(unit->isAlive() != target->alive) + return; + // Get original caster (if exist) and calculate damage/healing from him data - Unit *caster = m_originalCasterGUID ? m_originalCaster : m_caster; + Unit *caster = m_originalCaster ? m_originalCaster : m_caster; // Skip if m_originalCaster not avaiable if (!caster) @@ -976,57 +1034,63 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) // Fill base trigger info uint32 procAttacker = m_procAttacker; uint32 procVictim = m_procVictim; - uint32 procEx = PROC_EX_NONE; + uint32 procEx = m_procEx; + + m_spellAura = NULL; // Set aura to null for every target-make sure that pointer is not used for unit without aura applied //Spells with this flag cannot trigger if effect is casted on self // Slice and Dice, relentless strikes, eviscerate - //bool canEffectTrigger = (m_spellInfo->AttributesEx4 & (SPELL_ATTR_EX4_CANT_PROC_FROM_SELFCAST | SPELL_ATTR_EX4_UNK4) ? m_caster!=unitTarget : true) - // && m_canTrigger; + bool canEffectTrigger = m_canTrigger && (m_spellInfo->AttributesEx4 & (SPELL_ATTR_EX4_CANT_PROC_FROM_SELFCAST) ? m_caster!=unitTarget : true); + Unit * spellHitTarget = NULL; if (missInfo==SPELL_MISS_NONE) // In case spell hit target, do all effect on that target - DoSpellHitOnUnit(unit, mask); + spellHitTarget = unit; else if (missInfo == SPELL_MISS_REFLECT) // In case spell reflect from target, do all effect on caster (if hit) { if (target->reflectResult == SPELL_MISS_NONE) // If reflected spell hit caster -> do all effect on him - DoSpellHitOnUnit(m_caster, mask); + spellHitTarget = m_caster; } - /*else //TODO: This is a hack. need fix + + if(spellHitTarget) { - uint32 tempMask = 0; - for(uint32 i = 0; i < 3; ++i) - if(m_spellInfo->Effect[i] == SPELL_EFFECT_DUMMY - || m_spellInfo->Effect[i] == SPELL_EFFECT_TRIGGER_SPELL) - tempMask |= 1<<i; - if(tempMask &= mask) - DoSpellHitOnUnit(unit, tempMask); - }*/ + SpellMissInfo missInfo = DoSpellHitOnUnit(spellHitTarget, mask); + if(missInfo != SPELL_MISS_NONE) + { + if(missInfo != SPELL_MISS_MISS) + m_caster->SendSpellMiss(unit, m_spellInfo->Id, missInfo); + m_damage = 0; + spellHitTarget = NULL; + } + } + + // Do not take combo points on dodge + if (m_needComboPoints && m_targets.getUnitTargetGUID() == target->targetGUID) + if( missInfo != SPELL_MISS_NONE && missInfo != SPELL_MISS_MISS) + m_needComboPoints = false; // All calculated do it! // Do healing and triggers if (m_healing > 0) { - bool crit = caster->isSpellCrit(NULL, m_spellInfo, m_spellSchoolMask); + bool crit = caster->isSpellCrit(unitTarget, m_spellInfo, m_spellSchoolMask); uint32 addhealth = m_healing; if (crit) { procEx |= PROC_EX_CRITICAL_HIT; - addhealth = caster->SpellCriticalBonus(m_spellInfo, addhealth, NULL); + addhealth = caster->SpellCriticalHealingBonus(m_spellInfo, addhealth, NULL); } else procEx |= PROC_EX_NORMAL_HIT; - caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, addhealth, crit); - // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) - if (missInfo != SPELL_MISS_REFLECT) - caster->ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, addhealth, m_attackType, m_spellInfo, m_canTrigger); + if (canEffectTrigger && missInfo != SPELL_MISS_REFLECT) + caster->ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, addhealth, m_attackType, m_spellInfo, m_triggeredByAuraSpell); - int32 gain = unitTarget->ModifyHealth( int32(addhealth) ); + if (m_spellAura) + m_spellAura->SetProcDamage(addhealth); + int32 gain = caster->DealHeal(unitTarget, addhealth, m_spellInfo, crit); unitTarget->getHostilRefManager().threatAssist(caster, float(gain) * 0.5f, m_spellInfo); - if(caster->GetTypeId()==TYPEID_PLAYER) - if(BattleGround *bg = ((Player*)caster)->GetBattleGround()) - bg->UpdatePlayerScore(((Player*)caster), SCORE_HEALING_DONE, gain); } // Do damage and triggers else if (m_damage > 0) @@ -1036,75 +1100,43 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) // Add bonuses and fill damageInfo struct caster->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit); + caster->DealDamageMods(damageInfo.target,damageInfo.damage,&damageInfo.absorb); // Send log damage message to client caster->SendSpellNonMeleeDamageLog(&damageInfo); - procEx = createProcExtendMask(&damageInfo, missInfo); + procEx |= createProcExtendMask(&damageInfo, missInfo); procVictim |= PROC_FLAG_TAKEN_ANY_DAMAGE; - - caster->DealSpellDamage(&damageInfo, true); // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) - if (missInfo != SPELL_MISS_REFLECT) + if (canEffectTrigger && missInfo != SPELL_MISS_REFLECT) { - caster->ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, damageInfo.damage, m_attackType, m_spellInfo, m_canTrigger); + caster->ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, damageInfo.damage, m_attackType, m_spellInfo, m_triggeredByAuraSpell); if(caster->GetTypeId() == TYPEID_PLAYER && (m_spellInfo->Attributes & SPELL_ATTR_STOP_ATTACK_TARGET) == 0 && (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)) ((Player *)caster)->CastItemCombatSpell(unitTarget, m_attackType, procVictim, procEx); } - // Shadow Word: Death - deals damage equal to damage done to caster if victim is not killed - if (m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST && m_spellInfo->SpellFamilyFlags&0x0000000200000000LL && - caster != unitTarget && unitTarget->isAlive()) - { - // Redirect damage to caster if victim alive - m_caster->CastCustomSpell(m_caster, 32409, &m_damage, NULL, NULL, true); - } + if (m_spellAura) + m_spellAura->SetProcDamage(damageInfo.damage); + caster->DealSpellDamage(&damageInfo, true); + // Judgement of Blood - else if (m_spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN && m_spellInfo->SpellFamilyFlags & 0x0000000800000000LL && m_spellInfo->SpellIconID==153) + if (m_spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN && m_spellInfo->SpellFamilyFlags[1] & 0x00000008 && m_spellInfo->SpellIconID==153) { int32 damagePoint = damageInfo.damage * 33 / 100; m_caster->CastCustomSpell(m_caster, 32220, &damagePoint, NULL, NULL, true); } - // Bloodthirst - else if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR && m_spellInfo->SpellFamilyFlags & 0x40000000000LL) - { - uint32 BTAura = 0; - switch(m_spellInfo->Id) - { - case 23881: BTAura = 23885; break; - case 23892: BTAura = 23886; break; - case 23893: BTAura = 23887; break; - case 23894: BTAura = 23888; break; - case 25251: BTAura = 25252; break; - case 30335: BTAura = 30339; break; - default: - sLog.outError("Spell::EffectSchoolDMG: Spell %u not handled in BTAura",m_spellInfo->Id); - break; - } - if (BTAura) - m_caster->CastSpell(m_caster,BTAura,true); - } } // Passive spell hits/misses or active spells only misses (only triggers) else { // Fill base damage struct (unitTarget - is real spell target) SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, m_spellSchoolMask); - procEx = createProcExtendMask(&damageInfo, missInfo); + procEx |= createProcExtendMask(&damageInfo, missInfo); // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) - if (missInfo != SPELL_MISS_REFLECT) - caster->ProcDamageAndSpell(unit, procAttacker, procVictim, procEx, 0, m_attackType, m_spellInfo, m_canTrigger); - } - - // Call scripted function for AI if this spell is casted upon a creature (except pets) - if(IS_CREATURE_GUID(target->targetGUID)) - { - // cast at creature (or GO) quest objectives update at successful cast finished (+channel finished) - // ignore autorepeat/melee casts for speed (not exist quest for spells (hm... ) - if( m_caster->GetTypeId() == TYPEID_PLAYER && !IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive() ) - ((Player*)m_caster)->CastedCreatureOrGO(unit->GetEntry(),unit->GetGUID(),m_spellInfo->Id); + if (canEffectTrigger && missInfo != SPELL_MISS_REFLECT) + caster->ProcDamageAndSpell(unit, procAttacker, procVictim, procEx, 0, m_attackType, m_spellInfo, m_triggeredByAuraSpell); } if( !m_caster->IsFriendlyTo(unit) && !IsPositiveSpell(m_spellInfo->Id)) @@ -1116,54 +1148,86 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) else if(m_customAttr & SPELL_ATTR_CU_AURA_CC) { if(!unit->IsStandState()) - unit->SetStandState(PLAYER_STATE_NONE); + unit->SetStandState(UNIT_STAND_STATE_STAND); } } + + if(spellHitTarget) + { + //AI functions + if(spellHitTarget->GetTypeId() == TYPEID_UNIT) + { + if(((Creature*)spellHitTarget)->IsAIEnabled) + ((Creature*)spellHitTarget)->AI()->SpellHit(m_caster, m_spellInfo); + + // cast at creature (or GO) quest objectives update at successful cast finished (+channel finished) + // ignore pets or autorepeat/melee casts for speed (not exist quest for spells (hm... ) + if(m_originalCaster && m_originalCaster->IsControlledByPlayer() && !((Creature*)spellHitTarget)->isPet() && !IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive() ) + { + if ( Player* p = m_originalCaster->GetCharmerOrOwnerPlayerOrPlayerItself() ) + { + p->CastedCreatureOrGO(spellHitTarget->GetEntry(),spellHitTarget->GetGUID(),m_spellInfo->Id); + } + } + } + + if(m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->IsAIEnabled) + ((Creature*)m_caster)->AI()->SpellHitTarget(spellHitTarget, m_spellInfo); + + // Needs to be called after dealing damage/healing to not remove breaking on damage auras + DoTriggersOnSpellHit(spellHitTarget); + } } -void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) +SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) { if(!unit || !effectMask) - return; + return SPELL_MISS_EVADE; // Recheck immune (only for delayed spells) - if( m_spellInfo->speed && - !(m_spellInfo->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) - && (unit->IsImmunedToDamage(GetSpellSchoolMask(m_spellInfo),true) || - unit->IsImmunedToSpell(m_spellInfo,true) )) + if(m_spellInfo->speed && (unit->IsImmunedToDamage(m_spellInfo) || unit->IsImmunedToSpell(m_spellInfo))) + return SPELL_MISS_IMMUNE; + + if (unit->GetTypeId() == TYPEID_PLAYER) { - m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_IMMUNE); - m_damage = 0; - return; + ((Player*)unit)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, m_spellInfo->Id); + ((Player*)unit)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2, m_spellInfo->Id); + } + + if(m_caster->GetTypeId() == TYPEID_PLAYER) + { + ((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2, m_spellInfo->Id, 0, unit); } if( m_caster != unit ) { - if (unit->GetCharmerOrOwnerGUID() != m_caster->GetGUID()) + // Recheck UNIT_FLAG_NON_ATTACKABLE for delayed spells + if (m_spellInfo->speed > 0.0f && + unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE) && + unit->GetCharmerOrOwnerGUID() != m_caster->GetGUID()) { - if (unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - { - m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); - m_damage = 0; - return; - } + return SPELL_MISS_EVADE; } + if( !m_caster->IsFriendlyTo(unit) ) { // reset damage to 0 if target has Invisibility or Vanish aura (_only_ vanish, not stealth) and isn't visible for caster - bool isVisibleForHit = ( (unit->HasAuraType(SPELL_AURA_MOD_INVISIBILITY) || unit->HasAuraTypeWithFamilyFlags(SPELL_AURA_MOD_STEALTH, SPELLFAMILY_ROGUE ,SPELLFAMILYFLAG_ROGUE_VANISH)) && !unit->isVisibleForOrDetect(m_caster, true)) ? false : true; - + // I do not think this is a correct way to fix it. Sanctuary effect should make all delayed spells invalid // for delayed spells ignore not visible explicit target - if(m_spellInfo->speed > 0.0f && unit==m_targets.getUnitTarget() && !isVisibleForHit) + if(m_spellInfo->speed > 0.0f && unit == m_targets.getUnitTarget() + && (unit->m_invisibilityMask || m_caster->m_invisibilityMask + || unit->HasAuraTypeWithFamilyFlags(SPELL_AURA_MOD_STEALTH, SPELLFAMILY_ROGUE, SPELLFAMILYFLAG_ROGUE_VANISH)) + && !m_caster->canSeeOrDetect(unit, true)) { // that was causing CombatLog errors - //m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); - m_damage = 0; - return; + // return SPELL_MISS_EVADE; + return SPELL_MISS_MISS; // miss = do not send anything here } + unit->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_HITBYSPELL); - if(m_customAttr & SPELL_ATTR_CU_AURA_CC) - unit->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CC); + //TODO: This is a hack. But we do not know what types of stealth should be interrupted by CC + if((m_customAttr & SPELL_ATTR_CU_AURA_CC) && unit->IsControlledByPlayer()) + unit->RemoveAurasByType(SPELL_AURA_MOD_STEALTH); } else { @@ -1171,16 +1235,15 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) // TODO: this cause soul transfer bugged if(m_spellInfo->speed > 0.0f && unit->GetTypeId() == TYPEID_PLAYER && !IsPositiveSpell(m_spellInfo->Id)) { - m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); - m_damage = 0; - return; + return SPELL_MISS_EVADE; } // assisting case, healing and resurrection if(unit->hasUnitState(UNIT_STAT_ATTACK_PLAYER)) { m_caster->SetContestedPvP(); - //m_caster->UpdatePvP(true); + if(m_caster->GetTypeId() == TYPEID_PLAYER) + ((Player*)m_caster)->UpdatePvP(true); } if( unit->isInCombat() && !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) ) { @@ -1200,51 +1263,99 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) unit->IncrDiminishing(m_diminishGroup); } - for(uint32 effectNumber=0;effectNumber<3;effectNumber++) + uint8 aura_effmask = 0; + for (uint8 i = 0; i < 3; ++i) + if (effectMask & (1<<i) && (m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA || IsAreaAuraEffect(m_spellInfo->Effect[i]))) + aura_effmask |= 1<<i; + + if (aura_effmask) { - if (effectMask & (1<<effectNumber)) + Unit * caster = m_originalCaster ? m_originalCaster : m_caster; + Aura * Aur = new Aura(m_spellInfo, aura_effmask, m_currentBasePoints, unit, m_caster, caster, m_CastItem); + + if (!Aur->IsAreaAura()) { - HandleEffects(unit,NULL,NULL,effectNumber/*,m_damageMultipliers[effectNumber]*/); - //Only damage and heal spells need this - /*if ( m_applyMultiplierMask & (1 << effectNumber) ) + // Now Reduce spell duration using data received at spell hit + int32 duration = Aur->GetAuraMaxDuration(); + unit->ApplyDiminishingToDuration(m_diminishGroup,duration,caster,m_diminishLevel); + Aur->setDiminishGroup(m_diminishGroup); + + duration = caster->ModSpellDuration(m_spellInfo, unit, duration, Aur->IsPositive()); + + //mod duration of channeled aura by spell haste + if (IsChanneledSpell(m_spellInfo)) + caster->ModSpellCastTime(m_spellInfo, duration, this); + + if(duration != Aur->GetAuraMaxDuration()) { - // Get multiplier - float multiplier = m_spellInfo->DmgMultiplier[effectNumber]; - // Apply multiplier mods - if(m_originalCaster) - if(Player* modOwner = m_originalCaster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_EFFECT_PAST_FIRST, multiplier,this); - m_damageMultipliers[effectNumber] *= multiplier; - }*/ + Aur->SetAuraMaxDuration(duration); + Aur->SetAuraDuration(duration); + } + + // Prayer of Mending (jump animation), we need formal caster instead original for correct animation + if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST) + { + if(m_spellInfo->SpellFamilyFlags[1] & 0x000020) + m_caster->CastSpell(unit, 41637, true, NULL, NULL, m_originalCasterGUID); + } } + // Set aura only when successfully applied + if (unit->AddAura(Aur, false)) + m_spellAura = Aur; } - if(unit->GetTypeId() == TYPEID_UNIT && ((Creature*)unit)->IsAIEnabled) - ((Creature*)unit)->AI()->SpellHit(m_caster, m_spellInfo); + for(uint32 effectNumber = 0; effectNumber < 3; ++effectNumber) + { + if (effectMask & (1<<effectNumber)) + HandleEffects(unit,NULL,NULL,effectNumber); + } - if(m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->IsAIEnabled) - ((Creature*)m_caster)->AI()->SpellHitTarget(unit, m_spellInfo); + return SPELL_MISS_NONE; +} - // trigger only for first effect targets - if (m_ChanceTriggerSpells.size() && (effectMask & 0x1)) +void Spell::DoTriggersOnSpellHit(Unit *unit) +{ + // Apply additional spell effects to target + if (m_preCastSpell) + { + // Special spell id + // TODO: Handle all of special spells in one place? + if(m_preCastSpell==61988) + { + //Cast Forbearance + m_caster->CastSpell(unit,25771, true, m_CastItem); + // Cast Avenging Wrath Marker + m_caster->CastSpell(unit,61987, true, m_CastItem); + } + else + m_caster->CastSpell(unit,m_preCastSpell, true, m_CastItem); + } + + // spells with this flag can trigger only if not selfcast (eviscerate for example) + if (m_ChanceTriggerSpells.size() && (!((m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_CANT_PROC_FROM_SELFCAST) && unit==m_caster))) { int _duration=0; for(ChanceTriggerSpells::const_iterator i = m_ChanceTriggerSpells.begin(); i != m_ChanceTriggerSpells.end(); ++i) { + // SPELL_AURA_ADD_TARGET_TRIGGER auras shouldn't trigger auras without duration + // set duration equal to triggering spell if(roll_chance_i(i->second)) { m_caster->CastSpell(unit, i->first, true); - // SPELL_AURA_ADD_TARGET_TRIGGER auras shouldn't trigger auras without duration - // set duration equal to triggering spell - if (GetSpellDuration(i->first)==-1) + sLog.outDebug("Spell %d triggered spell %d by SPELL_AURA_ADD_TARGET_TRIGGER aura", m_spellInfo->Id, i->first); + } + if (GetSpellDuration(i->first)==-1) + { + if (Aura * triggeredAur = unit->GetAura(i->first->Id, m_caster->GetGUID())) { // get duration from aura-only once if (!_duration) { - Aura * aur = unit->GetAuraByCasterSpell(m_spellInfo->Id, m_caster->GetGUID()); + Aura * aur = unit->GetAura(m_spellInfo->Id, m_caster->GetGUID()); _duration = aur ? aur->GetAuraDuration() : -1; } - unit->SetAurasDurationByCasterSpell(i->first->Id, m_caster->GetGUID(), _duration); + triggeredAur->SetAuraDuration(_duration); + triggeredAur->SetPermanent(false); } } } @@ -1259,15 +1370,6 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) else unit->CastSpell(unit, *i, true, 0, 0, m_caster->GetGUID()); } - - //This is not needed with procflag patch - /*if(m_originalCaster) - { - if(m_customAttr & SPELL_ATTR_CU_EFFECT_HEAL) - m_originalCaster->ProcDamageAndSpell(unit, PROC_FLAG_HEAL, PROC_FLAG_NONE, 0, GetSpellSchoolMask(m_spellInfo), m_spellInfo); - if(m_originalCaster != unit && (m_customAttr & SPELL_ATTR_CU_EFFECT_DAMAGE)) - m_originalCaster->ProcDamageAndSpell(unit, PROC_FLAG_HIT_SPELL, PROC_FLAG_STRUCK_SPELL, 0, GetSpellSchoolMask(m_spellInfo), m_spellInfo); - }*/ } void Spell::DoAllEffectOnTarget(GOTargetInfo *target) @@ -1280,18 +1382,21 @@ void Spell::DoAllEffectOnTarget(GOTargetInfo *target) if(!effectMask) return; - GameObject* go = ObjectAccessor::GetGameObject(*m_caster, target->targetGUID); + GameObject* go = m_caster->GetMap()->GetGameObject(target->targetGUID); if(!go) return; - for(uint32 effectNumber=0;effectNumber<3;effectNumber++) - if (effectMask & (1<<effectNumber)) - HandleEffects(NULL,NULL,go,effectNumber); + for(uint32 effectNumber = 0; effectNumber < 3; ++effectNumber) + if (effectMask & (1 << effectNumber)) + HandleEffects(NULL, NULL, go, effectNumber); // cast at creature (or GO) quest objectives update at successful cast finished (+channel finished) // ignore autorepeat/melee casts for speed (not exist quest for spells (hm... ) - if( m_caster->GetTypeId() == TYPEID_PLAYER && !IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive() ) - ((Player*)m_caster)->CastedCreatureOrGO(go->GetEntry(),go->GetGUID(),m_spellInfo->Id); + if(m_originalCaster && m_originalCaster->IsControlledByPlayer() && !IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive() ) + { + if ( Player* p = m_originalCaster->GetCharmerOrOwnerPlayerOrPlayerItself() ) + p->CastedCreatureOrGO(go->GetEntry(),go->GetGUID(),m_spellInfo->Id); + } } void Spell::DoAllEffectOnTarget(ItemTargetInfo *target) @@ -1300,32 +1405,63 @@ void Spell::DoAllEffectOnTarget(ItemTargetInfo *target) if(!target->item || !effectMask) return; - for(uint32 effectNumber=0;effectNumber<3;effectNumber++) - if (effectMask & (1<<effectNumber)) + for(uint32 effectNumber = 0; effectNumber < 3; ++effectNumber) + if (effectMask & (1 << effectNumber)) HandleEffects(NULL, target->item, NULL, effectNumber); } -bool Spell::IsAliveUnitPresentInTargetList() +bool Spell::UpdateChanneledTargetList() { // Not need check return true if (m_needAliveTargetMask == 0) return true; uint8 needAliveTargetMask = m_needAliveTargetMask; + uint8 needAuraMask = 0; + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA) + needAuraMask |= 1<<i; - for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) + needAuraMask &= needAliveTargetMask; + + float range; + if(needAuraMask) + { + range = GetSpellMaxRange(m_spellInfo, IsPositiveSpell(m_spellInfo->Id)); + if(Player * modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this); + } + + for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) { if( ihit->missCondition == SPELL_MISS_NONE && (needAliveTargetMask & ihit->effectMask) ) { - Unit *unit = m_caster->GetGUID()==ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); + Unit *unit = m_caster->GetGUID() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); if (unit && unit->isAlive()) + { + if (needAuraMask & ihit->effectMask) + { + if(Aura * aur = unit->GetAura(m_spellInfo->Id, m_originalCasterGUID)) + { + if (m_caster != unit && !m_caster->IsWithinDistInMap(unit,range)) + { + ihit->effectMask &= ~aur->GetEffectMask(); + unit->RemoveAura(aur); + continue; + } + } + else // aura is dispelled + continue; + } + needAliveTargetMask &= ~ihit->effectMask; // remove from need alive mask effect that have alive target + } } } // is all effects from m_needAliveTargetMask have alive targets - return needAliveTargetMask==0; + return needAliveTargetMask == 0; } // Helper for Chain Healing @@ -1368,7 +1504,7 @@ struct TargetDistanceOrder : public std::binary_function<const Unit, const Unit, // functor for operator ">" bool operator()(const Unit* _Left, const Unit* _Right) const { - return (MainTarget->GetDistance(_Left) < MainTarget->GetDistance(_Right)); + return MainTarget->GetDistanceOrder(_Left,_Right); } }; @@ -1378,6 +1514,16 @@ void Spell::SearchChainTarget(std::list<Unit*> &TagUnitMap, float max_range, uin if(!cur) return; + // Get spell max affected targets + /*uint32 unMaxTargets = m_spellInfo->MaxAffectedTargets; + Unit::AuraList const& mod = m_caster->GetAurasByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS); + for(Unit::AuraList::const_iterator m = mod.begin(); m != mod.end(); ++m) + { + if (!(*m)->isAffectedOnSpell(m_spellInfo)) + continue; + unMaxTargets+=(*m)->GetAmount(); + }*/ + //FIXME: This very like horrible hack and wrong for most spells if(m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_MELEE) max_range += num * CHAIN_SPELL_JUMP_RADIUS; @@ -1423,7 +1569,7 @@ void Spell::SearchChainTarget(std::list<Unit*> &TagUnitMap, float max_range, uin if(cur->GetDistance(*next) > CHAIN_SPELL_JUMP_RADIUS) break; while(m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE - && !m_caster->isInFront(*next, max_range) + && !m_caster->isInFrontInMap(*next, max_range) || !m_caster->canSeeOrDetect(*next, false) || !cur->IsWithinLOSInMap(*next)) { @@ -1438,7 +1584,7 @@ void Spell::SearchChainTarget(std::list<Unit*> &TagUnitMap, float max_range, uin } } -void Spell::SearchAreaTarget(std::list<Unit*> &TagUnitMap, float radius, const uint32 type, SpellTargets TargetType, uint32 entry) +void Spell::SearchAreaTarget(std::list<Unit*> &TagUnitMap, float radius, SpellNotifyPushType type, SpellTargets TargetType, uint32 entry) { float x, y, z; switch(type) @@ -1481,6 +1627,9 @@ void Spell::SearchAreaTarget(std::list<Unit*> &TagUnitMap, float radius, const u m_caster->GetMap()->VisitWorld(x, y, radius, notifier); else m_caster->GetMap()->VisitAll(x, y, radius, notifier); + + if(m_customAttr & SPELL_ATTR_CU_EXCLUDE_SELF) + TagUnitMap.remove(m_caster); } WorldObject* Spell::SearchNearbyTarget(float range, SpellTargets TargetType) @@ -1493,7 +1642,7 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargets TargetType) SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(m_spellInfo->Id); if(lower == upper) { - sLog.outErrorDb("Spell (ID: %u) (caster Entry: %u) does not have record in `spell_script_target`", m_spellInfo->Id, m_caster->GetEntry()); + sLog.outDebug("Spell (ID: %u) (caster Entry: %u) does not have record in `spell_script_target`", m_spellInfo->Id, m_caster->GetEntry()); if(IsPositiveSpell(m_spellInfo->Id)) return SearchNearbyTarget(range, SPELL_TARGETS_ALLY); else @@ -1508,21 +1657,14 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargets TargetType) switch(i_spellST->second.type) { case SPELL_TARGET_TYPE_GAMEOBJECT: - { - GameObject* p_GameObject = NULL; - if(i_spellST->second.targetEntry) { - Trinity::NearestGameObjectEntryInObjectRangeCheck go_check(*m_caster,i_spellST->second.targetEntry,range); - Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck> checker(p_GameObject,go_check); - m_caster->VisitNearbyGridObject(range, checker); - - if(p_GameObject) + if(GameObject *go = m_caster->FindNearestGameObject(i_spellST->second.targetEntry, range)) { // remember found target and range, next attempt will find more near target with another entry + goScriptTarget = go; creatureScriptTarget = NULL; - goScriptTarget = p_GameObject; - range = go_check.GetLastRange(); + range = m_caster->GetDistance(goScriptTarget); } } else if( focusObject ) //Focus Object @@ -1536,25 +1678,18 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargets TargetType) } } break; - } case SPELL_TARGET_TYPE_CREATURE: + if(m_targets.getUnitTarget() && m_targets.getUnitTarget()->GetEntry() == i_spellST->second.targetEntry) + return m_targets.getUnitTarget(); case SPELL_TARGET_TYPE_DEAD: default: - { - Creature *p_Creature = NULL; - - Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster,i_spellST->second.targetEntry,i_spellST->second.type!=SPELL_TARGET_TYPE_DEAD,range); - Trinity::CreatureLastSearcher<Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(p_Creature, u_check); - m_caster->VisitNearbyObject(range, searcher); - - if(p_Creature ) + if(Creature *cre = m_caster->FindNearestCreature(i_spellST->second.targetEntry, range, i_spellST->second.type != SPELL_TARGET_TYPE_DEAD)) { - creatureScriptTarget = p_Creature; + creatureScriptTarget = cre; goScriptTarget = NULL; - range = u_check.GetLastRange(); + range = m_caster->GetDistance(creatureScriptTarget); } break; - } } } @@ -1568,7 +1703,7 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargets TargetType) { Unit *target = NULL; Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(m_caster, m_caster, range); - Trinity::UnitLastSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(target, u_check); + Trinity::UnitLastSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(m_caster, target, u_check); m_caster->VisitNearbyObject(range, searcher); return target; } @@ -1576,7 +1711,7 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargets TargetType) { Unit *target = NULL; Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(m_caster, m_caster, range); - Trinity::UnitLastSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(target, u_check); + Trinity::UnitLastSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(m_caster, target, u_check); m_caster->VisitNearbyObject(range, searcher); return target; } @@ -1602,9 +1737,9 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) case TARGET_UNIT_CASTER_FISHING: { //AddUnitTarget(m_caster, i); - float min_dis = GetSpellMinRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); - float max_dis = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); - float dis = m_caster->GetMap()->rand_norm() * (max_dis - min_dis) + min_dis; + float min_dis = GetSpellMinRange(m_spellInfo, true); + float max_dis = GetSpellMaxRange(m_spellInfo, true); + float dis = rand_norm() * (max_dis - min_dis) + min_dis; float x, y, z; m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE, dis); m_targets.setDestination(x, y, z); @@ -1615,13 +1750,22 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) AddUnitTarget(owner, i); break; case TARGET_UNIT_PET: - if(Pet* pet = m_caster->GetPet()) + if(Guardian* pet = m_caster->GetGuardianPet()) AddUnitTarget(pet, i); break; case TARGET_UNIT_PARTY_CASTER: case TARGET_UNIT_RAID_CASTER: pushType = PUSH_CASTER_CENTER; break; + case TARGET_UNIT_VEHICLE: + if(Vehicle *vehicle = m_caster->m_Vehicle) + AddUnitTarget(vehicle, i); + break; + case TARGET_UNIT_PASSENGER: + if(m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->isVehicle()) + if(Unit *unit = ((Vehicle*)m_caster)->GetPassenger(1)) // maybe not right + AddUnitTarget(unit, i); + break; } break; } @@ -1638,13 +1782,23 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) switch(cur) { case TARGET_UNIT_TARGET_ENEMY: - SelectMagnetTarget(); + if(Unit *magnet = m_caster->SelectMagnetTarget(target, m_spellInfo)) + if(magnet != target) + m_targets.setUnitTarget(magnet); + pushType = PUSH_CHAIN; + break; + case TARGET_UNIT_TARGET_ANY: + if(!IsPositiveSpell(m_spellInfo->Id)) + if(Unit *magnet = m_caster->SelectMagnetTarget(target, m_spellInfo)) + if(magnet != target) + m_targets.setUnitTarget(magnet); + pushType = PUSH_CHAIN; + break; case TARGET_UNIT_CHAINHEAL: pushType = PUSH_CHAIN; break; case TARGET_UNIT_TARGET_ALLY: case TARGET_UNIT_TARGET_RAID: - case TARGET_UNIT_TARGET_ANY: // SelectMagnetTarget()? case TARGET_UNIT_TARGET_PARTY: case TARGET_UNIT_MINIPET: AddUnitTarget(target, i); @@ -1659,38 +1813,41 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) case TARGET_TYPE_UNIT_NEARBY: { - float range = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); - if(modOwner) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this); - WorldObject *target = NULL; + float range; switch(cur) { case TARGET_UNIT_NEARBY_ENEMY: + range = GetSpellMaxRange(m_spellInfo, false); + if(modOwner) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this); target = SearchNearbyTarget(range, SPELL_TARGETS_ENEMY); break; case TARGET_UNIT_NEARBY_ALLY: case TARGET_UNIT_NEARBY_ALLY_UNK: case TARGET_UNIT_NEARBY_RAID: + range = GetSpellMaxRange(m_spellInfo, true); + if(modOwner) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this); target = SearchNearbyTarget(range, SPELL_TARGETS_ALLY); break; case TARGET_UNIT_NEARBY_ENTRY: + range = GetSpellMaxRange(m_spellInfo, IsPositiveSpell(m_spellInfo->Id)); + if(modOwner) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this); target = SearchNearbyTarget(range, SPELL_TARGETS_ENTRY); break; } if(!target) return; - else if(target->GetTypeId() == TYPEID_UNIT) + else if(target->GetTypeId() == TYPEID_GAMEOBJECT) + AddGOTarget((GameObject*)target, i); + else { pushType = PUSH_CHAIN; - if(!m_targets.getUnitTarget()) + if(m_targets.getUnitTarget() != target) m_targets.setUnitTarget((Unit*)target); } - else if(target->GetTypeId() == TYPEID_GAMEOBJECT) - AddGOTarget((GameObject*)target, i); break; } @@ -1728,11 +1885,11 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) float x, y, z, angle, dist; float objSize = m_caster->GetObjectSize(); - dist = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + dist = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); if(dist < objSize) dist = objSize; else if(cur == TARGET_DEST_CASTER_RANDOM) - dist = objSize + (dist - objSize) * m_caster->GetMap()->rand_norm(); + dist = objSize + (dist - objSize) * rand_norm(); switch(cur) { @@ -1772,11 +1929,11 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) float x, y, z, angle, dist; float objSize = target->GetObjectSize(); - dist = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + dist = target->GetSpellRadiusForTarget(target, sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); if(dist < objSize) dist = objSize; else if(cur == TARGET_DEST_CASTER_RANDOM) - dist = objSize + (dist - objSize) * m_caster->GetMap()->rand_norm(); + dist = objSize + (dist - objSize) * rand_norm(); switch(cur) { @@ -1788,7 +1945,7 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) case TARGET_DEST_TARGET_BACK_LEFT: angle = -3*M_PI/4; break; case TARGET_DEST_TARGET_BACK_RIGHT: angle = 3*M_PI/4; break; case TARGET_DEST_TARGET_FRONT_RIGHT:angle = M_PI/4; break; - default: angle = m_caster->GetMap()->rand_norm()*2*M_PI; break; + default: angle = rand_norm()*2*M_PI; break; } target->GetGroundPointAroundUnit(x, y, z, dist, angle); @@ -1821,13 +1978,13 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) case TARGET_DEST_DEST_BACK_LEFT: angle = -3*M_PI/4; break; case TARGET_DEST_DEST_BACK_RIGHT: angle = 3*M_PI/4; break; case TARGET_DEST_DEST_FRONT_RIGHT:angle = M_PI/4; break; - default: angle = m_caster->GetMap()->rand_norm()*2*M_PI; break; + default: angle = rand_norm()*2*M_PI; break; } float dist, x, y, z; - dist = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + dist = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); if (cur == TARGET_DEST_DEST_RANDOM) - dist *= m_caster->GetMap()->rand_norm(); + dist *= rand_norm(); x = m_targets.m_destX; y = m_targets.m_destY; @@ -1853,7 +2010,13 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) m_targets.setDestination(st->target_X, st->target_Y, st->target_Z); } else - sLog.outError( "SPELL: unknown target coordinates for spell ID %u\n", m_spellInfo->Id ); + { + sLog.outDebug( "SPELL: unknown target coordinates for spell ID %u", m_spellInfo->Id ); + Unit *target = NULL; + if(uint64 guid = m_caster->GetUInt64Value(UNIT_FIELD_TARGET)) + target = ObjectAccessor::GetUnit(*m_caster, guid); + m_targets.setDestination(target ? target : m_caster); + } break; case TARGET_DST_HOME: if(m_caster->GetTypeId() == TYPEID_PLAYER) @@ -1861,12 +2024,10 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) break; case TARGET_DST_NEARBY_ENTRY: { - float range = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); - if(modOwner) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this); + float range = GetSpellMaxRange(m_spellInfo, IsPositiveSpell(m_spellInfo->Id)); + if(modOwner) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this); - WorldObject *target = SearchNearbyTarget(range, SPELL_TARGETS_ENTRY); - if(target) + if(WorldObject *target = SearchNearbyTarget(range, SPELL_TARGETS_ENTRY)) m_targets.setDestination(target); break; } @@ -1893,6 +2054,8 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) case TARGET_DEST_CHANNEL: if(m_originalCaster->m_currentSpells[CURRENT_CHANNELED_SPELL]->m_targets.HasDst()) m_targets = m_originalCaster->m_currentSpells[CURRENT_CHANNELED_SPELL]->m_targets; + else if(Unit* target = m_originalCaster->m_currentSpells[CURRENT_CHANNELED_SPELL]->m_targets.getUnitTarget()) + m_targets.setDestination(target); else sLog.outError( "SPELL: cannot find channel spell destination for spell ID %u", m_spellInfo->Id ); break; @@ -1943,10 +2106,7 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) m_damageMultipliers[i] = 1.0f; m_applyMultiplierMask |= 1 << i; - float range = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); - if(modOwner) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this); - + float range; std::list<Unit*> unitList; switch(cur) @@ -1954,12 +2114,16 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) case TARGET_UNIT_NEARBY_ENEMY: case TARGET_UNIT_TARGET_ENEMY: case TARGET_UNIT_NEARBY_ENTRY: // fix me + range = GetSpellMaxRange(m_spellInfo, false); + if(modOwner) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this); SearchChainTarget(unitList, range, maxTargets, SPELL_TARGETS_ENEMY); break; case TARGET_UNIT_CHAINHEAL: case TARGET_UNIT_NEARBY_ALLY: // fix me case TARGET_UNIT_NEARBY_ALLY_UNK: case TARGET_UNIT_NEARBY_RAID: + range = GetSpellMaxRange(m_spellInfo, true); + if(modOwner) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this); SearchChainTarget(unitList, range, maxTargets, SPELL_TARGETS_CHAINHEAL); break; } @@ -1973,115 +2137,328 @@ void Spell::SetTargetMap(uint32 i, uint32 cur) else if(pushType) { // Dummy, just for client - if(spellmgr.EffectTargetType[m_spellInfo->Effect[i]] == SPELL_REQUIRE_DEST) + if(spellmgr.EffectTargetType[m_spellInfo->Effect[i]] != SPELL_REQUIRE_UNIT) return; - float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); - if(modOwner) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius, this); - - std::list<Unit*> unitList; - + float radius; + SpellTargets targetType; switch(cur) { case TARGET_UNIT_AREA_ENEMY_SRC: case TARGET_UNIT_AREA_ENEMY_DST: case TARGET_UNIT_CONE_ENEMY: case TARGET_UNIT_CONE_ENEMY_UNKNOWN: - SearchAreaTarget(unitList, radius, pushType, SPELL_TARGETS_ENEMY); + radius = GetSpellRadius(m_spellInfo, i, false); + targetType = SPELL_TARGETS_ENEMY; break; case TARGET_UNIT_AREA_ALLY_SRC: case TARGET_UNIT_AREA_ALLY_DST: case TARGET_UNIT_CONE_ALLY: - SearchAreaTarget(unitList, radius, pushType, SPELL_TARGETS_ALLY); + radius = GetSpellRadius(m_spellInfo, i, true); + targetType = SPELL_TARGETS_ALLY; break; - case TARGET_UNIT_AREA_PARTY_SRC: - case TARGET_UNIT_AREA_PARTY_DST: - m_caster->GetPartyMember(unitList, radius); //fix me - break; - case TARGET_OBJECT_AREA_SRC: // fix me - case TARGET_OBJECT_AREA_DST: - break; - case TARGET_UNIT_AREA_ENTRY_SRC: case TARGET_UNIT_AREA_ENTRY_DST: + case TARGET_UNIT_AREA_ENTRY_SRC: case TARGET_UNIT_CONE_ENTRY: // fix me + radius = GetSpellRadius(m_spellInfo, i, IsPositiveSpell(m_spellInfo->Id)); + targetType = SPELL_TARGETS_ENTRY; + break; + default: + radius = GetSpellRadius(m_spellInfo, i, true); + targetType = SPELL_TARGETS_NONE; + break; + } + + if(modOwner) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius, this); + radius *= m_spellValue->RadiusMod; + + std::list<Unit*> unitList; + if(targetType == SPELL_TARGETS_ENTRY) + { + SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(m_spellInfo->Id); + SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(m_spellInfo->Id); + if(lower == upper) { - SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(m_spellInfo->Id); - SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(m_spellInfo->Id); - if(lower == upper) + // Custom entries + // TODO: move these to sql + switch (m_spellInfo->Id) { - sLog.outErrorDb("Spell (ID: %u) (caster Entry: %u) does not have record in `spell_script_target`", m_spellInfo->Id, m_caster->GetEntry()); + case 46584: // Raise Dead + { + // TODO: change visual of corpses which gave ghoul? + // Allow corpses to be ghouled only once? + m_targets.m_targetMask &= ~TARGET_FLAG_DEST_LOCATION; + WorldObject* result = FindCorpseUsing<MaNGOS::RaiseDeadObjectCheck> (); + if(result) + { + switch(result->GetTypeId()) + { + case TYPEID_UNIT: + m_targets.setDestination(result); + } + } + break; + } + // Corpse Explosion + case 53717: + case 51325: + case 51326: + case 51327: + case 51328: + // Search for ghoul if our ghoul or dead body not valid unit target + if (!(m_targets.getUnitTarget() && (m_targets.getUnitTarget()->GetEntry() == 26125 && m_targets.getUnitTarget()->GetOwnerGUID() == m_caster->GetGUID() + || (m_targets.getUnitTarget()->getDeathState() == CORPSE + && m_targets.getUnitTarget()->GetTypeId()== TYPEID_UNIT + && !((Creature*)m_targets.getUnitTarget())->isDeadByDefault() + && !(m_targets.getUnitTarget()->GetCreatureTypeMask() & CREATURE_TYPEMASK_MECHANICAL_OR_ELEMENTAL)) + && m_targets.getUnitTarget()->GetDisplayId() == m_targets.getUnitTarget()->GetNativeDisplayId()))) + { + CleanupTargetList(); - if(IsPositiveEffect(m_spellInfo->Id, i)) - SearchAreaTarget(unitList, radius, pushType, SPELL_TARGETS_ALLY); - else - SearchAreaTarget(unitList, radius, pushType, SPELL_TARGETS_ENEMY); + WorldObject* result = FindCorpseUsing <Trinity::ExplodeCorpseObjectCheck> (); + + if(result) + { + switch(result->GetTypeId()) + { + case TYPEID_UNIT: + case TYPEID_PLAYER: + m_targets.setUnitTarget((Unit*)result); + break; + } + } + else + { + if (m_caster->GetTypeId()==TYPEID_PLAYER) + ((Player*)m_caster)->RemoveSpellCooldown(m_spellInfo->Id,true); + SendCastResult(SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW); + finish(false); + } + } + break; + + default: + sLog.outDebug("Spell (ID: %u) (caster Entry: %u) does not have record in `spell_script_target`", m_spellInfo->Id, m_caster->GetEntry()); + + if(m_spellInfo->Effect[i] == SPELL_EFFECT_TELEPORT_UNITS) + SearchAreaTarget(unitList, radius, pushType, SPELL_TARGETS_ENTRY, 0); + else if(IsPositiveEffect(m_spellInfo->Id, i)) + SearchAreaTarget(unitList, radius, pushType, SPELL_TARGETS_ALLY); + else + SearchAreaTarget(unitList, radius, pushType, SPELL_TARGETS_ENEMY); } - // let it be done in one check? - else + } + // let it be done in one check? + else + { + for(SpellScriptTarget::const_iterator i_spellST = lower; i_spellST != upper; ++i_spellST) { - for(SpellScriptTarget::const_iterator i_spellST = lower; i_spellST != upper; ++i_spellST) - { - if(i_spellST->second.type == SPELL_TARGET_TYPE_CREATURE) - SearchAreaTarget(unitList, radius, pushType, SPELL_TARGETS_ENTRY, i_spellST->second.targetEntry); - } + if(i_spellST->second.type == SPELL_TARGET_TYPE_CREATURE) + SearchAreaTarget(unitList, radius, pushType, SPELL_TARGETS_ENTRY, i_spellST->second.targetEntry); } - break; } - case TARGET_UNIT_PARTY_TARGET: - m_targets.getUnitTarget()->GetPartyMember(unitList, radius); - break; - case TARGET_UNIT_PARTY_CASTER: - m_caster->GetPartyMember(unitList, radius); - break; - case TARGET_UNIT_RAID_CASTER: - m_caster->GetRaidMember(unitList, radius); - break; - case TARGET_UNIT_CLASS_TARGET: + } + else if(targetType) + SearchAreaTarget(unitList, radius, pushType, targetType); + else + { + switch(cur) { - Player* targetPlayer = m_targets.getUnitTarget() && m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER - ? (Player*)m_targets.getUnitTarget() : NULL; - - Group* pGroup = targetPlayer ? targetPlayer->GetGroup() : NULL; - if(pGroup) + case TARGET_UNIT_AREA_PARTY_SRC: + case TARGET_UNIT_AREA_PARTY_DST: + m_caster->GetPartyMemberInDist(unitList, radius); //fix me + break; + case TARGET_OBJECT_AREA_SRC: // fix me + case TARGET_OBJECT_AREA_DST: { - for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + float x, y, z; + if(cur == TARGET_OBJECT_AREA_SRC) { - Player* Target = itr->getSource(); + if(m_targets.HasSrc()) + { + x = m_targets.m_srcX; + y = m_targets.m_srcY; + z = m_targets.m_srcZ; + } + else + break; + } + else if(m_targets.HasDst()) + { + x = m_targets.m_destX; + y = m_targets.m_destY; + z = m_targets.m_destZ; + } + else + break; + + Trinity::GameObjectInRangeCheck check(x, y, z, radius + 50); + std::list<GameObject*> goList; + Trinity::GameObjectListSearcher<Trinity::GameObjectInRangeCheck> searcher(m_caster, goList, check); + m_caster->GetMap()->VisitGrid(x, y, radius, searcher); + for(std::list<GameObject*>::iterator itr = goList.begin(); itr != goList.end(); ++itr) + AddGOTarget(*itr, i); + break; + } + case TARGET_UNIT_PARTY_TARGET: + m_targets.getUnitTarget()->GetPartyMemberInDist(unitList, radius); + break; + case TARGET_UNIT_PARTY_CASTER: + m_caster->GetPartyMemberInDist(unitList, radius); + break; + case TARGET_UNIT_RAID_CASTER: + m_caster->GetRaidMember(unitList, radius); + break; + case TARGET_UNIT_CLASS_TARGET: + { + Player* targetPlayer = m_targets.getUnitTarget() && m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER + ? (Player*)m_targets.getUnitTarget() : NULL; - // IsHostileTo check duel and controlled by enemy - if( Target && targetPlayer->IsWithinDistInMap(Target, radius) && - targetPlayer->getClass() == Target->getClass() && - !m_caster->IsHostileTo(Target) ) + Group* pGroup = targetPlayer ? targetPlayer->GetGroup() : NULL; + if(pGroup) + { + for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) { - AddUnitTarget(Target, i); + Player* Target = itr->getSource(); + + // IsHostileTo check duel and controlled by enemy + if( Target && targetPlayer->IsWithinDistInMap(Target, radius) && + targetPlayer->getClass() == Target->getClass() && + !m_caster->IsHostileTo(Target) ) + { + AddUnitTarget(Target, i); + } } } + else if(m_targets.getUnitTarget()) + AddUnitTarget(m_targets.getUnitTarget(), i); + break; } - else if(m_targets.getUnitTarget()) - AddUnitTarget(m_targets.getUnitTarget(), i); - break; } } if(!unitList.empty()) { - if(m_spellValue->MaxAffectedTargets) + if(uint32 maxTargets = m_spellValue->MaxAffectedTargets) { + Unit::AuraEffectList const& Auras = m_caster->GetAurasByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS); + for(Unit::AuraEffectList::const_iterator j = Auras.begin();j != Auras.end(); ++j) + if((*j)->isAffectedOnSpell(m_spellInfo)) + maxTargets += (*j)->GetAmount(); + if(m_spellInfo->Id == 5246) //Intimidating Shout unitList.remove(m_targets.getUnitTarget()); - Trinity::RandomResizeList(unitList, m_spellValue->MaxAffectedTargets); - }else if(m_spellInfo->Id == 27285) // Seed of Corruption proc spell - unitList.remove(m_targets.getUnitTarget()); + } + else + { + switch (m_spellInfo->Id) + { + case 27285: // Seed of Corruption proc spell + unitList.remove(m_targets.getUnitTarget()); + break; + case 55789: // Improved Icy Talons + case 59725: // Improved Spell Reflection - aoe aura + unitList.remove(m_caster); + break; + case 57699: //Replenishment (special target selection) 10 targets with lowest mana + { + typedef std::priority_queue<PrioritizeManaUnitWraper, std::vector<PrioritizeManaUnitWraper>, PrioritizeMana> TopMana; + TopMana manaUsers; + for (std::list<Unit*>::iterator itr = unitList.begin() ; itr != unitList.end();++itr) + { + if ((*itr)->getPowerType() == POWER_MANA) + { + PrioritizeManaUnitWraper WTarget(*itr); + manaUsers.push(WTarget); + } + } + + unitList.clear(); + while(!manaUsers.empty() && unitList.size()<10) + { + unitList.push_back(manaUsers.top().getUnit()); + manaUsers.pop(); + } + break; + } + case 52759: // Ancestral Awakening + { + typedef std::priority_queue<PrioritizeHealthUnitWraper, std::vector<PrioritizeHealthUnitWraper>, PrioritizeHealth> TopHealth; + TopHealth healedMembers; + for (std::list<Unit*>::iterator itr = unitList.begin() ; itr != unitList.end();++itr) + { + PrioritizeHealthUnitWraper WTarget(*itr); + healedMembers.push(WTarget); + } + + unitList.clear(); + while(!healedMembers.empty() && unitList.size()<1) + { + unitList.push_back(healedMembers.top().getUnit()); + healedMembers.pop(); + } + break; + } + } + if (m_spellInfo->EffectImplicitTargetA[i] == TARGET_DEST_TARGET_ANY + && m_spellInfo->EffectImplicitTargetB[i] == TARGET_UNIT_AREA_ALLY_DST)// Wild Growth, Circle of Healing, Glyph of holy light target special selection + { + typedef std::priority_queue<PrioritizeHealthUnitWraper, std::vector<PrioritizeHealthUnitWraper>, PrioritizeHealth> TopHealth; + TopHealth healedMembers; + for (std::list<Unit*>::iterator itr = unitList.begin() ; itr != unitList.end();++itr) + { + if ((*itr)->IsInRaidWith(m_targets.getUnitTarget())) + { + PrioritizeHealthUnitWraper WTarget(*itr); + healedMembers.push(WTarget); + } + } + unitList.clear(); + while(!healedMembers.empty() && unitList.size()<5) + { + unitList.push_back(healedMembers.top().getUnit()); + healedMembers.pop(); + } + } + // Death Pact + if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && m_spellInfo->SpellFamilyFlags[0] & 0x00080000) + { + Unit * unit_to_add = NULL; + for (std::list<Unit*>::iterator itr = unitList.begin() ; itr != unitList.end();++itr) + { + if ((*itr)->GetTypeId() == TYPEID_UNIT + && (*itr)->GetOwnerGUID() == m_caster->GetGUID() + && ((Creature*)(*itr))->GetCreatureInfo()->type == CREATURE_TYPE_UNDEAD) + { + unit_to_add = (*itr); + break; + } + } + if (unit_to_add) + { + unitList.clear(); + unitList.push_back(unit_to_add); + } + // Pet not found - remove cooldown + else + { + if (modOwner->GetTypeId()==TYPEID_PLAYER) + modOwner->RemoveSpellCooldown(m_spellInfo->Id,true); + SendCastResult(SPELL_FAILED_NO_PET); + finish(false); + } + } + } for(std::list<Unit*>::iterator itr = unitList.begin(); itr != unitList.end(); ++itr) AddUnitTarget(*itr, i); } - } // Chain or Area + } } -void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura) +void Spell::prepare(SpellCastTargets const* targets, AuraEffect* triggeredByAura) { if(m_CastItem) m_castItemGUID = m_CastItem->GetGUID(); @@ -2090,10 +2467,25 @@ void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura) m_targets = *targets; - m_spellState = SPELL_STATE_PREPARING; + if(!m_targets.getUnitTargetGUID() && m_spellInfo->Targets & TARGET_FLAG_UNIT) + { + Unit *target = NULL; + if(m_caster->GetTypeId() == TYPEID_UNIT) + target = m_caster->getVictim(); + else + target = ObjectAccessor::GetUnit(*m_caster, ((Player*)m_caster)->GetSelection()); - m_caster->GetPosition(m_castPositionX, m_castPositionY, m_castPositionZ); - m_castOrientation = m_caster->GetOrientation(); + if(target && IsValidSingleTargetSpell(target)) + m_targets.setUnitTarget(target); + else + { + SendCastResult(SPELL_FAILED_BAD_TARGETS); + finish(false); + return; + } + } + + m_spellState = SPELL_STATE_PREPARING; if(triggeredByAura) m_triggeredByAuraSpell = triggeredByAura->GetSpellProto(); @@ -2103,7 +2495,7 @@ void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura) m_caster->m_Events.AddEvent(Event, m_caster->m_Events.CalculateTime(1)); //Prevent casting at cast another spell (ServerSide check) - if(m_caster->IsNonMeleeSpellCasted(false, true) && m_cast_count) + if(m_caster->IsNonMeleeSpellCasted(false, true, true) && m_cast_count) { SendCastResult(SPELL_FAILED_SPELL_IN_PROGRESS); finish(false); @@ -2140,36 +2532,57 @@ void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura) // Fill cost data m_powerCost = CalculatePowerCost(); - uint8 result = CanCast(true); - if(result != 0 && !IsAutoRepeat()) //always cast autorepeat dummy for triggering + SpellCastResult result = CheckCast(true); + if(result != SPELL_CAST_OK && !IsAutoRepeat()) //always cast autorepeat dummy for triggering { if(triggeredByAura) { SendChannelUpdate(0); - triggeredByAura->SetAuraDuration(0); + triggeredByAura->GetParentAura()->SetAuraDuration(0); } SendCastResult(result); + finish(false); return; } // Prepare data for triggers - prepareDataForTriggerSystem(); + prepareDataForTriggerSystem(triggeredByAura); + + // Set combo point requirement + if (m_IsTriggeredSpell || m_CastItem || m_caster->GetTypeId()!=TYPEID_PLAYER) + m_needComboPoints = false; - // calculate cast time (calculated after first CanCast check to prevent charge counting for first CanCast fail) + // calculate cast time (calculated after first CheckCast check to prevent charge counting for first CheckCast fail) m_casttime = GetSpellCastTime(m_spellInfo, this); + //m_caster->ModSpellCastTime(m_spellInfo, m_casttime, this); // set timer base at cast time ReSetTimer(); - if(m_IsTriggeredSpell) + sLog.outDebug("Spell::prepare: spell id %u source %u caster %d target %d triggered %u", m_spellInfo->Id, m_caster->GetEntry(), m_originalCaster ? m_originalCaster->GetEntry() : -1, m_targets.getUnitTarget() ? m_targets.getUnitTarget()->GetEntry() : -1, m_IsTriggeredSpell ? 1 : 0); + + //Containers for channeled spells have to be set + //TODO:Apply this to all casted spells if needed + // Why check duration? 29350: channelled triggers channelled + if(m_IsTriggeredSpell && (!IsChanneledSpell(m_spellInfo) || !GetSpellMaxDuration(m_spellInfo))) cast(true); else { // stealth must be removed at cast starting (at show channel bar) // skip triggered spell (item equip spell casting and other not explicit character casts/item uses) if(isSpellBreakStealth(m_spellInfo) ) + { m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CAST); + for(uint32 i = 0; i < 3; ++i) + { + if(spellmgr.EffectTargetType[m_spellInfo->Effect[i]] == SPELL_REQUIRE_UNIT) + { + m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_SPELL_ATTACK); + break; + } + } + } m_caster->SetCurrentCastedSpell( this ); m_selfContainer = &(m_caster->m_currentSpells[GetCurrentContainer()]); @@ -2206,20 +2619,21 @@ void Spell::cancel() case SPELL_STATE_CASTING: { - for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) - { - if( ihit->missCondition == SPELL_MISS_NONE ) - { - Unit* unit = m_caster->GetGUID()==(*ihit).targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); - if( unit && unit->isAlive() ) - unit->RemoveAurasByCasterSpell(m_spellInfo->Id, m_caster->GetGUID()); - } - } + for(std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + if(ihit->missCondition == SPELL_MISS_NONE) + if(Unit* unit = m_caster->GetGUID() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID)) + if(unit->isAlive()) + unit->RemoveAurasDueToSpell(m_spellInfo->Id, m_originalCasterGUID, AURA_REMOVE_BY_CANCEL); - m_caster->RemoveAurasByCasterSpell(m_spellInfo->Id, m_caster->GetGUID()); SendChannelUpdate(0); SendInterrupted(0); SendCastResult(SPELL_FAILED_INTERRUPTED); + + // spell is canceled-take mods and clear list + if (m_caster->GetTypeId() == TYPEID_PLAYER) + ((Player*)m_caster)->RemoveSpellMods(this); + + m_appliedMods.clear(); } break; default: @@ -2241,45 +2655,46 @@ void Spell::cast(bool skipCheck) // update pointers base at GUIDs to prevent access to non-existed already object UpdatePointers(); - if(Unit *pTarget = m_targets.getUnitTarget()) - if(pTarget->isAlive() && (pTarget->HasAuraType(SPELL_AURA_MOD_STEALTH) || pTarget->HasAuraType(SPELL_AURA_MOD_INVISIBILITY)) && !pTarget->IsFriendlyTo(m_caster) && !pTarget->isVisibleForOrDetect(m_caster, true)) + if(Unit *target = m_targets.getUnitTarget()) + { + // three check: prepare, cast (m_casttime > 0), hit (delayed) + if(m_casttime && target->isAlive() + && (target->m_invisibilityMask || m_caster->m_invisibilityMask + || target->GetVisibility() == VISIBILITY_GROUP_STEALTH) + && !target->IsFriendlyTo(m_caster) && !m_caster->canSeeOrDetect(target, true)) { SendCastResult(SPELL_FAILED_BAD_TARGETS); finish(false); return; } - - SetExecutedCurrently(true); - uint8 castResult = 0; - - // cancel at lost main target unit - if(!m_targets.getUnitTarget() && m_targets.getUnitTargetGUID() && m_targets.getUnitTargetGUID() != m_caster->GetGUID()) + } + else { - cancel(); - SetExecutedCurrently(false); - return; + // cancel at lost main target unit + if(m_targets.getUnitTargetGUID() && m_targets.getUnitTargetGUID() != m_caster->GetGUID()) + { + cancel(); + return; + } } + SetExecutedCurrently(true); + if(m_caster->GetTypeId() != TYPEID_PLAYER && m_targets.getUnitTarget() && m_targets.getUnitTarget() != m_caster) m_caster->SetInFront(m_targets.getUnitTarget()); - if(!m_IsTriggeredSpell) + // Should this be done for original caster? + if (m_caster->GetTypeId()==TYPEID_PLAYER) { - castResult = CheckPower(); - if(castResult != 0) - { - SendCastResult(castResult); - finish(false); - SetExecutedCurrently(false); - return; - } + // Set spell which will drop charges for triggered cast spells + ((Player*)m_caster)->SetSpellModTakingSpell(this, true); } // triggered cast called from Spell::prepare where it was already checked - if(!skipCheck) + if(!m_IsTriggeredSpell || !skipCheck) { - castResult = CanCast(false); - if(castResult != 0) + SpellCastResult castResult = CheckCast(false); + if(castResult != SPELL_CAST_OK) { SendCastResult(castResult); finish(false); @@ -2290,51 +2705,85 @@ void Spell::cast(bool skipCheck) FillTargetMap(); - if(m_spellState == SPELL_STATE_FINISHED) // stop cast if spell marked as finish somewhere in Take*/FillTargetMap + if(m_spellInfo->SpellFamilyName) { - SetExecutedCurrently(false); - return; + if (m_spellInfo->excludeCasterAuraSpell && !IsPositiveSpell(m_spellInfo->excludeCasterAuraSpell)) + m_preCastSpell = m_spellInfo->excludeCasterAuraSpell; + else if (m_spellInfo->excludeTargetAuraSpell && !IsPositiveSpell(m_spellInfo->excludeTargetAuraSpell)) + m_preCastSpell = m_spellInfo->excludeTargetAuraSpell; + } + switch (m_spellInfo->SpellFamilyName) + { + case SPELLFAMILY_GENERIC: + { + if (m_spellInfo->Mechanic == MECHANIC_BANDAGE) // Bandages + m_preCastSpell = 11196; // Recently Bandaged + else if(m_spellInfo->SpellIconID == 1662 && m_spellInfo->AttributesEx & 0x20) + m_preCastSpell = 23230; // Blood Fury - Healing Reduction + break; + } } - // traded items have trade slot instead of guid in m_itemTargetGUID // set to real guid to be sent later to the client m_targets.updateTradeSlotItem(); + if (m_caster->GetTypeId() == TYPEID_PLAYER) + { + if (!m_IsTriggeredSpell && m_CastItem) + ((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM, m_CastItem->GetEntry()); + + ((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL, m_spellInfo->Id); + } + if(!m_IsTriggeredSpell) { - //TakePower(); + // Powers have to be taken before SendSpellGo + TakePower(); TakeReagents(); // we must remove reagents before HandleEffects to allow place crafted item in same slot } + // are there any spells need to be triggered after hit? + // handle SPELL_AURA_ADD_TARGET_TRIGGER auras + Unit::AuraEffectList const& targetTriggers = m_caster->GetAurasByType(SPELL_AURA_ADD_TARGET_TRIGGER); + for(Unit::AuraEffectList::const_iterator i = targetTriggers.begin(); i != targetTriggers.end(); ++i) + { + if (!(*i)->isAffectedOnSpell(m_spellInfo)) + continue; + SpellEntry const *auraSpellInfo = (*i)->GetSpellProto(); + uint32 auraSpellIdx = (*i)->GetEffIndex(); + if(SpellEntry const *spellInfo = sSpellStore.LookupEntry(auraSpellInfo->EffectTriggerSpell[auraSpellIdx])) + { + // Calculate chance at that moment (can be depend for example from combo points) + int32 chance = m_caster->CalculateSpellDamage(auraSpellInfo, auraSpellIdx, (*i)->GetBasePoints(), NULL); + m_ChanceTriggerSpells.push_back(std::make_pair(spellInfo, chance * (*i)->GetParentAura()->GetStackAmount())); + } + } + + if(m_customAttr & SPELL_ATTR_CU_DIRECT_DAMAGE) + CalculateDamageDoneForAllTargets(); + // CAST SPELL SendSpellCooldown(); //SendCastResult(castResult); SendSpellGo(); // we must send smsg_spell_go packet before m_castItem delete in TakeCastItem()... - if(m_customAttr & SPELL_ATTR_CU_DIRECT_DAMAGE) - CalculateDamageDoneForAllTargets(); - - //handle SPELL_AURA_ADD_TARGET_TRIGGER auras - //are there any spells need to be triggered after hit? - Unit::AuraList const& targetTriggers = m_caster->GetAurasByType(SPELL_AURA_ADD_TARGET_TRIGGER); - for(Unit::AuraList::const_iterator i = targetTriggers.begin(); i != targetTriggers.end(); ++i) + if(m_customAttr & SPELL_ATTR_CU_CHARGE) { - SpellEntry const *auraSpellInfo = (*i)->GetSpellProto(); - uint32 auraSpellIdx = (*i)->GetEffIndex(); - if (IsAffectedBy(auraSpellInfo, auraSpellIdx)) + for(uint32 i = 0; i < 3; ++i) { - if(SpellEntry const *spellInfo = sSpellStore.LookupEntry(auraSpellInfo->EffectTriggerSpell[auraSpellIdx])) + switch(m_spellInfo->Effect[i]) { - // Calculate chance at that moment (can be depend for example from combo points) - int32 chance = m_caster->CalculateSpellDamage(auraSpellInfo, auraSpellIdx, (*i)->GetBasePoints(), NULL); - m_ChanceTriggerSpells.push_back(std::make_pair(spellInfo, chance * (*i)->GetStackAmount())); + case SPELL_EFFECT_CHARGE: + case SPELL_EFFECT_JUMP: + case SPELL_EFFECT_JUMP2: + case SPELL_EFFECT_138: + HandleEffects(NULL,NULL,NULL,i); + m_effectMask |= (1<<i); + break; } } } - if(m_customAttr & SPELL_ATTR_CU_CHARGE) - EffectCharge(0); - // Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells if (m_spellInfo->speed > 0.0f && !IsChanneledSpell(m_spellInfo)) { @@ -2353,12 +2802,6 @@ void Spell::cast(bool skipCheck) handle_immediate(); } - // combo points should not be taken before SPELL_AURA_ADD_TARGET_TRIGGER auras are handled - if(!m_IsTriggeredSpell) - { - TakePower(); - } - if(m_customAttr & SPELL_ATTR_CU_LINK_CAST) { if(const std::vector<int32> *spell_triggered = spellmgr.GetSpellLinked(m_spellInfo->Id)) @@ -2369,6 +2812,9 @@ void Spell::cast(bool skipCheck) m_caster->CastSpell(m_targets.getUnitTarget() ? m_targets.getUnitTarget() : m_caster, *i, true); } + if (m_caster->GetTypeId()==TYPEID_PLAYER) + ((Player*)m_caster)->SetSpellModTakingSpell(this, false); + SetExecutedCurrently(false); } @@ -2377,9 +2823,18 @@ void Spell::handle_immediate() // start channeling if applicable if(IsChanneledSpell(m_spellInfo)) { - m_spellState = SPELL_STATE_CASTING; - m_caster->AddInterruptMask(m_spellInfo->ChannelInterruptFlags); - SendChannelStart(GetSpellDuration(m_spellInfo)); + int32 duration = GetSpellDuration(m_spellInfo); + if (duration) + { + //apply haste mods + m_caster->ModSpellCastTime(m_spellInfo, duration, this); + // Apply duration mod + if(Player* modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration); + m_spellState = SPELL_STATE_CASTING; + m_caster->AddInterruptMask(m_spellInfo->ChannelInterruptFlags); + SendChannelStart(duration); + } } // process immediate effects (items, ground, etc.) also initialize some variables @@ -2404,6 +2859,10 @@ void Spell::handle_immediate() uint64 Spell::handle_delayed(uint64 t_offset) { UpdatePointers(); + + if (m_caster->GetTypeId()==TYPEID_PLAYER) + ((Player*)m_caster)->SetSpellModTakingSpell(this, true); + uint64 next_time = 0; if (!m_immediateHandled) @@ -2437,6 +2896,7 @@ uint64 Spell::handle_delayed(uint64 t_offset) next_time = ighit->timeDelay; } } + // All targets passed - need finish phase if (next_time == 0) { @@ -2450,6 +2910,9 @@ uint64 Spell::handle_delayed(uint64 t_offset) } else { + if (m_caster->GetTypeId()==TYPEID_PLAYER) + ((Player*)m_caster)->SetSpellModTakingSpell(this, false); + // spell is unfinished, return next execution time return next_time; } @@ -2461,15 +2924,15 @@ void Spell::_handle_immediate_phase() HandleThreatSpells(m_spellInfo->Id); m_needSpellLog = IsNeedSendToClient(); - for(uint32 j = 0;j<3;j++) + for(uint32 j = 0; j < 3; ++j) { - if(m_spellInfo->Effect[j]==0) + if(m_spellInfo->Effect[j] == 0) continue; // apply Send Event effect to ground in case empty target lists if( m_spellInfo->Effect[j] == SPELL_EFFECT_SEND_EVENT && !HaveTargetsForEffect(j) ) { - HandleEffects(NULL,NULL,NULL, j); + HandleEffects(NULL, NULL, NULL, j); continue; } @@ -2496,14 +2959,26 @@ void Spell::_handle_immediate_phase() if(!m_targets.HasDst()) // FIXME: this will ignore dest set in effect m_targets.setDestination(m_caster); HandleEffects(m_originalCaster, NULL, NULL, j); + m_effectMask |= (1<<j); } else if(spellmgr.EffectTargetType[m_spellInfo->Effect[j]] == SPELL_REQUIRE_NONE) + { HandleEffects(m_originalCaster, NULL, NULL, j); + m_effectMask |= (1<<j); + } } } void Spell::_handle_finish_phase() { + // Take for real after all targets are processed + if (m_needComboPoints) + ((Player*)m_caster)->ClearComboPoints(); + + // Real add combo points from effects + if (m_caster->GetTypeId()==TYPEID_PLAYER) + ((Player*)m_caster)->GainSpellComboPoints(m_comboPointGain); + // spell log if(m_needSpellLog) SendLogExecute(); @@ -2515,93 +2990,20 @@ void Spell::SendSpellCooldown() return; Player* _player = (Player*)m_caster; - // Add cooldown for max (disable spell) - // Cooldown started on SendCooldownEvent call - if (m_spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) - { - _player->AddSpellCooldown(m_spellInfo->Id, 0, time(NULL) - 1); - return; - } - - // init cooldown values - uint32 cat = 0; - int32 rec = -1; - int32 catrec = -1; - - // some special item spells without correct cooldown in SpellInfo - // cooldown information stored in item prototype - // This used in same way in WorldSession::HandleItemQuerySingleOpcode data sending to client. - if(m_CastItem) + // mana/health/etc potions, disabled by client (until combat out as declarate) + if (m_CastItem && m_CastItem->IsPotion()) { - ItemPrototype const* proto = m_CastItem->GetProto(); - if(proto) - { - for(int idx = 0; idx < 5; ++idx) - { - if(proto->Spells[idx].SpellId == m_spellInfo->Id) - { - cat = proto->Spells[idx].SpellCategory; - rec = proto->Spells[idx].SpellCooldown; - catrec = proto->Spells[idx].SpellCategoryCooldown; - break; - } - } - } - } - - // if no cooldown found above then base at DBC data - if(rec < 0 && catrec < 0) - { - cat = m_spellInfo->Category; - rec = m_spellInfo->RecoveryTime; - catrec = m_spellInfo->CategoryRecoveryTime; + // need in some way provided data for Spell::finish SendCooldownEvent + _player->SetLastPotionId(m_CastItem->GetEntry()); + return; } - // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK) - // prevent 0 cooldowns set by another way - if (rec <= 0 && catrec <= 0 && (cat == 76 || cat == 351)) - rec = _player->GetAttackTime(RANGED_ATTACK); - - // Now we have cooldown data (if found any), time to apply mods - if(rec > 0) - _player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COOLDOWN, rec, this); - - if(catrec > 0) - _player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COOLDOWN, catrec, this); - - // replace negative cooldowns by 0 - if (rec < 0) rec = 0; - if (catrec < 0) catrec = 0; - - // no cooldown after applying spell mods - if( rec == 0 && catrec == 0) + // have infinity cooldown but set at aura apply // do not set cooldown for triggered spells (needed by reincarnation) + if(m_spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE || m_IsTriggeredSpell) return; - time_t curTime = time(NULL); - - time_t catrecTime = catrec ? curTime+catrec/1000 : 0; // in secs - time_t recTime = rec ? curTime+rec/1000 : catrecTime;// in secs - - // self spell cooldown - if(recTime > 0) - _player->AddSpellCooldown(m_spellInfo->Id, m_CastItem ? m_CastItem->GetEntry() : 0, recTime); - - // category spells - if (catrec > 0) - { - SpellCategoryStore::const_iterator i_scstore = sSpellCategoryStore.find(cat); - if(i_scstore != sSpellCategoryStore.end()) - { - for(SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset) - { - if(*i_scset == m_spellInfo->Id) // skip main spell, already handled above - continue; - - _player->AddSpellCooldown(m_spellInfo->Id, m_CastItem ? m_CastItem->GetEntry() : 0, catrecTime); - } - } - } + _player->AddSpellAndCategoryCooldowns(m_spellInfo,m_CastItem ? m_CastItem->GetEntry() : 0, this); } void Spell::update(uint32 difftime) @@ -2611,21 +3013,18 @@ void Spell::update(uint32 difftime) if(m_targets.getUnitTargetGUID() && !m_targets.getUnitTarget()) { + sLog.outDebug("Spell %u is cancelled due to removal of target.", m_spellInfo->Id); cancel(); return; } // check if the player caster has moved before the spell finished if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) && - (m_castPositionX != m_caster->GetPositionX() || m_castPositionY != m_caster->GetPositionY() || m_castPositionZ != m_caster->GetPositionZ()) && + m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) && (m_spellInfo->Effect[0] != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING))) { - // always cancel for channeled spells - //if( m_spellState == SPELL_STATE_CASTING ) - // cancel(); // don't cancel for melee, autorepeat, triggered and instant spells - //else - if(!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !m_IsTriggeredSpell && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT)) + if(!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !m_IsTriggeredSpell) cancel(); } @@ -2648,20 +3047,10 @@ void Spell::update(uint32 difftime) { if(m_timer > 0) { - if( m_caster->GetTypeId() == TYPEID_PLAYER ) - { - // check if player has jumped before the channeling finished - if(m_caster->HasUnitMovementFlag(MOVEMENTFLAG_JUMPING)) - cancel(); - - // check for incapacitating player states - //if( m_caster->hasUnitState(UNIT_STAT_STUNNED | UNIT_STAT_CONFUSED)) - // cancel(); - } - // check if there are alive targets left - if (!IsAliveUnitPresentInTargetList()) + if (!UpdateChanneledTargetList()) { + sLog.outDebug("Channeled spell %d is removed due to lack of targets", m_spellInfo->Id); SendChannelUpdate(0); finish(); } @@ -2679,30 +3068,33 @@ void Spell::update(uint32 difftime) // channeled spell processed independently for quest targeting // cast at creature (or GO) quest objectives update at successful cast channel finished // ignore autorepeat/melee casts for speed (not exist quest for spells (hm... ) - if( m_caster->GetTypeId() == TYPEID_PLAYER && !IsAutoRepeat() && !IsNextMeleeSwingSpell() ) + if( !IsAutoRepeat() && !IsNextMeleeSwingSpell() ) { - for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) + if ( Player* p = m_caster->GetCharmerOrOwnerPlayerOrPlayerItself() ) { - TargetInfo* target = &*ihit; - if(!IS_CREATURE_GUID(target->targetGUID)) - continue; + for(std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + { + TargetInfo* target = &*ihit; + if(!IS_CREATURE_GUID(target->targetGUID)) + continue; - Unit* unit = m_caster->GetGUID()==target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster,target->targetGUID); - if (unit==NULL) - continue; + Unit* unit = m_caster->GetGUID() == target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target->targetGUID); + if (unit == NULL) + continue; - ((Player*)m_caster)->CastedCreatureOrGO(unit->GetEntry(),unit->GetGUID(),m_spellInfo->Id); - } + p->CastedCreatureOrGO(unit->GetEntry(), unit->GetGUID(), m_spellInfo->Id); + } - for(std::list<GOTargetInfo>::iterator ihit= m_UniqueGOTargetInfo.begin();ihit != m_UniqueGOTargetInfo.end();++ihit) - { - GOTargetInfo* target = &*ihit; + for(std::list<GOTargetInfo>::iterator ihit = m_UniqueGOTargetInfo.begin(); ihit != m_UniqueGOTargetInfo.end(); ++ihit) + { + GOTargetInfo* target = &*ihit; - GameObject* go = ObjectAccessor::GetGameObject(*m_caster, target->targetGUID); - if(!go) - continue; + GameObject* go = m_caster->GetMap()->GetGameObject(target->targetGUID); + if(!go) + continue; - ((Player*)m_caster)->CastedCreatureOrGO(go->GetEntry(),go->GetGUID(),m_spellInfo->Id); + p->CastedCreatureOrGO(go->GetEntry(), go->GetGUID(), m_spellInfo->Id); + } } } @@ -2722,7 +3114,6 @@ void Spell::finish(bool ok) if(m_spellState == SPELL_STATE_FINISHED) return; - m_spellState = SPELL_STATE_FINISHED; if(IsChanneledSpell(m_spellInfo)) @@ -2731,18 +3122,41 @@ void Spell::finish(bool ok) if(!m_caster->IsNonMeleeSpellCasted(false, false, true)) m_caster->clearUnitState(UNIT_STAT_CASTING); + // Unsummon summon as possessed creatures on spell cancel + if(IsChanneledSpell(m_spellInfo) && m_caster->GetTypeId() == TYPEID_PLAYER) + { + if(Unit *charm = m_caster->GetCharm()) + if(charm->GetTypeId() == TYPEID_UNIT + && ((Creature*)charm)->HasSummonMask(SUMMON_MASK_PUPPET) + && charm->GetUInt32Value(UNIT_CREATED_BY_SPELL) == m_spellInfo->Id) + ((Puppet*)charm)->UnSummon(); + } + if(!ok) { //restore spell mods if (m_caster->GetTypeId() == TYPEID_PLAYER) + { ((Player*)m_caster)->RestoreSpellMods(this); + // cleanup after mod system + // triggered spell pointer can be not removed in some cases + ((Player*)m_caster)->SetSpellModTakingSpell(this, false); + } return; } - // other code related only to successfully finished spells - //remove spell mods - if (m_caster->GetTypeId() == TYPEID_PLAYER) - ((Player*)m_caster)->RemoveSpellMods(this); + if (m_caster->GetTypeId()==TYPEID_UNIT && ((Creature*)m_caster)->isSummon()) + { + // Unsummon statue + uint32 spell = m_caster->GetUInt32Value(UNIT_CREATED_BY_SPELL); + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell ); + if (spellInfo && spellInfo->SpellIconID==2056) + { + sLog.outDebug("Statue %d is unsummoned in spell %d finish", m_caster->GetGUIDLow(), m_spellInfo->Id); + m_caster->setDeathState(JUST_DIED); + return; + } + } // Okay to remove extra attacks if(IsSpellHaveEffect(m_spellInfo, SPELL_EFFECT_ADD_EXTRA_ATTACKS)) @@ -2750,10 +3164,7 @@ void Spell::finish(bool ok) // Heal caster for all health leech from all targets if (m_healthLeech) - { - m_caster->ModifyHealth(m_healthLeech); - m_caster->SendHealSpellLog(m_caster, m_spellInfo->Id, uint32(m_healthLeech)); - } + m_caster->DealHeal(m_caster, uint32(m_healthLeech), m_spellInfo); if (IsMeleeAttackResetSpell()) { @@ -2764,82 +3175,103 @@ void Spell::finish(bool ok) m_caster->resetAttackTimer(RANGED_ATTACK); } + // potions disabled by client, send event "not in combat" if need + if (!m_triggeredByAuraSpell && m_caster->GetTypeId() == TYPEID_PLAYER) + { + ((Player*)m_caster)->UpdatePotionCooldown(this); + + // triggered spell pointer can be not set in some cases + // this is needed for proper apply of triggered spell mods + ((Player*)m_caster)->SetSpellModTakingSpell(this, true); + } + // call triggered spell only at successful cast (after clear combo points -> for add some if need) // I assume what he means is that some triggered spells may add combo points if(!m_TriggerSpells.empty()) TriggerSpell(); + // Take mods after trigger spell (needed for 14177 to affect 48664) + // mods are taken only on succesfull cast and independantly from targets of the spell + if (m_caster->GetTypeId() == TYPEID_PLAYER) + { + ((Player*)m_caster)->RemoveSpellMods(this); + ((Player*)m_caster)->SetSpellModTakingSpell(this, false); + } + // Stop Attack for some spells if( m_spellInfo->Attributes & SPELL_ATTR_STOP_ATTACK_TARGET ) m_caster->AttackStop(); } -void Spell::SendCastResult(uint8 result) +void Spell::SendCastResult(SpellCastResult result) { + if(result == SPELL_CAST_OK) + return; + if (m_caster->GetTypeId() != TYPEID_PLAYER) return; if(((Player*)m_caster)->GetSession()->PlayerLoading()) // don't send cast results at loading time return; - if(result != 0) - { - WorldPacket data(SMSG_CAST_FAILED, (4+1+1)); - data << uint32(m_spellInfo->Id); - data << uint8(result); // problem - data << uint8(m_cast_count); // single cast or multi 2.3 (0/1) - switch (result) - { - case SPELL_FAILED_REQUIRES_SPELL_FOCUS: - data << uint32(m_spellInfo->RequiresSpellFocus); - break; - case SPELL_FAILED_REQUIRES_AREA: - // hardcode areas limitation case - switch(m_spellInfo->Id) - { - case 41617: // Cenarion Mana Salve - case 41619: // Cenarion Healing Salve - data << uint32(3905); - break; - case 41618: // Bottled Nethergon Energy - case 41620: // Bottled Nethergon Vapor - data << uint32(3842); - break; - case 45373: // Bloodberry Elixir - data << uint32(4075); - break; - default: // default case - data << uint32(m_spellInfo->AreaId); - break; - } - break; - case SPELL_FAILED_TOTEMS: - if(m_spellInfo->Totem[0]) - data << uint32(m_spellInfo->Totem[0]); - if(m_spellInfo->Totem[1]) - data << uint32(m_spellInfo->Totem[1]); - break; - case SPELL_FAILED_TOTEM_CATEGORY: - if(m_spellInfo->TotemCategory[0]) - data << uint32(m_spellInfo->TotemCategory[0]); - if(m_spellInfo->TotemCategory[1]) - data << uint32(m_spellInfo->TotemCategory[1]); - break; - case SPELL_FAILED_EQUIPPED_ITEM_CLASS: - data << uint32(m_spellInfo->EquippedItemClass); - data << uint32(m_spellInfo->EquippedItemSubClassMask); - data << uint32(m_spellInfo->EquippedItemInventoryTypeMask); - break; - } - ((Player*)m_caster)->GetSession()->SendPacket(&data); - } - else + SendCastResult((Player*)m_caster,m_spellInfo,m_cast_count,result); +} + +void Spell::SendCastResult(Player* caster, SpellEntry const* spellInfo, uint8 cast_count, SpellCastResult result) +{ + if(result == SPELL_CAST_OK) + return; + + WorldPacket data(SMSG_CAST_FAILED, (4+1+1)); + data << uint8(cast_count); // single cast or multi 2.3 (0/1) + data << uint32(spellInfo->Id); + data << uint8(result); // problem + switch (result) { - WorldPacket data(SMSG_CLEAR_EXTRA_AURA_INFO, (8+4)); - data.append(m_caster->GetPackGUID()); - data << uint32(m_spellInfo->Id); - ((Player*)m_caster)->GetSession()->SendPacket(&data); + case SPELL_FAILED_REQUIRES_SPELL_FOCUS: + data << uint32(spellInfo->RequiresSpellFocus); + break; + case SPELL_FAILED_REQUIRES_AREA: + // hardcode areas limitation case + switch(spellInfo->Id) + { + case 41617: // Cenarion Mana Salve + case 41619: // Cenarion Healing Salve + data << uint32(3905); + break; + case 41618: // Bottled Nethergon Energy + case 41620: // Bottled Nethergon Vapor + data << uint32(3842); + break; + case 45373: // Bloodberry Elixir + data << uint32(4075); + break; + default: // default case (don't must be) + data << uint32(0); + break; + } + break; + case SPELL_FAILED_TOTEMS: + if(spellInfo->Totem[0]) + data << uint32(spellInfo->Totem[0]); + if(spellInfo->Totem[1]) + data << uint32(spellInfo->Totem[1]); + break; + case SPELL_FAILED_TOTEM_CATEGORY: + if(spellInfo->TotemCategory[0]) + data << uint32(spellInfo->TotemCategory[0]); + if(spellInfo->TotemCategory[1]) + data << uint32(spellInfo->TotemCategory[1]); + break; + case SPELL_FAILED_EQUIPPED_ITEM_CLASS: + data << uint32(spellInfo->EquippedItemClass); + data << uint32(spellInfo->EquippedItemSubClassMask); + //data << uint32(spellInfo->EquippedItemInventoryTypeMask); + break; + default: + break; } + caster->GetSession()->SendPacket(&data); } void Spell::SendSpellStart() @@ -2847,13 +3279,18 @@ void Spell::SendSpellStart() if(!IsNeedSendToClient()) return; - sLog.outDebug("Sending SMSG_SPELL_START id=%u", m_spellInfo->Id); + //sLog.outDebug("Sending SMSG_SPELL_START id=%u", m_spellInfo->Id); uint32 castFlags = CAST_FLAG_UNKNOWN1; - if(IsRangedSpell()) + if(m_spellInfo->Attributes & SPELL_ATTR_REQ_AMMO) castFlags |= CAST_FLAG_AMMO; + if ((m_caster->GetTypeId() == TYPEID_PLAYER || + (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->isPet())) + && m_spellInfo->powerType != POWER_HEALTH ) + castFlags |= CAST_FLAG_POWER_LEFT_SELF; - Unit *target = m_targets.getUnitTarget() ? m_targets.getUnitTarget() : m_caster; + if(m_spellInfo->runeCostID) + castFlags |= CAST_FLAG_UNKNOWN10; WorldPacket data(SMSG_SPELL_START, (8+8+4+4+2)); if(m_CastItem) @@ -2862,14 +3299,17 @@ void Spell::SendSpellStart() data.append(m_caster->GetPackGUID()); data.append(m_caster->GetPackGUID()); - data << uint32(m_spellInfo->Id); - data << uint8(m_cast_count); // single cast or multi 2.3 (0/1) - data << uint16(castFlags); - data << uint32(m_timer); + data << uint8(m_cast_count); // pending spell cast? + data << uint32(m_spellInfo->Id); // spellId + data << uint32(castFlags); // cast flags + data << uint32(m_timer); // delay? m_targets.write(&data); - if( castFlags & CAST_FLAG_AMMO ) + if(castFlags & CAST_FLAG_POWER_LEFT_SELF) + data << uint32(m_caster->GetPower((Powers)m_spellInfo->powerType)); + + if ( castFlags & CAST_FLAG_AMMO ) WriteAmmoToPacket(&data); m_caster->SendMessageToSet(&data, true); @@ -2881,37 +3321,82 @@ void Spell::SendSpellGo() if(!IsNeedSendToClient()) return; - sLog.outDebug("Sending SMSG_SPELL_GO id=%u", m_spellInfo->Id); - - Unit *target = m_targets.getUnitTarget() ? m_targets.getUnitTarget() : m_caster; + //sLog.outDebug("Sending SMSG_SPELL_GO id=%u", m_spellInfo->Id); uint32 castFlags = CAST_FLAG_UNKNOWN3; - + // triggered spells with spell visual != 0 if(m_IsTriggeredSpell || m_triggeredByAuraSpell) castFlags |= CAST_FLAG_UNKNOWN0; - - if(IsRangedSpell()) + + if(m_spellInfo->Attributes & SPELL_ATTR_REQ_AMMO) castFlags |= CAST_FLAG_AMMO; // arrows/bullets visual + if ((m_caster->GetTypeId() == TYPEID_PLAYER || + (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->isPet())) + && m_spellInfo->powerType != POWER_HEALTH ) + castFlags |= CAST_FLAG_POWER_LEFT_SELF; // should only be sent to self, but the current messaging doesn't make that possible + + if((m_caster->GetTypeId() == TYPEID_PLAYER) && (m_caster->getClass() == CLASS_DEATH_KNIGHT) && m_spellInfo->runeCostID) + { + castFlags |= CAST_FLAG_UNKNOWN10; // same as in SMSG_SPELL_START + castFlags |= CAST_FLAG_UNKNOWN7; // rune cooldowns list + } WorldPacket data(SMSG_SPELL_GO, 50); // guess size + if(m_CastItem) data.append(m_CastItem->GetPackGUID()); else data.append(m_caster->GetPackGUID()); data.append(m_caster->GetPackGUID()); - data << uint32(m_spellInfo->Id); - data << uint16(castFlags); + data << uint8(m_cast_count); // pending spell cast? + data << uint32(m_spellInfo->Id); // spellId + data << uint32(castFlags); // cast flags data << uint32(getMSTime()); // timestamp WriteSpellGoTargets(&data); m_targets.write(&data); - if( castFlags & CAST_FLAG_AMMO ) + if(castFlags & CAST_FLAG_POWER_LEFT_SELF) + data << uint32(m_caster->GetPower((Powers)m_spellInfo->powerType)); + + if ( castFlags & CAST_FLAG_UNKNOWN7 ) // rune cooldowns list + { + uint8 v1 = m_runesState; + uint8 v2 = ((Player*)m_caster)->GetRunesState(); + data << uint8(v1); // runes state before + data << uint8(v2); // runes state after + for(uint8 i = 0; i < MAX_RUNES; ++i) + { + uint8 m = (1 << i); + if(m & v1) // usable before... + if(!(m & v2)) // ...but on cooldown now... + data << uint8(0); // some unknown byte (time?) + } + } + + if ( castFlags & CAST_FLAG_UNKNOWN4 ) // unknown wotlk + { + data << float(0); + data << uint32(0); + } + + if ( castFlags & CAST_FLAG_AMMO ) WriteAmmoToPacket(&data); + if ( castFlags & CAST_FLAG_UNKNOWN5 ) // unknown wotlk + { + data << uint32(0); + data << uint32(0); + } + + if ( m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION ) + { + data << uint8(0); + } + m_caster->SendMessageToSet(&data, true); } @@ -2948,7 +3433,40 @@ void Spell::WriteAmmoToPacket( WorldPacket * data ) } } } - // TODO: implement selection ammo data based at ranged weapon stored in equipmodel/equipinfo/equipslot fields + else + { + for (uint8 i = 0; i < 3; ++i) + { + if(uint32 item_id = m_caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i)) + { + if(ItemEntry const * itemEntry = sItemStore.LookupEntry(item_id)) + { + if(itemEntry->Class==ITEM_CLASS_WEAPON) + { + switch(itemEntry->SubClass) + { + case ITEM_SUBCLASS_WEAPON_THROWN: + ammoDisplayID = itemEntry->DisplayId; + ammoInventoryType = itemEntry->InventoryType; + break; + case ITEM_SUBCLASS_WEAPON_BOW: + case ITEM_SUBCLASS_WEAPON_CROSSBOW: + ammoDisplayID = 5996; // is this need fixing? + ammoInventoryType = INVTYPE_AMMO; + break; + case ITEM_SUBCLASS_WEAPON_GUN: + ammoDisplayID = 5998; // is this need fixing? + ammoInventoryType = INVTYPE_AMMO; + break; + } + + if(ammoDisplayID) + break; + } + } + } + } + } *data << uint32(ammoDisplayID); *data << uint32(ammoInventoryType); @@ -2956,16 +3474,39 @@ void Spell::WriteAmmoToPacket( WorldPacket * data ) void Spell::WriteSpellGoTargets( WorldPacket * data ) { - *data << (uint8)m_countOfHit; - for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) + // This function also fill data for channeled spells: + // m_needAliveTargetMask req for stop channelig if one target die + uint32 hit = m_UniqueGOTargetInfo.size(); // Always hits on GO + uint32 miss = 0; + for(std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + { + if ((*ihit).effectMask == 0) // No effect apply - all immuned add state + { + // possibly SPELL_MISS_IMMUNE2 for this?? + ihit->missCondition = SPELL_MISS_IMMUNE2; + ++miss; + } + else if ((*ihit).missCondition == SPELL_MISS_NONE) + ++hit; + else + ++miss; + } + + *data << (uint8)hit; + for(std::list<TargetInfo>::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + { if ((*ihit).missCondition == SPELL_MISS_NONE) // Add only hits + { *data << uint64(ihit->targetGUID); + m_needAliveTargetMask |=ihit->effectMask; + } + } - for(std::list<GOTargetInfo>::iterator ighit= m_UniqueGOTargetInfo.begin();ighit != m_UniqueGOTargetInfo.end();++ighit) + for(std::list<GOTargetInfo>::const_iterator ighit = m_UniqueGOTargetInfo.begin(); ighit != m_UniqueGOTargetInfo.end(); ++ighit) *data << uint64(ighit->targetGUID); // Always hits - *data << (uint8)m_countOfMiss; - for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) + *data << (uint8)miss; + for(std::list<TargetInfo>::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) { if( ihit->missCondition != SPELL_MISS_NONE ) // Add only miss { @@ -2975,6 +3516,9 @@ void Spell::WriteSpellGoTargets( WorldPacket * data ) *data << uint8(ihit->reflectResult); } } + // Reset m_needAliveTargetMask for non channeled spell + if(!IsChanneledSpell(m_spellInfo)) + m_needAliveTargetMask = 0; } void Spell::SendLogExecute() @@ -3032,37 +3576,25 @@ void Spell::SendLogExecute() data << uint32(0); break; case SPELL_EFFECT_OPEN_LOCK: - case SPELL_EFFECT_OPEN_LOCK_ITEM: if(Item *item = m_targets.getItemTarget()) data.append(item->GetPackGUID()); else data << uint8(0); break; case SPELL_EFFECT_CREATE_ITEM: + case SPELL_EFFECT_CREATE_ITEM_2: data << uint32(m_spellInfo->EffectItemType[0]); break; case SPELL_EFFECT_SUMMON: - case SPELL_EFFECT_SUMMON_WILD: - case SPELL_EFFECT_SUMMON_GUARDIAN: case SPELL_EFFECT_TRANS_DOOR: case SPELL_EFFECT_SUMMON_PET: - case SPELL_EFFECT_SUMMON_POSSESSED: - case SPELL_EFFECT_SUMMON_TOTEM: case SPELL_EFFECT_SUMMON_OBJECT_WILD: case SPELL_EFFECT_CREATE_HOUSE: case SPELL_EFFECT_DUEL: - case SPELL_EFFECT_SUMMON_TOTEM_SLOT1: - case SPELL_EFFECT_SUMMON_TOTEM_SLOT2: - case SPELL_EFFECT_SUMMON_TOTEM_SLOT3: - case SPELL_EFFECT_SUMMON_TOTEM_SLOT4: - case SPELL_EFFECT_SUMMON_PHANTASM: - case SPELL_EFFECT_SUMMON_CRITTER: case SPELL_EFFECT_SUMMON_OBJECT_SLOT1: case SPELL_EFFECT_SUMMON_OBJECT_SLOT2: case SPELL_EFFECT_SUMMON_OBJECT_SLOT3: case SPELL_EFFECT_SUMMON_OBJECT_SLOT4: - case SPELL_EFFECT_SUMMON_DEMON: - case SPELL_EFFECT_150: if(Unit *unit = m_targets.getUnitTarget()) data.append(unit->GetPackGUID()); else if(m_targets.getItemTargetGUID()) @@ -3081,6 +3613,13 @@ void Spell::SendLogExecute() else data << uint8(0); break; + case SPELL_EFFECT_RESURRECT: + case SPELL_EFFECT_RESURRECT_NEW: + if(Unit *unit = m_targets.getUnitTarget()) + data.append(unit->GetPackGUID()); + else + data << uint8(0); + break; default: return; } @@ -3094,13 +3633,16 @@ void Spell::SendInterrupted(uint8 result) { WorldPacket data(SMSG_SPELL_FAILURE, (8+4+1)); data.append(m_caster->GetPackGUID()); - data << m_spellInfo->Id; - data << result; + data << uint8(m_cast_count); + data << uint32(m_spellInfo->Id); + data << uint8(result); m_caster->SendMessageToSet(&data, true); data.Initialize(SMSG_SPELL_FAILED_OTHER, (8+4)); data.append(m_caster->GetPackGUID()); - data << m_spellInfo->Id; + data << uint8(m_cast_count); + data << uint32(m_spellInfo->Id); + data << uint8(result); m_caster->SendMessageToSet(&data, true); } @@ -3108,8 +3650,8 @@ void Spell::SendChannelUpdate(uint32 time) { if(time == 0) { - m_caster->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT,0); - m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL,0); + m_caster->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, 0); + m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL, 0); } if (m_caster->GetTypeId() != TYPEID_PLAYER) @@ -3129,9 +3671,9 @@ void Spell::SendChannelStart(uint32 duration) // select first not resisted target from target list for _0_ effect if(!m_UniqueTargetInfo.empty()) { - for(std::list<TargetInfo>::iterator itr= m_UniqueTargetInfo.begin();itr != m_UniqueTargetInfo.end();++itr) + for(std::list<TargetInfo>::const_iterator itr = m_UniqueTargetInfo.begin(); itr != m_UniqueTargetInfo.end(); ++itr) { - if( (itr->effectMask & (1<<0)) && itr->reflectResult==SPELL_MISS_NONE && itr->targetGUID != m_caster->GetGUID()) + if( (itr->effectMask & (1 << 0)) && itr->reflectResult == SPELL_MISS_NONE && itr->targetGUID != m_caster->GetGUID()) { target = ObjectAccessor::GetUnit(*m_caster, itr->targetGUID); break; @@ -3140,11 +3682,11 @@ void Spell::SendChannelStart(uint32 duration) } else if(!m_UniqueGOTargetInfo.empty()) { - for(std::list<GOTargetInfo>::iterator itr= m_UniqueGOTargetInfo.begin();itr != m_UniqueGOTargetInfo.end();++itr) + for(std::list<GOTargetInfo>::const_iterator itr = m_UniqueGOTargetInfo.begin(); itr != m_UniqueGOTargetInfo.end(); ++itr) { - if(itr->effectMask & (1<<0) ) + if(itr->effectMask & (1 << 0) ) { - target = ObjectAccessor::GetGameObject(*m_caster, itr->targetGUID); + target = m_caster->GetMap()->GetGameObject(itr->targetGUID); break; } } @@ -3168,10 +3710,19 @@ void Spell::SendChannelStart(uint32 duration) void Spell::SendResurrectRequest(Player* target) { - WorldPacket data(SMSG_RESURRECT_REQUEST, (8+4+2+4)); - data << m_caster->GetGUID(); - data << uint32(1) << uint16(0) << uint32(1); + // Both players and NPCs can resurrect using spells - have a look at creature 28487 for example + // However, the packet structure differs slightly + + const char* sentName = m_caster->GetTypeId() == TYPEID_PLAYER ? "" : m_caster->GetNameForLocaleIdx(target->GetSession()->GetSessionDbLocaleIndex()); + + WorldPacket data(SMSG_RESURRECT_REQUEST, (8+4+strlen(sentName)+1+1+1)); + data << uint64(m_caster->GetGUID()); + data << uint32(strlen(sentName) + 1); + data << sentName; + data << uint8(0); + + data << uint8(m_caster->GetTypeId() == TYPEID_PLAYER ? 0 : 1); target->GetSession()->SendPacket(&data); } @@ -3180,7 +3731,7 @@ void Spell::SendPlaySpellVisual(uint32 SpellID) if (m_caster->GetTypeId() != TYPEID_PLAYER) return; - WorldPacket data(SMSG_PLAY_SPELL_VISUAL, 12); + WorldPacket data(SMSG_PLAY_SPELL_VISUAL, 8 + 4); data << uint64(m_caster->GetGUID()); data << uint32(SpellID); // spell visual id? ((Player*)m_caster)->GetSession()->SendPacket(&data); @@ -3208,7 +3759,7 @@ void Spell::TakeCastItem() bool expendable = false; bool withoutCharges = false; - for (int i = 0; i<5; i++) + for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) { if (proto->Spells[i].SpellId) { @@ -3224,7 +3775,7 @@ void Spell::TakeCastItem() if (charges) { (charges > 0) ? --charges : ++charges; // abs(charges) less at 1 after use - if (proto->Stackable < 2) + if (proto->Stackable == 1) m_CastItem->SetSpellCharges(i, charges); m_CastItem->SetState(ITEM_CHANGED, (Player*)m_caster); } @@ -3250,7 +3801,7 @@ void Spell::TakeCastItem() void Spell::TakePower() { - if(m_CastItem || m_triggeredByAuraSpell || !m_powerCost) + if(m_CastItem || m_triggeredByAuraSpell) return; bool hit = true; @@ -3263,12 +3814,27 @@ void Spell::TakePower() { if(ihit->missCondition != SPELL_MISS_NONE && ihit->missCondition != SPELL_MISS_MISS/* && ihit->targetGUID!=m_caster->GetGUID()*/) hit = false; + if (ihit->missCondition != SPELL_MISS_NONE) + { + //lower spell cost on fail (by talent aura) + if(Player *modOwner = ((Player*)m_caster)->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_SPELL_COST_REFUND_ON_FAIL, m_powerCost); + } break; } - if(hit && NeedsComboPoints(m_spellInfo)) - ((Player*)m_caster)->ClearComboPoints(); } + Powers powerType = Powers(m_spellInfo->powerType); + + if(powerType == POWER_RUNE) + { + TakeRunePower(); + return; + } + + if (!m_powerCost) + return; + // health as power used if(m_spellInfo->powerType == POWER_HEALTH) { @@ -3282,18 +3848,146 @@ void Spell::TakePower() return; } - Powers powerType = Powers(m_spellInfo->powerType); - if(hit) m_caster->ModifyPower(powerType, -m_powerCost); else - m_caster->ModifyPower(powerType, -m_caster->GetMap()->irand(0, m_powerCost/4)); + m_caster->ModifyPower(powerType, -irand(0, m_powerCost/4)); // Set the five second timer if (powerType == POWER_MANA && m_powerCost > 0) m_caster->SetLastManaUse(getMSTime()); } +void Spell::TakeAmmo() +{ + if(m_attackType == RANGED_ATTACK && m_caster->GetTypeId() == TYPEID_PLAYER) + { + Item *pItem = ((Player*)m_caster)->GetWeaponForAttack( RANGED_ATTACK ); + + // wands don't have ammo + if(!pItem || pItem->IsBroken() || pItem->GetProto()->SubClass==ITEM_SUBCLASS_WEAPON_WAND) + return; + + if( pItem->GetProto()->InventoryType == INVTYPE_THROWN ) + { + if(pItem->GetMaxStackCount()==1) + { + // decrease durability for non-stackable throw weapon + ((Player*)m_caster)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_RANGED); + } + else + { + // decrease items amount for stackable throw weapon + uint32 count = 1; + ((Player*)m_caster)->DestroyItemCount( pItem, count, true); + } + } + else if(uint32 ammo = ((Player*)m_caster)->GetUInt32Value(PLAYER_AMMO_ID)) + ((Player*)m_caster)->DestroyItemCount(ammo, 1, true); + } +} + +SpellCastResult Spell::CheckRuneCost(uint32 runeCostID) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return SPELL_CAST_OK; + + Player *plr = (Player*)m_caster; + + if(plr->getClass() != CLASS_DEATH_KNIGHT) + return SPELL_CAST_OK; + + SpellRuneCostEntry const *src = sSpellRuneCostStore.LookupEntry(runeCostID); + + if(!src) + return SPELL_CAST_OK; + + if(src->NoRuneCost()) + return SPELL_CAST_OK; + + int32 runeCost[NUM_RUNE_TYPES]; // blood, frost, unholy, death + + for(uint32 i = 0; i < RUNE_DEATH; ++i) + runeCost[i] = src->RuneCost[i]; + + runeCost[RUNE_DEATH] = MAX_RUNES; // calculated later + + for(uint32 i = 0; i < MAX_RUNES; ++i) + { + uint8 rune = plr->GetCurrentRune(i); + if((plr->GetRuneCooldown(i) == 0) && (runeCost[rune] > 0)) + runeCost[rune]--; + } + + for(uint32 i = 0; i < RUNE_DEATH; ++i) + if(runeCost[i] > 0) + runeCost[RUNE_DEATH] += runeCost[i]; + + if(runeCost[RUNE_DEATH] > MAX_RUNES) + return SPELL_FAILED_NO_POWER; // not sure if result code is correct + + return SPELL_CAST_OK; +} + +void Spell::TakeRunePower() +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + Player *plr = (Player*)m_caster; + + if(plr->getClass() != CLASS_DEATH_KNIGHT) + return; + + SpellRuneCostEntry const *src = sSpellRuneCostStore.LookupEntry(m_spellInfo->runeCostID); + + if(!src || (src->NoRuneCost() && src->NoRunicPowerGain())) + return; + m_runesState = plr->GetRunesState(); // store previous state + + int32 runeCost[NUM_RUNE_TYPES]; // blood, frost, unholy, death + + for(uint32 i = 0; i < RUNE_DEATH; ++i) + { + runeCost[i] = src->RuneCost[i]; + } + + runeCost[RUNE_DEATH] = 0; // calculated later + + for(uint32 i = 0; i < MAX_RUNES; ++i) + { + uint8 rune = plr->GetCurrentRune(i); + if((plr->GetRuneCooldown(i) == 0) && (runeCost[rune] > 0)) + { + plr->SetRuneCooldown(i, RUNE_COOLDOWN); // 5*2=10 sec + runeCost[rune]--; + } + } + + runeCost[RUNE_DEATH] = runeCost[RUNE_BLOOD] + runeCost[RUNE_UNHOLY] + runeCost[RUNE_FROST]; + + if(runeCost[RUNE_DEATH] > 0) + { + for(uint32 i = 0; i < MAX_RUNES; ++i) + { + uint8 rune = plr->GetCurrentRune(i); + if((plr->GetRuneCooldown(i) == 0) && (rune == RUNE_DEATH)) + { + plr->SetRuneCooldown(i, RUNE_COOLDOWN); // 5*2=10 sec + runeCost[rune]--; + plr->ConvertRune(i, plr->GetBaseRune(i)); + if(runeCost[RUNE_DEATH] == 0) + break; + } + } + } + + // you can gain some runic power when use runes + float rp = src->runePowerGain; + rp *= sWorld.getRate(RATE_POWER_RUNICPOWER_INCOME); + plr->ModifyPower(POWER_RUNIC_POWER, (int32)rp); +} + void Spell::TakeReagents() { if(m_IsTriggeredSpell) // reagents used in triggered spell removed by original spell or don't must be removed. @@ -3302,13 +3996,15 @@ void Spell::TakeReagents() if (m_caster->GetTypeId() != TYPEID_PLAYER) return; - if (m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP && - m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION)) + // do not take reagents for these item casts + if (m_CastItem && m_CastItem->GetProto()->Flags & ITEM_FLAGS_TRIGGERED_CAST) return; Player* p_caster = (Player*)m_caster; + if (p_caster->CanNoReagentCast(m_spellInfo)) + return; - for(uint32 x=0;x<8;x++) + for(uint32 x = 0; x < 8; ++x) { if(m_spellInfo->Reagent[x] <= 0) continue; @@ -3322,7 +4018,7 @@ void Spell::TakeReagents() ItemPrototype const *proto = m_CastItem->GetProto(); if( proto && proto->ItemId == itemid ) { - for(int s=0;s<5;s++) + for(int s = 0; s < MAX_ITEM_PROTO_SPELLS; ++s) { // CastItem will be used up and does not count as reagent int32 charges = m_CastItem->GetSpellCharges(s); @@ -3338,7 +4034,7 @@ void Spell::TakeReagents() } // if getItemTarget is also spell reagent - if (m_targets.getItemTargetEntry()==itemid) + if (m_targets.getItemTargetEntry() == itemid) m_targets.setItemTarget(NULL); p_caster->DestroyItemCount(itemid, itemcount, true); @@ -3362,20 +4058,19 @@ void Spell::HandleThreatSpells(uint32 spellId) DEBUG_LOG("Spell %u, rank %u, added an additional %i threat", spellId, spellmgr.GetSpellRank(spellId), threatSpell->threat); } -void Spell::HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTarget,uint32 i, float /*DamageMultiplier*/) +void Spell::HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTarget,uint32 i) { + //effect has been handled, skip it + if(m_effectMask & (1<<i)) + return; + unitTarget = pUnitTarget; itemTarget = pItemTarget; gameObjTarget = pGOTarget; uint8 eff = m_spellInfo->Effect[i]; - uint32 mechanic = m_spellInfo->EffectMechanic[i]; - sLog.outDebug( "Spell: Effect : %u", eff); - - //Simply return. Do not display "immune" in red text on client - if(unitTarget && unitTarget->IsImmunedToSpellEffect(eff, mechanic)) - return; + sLog.outDebug( "Spell: %u Effect : %u", m_spellInfo->Id, eff); //we do not need DamageMultiplier here. damage = CalculateDamage(i, NULL); @@ -3383,21 +4078,8 @@ void Spell::HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTar if(eff<TOTAL_SPELL_EFFECTS) { //sLog.outDebug( "WORLD: Spell FX %d < TOTAL_SPELL_EFFECTS ", eff); - (*this.*SpellEffects[eff])(i); - } - /* - else - { - sLog.outDebug( "WORLD: Spell FX %d > TOTAL_SPELL_EFFECTS ", eff); - if (m_CastItem) - EffectEnchantItemTmp(i); - else - { - sLog.outError("SPELL: unknown effect %u spell id %u\n", - eff, m_spellInfo->Id); - } + (this->*SpellEffects[eff])(i); } - */ } void Spell::TriggerSpell() @@ -3409,15 +4091,17 @@ void Spell::TriggerSpell() } } -uint8 Spell::CanCast(bool strict) +SpellCastResult Spell::CheckCast(bool strict) { // check cooldowns to prevent cheating - if(!m_IsTriggeredSpell && m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->HasSpellCooldown(m_spellInfo->Id)) + if(m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->HasSpellCooldown(m_spellInfo->Id)) { - //triggered spells shouldn't be casted (cooldown check in handleproctriggerspell) - // if(m_triggeredByAuraSpell) - // return SPELL_FAILED_DONT_REPORT; - // else + //can cast triggered (by aura only?) spells while have this flag + if (!m_IsTriggeredSpell && ((Player*)m_caster)->HasFlag(PLAYER_FLAGS, PLAYER_ALLOW_ONLY_ABILITY)) + return SPELL_FAILED_SPELL_IN_PROGRESS; + if(m_triggeredByAuraSpell) + return SPELL_FAILED_DONT_REPORT; + else return SPELL_FAILED_NOT_READY; } @@ -3431,46 +4115,90 @@ uint8 Spell::CanCast(bool strict) // for now, ignore triggered spells if( strict && !m_IsTriggeredSpell) { - // Cannot be used in this stance/form - if(uint8 shapeError = GetErrorAtShapeshiftedCast(m_spellInfo, m_caster->m_form)) - return shapeError; + bool checkForm = true; + // Ignore form req aura + Unit::AuraEffectList const& ignore = m_caster->GetAurasByType(SPELL_AURA_MOD_IGNORE_SHAPESHIFT); + for(Unit::AuraEffectList::const_iterator i = ignore.begin(); i != ignore.end(); ++i) + { + if (!(*i)->isAffectedOnSpell(m_spellInfo)) + continue; + checkForm = false; + break; + } + if (checkForm) + { + // Cannot be used in this stance/form + SpellCastResult shapeError = GetErrorAtShapeshiftedCast(m_spellInfo, m_caster->m_form); + if(shapeError != SPELL_CAST_OK) + return shapeError; - if ((m_spellInfo->Attributes & SPELL_ATTR_ONLY_STEALTHED) && !(m_caster->HasStealthAura())) - return SPELL_FAILED_ONLY_STEALTHED; + if ((m_spellInfo->Attributes & SPELL_ATTR_ONLY_STEALTHED) && !(m_caster->HasStealthAura())) + return SPELL_FAILED_ONLY_STEALTHED; + } } - // caster state requirements - if(m_spellInfo->CasterAuraState && !m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraState))) - return SPELL_FAILED_CASTER_AURASTATE; - if(m_spellInfo->CasterAuraStateNot && m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraStateNot))) - return SPELL_FAILED_CASTER_AURASTATE; + bool reqCombat=true; + Unit::AuraEffectList const& stateAuras = m_caster->GetAurasByType(SPELL_AURA_ABILITY_IGNORE_AURASTATE); + for(Unit::AuraEffectList::const_iterator j = stateAuras.begin();j != stateAuras.end(); ++j) + { + if((*j)->isAffectedOnSpell(m_spellInfo)) + { + if ((*j)->GetMiscValue()==1) + { + reqCombat=false; + break; + } + } + } + + // caster state requirements + // not for triggered spells (needed by execute) + if (!m_IsTriggeredSpell) + { + if(m_spellInfo->CasterAuraState && !m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraState), m_spellInfo, m_caster)) + return SPELL_FAILED_CASTER_AURASTATE; + if(m_spellInfo->CasterAuraStateNot && m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraStateNot), m_spellInfo, m_caster)) + return SPELL_FAILED_CASTER_AURASTATE; + + if(m_spellInfo->casterAuraSpell && !m_caster->HasAura(m_spellInfo->casterAuraSpell)) + return SPELL_FAILED_CASTER_AURASTATE; + if(m_spellInfo->excludeCasterAuraSpell && m_caster->HasAura(m_spellInfo->excludeCasterAuraSpell)) + return SPELL_FAILED_CASTER_AURASTATE; + + if(reqCombat && m_caster->isInCombat() && IsNonCombatSpell(m_spellInfo)) + return SPELL_FAILED_AFFECTING_COMBAT; + } // cancel autorepeat spells if cast start when moving // (not wand currently autorepeat cast delayed to moving stop anyway in spell update code) if( m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->isMoving() ) { // skip stuck spell to allow use it in falling case and apply spell limitations at movement - if( (!m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING) || m_spellInfo->Effect[0] != SPELL_EFFECT_STUCK) && + if( (!((Player*)m_caster)->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FALLING) || m_spellInfo->Effect[0] != SPELL_EFFECT_STUCK) && (IsAutoRepeat() || (m_spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) != 0) ) return SPELL_FAILED_MOVING; } - Unit *target = m_targets.getUnitTarget(); - - if(target) + if(Unit *target = m_targets.getUnitTarget()) { // target state requirements (not allowed state), apply to self also - if(m_spellInfo->TargetAuraStateNot && target->HasAuraState(AuraState(m_spellInfo->TargetAuraStateNot))) + if(!m_IsTriggeredSpell && m_spellInfo->TargetAuraStateNot && target->HasAuraState(AuraState(m_spellInfo->TargetAuraStateNot), m_spellInfo, m_caster)) + return SPELL_FAILED_TARGET_AURASTATE; + + if(m_spellInfo->targetAuraSpell && !target->HasAura(m_spellInfo->targetAuraSpell)) + return SPELL_FAILED_TARGET_AURASTATE; + + if(m_spellInfo->excludeTargetAuraSpell && target->HasAura(m_spellInfo->excludeTargetAuraSpell)) return SPELL_FAILED_TARGET_AURASTATE; if(target != m_caster) { // target state requirements (apply to non-self only), to allow cast affects to self like Dirty Deeds - if(m_spellInfo->TargetAuraState && !target->HasAuraState(AuraState(m_spellInfo->TargetAuraState))) + if(!m_IsTriggeredSpell && m_spellInfo->TargetAuraState && !target->HasAuraState(AuraState(m_spellInfo->TargetAuraState), m_spellInfo, m_caster)) return SPELL_FAILED_TARGET_AURASTATE; // Not allow casting on flying player - if (target->isInFlight()) + if (target->hasUnitState(UNIT_STAT_UNATTACKABLE)) return SPELL_FAILED_BAD_TARGETS; if(!m_IsTriggeredSpell && VMAP::VMapFactory::checkSpellForLoS(m_spellInfo->Id) && !m_caster->IsWithinLOSInMap(target)) @@ -3479,22 +4207,32 @@ uint8 Spell::CanCast(bool strict) // auto selection spell rank implemented in WorldSession::HandleCastSpellOpcode // this case can be triggered if rank not found (too low-level target for first rank) if(m_caster->GetTypeId() == TYPEID_PLAYER && !IsPassiveSpell(m_spellInfo->Id) && !m_CastItem) - { for(int i=0;i<3;i++) - { if(IsPositiveEffect(m_spellInfo->Id, i) && m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA) if(target->getLevel() + 10 < m_spellInfo->spellLevel) return SPELL_FAILED_LOWLEVEL; - } + } + else if (m_caster->GetTypeId() == TYPEID_PLAYER) // Target - is player caster + { + // Additional check for some spells + // If 0 spell effect empty - client not send target data (need use selection) + // TODO: check it on next client version + if (m_targets.m_targetMask == TARGET_FLAG_SELF && + m_spellInfo->EffectImplicitTargetA[1] == TARGET_UNIT_TARGET_ENEMY) + { + if (target = m_caster->GetUnit(*m_caster, ((Player *)m_caster)->GetSelection())) + m_targets.setUnitTarget(target); + else + return SPELL_FAILED_BAD_TARGETS; } } // check pet presents - for(int j=0;j<3;j++) + for(int j = 0; j < 3; ++j) { if(m_spellInfo->EffectImplicitTargetA[j] == TARGET_UNIT_PET) { - target = m_caster->GetPet(); + target = m_caster->GetGuardianPet(); if(!target) { if(m_triggeredByAuraSpell) // not report pet not existence for triggered spells @@ -3512,7 +4250,7 @@ uint8 Spell::CanCast(bool strict) { if(!CheckTargetCreatureType(target)) { - if(target->GetTypeId()==TYPEID_PLAYER) + if(target->GetTypeId() == TYPEID_PLAYER) return SPELL_FAILED_TARGET_IS_PLAYER; else return SPELL_FAILED_BAD_TARGETS; @@ -3521,7 +4259,7 @@ uint8 Spell::CanCast(bool strict) // TODO: this check can be applied and for player to prevent cheating when IsPositiveSpell will return always correct result. // check target for pet/charmed casts (not self targeted), self targeted cast used for area effects and etc - if(m_caster != target && m_caster->GetTypeId()==TYPEID_UNIT && m_caster->GetCharmerOrOwnerGUID()) + if(m_caster != target && m_caster->GetTypeId() == TYPEID_UNIT && m_caster->GetCharmerOrOwnerGUID()) { // check correctness positive/negative cast target (pet cast real check and cheating check) if(IsPositiveSpell(m_spellInfo->Id)) @@ -3538,14 +4276,15 @@ uint8 Spell::CanCast(bool strict) } if(IsPositiveSpell(m_spellInfo->Id)) - { - if(target->IsImmunedToSpell(m_spellInfo,false)) + if(target->IsImmunedToSpell(m_spellInfo)) return SPELL_FAILED_TARGET_AURASTATE; - } //Must be behind the target. if( m_spellInfo->AttributesEx2 == 0x100000 && (m_spellInfo->AttributesEx & 0x200) == 0x200 && target->HasInArc(M_PI, m_caster) - && (m_spellInfo->SpellFamilyName != SPELLFAMILY_DRUID || m_spellInfo->SpellFamilyFlags != 0x0000000000020000LL)) + //Exclusion for Pounce: Facing Limitation was removed in 2.0.1, but it still uses the same, old Ex-Flags + && (!(m_spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && m_spellInfo->SpellFamilyFlags.IsEqual(0x20000,0,0))) + //Mutilate no longer requires you be behind the target as of patch 3.0.3 + && (!(m_spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && m_spellInfo->SpellFamilyFlags[1] & 0x200000))) { SendInterrupted(2); return SPELL_FAILED_NOT_BEHIND; @@ -3560,12 +4299,11 @@ uint8 Spell::CanCast(bool strict) // check if target is in combat if (target != m_caster && (m_spellInfo->AttributesEx & SPELL_ATTR_EX_NOT_IN_COMBAT_TARGET) && target->isInCombat()) - { return SPELL_FAILED_TARGET_AFFECTING_COMBAT; - } } + // Spell casted only on battleground - if((m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_BATTLEGROUND) && m_caster->GetTypeId()==TYPEID_PLAYER) + if((m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_BATTLEGROUND) && m_caster->GetTypeId() == TYPEID_PLAYER) if(!((Player*)m_caster)->InBattleGround()) return SPELL_FAILED_ONLY_BATTLEGROUNDS; @@ -3573,14 +4311,22 @@ uint8 Spell::CanCast(bool strict) // - with greater than 15 min CD without SPELL_ATTR_EX4_USABLE_IN_ARENA flag // - with SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA flag if( (m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA) || - GetSpellRecoveryTime(m_spellInfo) > 15 * MINUTE * 1000 && !(m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_USABLE_IN_ARENA) ) + GetSpellRecoveryTime(m_spellInfo) > 15 * MINUTE * IN_MILISECONDS && !(m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_USABLE_IN_ARENA) ) if(MapEntry const* mapEntry = sMapStore.LookupEntry(m_caster->GetMapId())) if(mapEntry->IsBattleArena()) return SPELL_FAILED_NOT_IN_ARENA; // zone check - if(!IsSpellAllowedInLocation(m_spellInfo,m_caster->GetMapId(),m_caster->GetZoneId(),m_caster->GetAreaId())) - return SPELL_FAILED_REQUIRES_AREA; + if(m_caster->GetTypeId() == TYPEID_UNIT || !((Player*)m_caster)->isGameMaster()) + { + uint32 zone, area; + m_caster->GetZoneAndAreaId(zone,area); + + SpellCastResult locRes= spellmgr.GetSpellAllowedInLocationError(m_spellInfo,m_caster->GetMapId(),zone,area, + m_caster->GetTypeId()==TYPEID_PLAYER ? ((Player*)m_caster) : NULL); + if(locRes != SPELL_CAST_OK) + return locRes; + } // not let players cast spells at mount (and let do it to creatures) if( m_caster->IsMounted() && m_caster->GetTypeId()==TYPEID_PLAYER && !m_IsTriggeredSpell && @@ -3594,13 +4340,16 @@ uint8 Spell::CanCast(bool strict) // always (except passive spells) check items (focus object can be required for any type casts) if(!IsPassiveSpell(m_spellInfo->Id)) - if(uint8 castResult = CheckItems()) + { + SpellCastResult castResult = CheckItems(); + if(castResult != SPELL_CAST_OK) return castResult; + } /*//ImpliciteTargetA-B = 38, If fact there is 0 Spell with ImpliciteTargetB=38 - if(m_UniqueTargetInfo.empty()) // skip second canCast apply (for delayed spells for example) + if(m_UniqueTargetInfo.empty()) // skip second CheckCast apply (for delayed spells for example) { - for(uint8 j = 0; j < 3; j++) + for(uint8 j = 0; j < 3; ++j) { if( m_spellInfo->EffectImplicitTargetA[j] == TARGET_UNIT_NEARBY_ENTRY || m_spellInfo->EffectImplicitTargetB[j] == TARGET_UNIT_NEARBY_ENTRY && m_spellInfo->EffectImplicitTargetA[j] != TARGET_UNIT_CASTER || @@ -3633,7 +4382,7 @@ uint8 Spell::CanCast(bool strict) cell.data.Part.reserved = ALL_DISTRICT; MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*m_caster,i_spellST->second.targetEntry,range); - MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(p_GameObject,go_check); + MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(m_caster, p_GameObject,go_check); TypeContainerVisitor<MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker); CellLock<GridReadGuard> cell_lock(cell, p); @@ -3647,7 +4396,7 @@ uint8 Spell::CanCast(bool strict) range = go_check.GetLastRange(); } } - else if( focusObject ) //Focus Object + else if( focusObject ) // Focus Object { float frange = m_caster->GetDistance(focusObject); if(range >= frange) @@ -3671,7 +4420,7 @@ uint8 Spell::CanCast(bool strict) cell.SetNoCreate(); // Really don't know what is that??? MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster,i_spellST->second.targetEntry,i_spellST->second.type!=SPELL_TARGET_TYPE_DEAD,range); - MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(p_Creature, u_check); + MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(m_caster, p_Creature, u_check); TypeContainerVisitor<MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck>, GridTypeMapContainer > grid_creature_searcher(searcher); @@ -3734,13 +4483,16 @@ uint8 Spell::CanCast(bool strict) if(!m_IsTriggeredSpell) { - if(uint8 castResult = CheckRange(strict)) + SpellCastResult castResult = CheckRange(strict); + if(castResult != SPELL_CAST_OK) return castResult; - if(uint8 castResult = CheckPower()) + castResult = CheckPower(); + if(castResult != SPELL_CAST_OK) return castResult; - if(uint8 castResult = CheckCasterAuras()) + castResult = CheckCasterAuras(); + if(castResult != SPELL_CAST_OK) return castResult; } @@ -3751,21 +4503,16 @@ uint8 Spell::CanCast(bool strict) { case SPELL_EFFECT_DUMMY: { - if(m_spellInfo->SpellIconID == 1648) // Execute - { - if(!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetHealth() > m_targets.getUnitTarget()->GetMaxHealth()*0.2) - return SPELL_FAILED_BAD_TARGETS; - } - else if (m_spellInfo->Id == 51582) // Rocket Boots Engaged + if (m_spellInfo->Id == 51582) // Rocket Boots Engaged { if(m_caster->IsInWater()) return SPELL_FAILED_ONLY_ABOVEWATER; } - else if(m_spellInfo->SpellIconID==156) // Holy Shock + else if(m_spellInfo->SpellIconID == 156) // Holy Shock { // spell different for friends and enemies // hart version required facing - if(m_targets.getUnitTarget() && !m_caster->IsFriendlyTo(m_targets.getUnitTarget()) && !m_caster->HasInArc( M_PI, target )) + if(m_targets.getUnitTarget() && !m_caster->IsFriendlyTo(m_targets.getUnitTarget()) && !m_caster->HasInArc( M_PI, m_targets.getUnitTarget() )) return SPELL_FAILED_UNIT_NOT_INFRONT; } else if (m_spellInfo->Id == 19938) // Awaken Peon @@ -3776,25 +4523,15 @@ uint8 Spell::CanCast(bool strict) } break; } - case SPELL_EFFECT_SCHOOL_DAMAGE: - { - // Hammer of Wrath - if(m_spellInfo->SpellVisual == 7250) - { - if (!m_targets.getUnitTarget()) - return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - - if(m_targets.getUnitTarget()->GetHealth() > m_targets.getUnitTarget()->GetMaxHealth()*0.2) - return SPELL_FAILED_BAD_TARGETS; - } - break; - } case SPELL_EFFECT_LEARN_SPELL: { + if (m_caster->GetTypeId() != TYPEID_PLAYER) + return SPELL_FAILED_BAD_TARGETS; + if(m_spellInfo->EffectImplicitTargetA[i] != TARGET_UNIT_PET) break; - Pet* pet = m_caster->GetPet(); + Pet* pet = ((Player*)m_caster)->GetPet(); if(!pet) return SPELL_FAILED_NO_PET; @@ -3804,21 +4541,17 @@ uint8 Spell::CanCast(bool strict) if(!learn_spellproto) return SPELL_FAILED_NOT_KNOWN; - if(!pet->CanTakeMoreActiveSpells(learn_spellproto->Id)) - return SPELL_FAILED_TOO_MANY_SKILLS; - if(m_spellInfo->spellLevel > pet->getLevel()) return SPELL_FAILED_LOWLEVEL; - if(!pet->HasTPForSpell(learn_spellproto->Id)) - return SPELL_FAILED_TRAINING_POINTS; - break; } case SPELL_EFFECT_LEARN_PET_SPELL: { - Pet* pet = m_caster->GetPet(); + if (m_caster->GetTypeId() != TYPEID_PLAYER) + return SPELL_FAILED_BAD_TARGETS; + Pet* pet = ((Player*)m_caster)->GetPet(); if(!pet) return SPELL_FAILED_NO_PET; @@ -3827,31 +4560,29 @@ uint8 Spell::CanCast(bool strict) if(!learn_spellproto) return SPELL_FAILED_NOT_KNOWN; - if(!pet->CanTakeMoreActiveSpells(learn_spellproto->Id)) - return SPELL_FAILED_TOO_MANY_SKILLS; - if(m_spellInfo->spellLevel > pet->getLevel()) return SPELL_FAILED_LOWLEVEL; - if(!pet->HasTPForSpell(learn_spellproto->Id)) - return SPELL_FAILED_TRAINING_POINTS; - break; } case SPELL_EFFECT_FEED_PET: { - if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_targets.getItemTarget() ) + if (m_caster->GetTypeId() != TYPEID_PLAYER) + return SPELL_FAILED_BAD_TARGETS; + + Item* foodItem = m_targets.getItemTarget(); + if(!foodItem) return SPELL_FAILED_BAD_TARGETS; - Pet* pet = m_caster->GetPet(); + Pet* pet = ((Player*)m_caster)->GetPet(); if(!pet) return SPELL_FAILED_NO_PET; - if(!pet->HaveInDiet(m_targets.getItemTarget()->GetProto())) + if(!pet->HaveInDiet(foodItem->GetProto())) return SPELL_FAILED_WRONG_PET_FOOD; - if(!pet->GetCurrentFoodBenefitLevel(m_targets.getItemTarget()->GetProto()->ItemLevel)) + if(!pet->GetCurrentFoodBenefitLevel(foodItem->GetProto()->ItemLevel)) return SPELL_FAILED_FOOD_LOWLEVEL; if(m_caster->isInCombat() || pet->isInCombat()) @@ -3865,7 +4596,7 @@ uint8 Spell::CanCast(bool strict) // Can be area effect, Check only for players and not check if target - caster (spell can have multiply drain/burn effects) if(m_caster->GetTypeId() == TYPEID_PLAYER) if(Unit* target = m_targets.getUnitTarget()) - if(target!=m_caster && target->getPowerType()!=m_spellInfo->EffectMiscValue[i]) + if(target != m_caster && target->getPowerType() != m_spellInfo->EffectMiscValue[i]) return SPELL_FAILED_BAD_TARGETS; break; } @@ -3894,19 +4625,18 @@ uint8 Spell::CanCast(bool strict) int32 skillValue = ((Player*)m_caster)->GetSkillValue(skill); int32 TargetLevel = m_targets.getUnitTarget()->getLevel(); - int32 ReqValue = (skillValue < 100 ? (TargetLevel-10)*10 : TargetLevel*5); + int32 ReqValue = (skillValue < 100 ? (TargetLevel-10) * 10 : TargetLevel * 5); if (ReqValue > skillValue) return SPELL_FAILED_LOW_CASTLEVEL; // chance for fail at orange skinning attempt if( (m_selfContainer && (*m_selfContainer) == this) && skillValue < sWorld.GetConfigMaxSkillValue() && - (ReqValue < 0 ? 0 : ReqValue) > m_caster->GetMap()->irand(skillValue-25, skillValue+37) ) + (ReqValue < 0 ? 0 : ReqValue) > irand(skillValue - 25, skillValue + 37) ) return SPELL_FAILED_TRY_AGAIN; break; } - case SPELL_EFFECT_OPEN_LOCK_ITEM: case SPELL_EFFECT_OPEN_LOCK: { if( m_spellInfo->EffectImplicitTargetA[i] != TARGET_GAMEOBJECT && @@ -3923,130 +4653,44 @@ uint8 Spell::CanCast(bool strict) // In BattleGround players can use only flags and banners if( ((Player*)m_caster)->InBattleGround() && - !((Player*)m_caster)->isAllowUseBattleGroundObject() ) + !((Player*)m_caster)->CanUseBattleGroundObject() ) return SPELL_FAILED_TRY_AGAIN; // get the lock entry - LockEntry const *lockInfo = NULL; - if (GameObject* go=m_targets.getGOTarget()) - lockInfo = sLockStore.LookupEntry(go->GetLockId()); - else if(Item* itm=m_targets.getItemTarget()) - lockInfo = sLockStore.LookupEntry(itm->GetProto()->LockID); - - // check lock compatibility - if (lockInfo) + uint32 lockId = 0; + if (GameObject* go = m_targets.getGOTarget()) { - // check for lock - key pair (checked by client also, just prevent cheating - bool ok_key = false; - for(int it = 0; it < 5; ++it) - { - switch(lockInfo->keytype[it]) - { - case LOCK_KEY_NONE: - break; - case LOCK_KEY_ITEM: - { - if(lockInfo->key[it]) - { - if(m_CastItem && m_CastItem->GetEntry()==lockInfo->key[it]) - ok_key =true; - break; - } - } - case LOCK_KEY_SKILL: - { - if(uint32(m_spellInfo->EffectMiscValue[i])!=lockInfo->key[it]) - break; - - switch(lockInfo->key[it]) - { - case LOCKTYPE_HERBALISM: - if(((Player*)m_caster)->HasSkill(SKILL_HERBALISM)) - ok_key =true; - break; - case LOCKTYPE_MINING: - if(((Player*)m_caster)->HasSkill(SKILL_MINING)) - ok_key =true; - break; - default: - ok_key =true; - break; - } - } - } - if(ok_key) - break; - } - - if(!ok_key) + lockId = go->GetLockId(); + if (!lockId) return SPELL_FAILED_BAD_TARGETS; } + else if(Item* itm = m_targets.getItemTarget()) + lockId = itm->GetProto()->LockID; - // chance for fail at orange mining/herb/LockPicking gathering attempt - if (!m_selfContainer || ((*m_selfContainer) != this)) - break; + SkillType skillId = SKILL_NONE; + int32 reqSkillValue = 0; + int32 skillValue = 0; - // get the skill value of the player - int32 SkillValue = 0; - bool canFailAtMax = true; - if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_HERBALISM) - { - SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_HERBALISM); - canFailAtMax = false; - } - else if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_MINING) - { - SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_MINING); - canFailAtMax = false; - } - else if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_PICKLOCK) - SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_LOCKPICKING); - - // castitem check: rogue using skeleton keys. the skill values should not be added in this case. - if(m_CastItem) - SkillValue = 0; - - // add the damage modifier from the spell casted (cheat lock / skeleton key etc.) (use m_currentBasePoints, CalculateDamage returns wrong value) - // TODO: is this a hack? - SkillValue += m_currentBasePoints[i]+1; + // check lock compatibility + SpellCastResult res = CanOpenLock(i, lockId, skillId, reqSkillValue, skillValue); + if(res != SPELL_CAST_OK) + return res; - // get the required lock value - int32 ReqValue=0; - if (lockInfo) + // chance for fail at orange mining/herb/LockPicking gathering attempt + // second check prevent fail at rechecks + if(skillId != SKILL_NONE && (!m_selfContainer || ((*m_selfContainer) != this))) { - // check for lock - key pair - bool ok = false; - for(int it = 0; it < 5; ++it) - { - if(lockInfo->keytype[it]==LOCK_KEY_ITEM && lockInfo->key[it] && m_CastItem && m_CastItem->GetEntry()==lockInfo->key[it]) - { - // if so, we're good to go - ok = true; - break; - } - } - if(ok) - break; + bool canFailAtMax = skillId != SKILL_HERBALISM && skillId != SKILL_MINING; - if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_PICKLOCK) - ReqValue = lockInfo->requiredlockskill; - else - ReqValue = lockInfo->requiredminingskill; + // chance for failure in orange gather / lockpick (gathering skill can't fail at maxskill) + if((canFailAtMax || skillValue < sWorld.GetConfigMaxSkillValue()) && reqSkillValue > irand(skillValue - 25, skillValue + 37)) + return SPELL_FAILED_TRY_AGAIN; } - - // skill doesn't meet the required value - if (ReqValue > SkillValue) - return SPELL_FAILED_LOW_CASTLEVEL; - - // chance for failure in orange gather / lockpick (gathering skill can't fail at maxskill) - if((canFailAtMax || SkillValue < sWorld.GetConfigMaxSkillValue()) && ReqValue > m_caster->GetMap()->irand(SkillValue-25, SkillValue+37)) - return SPELL_FAILED_TRY_AGAIN; - break; } case SPELL_EFFECT_SUMMON_DEAD_PET: { - Creature *pet = m_caster->GetPet(); + Creature *pet = m_caster->GetGuardianPet(); if(!pet) return SPELL_FAILED_NO_PET; @@ -4055,34 +4699,26 @@ uint8 Spell::CanCast(bool strict) break; } - // This is generic summon effect now and don't make this check for summon types similar - // SPELL_EFFECT_SUMMON_CRITTER, SPELL_EFFECT_SUMMON_WILD or SPELL_EFFECT_SUMMON_GUARDIAN. - // These won't show up in m_caster->GetPetGUID() + // This is generic summon effect case SPELL_EFFECT_SUMMON: { - switch(m_spellInfo->EffectMiscValueB[i]) + SummonPropertiesEntry const *SummonProperties = sSummonPropertiesStore.LookupEntry(m_spellInfo->EffectMiscValueB[i]); + if(!SummonProperties) + break; + switch(SummonProperties->Category) { - case SUMMON_TYPE_POSESSED: - case SUMMON_TYPE_POSESSED2: - case SUMMON_TYPE_POSESSED3: - case SUMMON_TYPE_DEMON: - case SUMMON_TYPE_SUMMON: - { + case SUMMON_CATEGORY_PET: if(m_caster->GetPetGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; - + case SUMMON_CATEGORY_PUPPET: if(m_caster->GetCharmGUID()) return SPELL_FAILED_ALREADY_HAVE_CHARM; break; - } } break; } - // Don't make this check for SPELL_EFFECT_SUMMON_CRITTER, SPELL_EFFECT_SUMMON_WILD or SPELL_EFFECT_SUMMON_GUARDIAN. - // These won't show up in m_caster->GetPetGUID() - case SPELL_EFFECT_SUMMON_POSSESSED: + // Not used for summon? case SPELL_EFFECT_SUMMON_PHANTASM: - case SPELL_EFFECT_SUMMON_DEMON: { if(m_caster->GetPetGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; @@ -4096,13 +4732,11 @@ uint8 Spell::CanCast(bool strict) { if(m_caster->GetPetGUID()) //let warlock do a replacement summon { - - Pet* pet = ((Player*)m_caster)->GetPet(); - if (m_caster->GetTypeId()==TYPEID_PLAYER && m_caster->getClass()==CLASS_WARLOCK) { if (strict) //starting cast, trigger pet stun (cast by pet so it doesn't attack player) - pet->CastSpell(pet, 32752, true, NULL, NULL, pet->GetGUID()); + if(Pet* pet = ((Player*)m_caster)->GetPet()) + pet->CastSpell(pet, 32752, true, NULL, NULL, pet->GetGUID()); } else return SPELL_FAILED_ALREADY_HAVE_SUMMON; @@ -4115,13 +4749,13 @@ uint8 Spell::CanCast(bool strict) } case SPELL_EFFECT_SUMMON_PLAYER: { - if(m_caster->GetTypeId()!=TYPEID_PLAYER) + if(m_caster->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_BAD_TARGETS; if(!((Player*)m_caster)->GetSelection()) return SPELL_FAILED_BAD_TARGETS; Player* target = objmgr.GetPlayer(((Player*)m_caster)->GetSelection()); - if( !target || ((Player*)m_caster)==target || !target->IsInSameRaidWith((Player*)m_caster) ) + if( !target || ((Player*)m_caster) == target || !target->IsInSameRaidWith((Player*)m_caster) ) return SPELL_FAILED_BAD_TARGETS; // check if our map is dungeon @@ -4147,7 +4781,7 @@ uint8 Spell::CanCast(bool strict) } case SPELL_EFFECT_STEAL_BENEFICIAL_BUFF: { - if (m_targets.getUnitTarget()==m_caster) + if (m_targets.getUnitTarget() == m_caster) return SPELL_FAILED_BAD_TARGETS; break; } @@ -4161,27 +4795,46 @@ uint8 Spell::CanCast(bool strict) { case SPELL_AURA_DUMMY: { - if(m_spellInfo->Id == 1515) + //custom check + switch(m_spellInfo->Id) { - if (!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER) - return SPELL_FAILED_BAD_IMPLICIT_TARGETS; + case 61336: + if(m_caster->GetTypeId() != TYPEID_PLAYER || !((Player*)m_caster)->IsInFeralForm()) + return SPELL_FAILED_ONLY_SHAPESHIFT; + break; + case 1515: + { + if (m_caster->GetTypeId() != TYPEID_PLAYER) + return SPELL_FAILED_BAD_TARGETS; - if (m_targets.getUnitTarget()->getLevel() > m_caster->getLevel()) - return SPELL_FAILED_HIGHLEVEL; + if (!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER) + return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - // use SMSG_PET_TAME_FAILURE? - if (!((Creature*)m_targets.getUnitTarget())->GetCreatureInfo()->isTameable ()) - return SPELL_FAILED_BAD_TARGETS; + Creature* target = (Creature*)m_targets.getUnitTarget(); - if(m_caster->GetPetGUID()) - return SPELL_FAILED_ALREADY_HAVE_SUMMON; + if (target->getLevel() > m_caster->getLevel()) + return SPELL_FAILED_HIGHLEVEL; - if(m_caster->GetCharmGUID()) - return SPELL_FAILED_ALREADY_HAVE_CHARM; + // use SMSG_PET_TAME_FAILURE? + if (!target->GetCreatureInfo()->isTameable (((Player*)m_caster)->CanTameExoticPets())) + return SPELL_FAILED_BAD_TARGETS; + + if(m_caster->GetPetGUID()) + return SPELL_FAILED_ALREADY_HAVE_SUMMON; + + if(m_caster->GetCharmGUID()) + return SPELL_FAILED_ALREADY_HAVE_CHARM; + + break; + } + default: + break; } - }break; + break; + } case SPELL_AURA_MOD_POSSESS: case SPELL_AURA_MOD_CHARM: + //case SPELL_AURA_MOD_POSSESS_PET: { if(m_caster->GetPetGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; @@ -4192,13 +4845,15 @@ uint8 Spell::CanCast(bool strict) if(m_caster->GetCharmerGUID()) return SPELL_FAILED_CHARMED; - if(!m_targets.getUnitTarget()) + Unit *target = m_targets.getUnitTarget(); + if(!target || target->GetTypeId() == TYPEID_UNIT + && ((Creature*)target)->isVehicle()) return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - if(m_targets.getUnitTarget()->GetCharmerGUID()) + if(target->GetCharmerGUID()) return SPELL_FAILED_CHARMED; - if(int32(m_targets.getUnitTarget()->getLevel()) > CalculateDamage(i,m_targets.getUnitTarget())) + if(int32(target->getLevel()) > CalculateDamage(i, target)) return SPELL_FAILED_HIGHLEVEL; break; @@ -4208,21 +4863,18 @@ uint8 Spell::CanCast(bool strict) if (m_caster->IsInWater()) return SPELL_FAILED_ONLY_ABOVEWATER; - if (m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->GetTransport()) + if (m_caster->GetTypeId() == TYPEID_PLAYER && ((Player*)m_caster)->GetTransport()) return SPELL_FAILED_NO_MOUNTS_ALLOWED; // Ignore map check if spell have AreaId. AreaId already checked and this prevent special mount spells - if (m_caster->GetTypeId()==TYPEID_PLAYER && !sMapStore.LookupEntry(m_caster->GetMapId())->IsMountAllowed() && !m_IsTriggeredSpell && !m_spellInfo->AreaId) - return SPELL_FAILED_NO_MOUNTS_ALLOWED; - - if (m_caster->GetAreaId()==35) + if (m_caster->GetTypeId() == TYPEID_PLAYER && !sMapStore.LookupEntry(m_caster->GetMapId())->IsMountAllowed() && !m_IsTriggeredSpell && !m_spellInfo->AreaGroupId) return SPELL_FAILED_NO_MOUNTS_ALLOWED; ShapeshiftForm form = m_caster->m_form; if( form == FORM_CAT || form == FORM_TREE || form == FORM_TRAVEL || form == FORM_AQUA || form == FORM_BEAR || form == FORM_DIREBEAR || form == FORM_CREATUREBEAR || form == FORM_GHOSTWOLF || form == FORM_FLIGHT || - form == FORM_FLIGHT_EPIC || form == FORM_MOONKIN ) + form == FORM_FLIGHT_EPIC || form == FORM_MOONKIN || form == FORM_METAMORPHOSIS ) return SPELL_FAILED_NOT_SHAPESHIFT; break; @@ -4238,14 +4890,13 @@ uint8 Spell::CanCast(bool strict) break; } - case SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED: + case SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED: case SPELL_AURA_FLY: { - // not allow cast fly spells at old maps by players (all spells is self target) - if(m_caster->GetTypeId()==TYPEID_PLAYER) + // not allow cast fly spells at old maps by players (all spells is self target) + if(m_originalCaster && m_originalCaster->GetTypeId()==TYPEID_PLAYER) { - if( !((Player*)m_caster)->isGameMaster() && - GetVirtualMapForMapAndZone(m_caster->GetMapId(),m_caster->GetZoneId()) != 530) + if( !((Player*)m_originalCaster)->IsAllowUseFlyMountsHere() ) return SPELL_FAILED_NOT_HERE; } break; @@ -4255,10 +4906,10 @@ uint8 Spell::CanCast(bool strict) if (!m_targets.getUnitTarget()) return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - if (m_caster->GetTypeId()!=TYPEID_PLAYER || m_CastItem) + if (m_caster->GetTypeId() != TYPEID_PLAYER || m_CastItem) break; - if(m_targets.getUnitTarget()->getPowerType()!=POWER_MANA) + if(m_targets.getUnitTarget()->getPowerType() != POWER_MANA) return SPELL_FAILED_BAD_TARGETS; break; @@ -4269,41 +4920,38 @@ uint8 Spell::CanCast(bool strict) } // all ok - return 0; + return SPELL_CAST_OK; } -int16 Spell::PetCanCast(Unit* target) +SpellCastResult Spell::CheckPetCast(Unit* target) { - if(!m_caster->isAlive()) + if(!m_caster->isAlive() && !(m_spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_DEAD)) return SPELL_FAILED_CASTER_DEAD; - if(m_caster->IsNonMeleeSpellCasted(false) && !m_IsTriggeredSpell) //prevent spellcast interruption by another spellcast + if(m_caster->hasUnitState(UNIT_STAT_CASTING) && !m_IsTriggeredSpell) //prevent spellcast interruption by another spellcast return SPELL_FAILED_SPELL_IN_PROGRESS; if(m_caster->isInCombat() && IsNonCombatSpell(m_spellInfo)) return SPELL_FAILED_AFFECTING_COMBAT; - if(m_caster->GetTypeId()==TYPEID_UNIT && (((Creature*)m_caster)->isPet() || m_caster->isCharmed())) - { //dead owner (pets still alive when owners ressed?) - if(m_caster->GetCharmerOrOwner() && !m_caster->GetCharmerOrOwner()->isAlive()) - return SPELL_FAILED_CASTER_DEAD; + if(Unit *owner = m_caster->GetCharmerOrOwner()) + if(!owner->isAlive()) + return SPELL_FAILED_CASTER_DEAD; if(!target && m_targets.getUnitTarget()) target = m_targets.getUnitTarget(); - bool need = false; - for(uint32 i = 0;i<3;i++) + for(uint32 i = 0; i < 3; ++i) { - if(m_spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_TARGET_ENEMY || m_spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_TARGET_ALLY || m_spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_TARGET_ANY || m_spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_TARGET_PARTY || m_spellInfo->EffectImplicitTargetA[i] == TARGET_DST_TARGET_ENEMY) + if(spellmgr.SpellTargetType[m_spellInfo->EffectImplicitTargetA[i]] == TARGET_TYPE_UNIT_TARGET + || spellmgr.SpellTargetType[m_spellInfo->EffectImplicitTargetA[i]] == TARGET_TYPE_DEST_TARGET) { - need = true; if(!target) return SPELL_FAILED_BAD_IMPLICIT_TARGETS; + m_targets.setUnitTarget(target); break; } } - if(need) - m_targets.setUnitTarget(target); Unit* _target = m_targets.getUnitTarget(); @@ -4318,32 +4966,25 @@ int16 Spell::PetCanCast(Unit* target) //cooldown if(((Creature*)m_caster)->HasSpellCooldown(m_spellInfo->Id)) return SPELL_FAILED_NOT_READY; - } - uint16 result = CanCast(true); - if(result != 0) - return result; - else - return -1; //this allows to check spell fail 0, in combat + return CheckCast(true); } -uint8 Spell::CheckCasterAuras() const +SpellCastResult Spell::CheckCasterAuras() const { - // Flag drop spells totally immuned to caster auras - // FIXME: find more nice check for all totally immuned spells - // AttributesEx3 & 0x10000000? - if(m_spellInfo->Id==23336 || m_spellInfo->Id==23334 || m_spellInfo->Id==34991) - return 0; + // spells totally immuned to caster auras ( wsg flag drop, give marks etc + if(m_spellInfo->AttributesEx6& SPELL_ATTR_EX6_IGNORE_CASTER_AURAS) + return SPELL_CAST_OK; uint8 school_immune = 0; uint32 mechanic_immune = 0; uint32 dispel_immune = 0; - //Check if the spell grants school or mechanic immunity. - //We use bitmasks so the loop is done only once and not on every aura check below. + // Check if the spell grants school or mechanic immunity. + // We use bitmasks so the loop is done only once and not on every aura check below. if ( m_spellInfo->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY ) { - for(int i = 0;i < 3; i ++) + for(int i = 0; i < 3; ++i) { if(m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_SCHOOL_IMMUNITY) school_immune |= uint32(m_spellInfo->EffectMiscValue[i]); @@ -4353,26 +4994,27 @@ uint8 Spell::CheckCasterAuras() const dispel_immune |= GetDispellMask(DispelType(m_spellInfo->EffectMiscValue[i])); } //immune movement impairment and loss of control - if(m_spellInfo->Id==(uint32)42292) + if(m_spellInfo->Id==42292 || m_spellInfo->Id==59752) mechanic_immune = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; } - //Check whether the cast should be prevented by any state you might have. - uint8 prevented_reason = 0; + // Check whether the cast should be prevented by any state you might have. + SpellCastResult prevented_reason = SPELL_CAST_OK; // Have to check if there is a stun aura. Otherwise will have problems with ghost aura apply while logging out - if(!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED) && m_caster->HasAuraType(SPELL_AURA_MOD_STUN)) + uint32 unitflag = m_caster->GetUInt32Value(UNIT_FIELD_FLAGS); // Get unit state + if(unitflag & UNIT_FLAG_STUNNED && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED)) prevented_reason = SPELL_FAILED_STUNNED; - else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED) && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED)) + else if(unitflag & UNIT_FLAG_CONFUSED && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED)) prevented_reason = SPELL_FAILED_CONFUSED; - else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING) && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED)) + else if(unitflag & UNIT_FLAG_FLEEING && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED)) prevented_reason = SPELL_FAILED_FLEEING; - else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED) && m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_SILENCE) + else if(unitflag & UNIT_FLAG_SILENCED && m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE) prevented_reason = SPELL_FAILED_SILENCED; - else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED) && m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_PACIFY) + else if(unitflag & UNIT_FLAG_PACIFIED && m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_PACIFY) prevented_reason = SPELL_FAILED_PACIFIED; // Attr must make flag drop spell totally immune from all effects - if(prevented_reason) + if(prevented_reason != SPELL_CAST_OK) { if(school_immune || mechanic_immune || dispel_immune) { @@ -4382,7 +5024,7 @@ uint8 Spell::CheckCasterAuras() const { if(itr->second) { - if( GetSpellMechanicMask(itr->second->GetSpellProto(), itr->second->GetEffIndex()) & mechanic_immune ) + if( GetAllSpellMechanicMask(itr->second->GetSpellProto()) & mechanic_immune ) continue; if( GetSpellSchoolMask(itr->second->GetSpellProto()) & school_immune ) continue; @@ -4391,68 +5033,75 @@ uint8 Spell::CheckCasterAuras() const //Make a second check for spell failed so the right SPELL_FAILED message is returned. //That is needed when your casting is prevented by multiple states and you are only immune to some of them. - switch(itr->second->GetModifier()->m_auraname) + for (uint8 i=0;i<MAX_SPELL_EFFECTS;++i) { - case SPELL_AURA_MOD_STUN: - if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED)) - return SPELL_FAILED_STUNNED; - break; - case SPELL_AURA_MOD_CONFUSE: - if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED)) - return SPELL_FAILED_CONFUSED; - break; - case SPELL_AURA_MOD_FEAR: - if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED)) - return SPELL_FAILED_FLEEING; - break; - case SPELL_AURA_MOD_SILENCE: - case SPELL_AURA_MOD_PACIFY: - case SPELL_AURA_MOD_PACIFY_SILENCE: - if( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_PACIFY) - return SPELL_FAILED_PACIFIED; - else if ( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_SILENCE) - return SPELL_FAILED_SILENCED; - break; + if (AuraEffect * part = itr->second->GetPartAura(i)) + { + switch(part->GetAuraName()) + { + case SPELL_AURA_MOD_STUN: + if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED)) + return SPELL_FAILED_STUNNED; + break; + case SPELL_AURA_MOD_CONFUSE: + if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED)) + return SPELL_FAILED_CONFUSED; + break; + case SPELL_AURA_MOD_FEAR: + if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED)) + return SPELL_FAILED_FLEEING; + break; + case SPELL_AURA_MOD_SILENCE: + case SPELL_AURA_MOD_PACIFY: + case SPELL_AURA_MOD_PACIFY_SILENCE: + if( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_PACIFY) + return SPELL_FAILED_PACIFIED; + else if ( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_SILENCE) + return SPELL_FAILED_SILENCED; + break; + } + } } } } } - //You are prevented from casting and the spell casted does not grant immunity. Return a failed error. + // You are prevented from casting and the spell casted does not grant immunity. Return a failed error. else return prevented_reason; } - return 0; // all ok + return SPELL_CAST_OK; } bool Spell::CanAutoCast(Unit* target) { uint64 targetguid = target->GetGUID(); - for(uint32 j = 0;j<3;j++) + for(uint32 j = 0; j < 3; ++j) { if(m_spellInfo->Effect[j] == SPELL_EFFECT_APPLY_AURA) { if( m_spellInfo->StackAmount <= 1) { - if( target->HasAura(m_spellInfo->Id, j) ) + if( target->HasAuraEffect(m_spellInfo->Id, j) ) return false; } else { - if( target->GetAuras().count(Unit::spellEffectPair(m_spellInfo->Id, j)) >= m_spellInfo->StackAmount) - return false; + if( AuraEffect * aureff = target->GetAuraEffect(m_spellInfo->Id, j)) + if (aureff->GetParentAura()->GetStackAmount() >= m_spellInfo->StackAmount) + return false; } } else if ( IsAreaAuraEffect( m_spellInfo->Effect[j] )) { - if( target->HasAura(m_spellInfo->Id, j) ) - return false; + if( target->HasAuraEffect(m_spellInfo->Id, j) ) + return false; } } - int16 result = PetCanCast(target); + SpellCastResult result = CheckPetCast(target); - if(result == -1 || result == SPELL_FAILED_UNIT_NOT_INFRONT) + if(result == SPELL_CAST_OK || result == SPELL_FAILED_UNIT_NOT_INFRONT) { FillTargetMap(); //check if among target units, our WANTED target is as well (->only self cast spells return false) @@ -4463,12 +5112,13 @@ bool Spell::CanAutoCast(Unit* target) return false; //target invalid } -uint8 Spell::CheckRange(bool strict) +SpellCastResult Spell::CheckRange(bool strict) { //float range_mod; // self cast doesn't need range checking -- also for Starshards fix - if (m_spellInfo->rangeIndex == 1) return 0; + if (m_spellInfo->rangeIndex == 1) + return SPELL_CAST_OK; // i do not know why we need this /*if (strict) //add radius of caster @@ -4477,15 +5127,15 @@ uint8 Spell::CheckRange(bool strict) range_mod = 6.25;*/ SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex); - float max_range = GetSpellMaxRange(srange); // + range_mod; - float min_range = GetSpellMinRange(srange); + + Unit *target = m_targets.getUnitTarget(); + float max_range = m_caster->GetSpellMaxRangeForTarget(target, srange); // + range_mod; + float min_range = m_caster->GetSpellMinRangeForTarget(target, srange); uint32 range_type = GetSpellRangeType(srange); if(Player* modOwner = m_caster->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, max_range, this); - Unit *target = m_targets.getUnitTarget(); - if(target && target != m_caster) { if(range_type == SPELL_RANGE_MELEE) @@ -4512,14 +5162,13 @@ uint8 Spell::CheckRange(bool strict) if(m_targets.m_targetMask == TARGET_FLAG_DEST_LOCATION && m_targets.m_destX != 0 && m_targets.m_destY != 0 && m_targets.m_destZ != 0) { - float dist = m_caster->GetDistance(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ); - if(dist > max_range) + if(!m_caster->IsWithinDist3d(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, max_range)) return SPELL_FAILED_OUT_OF_RANGE; - if(dist < min_range) + if(m_caster->IsWithinDist3d(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, min_range)) return SPELL_FAILED_TOO_CLOSE; } - return 0; // ok + return SPELL_CAST_OK; } int32 Spell::CalculatePowerCost() @@ -4559,9 +5208,12 @@ int32 Spell::CalculatePowerCost() case POWER_FOCUS: case POWER_ENERGY: case POWER_HAPPINESS: - // case POWER_RUNES: powerCost += m_spellInfo->ManaCostPercentage * m_caster->GetMaxPower(Powers(m_spellInfo->powerType)) / 100; break; + case POWER_RUNE: + case POWER_RUNIC_POWER: + sLog.outDebug("Spell::CalculateManaCost: Not implemented yet!"); + break; default: sLog.outError("Spell::CalculateManaCost: Unknown power type '%d' in spell %d", m_spellInfo->powerType, m_spellInfo->Id); return 0; @@ -4587,39 +5239,43 @@ int32 Spell::CalculatePowerCost() return powerCost; } -uint8 Spell::CheckPower() +SpellCastResult Spell::CheckPower() { // item cast not used power if(m_CastItem) - return 0; + return SPELL_CAST_OK; // health as power used - need check health amount if(m_spellInfo->powerType == POWER_HEALTH) { if(m_caster->GetHealth() <= m_powerCost) return SPELL_FAILED_CASTER_AURASTATE; - return 0; + return SPELL_CAST_OK; } // Check valid power type if( m_spellInfo->powerType >= MAX_POWERS ) { - sLog.outError("Spell::CheckMana: Unknown power type '%d'", m_spellInfo->powerType); + sLog.outError("Spell::CheckPower: Unknown power type '%d'", m_spellInfo->powerType); return SPELL_FAILED_UNKNOWN; } + + SpellCastResult failReason = CheckRuneCost(m_spellInfo->runeCostID); + if(failReason != SPELL_CAST_OK) + return failReason; + // Check power amount Powers powerType = Powers(m_spellInfo->powerType); if(m_caster->GetPower(powerType) < m_powerCost) return SPELL_FAILED_NO_POWER; else - return 0; + return SPELL_CAST_OK; } -uint8 Spell::CheckItems() +SpellCastResult Spell::CheckItems() { if (m_caster->GetTypeId() != TYPEID_PLAYER) - return 0; + return SPELL_CAST_OK; - uint32 itemid, itemcount; Player* p_caster = (Player*)m_caster; if(!m_CastItem) @@ -4629,77 +5285,72 @@ uint8 Spell::CheckItems() } else { - itemid = m_CastItem->GetEntry(); - if( !p_caster->HasItemCount(itemid,1) ) + uint32 itemid = m_CastItem->GetEntry(); + if( !p_caster->HasItemCount(itemid, 1) ) return SPELL_FAILED_ITEM_NOT_READY; - else - { - ItemPrototype const *proto = m_CastItem->GetProto(); - if(!proto) - return SPELL_FAILED_ITEM_NOT_READY; - for (int i = 0; i<5; i++) - { - if (proto->Spells[i].SpellCharges) - { - if(m_CastItem->GetSpellCharges(i)==0) - return SPELL_FAILED_NO_CHARGES_REMAIN; - } - } + ItemPrototype const *proto = m_CastItem->GetProto(); + if(!proto) + return SPELL_FAILED_ITEM_NOT_READY; - uint32 ItemClass = proto->Class; - if (ItemClass == ITEM_CLASS_CONSUMABLE && m_targets.getUnitTarget()) + for (int i = 0; i < 5; ++i) + if (proto->Spells[i].SpellCharges) + if(m_CastItem->GetSpellCharges(i) == 0) + return SPELL_FAILED_NO_CHARGES_REMAIN; + + // consumable cast item checks + if (proto->Class == ITEM_CLASS_CONSUMABLE && m_targets.getUnitTarget()) + { + // such items should only fail if there is no suitable effect at all - see Rejuvenation Potions for example + SpellCastResult failReason = SPELL_CAST_OK; + for (int i = 0; i < 3; i++) { - // such items should only fail if there is no suitable effect at all - see Rejuvenation Potions for example - uint8 failReason = 0; - for (int i = 0; i < 3; i++) - { // skip check, pet not required like checks, and for TARGET_UNIT_PET m_targets.getUnitTarget() is not the real target but the caster if (m_spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_PET) - continue; + continue; - if (m_spellInfo->Effect[i] == SPELL_EFFECT_HEAL) + if (m_spellInfo->Effect[i] == SPELL_EFFECT_HEAL) + { + if (m_targets.getUnitTarget()->GetHealth() == m_targets.getUnitTarget()->GetMaxHealth()) { - if (m_targets.getUnitTarget()->GetHealth() == m_targets.getUnitTarget()->GetMaxHealth()) - { - failReason = (uint8)SPELL_FAILED_ALREADY_AT_FULL_HEALTH; - continue; - } - else - { - failReason = 0; - break; - } + failReason = SPELL_FAILED_ALREADY_AT_FULL_HEALTH; + continue; + } + else + { + failReason = SPELL_CAST_OK; + break; } + } - // Mana Potion, Rage Potion, Thistle Tea(Rogue), ... - if (m_spellInfo->Effect[i] == SPELL_EFFECT_ENERGIZE) + // Mana Potion, Rage Potion, Thistle Tea(Rogue), ... + if (m_spellInfo->Effect[i] == SPELL_EFFECT_ENERGIZE) + { + if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS) { - if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS) - { - failReason = (uint8)SPELL_FAILED_ALREADY_AT_FULL_POWER; - continue; - } + failReason = SPELL_FAILED_ALREADY_AT_FULL_POWER; + continue; + } - Powers power = Powers(m_spellInfo->EffectMiscValue[i]); - if (m_targets.getUnitTarget()->GetPower(power) == m_targets.getUnitTarget()->GetMaxPower(power)) - { - failReason = (uint8)SPELL_FAILED_ALREADY_AT_FULL_POWER; - continue; - } - else - { - failReason = 0; - break; - } + Powers power = Powers(m_spellInfo->EffectMiscValue[i]); + if (m_targets.getUnitTarget()->GetPower(power) == m_targets.getUnitTarget()->GetMaxPower(power)) + { + failReason = SPELL_FAILED_ALREADY_AT_FULL_POWER; + continue; + } + else + { + failReason = SPELL_CAST_OK; + break; } } - if (failReason) - return failReason; } + if (failReason != SPELL_CAST_OK) + return failReason; } } + // check target item if(m_targets.getItemTargetGUID()) { if(m_caster->GetTypeId() != TYPEID_PLAYER) @@ -4718,6 +5369,7 @@ uint8 Spell::CheckItems() return SPELL_FAILED_EQUIPPED_ITEM_CLASS; } + // check spell focus object if(m_spellInfo->RequiresSpellFocus) { CellPair p(Trinity::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY())); @@ -4725,86 +5377,92 @@ uint8 Spell::CheckItems() cell.data.Part.reserved = ALL_DISTRICT; GameObject* ok = NULL; - Trinity::GameObjectFocusCheck go_check(m_caster,m_spellInfo->RequiresSpellFocus); - Trinity::GameObjectSearcher<Trinity::GameObjectFocusCheck> checker(ok,go_check); + MaNGOS::GameObjectFocusCheck go_check(m_caster,m_spellInfo->RequiresSpellFocus); + MaNGOS::GameObjectSearcher<MaNGOS::GameObjectFocusCheck> checker(m_caster, ok, go_check); TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectFocusCheck>, GridTypeMapContainer > object_checker(checker); CellLock<GridReadGuard> cell_lock(cell, p); cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster)); if(!ok) - return (uint8)SPELL_FAILED_REQUIRES_SPELL_FOCUS; + return SPELL_FAILED_REQUIRES_SPELL_FOCUS; focusObject = ok; // game object found in range } - if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP && - m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION))) + // do not take reagents for these item casts + if (!(m_CastItem && m_CastItem->GetProto()->Flags & ITEM_FLAGS_TRIGGERED_CAST)) { - for(uint32 i=0;i<8;i++) + // check reagents (ignore triggered spells with reagents processed by original spell) and special reagent ignore case. + if (!m_IsTriggeredSpell && !p_caster->CanNoReagentCast(m_spellInfo)) { - if(m_spellInfo->Reagent[i] <= 0) - continue; + for(uint32 i=0;i<8;i++) + { + if(m_spellInfo->Reagent[i] <= 0) + continue; - itemid = m_spellInfo->Reagent[i]; - itemcount = m_spellInfo->ReagentCount[i]; + uint32 itemid = m_spellInfo->Reagent[i]; + uint32 itemcount = m_spellInfo->ReagentCount[i]; - // if CastItem is also spell reagent - if( m_CastItem && m_CastItem->GetEntry() == itemid ) - { - ItemPrototype const *proto = m_CastItem->GetProto(); - if(!proto) - return SPELL_FAILED_ITEM_NOT_READY; - for(int s=0;s<5;s++) + // if CastItem is also spell reagent + if( m_CastItem && m_CastItem->GetEntry() == itemid ) { - // CastItem will be used up and does not count as reagent - int32 charges = m_CastItem->GetSpellCharges(s); - if (proto->Spells[s].SpellCharges < 0 && abs(charges) < 2) + ItemPrototype const *proto = m_CastItem->GetProto(); + if(!proto) + return SPELL_FAILED_ITEM_NOT_READY; + for(int s=0; s < MAX_ITEM_PROTO_SPELLS; ++s) { - ++itemcount; - break; + // CastItem will be used up and does not count as reagent + int32 charges = m_CastItem->GetSpellCharges(s); + if (proto->Spells[s].SpellCharges < 0 && abs(charges) < 2) + { + ++itemcount; + break; + } } } + if( !p_caster->HasItemCount(itemid,itemcount) ) + return SPELL_FAILED_ITEM_NOT_READY; //0x54 } - if( !p_caster->HasItemCount(itemid,itemcount) ) - return (uint8)SPELL_FAILED_ITEM_NOT_READY; //0x54 } - } - uint32 totems = 2; - for(int i=0;i<2;++i) - { - if(m_spellInfo->Totem[i] != 0) + // check totem-item requirements (items presence in inventory) + uint32 totems = 2; + for(int i = 0; i < 2 ; ++i) { - if( p_caster->HasItemCount(m_spellInfo->Totem[i],1) ) + if(m_spellInfo->Totem[i] != 0) { - totems -= 1; - continue; - } - }else - totems -= 1; - } - if(totems != 0) - return (uint8)SPELL_FAILED_TOTEMS; //0x7C + if( p_caster->HasItemCount(m_spellInfo->Totem[i],1) ) + { + totems -= 1; + continue; + } + }else + totems -= 1; + } + if(totems != 0) + return SPELL_FAILED_TOTEMS; //0x7C - //Check items for TotemCategory - uint32 TotemCategory = 2; - for(int i=0;i<2;++i) - { - if(m_spellInfo->TotemCategory[i] != 0) + // Check items for TotemCategory (items presence in inventory) + uint32 TotemCategory = 2; + for(int i= 0; i < 2; ++i) { - if( p_caster->HasItemTotemCategory(m_spellInfo->TotemCategory[i]) ) + if(m_spellInfo->TotemCategory[i] != 0) { - TotemCategory -= 1; - continue; + if( p_caster->HasItemTotemCategory(m_spellInfo->TotemCategory[i]) ) + { + TotemCategory -= 1; + continue; + } } + else + TotemCategory -= 1; } - else - TotemCategory -= 1; + if(TotemCategory != 0) + return SPELL_FAILED_TOTEM_CATEGORY; //0x7B } - if(TotemCategory != 0) - return (uint8)SPELL_FAILED_TOTEM_CATEGORY; //0x7B + // special checks for spell effects for(int i = 0; i < 3; i++) { switch (m_spellInfo->Effect[i]) @@ -4824,6 +5482,24 @@ uint8 Spell::CheckItems() break; } case SPELL_EFFECT_ENCHANT_ITEM: + if(m_spellInfo->EffectItemType[i] && m_targets.getItemTarget() + && (m_targets.getItemTarget()->IsWeaponVellum() || m_targets.getItemTarget()->IsArmorVellum())) + { + // cannot enchant vellum for other player + if (m_targets.getItemTarget()->GetOwner()!=m_caster) + return SPELL_FAILED_NOT_TRADEABLE; + // do not allow to enchant vellum from scroll made by vellum-prevent exploit + if (m_CastItem && m_CastItem->GetProto()->Flags & ITEM_FLAGS_TRIGGERED_CAST) + return SPELL_FAILED_TOTEM_CATEGORY; + ItemPosCountVec dest; + uint8 msg = p_caster->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, m_spellInfo->EffectItemType[i], 1 ); + if (msg != EQUIP_ERR_OK ) + { + p_caster->SendEquipError( msg, NULL, NULL ); + return SPELL_FAILED_DONT_REPORT; + } + } + case SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC: { Item* targetItem = m_targets.getItemTarget(); if(!targetItem) @@ -4907,13 +5583,36 @@ uint8 Spell::CheckItems() return SPELL_FAILED_LOW_CASTLEVEL; //make sure the player has the required ores in inventory if(m_targets.getItemTarget()->GetCount() < 5) - return SPELL_FAILED_PROSPECT_NEED_MORE; + return SPELL_FAILED_NEED_MORE_ITEMS; if(!LootTemplates_Prospecting.HaveLootFor(m_targets.getItemTargetEntry())) return SPELL_FAILED_CANT_BE_PROSPECTED; break; } + case SPELL_EFFECT_MILLING: + { + if(!m_targets.getItemTarget()) + return SPELL_FAILED_CANT_BE_MILLED; + //ensure item is a millable herb + if(!(m_targets.getItemTarget()->GetProto()->BagFamily & BAG_FAMILY_MASK_HERBS) || m_targets.getItemTarget()->GetProto()->Class != ITEM_CLASS_TRADE_GOODS) + return SPELL_FAILED_CANT_BE_MILLED; + //prevent milling in trade slot + if( m_targets.getItemTarget()->GetOwnerGUID() != m_caster->GetGUID() ) + return SPELL_FAILED_CANT_BE_MILLED; + //Check for enough skill in inscription + uint32 item_millingskilllevel = m_targets.getItemTarget()->GetProto()->RequiredSkillRank; + if(item_millingskilllevel >p_caster->GetSkillValue(SKILL_INSCRIPTION)) + return SPELL_FAILED_LOW_CASTLEVEL; + //make sure the player has the required herbs in inventory + if(m_targets.getItemTarget()->GetCount() < 5) + return SPELL_FAILED_NEED_MORE_ITEMS; + + if(!LootTemplates_Milling.HaveLootFor(m_targets.getItemTargetEntry())) + return SPELL_FAILED_CANT_BE_MILLED; + + break; + } case SPELL_EFFECT_WEAPON_DAMAGE: case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: { @@ -4958,11 +5657,11 @@ uint8 Spell::CheckItems() { case ITEM_SUBCLASS_WEAPON_BOW: case ITEM_SUBCLASS_WEAPON_CROSSBOW: - if(ammoProto->SubClass!=ITEM_SUBCLASS_ARROW) + if(ammoProto->SubClass != ITEM_SUBCLASS_ARROW) return SPELL_FAILED_NO_AMMO; break; case ITEM_SUBCLASS_WEAPON_GUN: - if(ammoProto->SubClass!=ITEM_SUBCLASS_BULLET) + if(ammoProto->SubClass != ITEM_SUBCLASS_BULLET) return SPELL_FAILED_NO_AMMO; break; default: @@ -4973,6 +5672,7 @@ uint8 Spell::CheckItems() return SPELL_FAILED_NO_AMMO; }; break; case ITEM_SUBCLASS_WEAPON_WAND: + break; default: break; } @@ -4982,7 +5682,39 @@ uint8 Spell::CheckItems() } } - return uint8(0); + // check weapon presence in slots for main/offhand weapons + if(m_spellInfo->EquippedItemClass >=0) + { + // main hand weapon required + if(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_MAIN_HAND) + { + Item* item = ((Player*)m_caster)->GetWeaponForAttack(BASE_ATTACK); + + // skip spell if no weapon in slot or broken + if(!item || item->IsBroken() ) + return m_IsTriggeredSpell? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_EQUIPPED_ITEM_CLASS; + + // skip spell if weapon not fit to triggered spell + if(!item->IsFitToSpellRequirements(m_spellInfo)) + return m_IsTriggeredSpell? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_EQUIPPED_ITEM_CLASS; + } + + // offhand hand weapon required + if(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_REQ_OFFHAND) + { + Item* item = ((Player*)m_caster)->GetWeaponForAttack(OFF_ATTACK); + + // skip spell if no weapon in slot or broken + if(!item || item->IsBroken() ) + return m_IsTriggeredSpell? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_EQUIPPED_ITEM_CLASS; + + // skip spell if weapon not fit to triggered spell + if(!item->IsFitToSpellRequirements(m_spellInfo)) + return m_IsTriggeredSpell? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_EQUIPPED_ITEM_CLASS; + } + } + + return SPELL_CAST_OK; } void Spell::Delayed() // only called in DealDamage() @@ -4993,18 +5725,22 @@ void Spell::Delayed() // only called in DealDamage() //if (m_spellState == SPELL_STATE_DELAYED) // return; // spell is active and can't be time-backed + if(isDelayableNoMore()) // Spells may only be delayed twice + return; + // spells not loosing casting time ( slam, dynamites, bombs.. ) //if(!(m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_DAMAGE)) // return; - //check resist chance - int32 resistChance = 100; //must be initialized to 100 for percent modifiers - ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME,resistChance, this); - resistChance += m_caster->GetTotalAuraModifier(SPELL_AURA_RESIST_PUSHBACK) - 100; - if (roll_chance_i(resistChance)) + //check pushback reduce + int32 delaytime = 500; // spellcasting delay is normally 500ms + int32 delayReduce = 100; // must be initialized to 100 for percent modifiers + ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); + delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; + if(delayReduce >= 100) return; - int32 delaytime = GetNextDelayAtDamageMsTime(); + delaytime = delaytime * (100 - delayReduce) / 100; if(int32(m_timer) + delaytime > m_casttime) { @@ -5014,13 +5750,13 @@ void Spell::Delayed() // only called in DealDamage() else m_timer += delaytime; - sLog.outDetail("Spell %u partially interrupted for (%d) ms at damage",m_spellInfo->Id,delaytime); + sLog.outDetail("Spell %u partially interrupted for (%d) ms at damage", m_spellInfo->Id, delaytime); WorldPacket data(SMSG_SPELL_DELAYED, 8+4); data.append(m_caster->GetPackGUID()); data << uint32(delaytime); - m_caster->SendMessageToSet(&data,true); + m_caster->SendMessageToSet(&data, true); } void Spell::DelayedChannel() @@ -5028,14 +5764,18 @@ void Spell::DelayedChannel() if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER || getState() != SPELL_STATE_CASTING) return; - //check resist chance - int32 resistChance = 100; //must be initialized to 100 for percent modifiers - ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME,resistChance, this); - resistChance += m_caster->GetTotalAuraModifier(SPELL_AURA_RESIST_PUSHBACK) - 100; - if (roll_chance_i(resistChance)) + if(isDelayableNoMore()) // Spells may only be delayed twice + return; + + //check pushback reduce + int32 delaytime = GetSpellDuration(m_spellInfo) * 25 / 100; // channeling delay is normally 25% of its time per hit + int32 delayReduce = 100; // must be initialized to 100 for percent modifiers + ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); + delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; + if(delayReduce >= 100) return; - int32 delaytime = GetNextDelayAtDamageMsTime(); + delaytime = delaytime * (100 - delayReduce) / 100; if(int32(m_timer) < delaytime) { @@ -5047,22 +5787,19 @@ void Spell::DelayedChannel() sLog.outDebug("Spell %u partially interrupted for %i ms, new duration: %u ms", m_spellInfo->Id, delaytime, m_timer); - for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) + for(std::list<TargetInfo>::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) { if ((*ihit).missCondition == SPELL_MISS_NONE) { - Unit* unit = m_caster->GetGUID()==ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); + Unit* unit = m_caster->GetGUID() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); if (unit) { - for (int j=0;j<3;j++) - if( ihit->effectMask & (1<<j) ) - unit->DelayAura(m_spellInfo->Id, j, delaytime); + unit->DelayAura(m_spellInfo->Id, m_caster->GetGUID(), delaytime); } - } } - for(int j = 0; j < 3; j++) + for(int j = 0; j < 3; ++j) { // partially interrupt persistent area auras DynamicObject* dynObj = m_caster->GetDynObject(m_spellInfo->Id, j); @@ -5089,17 +5826,15 @@ void Spell::UpdatePointers() m_targets.Update(m_caster); } -bool Spell::IsAffectedBy(SpellEntry const *spellInfo, uint32 effectId) -{ - return spellmgr.IsAffectedBySpell(m_spellInfo,spellInfo->Id,effectId,spellInfo->EffectItemType[effectId]); -} - bool Spell::CheckTargetCreatureType(Unit* target) const { uint32 spellCreatureTargetMask = m_spellInfo->TargetCreatureType; - // Curse of Doom : not find another way to fix spell target check :/ - if(m_spellInfo->SpellFamilyName==SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags == 0x0200000000LL) + // Curse of Doom or Exorcism + // These spells cannot be cast on players, however there is no clientside check for them + // so there is no attribute flag in dbcs which would mark these spells - we need to check them by spellfamily + if(m_spellInfo->SpellFamilyName==SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags.IsEqual(0,0x02,0) + || m_spellInfo->SpellFamilyName==SPELLFAMILY_PALADIN && m_spellInfo->SpellFamilyFlags.IsEqual(0,0x2,0)) { // not allow cast at player if(target->GetTypeId()==TYPEID_PLAYER) @@ -5136,12 +5871,18 @@ CurrentSpellTypes Spell::GetCurrentContainer() bool Spell::CheckTarget(Unit* target, uint32 eff) { // Check targets for creature type mask and remove not appropriate (skip explicit self target case, maybe need other explicit targets) - if(m_spellInfo->EffectImplicitTargetA[eff]!=TARGET_UNIT_CASTER) + if(m_spellInfo->EffectImplicitTargetA[eff] != TARGET_UNIT_CASTER) { if (!CheckTargetCreatureType(target)) return false; } + // Check Aura spell req (need for AoE spells) + if(m_spellInfo->targetAuraSpell && !target->HasAura(m_spellInfo->targetAuraSpell)) + return false; + if (m_spellInfo->excludeTargetAuraSpell && target->HasAura(m_spellInfo->excludeTargetAuraSpell)) + return false; + // Check targets for not_selectable unit flag and remove // A player can cast spells on his pet (or other controlled unit) though in any state if (target != m_caster && target->GetCharmerOrOwnerGUID() != m_caster->GetGUID()) @@ -5159,9 +5900,9 @@ bool Spell::CheckTarget(Unit* target, uint32 eff) } //Check player targets and remove if in GM mode or GM invisibility (for not self casting case) - if( target != m_caster && target->GetTypeId()==TYPEID_PLAYER) + if( target != m_caster && target->GetTypeId() == TYPEID_PLAYER) { - if(((Player*)target)->GetVisibility()==VISIBILITY_OFF) + if(((Player*)target)->GetVisibility() == VISIBILITY_OFF) return false; if(((Player*)target)->isGameMaster() && !IsPositiveSpell(m_spellInfo->Id)) @@ -5178,21 +5919,21 @@ bool Spell::CheckTarget(Unit* target, uint32 eff) case SPELL_EFFECT_SUMMON_PLAYER: // from anywhere break; case SPELL_EFFECT_DUMMY: - if(m_spellInfo->Id!=20577) // Cannibalize + if(m_spellInfo->Id != 20577) // Cannibalize break; //fall through case SPELL_EFFECT_RESURRECT_NEW: // player far away, maybe his corpse near? - if(target!=m_caster && !target->IsWithinLOSInMap(m_caster)) + if(target != m_caster && !target->IsWithinLOSInMap(m_caster)) { if(!m_targets.getCorpseTargetGUID()) return false; - Corpse *corpse = ObjectAccessor::GetCorpse(*m_caster,m_targets.getCorpseTargetGUID()); + Corpse *corpse = ObjectAccessor::GetCorpse(*m_caster, m_targets.getCorpseTargetGUID()); if(!corpse) return false; - if(target->GetGUID()!=corpse->GetOwnerGUID()) + if(target->GetGUID() != corpse->GetOwnerGUID()) return false; if(!corpse->IsWithinLOSInMap(m_caster)) @@ -5202,7 +5943,13 @@ bool Spell::CheckTarget(Unit* target, uint32 eff) // all ok by some way or another, skip normal check break; default: // normal case - if(target!=m_caster && !target->IsWithinLOSInMap(m_caster)) + // Get GO cast coordinates if original caster -> GO + WorldObject *caster = NULL; + if (IS_GAMEOBJECT_GUID(m_originalCasterGUID)) + caster = m_caster->GetMap()->GetGameObject(m_originalCasterGUID); + if (!caster) + caster = m_caster; + if(target != m_caster && !target->IsWithinLOSInMap(caster)) return false; break; } @@ -5210,59 +5957,24 @@ bool Spell::CheckTarget(Unit* target, uint32 eff) return true; } -Unit* Spell::SelectMagnetTarget() -{ - Unit* target = m_targets.getUnitTarget(); - - if(target && m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && target->HasAuraType(SPELL_AURA_SPELL_MAGNET)) //Attributes & 0x10 what is this? - { - Unit::AuraList const& magnetAuras = target->GetAurasByType(SPELL_AURA_SPELL_MAGNET); - for(Unit::AuraList::const_iterator itr = magnetAuras.begin(); itr != magnetAuras.end(); ++itr) - { - if(Unit* magnet = (*itr)->GetCaster()) - { - if((*itr)->m_procCharges>0) - { - (*itr)->SetAuraProcCharges((*itr)->m_procCharges-1); - target = magnet; - m_targets.setUnitTarget(target); - AddUnitTarget(target, 0); - uint64 targetGUID = target->GetGUID(); - for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) - { - if (targetGUID == ihit->targetGUID) // Found in list - { - (*ihit).damage = target->GetHealth(); - break; - } - } - break; - } - } - } - } - - return target; -} - bool Spell::IsNeedSendToClient() const { - return m_spellInfo->SpellVisual!=0 || IsChanneledSpell(m_spellInfo) || + return m_spellInfo->SpellVisual[0] || m_spellInfo->SpellVisual[1] || IsChanneledSpell(m_spellInfo) || m_spellInfo->speed > 0.0f || !m_triggeredByAuraSpell && !m_IsTriggeredSpell; } bool Spell::HaveTargetsForEffect( uint8 effect ) const { - for(std::list<TargetInfo>::const_iterator itr= m_UniqueTargetInfo.begin();itr != m_UniqueTargetInfo.end();++itr) - if(itr->effectMask & (1<<effect)) + for(std::list<TargetInfo>::const_iterator itr = m_UniqueTargetInfo.begin(); itr != m_UniqueTargetInfo.end(); ++itr) + if(itr->effectMask & (1 << effect)) return true; - for(std::list<GOTargetInfo>::const_iterator itr= m_UniqueGOTargetInfo.begin();itr != m_UniqueGOTargetInfo.end();++itr) - if(itr->effectMask & (1<<effect)) + for(std::list<GOTargetInfo>::const_iterator itr = m_UniqueGOTargetInfo.begin(); itr != m_UniqueGOTargetInfo.end(); ++itr) + if(itr->effectMask & (1 << effect)) return true; - for(std::list<ItemTargetInfo>::const_iterator itr= m_UniqueItemInfo.begin();itr != m_UniqueItemInfo.end();++itr) - if(itr->effectMask & (1<<effect)) + for(std::list<ItemTargetInfo>::const_iterator itr = m_UniqueItemInfo.begin(); itr != m_UniqueItemInfo.end(); ++itr) + if(itr->effectMask & (1 << effect)) return true; return false; @@ -5285,7 +5997,7 @@ SpellEvent::~SpellEvent() else { sLog.outError("~SpellEvent: %s %u tried to delete non-deletable spell %u. Was not deleted, causes memory leak.", - (m_Spell->GetCaster()->GetTypeId()==TYPEID_PLAYER?"Player":"Creature"), m_Spell->GetCaster()->GetGUIDLow(),m_Spell->m_spellInfo->Id); + (m_Spell->GetCaster()->GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature"), m_Spell->GetCaster()->GetGUIDLow(), m_Spell->m_spellInfo->Id); } } @@ -5304,8 +6016,7 @@ bool SpellEvent::Execute(uint64 e_time, uint32 p_time) if (m_Spell->IsDeletable()) { // check, if we do have unfinished triggered spells - - return(true); // spell is deletable, finish event + return true; // spell is deletable, finish event } // event will be re-added automatically at the end of routine) } break; @@ -5317,6 +6028,7 @@ bool SpellEvent::Execute(uint64 e_time, uint32 p_time) { // no, we aren't, do the typical update // check, if we have channeled spell on our hands + /* if (IsChanneledSpell(m_Spell->m_spellInfo)) { // evented channeled spell is processed separately, casted once after delay, and not destroyed till finish @@ -5326,17 +6038,20 @@ bool SpellEvent::Execute(uint64 e_time, uint32 p_time) // another non-melee non-delayed spell is casted now, abort m_Spell->cancel(); } - // Check if target of channeled spell still in range - else if (m_Spell->CheckRange(false)) - m_Spell->cancel(); else { + // Set last not triggered spell for apply spellmods + ((Player*)m_Spell->GetCaster())->SetSpellModTakingSpell(m_Spell, true); // do the action (pass spell to channeling state) m_Spell->handle_immediate(); + + // And remove after effect handling + ((Player*)m_Spell->GetCaster())->SetSpellModTakingSpell(m_Spell, false); } // event will be re-added automatically at the end of routine) } else + */ { // run the spell handler and think about what we can do next uint64 t_offset = e_time - m_Spell->GetDelayStart(); @@ -5345,7 +6060,7 @@ bool SpellEvent::Execute(uint64 e_time, uint32 p_time) { // re-add us to the queue m_Spell->GetCaster()->m_Events.AddEvent(this, m_Spell->GetDelayStart() + n_offset, false); - return(false); // event not complete + return false; // event not complete } // event complete // finish update event will be re-added automatically at the end of routine) @@ -5357,7 +6072,7 @@ bool SpellEvent::Execute(uint64 e_time, uint32 p_time) m_Spell->SetDelayStart(e_time); // re-plan the event for the delay moment m_Spell->GetCaster()->m_Events.AddEvent(this, e_time + m_Spell->GetDelayMoment(), false); - return(false); // event not complete + return false; // event not complete } } break; @@ -5370,7 +6085,7 @@ bool SpellEvent::Execute(uint64 e_time, uint32 p_time) // spell processing not complete, plan event on the next update interval m_Spell->GetCaster()->m_Events.AddEvent(this, e_time + 1, false); - return(false); // event not complete + return false; // event not complete } void SpellEvent::Abort(uint64 /*e_time*/) @@ -5431,6 +6146,14 @@ void Spell::CalculateDamageDoneForAllTargets() } } + bool usesAmmo=true; + Unit::AuraEffectList const& Auras = m_caster->GetAurasByType(SPELL_AURA_ABILITY_CONSUME_NO_AMMO); + for(Unit::AuraEffectList::const_iterator j = Auras.begin();j != Auras.end(); ++j) + { + if((*j)->isAffectedOnSpell(m_spellInfo)) + usesAmmo=false; + } + for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) { TargetInfo &target = *ihit; @@ -5440,12 +6163,34 @@ void Spell::CalculateDamageDoneForAllTargets() continue; Unit* unit = m_caster->GetGUID()==target.targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target.targetGUID); - if (!unit) + if (!unit) // || !unit->isAlive()) do we need to check alive here? continue; + if (usesAmmo) + { + bool ammoTaken=false; + for (uint8 i=0;i<3;i++) + { + if (!(mask & 1<<i)) + continue; + switch (m_spellInfo->Effect[i]) + { + case SPELL_EFFECT_SCHOOL_DAMAGE: + case SPELL_EFFECT_WEAPON_DAMAGE: + case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: + case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: + case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: + ammoTaken=true; + TakeAmmo(); + } + if (ammoTaken) + break; + } + } + if (target.missCondition==SPELL_MISS_NONE) // In case spell hit target, do all effect on that target { - target.damage += CalculateDamageDone(unit, mask, multiplier); + target.damage += CalculateDamageDone(unit, mask, multiplier); target.crit = m_caster->isSpellCrit(unit, m_spellInfo, m_spellSchoolMask, m_attackType); } else if (target.missCondition == SPELL_MISS_REFLECT) // In case spell reflect from target, do all effect on caster (if hit) @@ -5507,6 +6252,68 @@ int32 Spell::CalculateDamageDone(Unit *unit, const uint32 effectMask, float *mul return damageDone; } +SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& skillId, int32& reqSkillValue, int32& skillValue) +{ + if(!lockId) // possible case for GO and maybe for items. + return SPELL_CAST_OK; + + // Get LockInfo + LockEntry const *lockInfo = sLockStore.LookupEntry(lockId); + + if (!lockInfo) + return SPELL_FAILED_BAD_TARGETS; + + bool reqKey = false; // some locks not have reqs + + for(int j = 0; j < 8; ++j) + { + switch(lockInfo->Type[j]) + { + // check key item (many fit cases can be) + case LOCK_KEY_ITEM: + if(lockInfo->Index[j] && m_CastItem && m_CastItem->GetEntry()==lockInfo->Index[j]) + return SPELL_CAST_OK; + reqKey = true; + break; + // check key skill (only single first fit case can be) + case LOCK_KEY_SKILL: + { + reqKey = true; + + // wrong locktype, skip + if(uint32(m_spellInfo->EffectMiscValue[effIndex]) != lockInfo->Index[j]) + continue; + + skillId = SkillByLockType(LockType(lockInfo->Index[j])); + + if ( skillId != SKILL_NONE ) + { + // skill bonus provided by casting spell (mostly item spells) + // add the damage modifier from the spell casted (cheat lock / skeleton key etc.) (use m_currentBasePoints, CalculateDamage returns wrong value) + uint32 spellSkillBonus = uint32(m_currentBasePoints[effIndex]+1); + reqSkillValue = lockInfo->Skill[j]; + + // castitem check: rogue using skeleton keys. the skill values should not be added in this case. + skillValue = m_CastItem || m_caster->GetTypeId()!= TYPEID_PLAYER ? + 0 : ((Player*)m_caster)->GetSkillValue(skillId); + + skillValue += spellSkillBonus; + + if (skillValue < reqSkillValue) + return SPELL_FAILED_LOW_CASTLEVEL; + } + + return SPELL_CAST_OK; + } + } + } + + if(reqKey) + return SPELL_FAILED_BAD_TARGETS; + + return SPELL_CAST_OK; +} + void Spell::SetSpellValue(SpellValueMod mod, int32 value) { switch(mod) @@ -5523,8 +6330,89 @@ void Spell::SetSpellValue(SpellValueMod mod, int32 value) m_spellValue->EffectBasePoints[2] = value - int32(m_spellInfo->EffectBaseDice[2]); m_currentBasePoints[2] = m_spellValue->EffectBasePoints[2]; break; + case SPELLVALUE_RADIUS_MOD: + m_spellValue->RadiusMod = (float)value / 10000; + break; case SPELLVALUE_MAX_TARGETS: m_spellValue->MaxAffectedTargets = (uint32)value; break; } } + +void Spell::FillRaidOrPartyTargets( UnitList &TagUnitMap, Unit* target, float radius, bool raid, bool withPets, bool withcaster ) +{ + Player *pTarget = target->GetCharmerOrOwnerPlayerOrPlayerItself(); + Group *pGroup = pTarget ? pTarget->GetGroup() : NULL; + + if (pGroup) + { + uint8 subgroup = pTarget->GetSubGroup(); + + for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* Target = itr->getSource(); + + // IsHostileTo check duel and controlled by enemy + if (Target && (raid || subgroup==Target->GetSubGroup()) + && !m_caster->IsHostileTo(Target)) + { + if (Target==m_caster && withcaster || + Target!=m_caster && m_caster->IsWithinDistInMap(Target, radius)) + TagUnitMap.push_back(Target); + + if (withPets) + if (Pet* pet = Target->GetPet()) + if (pet==m_caster && withcaster || + pet!=m_caster && m_caster->IsWithinDistInMap(pet, radius)) + TagUnitMap.push_back(pet); + } + } + } + else + { + Unit* ownerOrSelf = pTarget ? pTarget : target->GetCharmerOrOwnerOrSelf(); + if (ownerOrSelf==m_caster && withcaster || + ownerOrSelf!=m_caster && m_caster->IsWithinDistInMap(ownerOrSelf, radius)) + TagUnitMap.push_back(ownerOrSelf); + + if (withPets) + if (Guardian* pet = ownerOrSelf->GetGuardianPet()) + if (pet==m_caster && withcaster || + pet!=m_caster && m_caster->IsWithinDistInMap(pet, radius)) + TagUnitMap.push_back(pet); + } +} + +void Spell::FillRaidOrPartyManaPriorityTargets( UnitList &TagUnitMap, Unit* target, float radius, uint32 count, bool raid, bool withPets, bool withCaster ) +{ + FillRaidOrPartyTargets(TagUnitMap,target,radius,raid,withPets,withCaster); + + PrioritizeManaUnitQueue manaUsers; + for(UnitList::const_iterator itr = TagUnitMap.begin(); itr != TagUnitMap.end() && manaUsers.size() < count; ++itr) + if ((*itr)->getPowerType() == POWER_MANA && !(*itr)->isDead()) + manaUsers.push(PrioritizeManaUnitWraper(*itr)); + + TagUnitMap.clear(); + while(!manaUsers.empty()) + { + TagUnitMap.push_back(manaUsers.top().getUnit()); + manaUsers.pop(); + } +} + +void Spell::FillRaidOrPartyHealthPriorityTargets( UnitList &TagUnitMap, Unit* target, float radius, uint32 count, bool raid, bool withPets, bool withCaster ) +{ + FillRaidOrPartyTargets(TagUnitMap,target,radius,raid,withPets,withCaster); + + PrioritizeHealthUnitQueue healthQueue; + for(UnitList::const_iterator itr = TagUnitMap.begin(); itr != TagUnitMap.end() && healthQueue.size() < count; ++itr) + if (!(*itr)->isDead()) + healthQueue.push(PrioritizeHealthUnitWraper(*itr)); + + TagUnitMap.clear(); + while(!healthQueue.empty()) + { + TagUnitMap.push_back(healthQueue.top().getUnit()); + healthQueue.pop(); + } +} |