diff options
Diffstat (limited to 'src/game/Spell.cpp')
-rw-r--r-- | src/game/Spell.cpp | 1007 |
1 files changed, 666 insertions, 341 deletions
diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index b641ee3fa78..aa91ff65ff6 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 @@ -43,7 +43,6 @@ #include "CellImpl.h" #include "Policies/SingletonImp.h" #include "SharedDefines.h" -#include "Tools.h" #include "LootMgr.h" #include "VMapFactory.h" #include "BattleGround.h" @@ -120,6 +119,14 @@ void SpellCastTargets::setDestination(Unit *target, bool send) 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; @@ -178,16 +185,20 @@ bool SpellCastTargets::read ( WorldPacket * data, Unit *caster ) 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 ) @@ -202,7 +213,10 @@ bool SpellCastTargets::read ( WorldPacket * data, Unit *caster ) if( m_targetMask & (TARGET_FLAG_SOURCE_LOCATION | 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; @@ -219,10 +233,6 @@ bool SpellCastTargets::read ( WorldPacket * data, Unit *caster ) *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; @@ -242,7 +252,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()); @@ -267,7 +277,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; @@ -305,7 +322,7 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi 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; @@ -355,14 +372,14 @@ 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; - //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 @@ -372,7 +389,7 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi // determine reflection m_canReflect = false; - if(m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && (m_spellInfo->AttributesEx2 & 0x4)==0) + if(m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && !(m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_CANT_REFLECTED)) { for(int j=0;j<3;j++) { @@ -461,7 +478,7 @@ void Spell::FillTargetMap() WorldObject* result = NULL; Trinity::CannibalizeObjectCheck u_check(m_caster, max_range); - Trinity::WorldObjectSearcher<Trinity::CannibalizeObjectCheck > searcher(result, u_check); + Trinity::WorldObjectSearcher<Trinity::CannibalizeObjectCheck > searcher(m_caster, result, u_check); m_caster->VisitNearbyGridObject(max_range, searcher); if(!result) m_caster->VisitNearbyWorldObject(max_range, searcher); @@ -511,6 +528,8 @@ 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: @@ -543,7 +562,9 @@ 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: tmpUnitMap.push_back(m_caster); break; @@ -553,9 +574,10 @@ void Spell::FillTargetMap() 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;*/ @@ -596,7 +618,6 @@ void Spell::FillTargetMap() } } - if(IsChanneledSpell(m_spellInfo) && !tmpUnitMap.empty()) m_needAliveTargetMask |= (1<<i); @@ -633,34 +654,43 @@ void Spell::prepareDataForTriggerSystem() // Ņan spell trigger another or not ( m_canTrigger ) // Create base triggers flags for Attacker and Victim ( m_procAttacker and m_procVictim) //========================================================================================== - // Fill flag can spell trigger or not - if (!m_IsTriggeredSpell) + // TODO: possible exist spell attribute for this + m_canTrigger = false; + + if (m_CastItem) + m_canTrigger = false; // Do not trigger from item cast spell + else 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) + + if (!m_canTrigger) // 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; + if (m_spellInfo->SpellFamilyFlags[0] & 0x200080) 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; + if (m_spellInfo->SpellFamilyFlags[1] & 0x00008000 || m_spellInfo->SpellFamilyFlags[0] & 0x00000060) m_canTrigger = true; + break; + case SPELLFAMILY_PRIEST: // For Penance heal/damage triggers need do it + if (m_spellInfo->SpellFamilyFlags[1] & 0x00018000) 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; + case SPELLFAMILY_ROGUE: // For poisons need do it + if (m_spellInfo->SpellFamilyFlags[1] & 0x00000010 || m_spellInfo->SpellFamilyFlags[0] & 0x1001E000) m_canTrigger = true; break; - case SPELLFAMILY_PALADIN: // For Holy Shock triggers need do it - if (m_spellInfo->SpellFamilyFlags & 0x0001000000200000LL) m_canTrigger = true; + case SPELLFAMILY_HUNTER: // Hunter Rapid Killing/Explosive Trap Effect/Immolation Trap Effect/Frost Trap Aura/Snake Trap Effect/Explosive Shot + if (m_spellInfo->SpellFamilyFlags[1] & 0x01002000 + || m_spellInfo->SpellFamilyFlags[0] & 0x00000214 || + m_spellInfo->SpellFamilyFlags[2] & 0x200) m_canTrigger = true; + break; + case SPELLFAMILY_PALADIN: // For Judgements (all) / Holy Shock triggers need do it + if (m_spellInfo->SpellFamilyFlags[1] & 0x00010009 || m_spellInfo->SpellFamilyFlags[0] & 0x00B80400) 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) @@ -670,21 +700,30 @@ 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; + // Auto attack + if (m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG) + { + m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_HIT; + m_procVictim = PROC_FLAG_TAKEN_RANGED_HIT; + } + else // Ranged spell attack + { + 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 + if (IsPositiveSpell(m_spellInfo->Id)) // Check for positive spell { m_procAttacker = PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL; m_procVictim = PROC_FLAG_TAKEN_POSITIVE_SPELL; } - else if (m_spellInfo->Id == 5019) // Wands + else if (m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG) // Wands auto attack { - m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT; - m_procVictim = PROC_FLAG_TAKEN_RANGED_SPELL_HIT; + m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_HIT; + m_procVictim = PROC_FLAG_TAKEN_RANGED_HIT; } - else + else // Negative spell { m_procAttacker = PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT; m_procVictim = PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT; @@ -693,7 +732,7 @@ void Spell::prepareDataForTriggerSystem() } // 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; } @@ -702,8 +741,6 @@ void Spell::CleanupTargetList() m_UniqueTargetInfo.clear(); m_UniqueGOTargetInfo.clear(); m_UniqueItemInfo.clear(); - m_countOfHit = 0; - m_countOfMiss = 0; m_delayMoment = 0; } @@ -712,6 +749,9 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex) if( m_spellInfo->Effect[effIndex]==0 ) 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 @@ -719,7 +759,8 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex) { 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; } } @@ -729,7 +770,7 @@ 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.damage = 0; @@ -743,11 +784,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) { @@ -828,8 +864,6 @@ void Spell::AddGOTarget(GameObject* pVictim, uint32 effIndex) else target.timeDelay = 0LL; - ++m_countOfHit; - // Add target to list m_UniqueGOTargetInfo.push_back(target); } @@ -863,83 +897,6 @@ void Spell::AddItemTarget(Item* pitem, uint32 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 +906,13 @@ 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; // 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) @@ -1005,7 +960,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) 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; @@ -1044,44 +999,12 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) caster->DealSpellDamage(&damageInfo, true); - // 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 - damageInfo.target = caster; - damageInfo.absorb = 0; - damageInfo.resist = 0; - damageInfo.blocked = 0; - // Send log damage message to client - caster->SendSpellNonMeleeDamageLog(&damageInfo); - 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 @@ -1105,14 +1028,14 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) if( !m_caster->IsFriendlyTo(unit) && !IsPositiveSpell(m_spellInfo->Id)) { - if( !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) ) + if( !(m_spellInfo->AttributesEx & SPELL_ATTR_EX_NO_INITIAL_AGGRO) ) { m_caster->CombatStart(unit); } else if(m_customAttr & SPELL_ATTR_CU_AURA_CC) { if(!unit->IsStandState()) - unit->SetStandState(PLAYER_STATE_NONE); + unit->SetStandState(UNIT_STAND_STATE_STAND); } } } @@ -1125,25 +1048,37 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) // 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) )) + && (unit->IsImmunedToDamage(GetSpellSchoolMask(m_spellInfo)) || + unit->IsImmunedToSpell(m_spellInfo))) { m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_IMMUNE); m_damage = 0; return; } + if (unit->GetTypeId() == TYPEID_PLAYER) + { + ((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_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); m_damage = 0; - return; - } + return; } + if( !m_caster->IsFriendlyTo(unit) ) { // for delayed spells ignore not visible explicit target @@ -1172,7 +1107,7 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) // assisting case, healing and resurrection if(unit->hasUnitState(UNIT_STAT_ATTACK_PLAYER)) m_caster->SetContestedPvP(); - if( unit->isInCombat() && !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) ) + if( unit->isInCombat() && !(m_spellInfo->AttributesEx & SPELL_ATTR_EX_NO_INITIAL_AGGRO) ) { m_caster->SetInCombatState(unit->GetCombatTimer() > 0); unit->getHostilRefManager().threatAssist(m_caster, 0.0f); @@ -1190,6 +1125,10 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) unit->IncrDiminishing(m_diminishGroup); } + // Apply additional spell effects to target + if (m_preCastSpell) + m_caster->CastSpell(unit,m_preCastSpell, true, m_CastItem); + for(uint32 effectNumber=0;effectNumber<3;effectNumber++) { if (effectMask & (1<<effectNumber)) @@ -1344,6 +1283,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)->GetModifier()->m_amount; + }*/ + //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; @@ -1455,7 +1404,7 @@ Unit* Spell::SearchNearbyTarget(float radius, SpellTargets TargetType, uint32 en { Creature* target = NULL; Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster, entry, true, radius); - Trinity::CreatureLastSearcher<Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(target, u_check); + Trinity::CreatureLastSearcher<Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(m_caster, target, u_check); m_caster->VisitNearbyObject(radius, searcher); return target; } @@ -1469,13 +1418,13 @@ Unit* Spell::SearchNearbyTarget(float radius, SpellTargets TargetType, uint32 en case SPELL_TARGETS_ENEMY: { Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(m_caster, m_caster, radius); - Trinity::UnitLastSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(target, u_check); + Trinity::UnitLastSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(m_caster, target, u_check); m_caster->VisitNearbyObject(radius, searcher); }break; case SPELL_TARGETS_ALLY: { Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(m_caster, m_caster, radius); - Trinity::UnitLastSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(target, u_check); + Trinity::UnitLastSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(m_caster, target, u_check); m_caster->VisitNearbyObject(radius, searcher); }break; } @@ -1494,6 +1443,13 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) uint32 EffectChainTarget = m_spellInfo->EffectChainTarget[i]; uint32 unMaxTargets = m_spellInfo->MaxAffectedTargets; + Unit::AuraList const& Auras = m_caster->GetAurasByType(SPELL_AURA_MOD_ABILITY_AFFECTED_TARGETS); + for(Unit::AuraList::const_iterator j = Auras.begin();j != Auras.end(); ++j) + { + if((*j)->isAffectedOnSpell(m_spellInfo)) + unMaxTargets+=(*j)->GetModifier()->m_amount; + } + if(m_originalCaster) { if(Player* modOwner = m_originalCaster->GetSpellModOwner()) @@ -1559,7 +1515,6 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) case TARGET_UNIT_TARGET_RAID: case TARGET_UNIT_TARGET_ANY: // SelectMagnetTarget()? case TARGET_UNIT_TARGET_PARTY: - case TARGET_UNIT_SINGLE_UNKNOWN: TagUnitMap.push_back(target); break; case TARGET_UNIT_PARTY_TARGET: @@ -1698,7 +1653,6 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) m_targets.setDestination(((Player*)m_caster)->m_homebindX,((Player*)m_caster)->m_homebindY,((Player*)m_caster)->m_homebindZ, true, ((Player*)m_caster)->m_homebindMapId); break; - case TARGET_IN_FRONT_OF_CASTER: case TARGET_UNIT_CONE_ENEMY_UNKNOWN: if(m_customAttr & SPELL_ATTR_CU_CONE_BACK) @@ -1714,6 +1668,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) // nearby target case TARGET_UNIT_NEARBY_ALLY: + case TARGET_UNIT_NEARBY_ALLY_UNK: if(!m_targets.getUnitTarget()) m_targets.setUnitTarget(SearchNearbyTarget(radius, SPELL_TARGETS_ALLY)); if(m_targets.getUnitTarget()) @@ -1724,7 +1679,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) SearchChainTarget(TagUnitMap, radius, EffectChainTarget, SPELL_TARGETS_ALLY); } break; - case TARGET_RANDOM_ENEMY_CHAIN_IN_AREA: + case TARGET_UNIT_NEARBY_ENEMY: if(!m_targets.getUnitTarget()) m_targets.setUnitTarget(SearchNearbyTarget(radius, SPELL_TARGETS_ENEMY)); if(m_targets.getUnitTarget()) @@ -1765,7 +1720,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) 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); + Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck> checker(m_caster, p_GameObject,go_check); m_caster->VisitNearbyGridObject(range, checker); if(p_GameObject) @@ -1795,7 +1750,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) 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); + Trinity::CreatureLastSearcher<Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(m_caster, p_Creature, u_check); m_caster->VisitNearbyObject(range, searcher); if(p_Creature ) @@ -2011,7 +1966,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) } } -void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura) +void Spell::prepare(SpellCastTargets const* targets, Aura* triggeredByAura) { if(m_CastItem) m_castItemGUID = m_CastItem->GetGUID(); @@ -2033,7 +1988,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); @@ -2129,11 +2084,11 @@ void Spell::cancel(bool report) { Unit* unit = m_caster->GetGUID()==(*ihit).targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); if( unit && unit->isAlive() ) - unit->RemoveAurasDueToCasterSpell(m_spellInfo->Id, m_caster->GetGUID()); + unit->RemoveAurasByCasterSpell(m_spellInfo->Id, m_caster->GetGUID()); } } - m_caster->RemoveAurasDueToCasterSpell(m_spellInfo->Id, m_caster->GetGUID()); + m_caster->RemoveAurasByCasterSpell(m_spellInfo->Id, m_caster->GetGUID()); SendChannelUpdate(0); SendInterrupted(0); SendCastResult(report ? SPELL_FAILED_INTERRUPTED : SPELL_FAILED_DONT_REPORT); @@ -2210,16 +2165,43 @@ 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) + m_preCastSpell = m_spellInfo->excludeCasterAuraSpell; + else if (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; + } + case SPELLFAMILY_PRIEST: + { + if (m_spellInfo->Id == 47585) // Dispersion (transform) + m_preCastSpell = 60069; // Dispersion (mana regen) + 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_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(); @@ -2255,26 +2237,25 @@ void Spell::cast(bool skipCheck) handle_immediate(); } - //handle SPELL_AURA_ADD_TARGET_TRIGGER auras //are there any spells need to be triggered after hit? + // handle SPELL_AURA_ADD_TARGET_TRIGGER auras Unit::AuraList const& targetTriggers = m_caster->GetAurasByType(SPELL_AURA_ADD_TARGET_TRIGGER); for(Unit::AuraList::const_iterator i = targetTriggers.begin(); i != targetTriggers.end(); ++i) { - SpellEntry const *auraSpellInfo = (*i)->GetSpellProto(); - uint32 auraSpellIdx = (*i)->GetEffIndex(); - if (IsAffectedBy(auraSpellInfo, auraSpellIdx)) - { - for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) - if( ihit->effectMask & (1<<auraSpellIdx) ) + if (!(*i)->isAffectedOnSpell(m_spellInfo)) + continue; + for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) + if( ihit->missCondition == SPELL_MISS_NONE ) { // check m_caster->GetGUID() let load auras at login and speedup most often case Unit *unit = m_caster->GetGUID()== ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); if (unit && unit->isAlive()) { + SpellEntry const *auraSpellInfo = (*i)->GetSpellProto(); + uint32 auraSpellIdx = (*i)->GetEffIndex(); // Calculate chance at that moment (can be depend for example from combo points) int32 chance = m_caster->CalculateSpellDamage(auraSpellInfo, auraSpellIdx, (*i)->GetBasePoints(),unit); chance *= (*i)->GetStackAmount(); - if(roll_chance_i(chance)) { if(SpellEntry const *spellInfo = sSpellStore.LookupEntry(auraSpellInfo->EffectTriggerSpell[auraSpellIdx])) @@ -2282,7 +2263,6 @@ void Spell::cast(bool skipCheck) } } } - } } // combo points should not be taken before SPELL_AURA_ADD_TARGET_TRIGGER auras are handled @@ -2310,9 +2290,16 @@ 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 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 @@ -2493,7 +2480,7 @@ void Spell::SendSpellCooldown() // 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)) + if (rec <= 0 && catrec <= 0 && (cat == 76 || IsAutoRepeatRangedSpell(m_spellInfo) && m_spellInfo->Id != SPELL_ID_AUTOSHOT)) rec = _player->GetAttackTime(RANGED_ATTACK); // Now we have cooldown data (if found any), time to apply mods @@ -2531,7 +2518,7 @@ void Spell::SendSpellCooldown() 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->AddSpellCooldown(*i_scset, m_CastItem ? m_CastItem->GetEntry() : 0, catrecTime); } } } @@ -2709,9 +2696,9 @@ void Spell::SendCastResult(uint8 result) if(result != 0) { WorldPacket data(SMSG_CAST_FAILED, (4+1+1)); + data << uint8(m_cast_count); // single cast or multi 2.3 (0/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: @@ -2732,8 +2719,8 @@ void Spell::SendCastResult(uint8 result) case 45373: // Bloodberry Elixir data << uint32(4075); break; - default: // default case - data << uint32(m_spellInfo->AreaId); + default: // default case (don't must be) + data << uint32(0); break; } break; @@ -2752,18 +2739,11 @@ void Spell::SendCastResult(uint8 result) case SPELL_FAILED_EQUIPPED_ITEM_CLASS: data << uint32(m_spellInfo->EquippedItemClass); data << uint32(m_spellInfo->EquippedItemSubClassMask); - data << uint32(m_spellInfo->EquippedItemInventoryTypeMask); + //data << uint32(m_spellInfo->EquippedItemInventoryTypeMask); break; } ((Player*)m_caster)->GetSession()->SendPacket(&data); } - else - { - 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); - } } void Spell::SendSpellStart() @@ -2777,6 +2757,9 @@ void Spell::SendSpellStart() if(IsRangedSpell()) castFlags |= CAST_FLAG_AMMO; + if(m_spellInfo->runeCostID) + castFlags |= CAST_FLAG_UNKNOWN10; + Unit *target = m_targets.getUnitTarget() ? m_targets.getUnitTarget() : m_caster; WorldPacket data(SMSG_SPELL_START, (8+8+4+4+2)); @@ -2786,14 +2769,32 @@ 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_UNKNOWN6 ) // predicted power? + data << uint32(0); + + if ( castFlags & CAST_FLAG_UNKNOWN7 ) // rune cooldowns list + { + uint8 v1 = 0;//m_runesState; + uint8 v2 = 0;//((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_AMMO ) WriteAmmoToPacket(&data); m_caster->SendMessageToSet(&data, true); @@ -2813,24 +2814,68 @@ void Spell::SendSpellGo() if(IsRangedSpell()) castFlags |= CAST_FLAG_AMMO; // arrows/bullets visual + 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_UNKNOWN6; // makes cooldowns visible + 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_UNKNOWN6 ) // unknown wotlk, predicted power? + data << uint32(0); + + 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); } @@ -2875,15 +2920,36 @@ void Spell::WriteAmmoToPacket( WorldPacket * data ) void Spell::WriteSpellGoTargets( WorldPacket * data ) { - *data << (uint8)m_countOfHit; + // 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>::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) *data << uint64(ighit->targetGUID); // Always hits - *data << (uint8)m_countOfMiss; + *data << (uint8)miss; for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) { if( ihit->missCondition != SPELL_MISS_NONE ) // Add only miss @@ -2894,6 +2960,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() @@ -2958,30 +3027,19 @@ void Spell::SendLogExecute() 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()) @@ -3000,6 +3058,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; } @@ -3013,13 +3078,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); } @@ -3087,10 +3155,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); } @@ -3143,7 +3220,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); } @@ -3182,6 +3259,12 @@ 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)) @@ -3203,6 +3286,12 @@ void Spell::TakePower() Powers powerType = Powers(m_spellInfo->powerType); + if(powerType == POWER_RUNE) + { + TakeRunePower(); + return; + } + if(hit) m_caster->ModifyPower(powerType, -m_powerCost); else @@ -3213,6 +3302,145 @@ void Spell::TakePower() 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); + } +} + +uint8 Spell::CheckRuneCost(uint32 runeCostID) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return 0; + + Player *plr = (Player*)m_caster; + + if(plr->getClass() != CLASS_DEATH_KNIGHT) + return 0; + + SpellRuneCostEntry const *src = sSpellRuneCostStore.LookupEntry(runeCostID); + + if(!src) + return 0; + + if(src->NoRuneCost()) + return 0; + + 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)) + { + runeCost[rune]--; + } + } + + for(uint32 i = 0; i < RUNE_DEATH; ++i) + { + if(runeCost[i] > 0) + { + runeCost[RUNE_DEATH] += runeCost[i]; + } + } + + if(runeCost[RUNE_DEATH] > 0) + return SPELL_FAILED_NO_POWER; // not sure if result code is correct + + return 0; +} + +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. @@ -3221,11 +3449,9 @@ 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)) - return; - Player* p_caster = (Player*)m_caster; + if (p_caster->CanNoReagentCast(m_spellInfo)) + return; for(uint32 x=0;x<8;x++) { @@ -3288,14 +3514,9 @@ void Spell::HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTar 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; - //we do not need DamageMultiplier here. damage = CalculateDamage(i, NULL); @@ -3349,12 +3570,25 @@ 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::AuraList const& ignore = m_caster->GetAurasByType(SPELL_AURA_MOD_IGNORE_SHAPESHIFT); + for(Unit::AuraList::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 + if(uint8 shapeError = GetErrorAtShapeshiftedCast(m_spellInfo, m_caster->m_form)) + 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 @@ -3363,6 +3597,12 @@ uint8 Spell::CanCast(bool strict) if(m_spellInfo->CasterAuraStateNot && m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraStateNot))) return SPELL_FAILED_CASTER_AURASTATE; + // Caster aura req check if need + 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; + // 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() ) @@ -3381,6 +3621,12 @@ uint8 Spell::CanCast(bool strict) if(m_spellInfo->TargetAuraStateNot && target->HasAuraState(AuraState(m_spellInfo->TargetAuraStateNot))) return SPELL_FAILED_TARGET_AURASTATE; + // Target aura req check if need + if(m_spellInfo->targetAuraSpell && !target->HasAura(m_spellInfo->targetAuraSpell)) + return SPELL_FAILED_CASTER_AURASTATE; + if(m_spellInfo->excludeTargetAuraSpell && target->HasAura(m_spellInfo->excludeTargetAuraSpell)) + return SPELL_FAILED_CASTER_AURASTATE; + if(target != m_caster) { // target state requirements (apply to non-self only), to allow cast affects to self like Dirty Deeds @@ -3406,6 +3652,20 @@ uint8 Spell::CanCast(bool strict) } } } + 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_CHAIN_DAMAGE) + { + 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++) @@ -3457,13 +3717,14 @@ 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))) { SendInterrupted(2); return SPELL_FAILED_NOT_BEHIND; @@ -3497,8 +3758,8 @@ uint8 Spell::CanCast(bool strict) 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(uint8 res= GetSpellAllowedInLocationError(m_spellInfo,m_caster->GetMapId(),m_caster->GetZoneId(),m_caster->GetAreaId())) + return res; // not let players cast spells at mount (and let do it to creatures) if( m_caster->IsMounted() && m_caster->GetTypeId()==TYPEID_PLAYER && !m_IsTriggeredSpell && @@ -3551,7 +3812,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); @@ -3589,7 +3850,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); @@ -3697,7 +3958,7 @@ uint8 Spell::CanCast(bool strict) case SPELL_EFFECT_SCHOOL_DAMAGE: { // Hammer of Wrath - if(m_spellInfo->SpellVisual == 7250) + if(m_spellInfo->SpellVisual[0] == 7250) { if (!m_targets.getUnitTarget()) return SPELL_FAILED_BAD_IMPLICIT_TARGETS; @@ -3722,15 +3983,9 @@ 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: @@ -3745,20 +4000,18 @@ 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(); @@ -3766,10 +4019,10 @@ uint8 Spell::CanCast(bool strict) 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()) @@ -3856,27 +4109,27 @@ uint8 Spell::CanCast(bool strict) { // check for lock - key pair (checked by client also, just prevent cheating bool ok_key = false; - for(int it = 0; it < 5; ++it) + for(int it = 0; it < 8; ++it) { - switch(lockInfo->keytype[it]) + switch(lockInfo->Type[it]) { case LOCK_KEY_NONE: break; case LOCK_KEY_ITEM: { - if(lockInfo->key[it]) + if(lockInfo->Index[it]) { - if(m_CastItem && m_CastItem->GetEntry()==lockInfo->key[it]) + if(m_CastItem && m_CastItem->GetEntry()==lockInfo->Index[it]) ok_key =true; break; } } case LOCK_KEY_SKILL: { - if(uint32(m_spellInfo->EffectMiscValue[i])!=lockInfo->key[it]) + if(uint32(m_spellInfo->EffectMiscValue[i])!=lockInfo->Index[it]) break; - switch(lockInfo->key[it]) + switch(lockInfo->Index[it]) { case LOCKTYPE_HERBALISM: if(((Player*)m_caster)->HasSkill(SKILL_HERBALISM)) @@ -3934,9 +4187,9 @@ uint8 Spell::CanCast(bool strict) { // check for lock - key pair bool ok = false; - for(int it = 0; it < 5; ++it) + for(int it = 0; it < 8; ++it) { - if(lockInfo->keytype[it]==LOCK_KEY_ITEM && lockInfo->key[it] && m_CastItem && m_CastItem->GetEntry()==lockInfo->key[it]) + if(lockInfo->Type[it]==LOCK_KEY_ITEM && lockInfo->Index[it] && m_CastItem && m_CastItem->GetEntry()==lockInfo->Index[it]) { // if so, we're good to go ok = true; @@ -3947,9 +4200,9 @@ uint8 Spell::CanCast(bool strict) break; if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_PICKLOCK) - ReqValue = lockInfo->requiredlockskill; + ReqValue = lockInfo->Skill[1]; else - ReqValue = lockInfo->requiredminingskill; + ReqValue = lockInfo->Skill[0]; } // skill doesn't meet the required value @@ -3973,9 +4226,7 @@ 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]) @@ -3996,11 +4247,8 @@ uint8 Spell::CanCast(bool strict) } 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; @@ -4145,17 +4393,14 @@ uint8 Spell::CanCast(bool strict) 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; @@ -4174,11 +4419,10 @@ uint8 Spell::CanCast(bool strict) case SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED: case SPELL_AURA_FLY: { - // not allow cast fly spells at old maps by players (all spells is self target) + // not allow cast fly spells at old maps by players (all spells is self target) if(m_caster->GetTypeId()==TYPEID_PLAYER) { - if( !((Player*)m_caster)->isGameMaster() && - GetVirtualMapForMapAndZone(m_caster->GetMapId(),m_caster->GetZoneId()) != 530) + if( !((Player*)m_caster)->IsAllowUseFlyMountsHere() ) return SPELL_FAILED_NOT_HERE; } break; @@ -4293,15 +4537,16 @@ uint8 Spell::CheckCasterAuras() const //Check whether the cast should be prevented by any state you might have. uint8 prevented_reason = 0; // 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 @@ -4492,9 +4737,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; @@ -4539,6 +4787,11 @@ uint8 Spell::CheckPower() sLog.outError("Spell::CheckMana: Unknown power type '%d'", m_spellInfo->powerType); return SPELL_FAILED_UNKNOWN; } + + uint8 failReason = CheckRuneCost(m_spellInfo->runeCostID); + if(failReason) + return failReason; + // Check power amount Powers powerType = Powers(m_spellInfo->powerType); if(m_caster->GetPower(powerType) < m_powerCost) @@ -4658,8 +4911,8 @@ 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); @@ -4671,8 +4924,7 @@ uint8 Spell::CheckItems() 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))) + if (!p_caster->CanNoReagentCast(m_spellInfo)) { for(uint32 i=0;i<8;i++) { @@ -4757,6 +5009,7 @@ uint8 Spell::CheckItems() break; } case SPELL_EFFECT_ENCHANT_ITEM: + case SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC: { Item* targetItem = m_targets.getItemTarget(); if(!targetItem) @@ -4840,13 +5093,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: { @@ -4926,18 +5202,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) { @@ -4961,14 +5241,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; - int32 delaytime = GetNextDelayAtDamageMsTime(); + //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; + + delaytime = delaytime * (100 - delayReduce) / 100; if(int32(m_timer) < delaytime) { @@ -5022,9 +5306,9 @@ void Spell::UpdatePointers() m_targets.Update(m_caster); } -bool Spell::IsAffectedBy(SpellEntry const *spellInfo, uint32 effectId) +bool Spell::IsAffectedByAura(Aura *aura) { - return spellmgr.IsAffectedBySpell(m_spellInfo,spellInfo->Id,effectId,spellInfo->EffectItemType[effectId]); + return spellmgr.IsAffectedByMod(m_spellInfo, aura->getAuraSpellMod()); } bool Spell::CheckTargetCreatureType(Unit* target) const @@ -5032,7 +5316,7 @@ 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) + if(m_spellInfo->SpellFamilyName==SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags.IsEqual(0,0x02,0)) { // not allow cast at player if(target->GetTypeId()==TYPEID_PLAYER) @@ -5075,6 +5359,12 @@ bool Spell::CheckTarget( Unit* target, uint32 eff, bool hitPhase ) 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()) @@ -5135,7 +5425,13 @@ bool Spell::CheckTarget( Unit* target, uint32 eff, bool hitPhase ) // 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 (m_originalCasterGUID) + caster = ObjectAccessor::GetGameObject(*m_caster, m_originalCasterGUID); + if (!caster) + caster = m_caster; + if(target!=m_caster && !target->IsWithinLOSInMap(caster)) return false; break; } @@ -5154,9 +5450,8 @@ Unit* Spell::SelectMagnetTarget() { if(Unit* magnet = (*itr)->GetCaster()) { - if((*itr)->m_procCharges>0) + if((*itr)->DropAuraCharge()) { - (*itr)->SetAuraProcCharges((*itr)->m_procCharges-1); target = magnet; m_targets.setUnitTarget(target); AddUnitTarget(target, 0); @@ -5180,7 +5475,7 @@ Unit* Spell::SelectMagnetTarget() 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; } @@ -5361,6 +5656,14 @@ void Spell::CalculateDamageDoneForAllTargets() } } + bool usesAmmo=true; + Unit::AuraList const& Auras = m_caster->GetAurasByType(SPELL_AURA_ABILITY_CONSUME_NO_AMMO); + for(Unit::AuraList::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; @@ -5373,6 +5676,28 @@ void Spell::CalculateDamageDoneForAllTargets() if (!unit) 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); else if (target.missCondition == SPELL_MISS_REFLECT) // In case spell reflect from target, do all effect on caster (if hit) |