diff options
Diffstat (limited to 'src/game/Spell.cpp')
-rw-r--r-- | src/game/Spell.cpp | 2061 |
1 files changed, 1219 insertions, 842 deletions
diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index 1e9a2a7d60e..8291056e15f 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 @@ -35,7 +35,6 @@ #include "Unit.h" #include "Spell.h" #include "DynamicObject.h" -#include "SpellAuras.h" #include "Group.h" #include "UpdateData.h" #include "MapManager.h" @@ -43,14 +42,13 @@ #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" -#define SPELL_CHANNEL_UPDATE_INTERVAL 1000 +#define SPELL_CHANNEL_UPDATE_INTERVAL (1*IN_MILISECONDS) extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS]; @@ -120,6 +118,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; @@ -171,6 +177,8 @@ bool SpellCastTargets::read ( WorldPacket * data, Unit *caster ) if(data->rpos()+4 > data->size()) return false; + //data->hexlike(); + *data >> m_targetMask; sLog.outDebug("Spell read, target mask = %u", m_targetMask); @@ -178,19 +186,23 @@ 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_SOURCE_LOCATION ) + 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()) return false; @@ -198,17 +210,23 @@ bool SpellCastTargets::read ( WorldPacket * data, Unit *caster ) *data >> m_srcX >> m_srcY >> m_srcZ; if(!Trinity::IsValidMapCoord(m_srcX, m_srcY, m_srcZ)) return false; - }*/ - if( m_targetMask & (TARGET_FLAG_SOURCE_LOCATION | TARGET_FLAG_DEST_LOCATION) ) + m_hasDest = true; + } + + 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; - m_hasDest = true; if(!Trinity::IsValidMapCoord(m_destX, m_destY, m_destZ)) return false; + + m_hasDest = true; } if( m_targetMask & TARGET_FLAG_STRING ) @@ -219,10 +237,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 +256,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 +281,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 +326,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; @@ -320,7 +341,7 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi 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 @@ -355,14 +376,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 +393,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++) { @@ -457,11 +478,11 @@ void Spell::FillTargetMap() { // non-standard target selection SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex); - float max_range = GetSpellMaxRange(srange); + float max_range = GetSpellMaxRangeForHostile(srange); 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,12 +532,15 @@ 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: if(m_targets.getUnitTarget()) tmpUnitMap.push_back(m_targets.getUnitTarget()); - 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) tmpUnitMap.push_back(m_caster); break; case SPELL_EFFECT_SUMMON_PLAYER: @@ -543,19 +567,22 @@ 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; case SPELL_EFFECT_LEARN_PET_SPELL: - if(Pet* pet = m_caster->GetPet()) + if(Guardian* pet = m_caster->GetGuardianPet()) tmpUnitMap.push_back(pet); 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,13 +623,12 @@ void Spell::FillTargetMap() } } - if(IsChanneledSpell(m_spellInfo) && !tmpUnitMap.empty()) m_needAliveTargetMask |= (1<<i); for (std::list<Unit*>::iterator itr = tmpUnitMap.begin() ; itr != tmpUnitMap.end();) { - if(!CheckTarget(*itr, i, false )) + if(!CheckTarget(*itr, i)) { itr = tmpUnitMap.erase(itr); continue; @@ -633,37 +659,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_ROGUE: // mutilate mainhand + offhand - if (m_spellInfo->SpellFamilyFlags & 0x600000000LL) m_canTrigger = true; + 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) @@ -673,21 +705,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; @@ -696,7 +737,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; } @@ -705,8 +746,6 @@ void Spell::CleanupTargetList() m_UniqueTargetInfo.clear(); m_UniqueGOTargetInfo.clear(); m_UniqueItemInfo.clear(); - m_countOfHit = 0; - m_countOfMiss = 0; m_delayMoment = 0; } @@ -715,6 +754,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 @@ -722,7 +764,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; } } @@ -732,7 +775,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; @@ -746,11 +789,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) { @@ -831,8 +869,6 @@ void Spell::AddGOTarget(GameObject* pVictim, uint32 effIndex) else target.timeDelay = 0LL; - ++m_countOfHit; - // Add target to list m_UniqueGOTargetInfo.push_back(target); } @@ -866,83 +902,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) { @@ -952,15 +911,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) @@ -1008,7 +965,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; @@ -1047,44 +1004,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 @@ -1097,6 +1022,14 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) caster->ProcDamageAndSpell(unit, procAttacker, procVictim, procEx, 0, m_attackType, m_spellInfo); } + // Take combo points after effects handling (combo points are used in effect handling) + if(!m_IsTriggeredSpell && !m_CastItem + && NeedsComboPoints(m_spellInfo) + && m_caster->GetTypeId()==TYPEID_PLAYER + && target->targetGUID == m_targets.getUnitTargetGUID() + && (missInfo == SPELL_MISS_NONE || missInfo == SPELL_MISS_MISS)) + ((Player*)m_caster)->ClearComboPoints(); + // Call scripted function for AI if this spell is casted upon a creature (except pets) if(IS_CREATURE_GUID(target->targetGUID)) { @@ -1108,14 +1041,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); } } } @@ -1128,25 +1061,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 @@ -1178,7 +1123,7 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) m_caster->SetContestedPvP(); //m_caster->UpdatePvP(true); } - 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); @@ -1196,9 +1141,60 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) unit->IncrDiminishing(m_diminishGroup); } + // 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); + } + uint8 t_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]))) + t_effmask |=1<<i; + + if (t_effmask) + { + Unit * caster = m_originalCaster ? m_originalCaster : m_caster; + Aura * Aur= new Aura(m_spellInfo, t_effmask, &m_currentBasePoints[0], unit, caster , m_CastItem); + + if (!Aur->IsAreaAura()) + { + // 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()) + { + 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 && (m_spellInfo->SpellFamilyFlags[1] & 0x000020)) + m_caster->CastSpell(unit, 41637, true, NULL, NULL, m_originalCasterGUID); + } + unit->AddAura(Aur); + } + t_effmask = effectMask& ~t_effmask; for(uint32 effectNumber=0;effectNumber<3;effectNumber++) { - if (effectMask & (1<<effectNumber)) + if (t_effmask & (1<<effectNumber)) { HandleEffects(unit,NULL,NULL,effectNumber/*,m_damageMultipliers[effectNumber]*/); //Only damage and heal spells need this @@ -1226,20 +1222,23 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) 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) + } + 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); } } } @@ -1373,6 +1372,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; @@ -1484,7 +1493,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; } @@ -1498,13 +1507,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; } @@ -1513,21 +1522,35 @@ Unit* Spell::SearchNearbyTarget(float radius, SpellTargets TargetType, uint32 en void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) { - float radius; + float radius_h, radius_f; if (m_spellInfo->EffectRadiusIndex[i]) - radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + { + radius_h = GetSpellRadiusForHostile(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + radius_f = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + } else - radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); + { + radius_h = GetSpellMaxRangeForHostile(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); + radius_f = GetSpellMaxRangeForFriend(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); + } //Chain: 2, 6, 22, 25, 45, 77 uint32 EffectChainTarget = m_spellInfo->EffectChainTarget[i]; uint32 unMaxTargets = m_spellInfo->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)) + unMaxTargets+=(*j)->GetAmount(); + } + if(m_originalCaster) { if(Player* modOwner = m_originalCaster->GetSpellModOwner()) { - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius,this); + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius_f,this); + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius_h,this); modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, EffectChainTarget, this); } } @@ -1554,17 +1577,17 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) TagUnitMap.push_back(owner); break; case TARGET_UNIT_PET: - if(Pet* pet = m_caster->GetPet()) + if(Guardian* pet = m_caster->GetGuardianPet()) TagUnitMap.push_back(pet); break; case TARGET_UNIT_PARTY_CASTER: - m_caster->GetPartyMember(TagUnitMap, radius); + m_caster->GetPartyMember(TagUnitMap, radius_f); break; case TARGET_UNIT_RAID: - if(Unit *target = m_targets.getUnitTarget()) - TagUnitMap.push_back(target); - else - m_caster->GetRaidMember(TagUnitMap, radius); + //if(Unit *target = m_targets.getUnitTarget()) + // TagUnitMap.push_back(target); + //else + m_caster->GetRaidMember(TagUnitMap, radius_f); break; } }break; @@ -1581,30 +1604,29 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) switch(cur) { case TARGET_UNIT_MINIPET: - if( target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->isPet() && ((Pet*)target)->getPetType() == MINI_PET) + if(target->GetGUID() == m_caster->m_SummonSlot[4]) TagUnitMap.push_back(target); 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_SINGLE_UNKNOWN: TagUnitMap.push_back(target); break; case TARGET_UNIT_PARTY_TARGET: - target->GetPartyMember(TagUnitMap, radius); + target->GetPartyMember(TagUnitMap, radius_f); break; case TARGET_UNIT_TARGET_ENEMY: if(EffectChainTarget <= 1) TagUnitMap.push_back(SelectMagnetTarget()); else if(SelectMagnetTarget()) //TODO: chain target should also use magnet target - SearchChainTarget(TagUnitMap, radius, EffectChainTarget, SPELL_TARGETS_ENEMY); + SearchChainTarget(TagUnitMap, radius_h, EffectChainTarget, SPELL_TARGETS_ENEMY); break; case TARGET_UNIT_CHAINHEAL: if(EffectChainTarget <= 1) TagUnitMap.push_back(target); else - SearchChainTarget(TagUnitMap, radius, EffectChainTarget, SPELL_TARGETS_CHAINHEAL); + SearchChainTarget(TagUnitMap, radius_f, EffectChainTarget, SPELL_TARGETS_CHAINHEAL); break; } }break; @@ -1651,17 +1673,17 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) case TARGET_UNIT_AREA_ENEMY_GROUND: m_targets.m_targetMask |= TARGET_FLAG_DEST_LOCATION; case TARGET_UNIT_AREA_ENEMY: - SearchAreaTarget(TagUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_ENEMY); + SearchAreaTarget(TagUnitMap, radius_h, PUSH_DEST_CENTER, SPELL_TARGETS_ENEMY); break; case TARGET_UNIT_AREA_ALLY_GROUND: m_targets.m_targetMask |= TARGET_FLAG_DEST_LOCATION; case TARGET_UNIT_AREA_ALLY: - SearchAreaTarget(TagUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_ALLY); + SearchAreaTarget(TagUnitMap, radius_f, PUSH_DEST_CENTER, SPELL_TARGETS_ALLY); break; case TARGET_UNIT_AREA_PARTY_GROUND: m_targets.m_targetMask |= TARGET_FLAG_DEST_LOCATION; case TARGET_UNIT_AREA_PARTY: - m_caster->GetPartyMember(TagUnitMap, radius); + m_caster->GetPartyMember(TagUnitMap, radius_f); break; case TARGET_UNIT_AREA_ENTRY_GROUND: m_targets.m_targetMask |= TARGET_FLAG_DEST_LOCATION; @@ -1671,7 +1693,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(m_spellInfo->Id); if(lower==upper) { - SearchAreaTarget(TagUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_ENEMY); + SearchAreaTarget(TagUnitMap, radius_h, PUSH_DEST_CENTER, SPELL_TARGETS_ENEMY); sLog.outErrorDb("Spell (ID: %u) (caster Entry: %u) does not have record in `spell_script_target`", m_spellInfo->Id, m_caster->GetEntry()); break; } @@ -1683,7 +1705,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) sLog.outError( "SPELL: spell ID %u requires non-creature target\n", m_spellInfo->Id ); continue; } - SearchAreaTarget(TagUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_ENTRY, i_spellST->second.targetEntry); + SearchAreaTarget(TagUnitMap, radius_f, PUSH_DEST_CENTER, SPELL_TARGETS_ENTRY, i_spellST->second.targetEntry); } } break; @@ -1727,41 +1749,41 @@ 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) - SearchAreaTarget(TagUnitMap, radius, PUSH_IN_BACK, SPELL_TARGETS_ENEMY); + SearchAreaTarget(TagUnitMap, radius_h, PUSH_IN_BACK, SPELL_TARGETS_ENEMY); else if(m_customAttr & SPELL_ATTR_CU_CONE_LINE) - SearchAreaTarget(TagUnitMap, radius, PUSH_IN_LINE, SPELL_TARGETS_ENEMY); + SearchAreaTarget(TagUnitMap, radius_h, PUSH_IN_LINE, SPELL_TARGETS_ENEMY); else - SearchAreaTarget(TagUnitMap, radius, PUSH_IN_FRONT, SPELL_TARGETS_ENEMY); + SearchAreaTarget(TagUnitMap, radius_h, PUSH_IN_FRONT, SPELL_TARGETS_ENEMY); break; case TARGET_UNIT_CONE_ALLY: - SearchAreaTarget(TagUnitMap, radius, PUSH_IN_FRONT, SPELL_TARGETS_ALLY); + SearchAreaTarget(TagUnitMap, radius_f, PUSH_IN_FRONT, SPELL_TARGETS_ALLY); break; // 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)); + m_targets.setUnitTarget(SearchNearbyTarget(radius_f, SPELL_TARGETS_ALLY)); if(m_targets.getUnitTarget()) { if(EffectChainTarget <= 1) TagUnitMap.push_back(m_targets.getUnitTarget()); else - SearchChainTarget(TagUnitMap, radius, EffectChainTarget, SPELL_TARGETS_ALLY); + SearchChainTarget(TagUnitMap, radius_f, 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)); + m_targets.setUnitTarget(SearchNearbyTarget(radius_h, SPELL_TARGETS_ENEMY)); if(m_targets.getUnitTarget()) { if(EffectChainTarget <= 1) TagUnitMap.push_back(m_targets.getUnitTarget()); else - SearchChainTarget(TagUnitMap, radius, EffectChainTarget, SPELL_TARGETS_ENEMY); + SearchChainTarget(TagUnitMap, radius_h, EffectChainTarget, SPELL_TARGETS_ENEMY); } break; case TARGET_SCRIPT: @@ -1778,7 +1800,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) } SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex); - float range = GetSpellMaxRange(srange); + float range = GetSpellMaxRangeForHostile(srange); Creature* creatureScriptTarget = NULL; GameObject* goScriptTarget = NULL; @@ -1794,7 +1816,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) @@ -1824,7 +1846,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 ) @@ -1873,7 +1895,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) Player* Target = itr->getSource(); // IsHostileTo check duel and controlled by enemy - if( Target && targetPlayer->IsWithinDistInMap(Target, radius) && + if( Target && targetPlayer->IsWithinDistInMap(Target, radius_f) && targetPlayer->getClass() == Target->getClass() && !m_caster->IsHostileTo(Target) ) { @@ -1904,7 +1926,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) 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) @@ -1947,7 +1969,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) 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) @@ -1983,7 +2005,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) float x, y, z, angle, dist; - dist = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + dist = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); if (cur == TARGET_DEST_DEST_RANDOM) dist *= rand_norm(); @@ -2024,10 +2046,71 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) advance(itr, urand(0, TagUnitMap.size() - 1)); TagUnitMap.erase(itr); } + + /*if(m_spellInfo->Id==57669) //Replenishment (special target selection) + { + if(pGroup) + { + typedef std::priority_queue<PrioritizeManaPlayerWraper, std::vector<PrioritizeManaPlayerWraper>, PrioritizeMana> Top10; + Top10 manaUsers; + + for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL && manaUsers.size() < 10; itr = itr->next()) + { + Player* Target = itr->getSource(); + if (m_caster->GetGUID() != Target->GetGUID() && Target->getPowerType() == POWER_MANA && + !Target->isDead() && m_caster->IsWithinDistInMap(Target, radius)) + { + PrioritizeManaPlayerWraper WTarget(Target); + manaUsers.push(WTarget); + } + } + + while(!manaUsers.empty()) + { + TagUnitMap.push_back(manaUsers.top().getPlayer()); + manaUsers.pop(); + } + } + else + { + Unit* ownerOrSelf = pTarget ? pTarget : m_caster->GetCharmerOrOwnerOrSelf(); + if ((ownerOrSelf==m_caster || m_caster->IsWithinDistInMap(ownerOrSelf, radius)) && + ownerOrSelf->getPowerType() == POWER_MANA) + TagUnitMap.push_back(ownerOrSelf); + + if(Pet* pet = ownerOrSelf->GetGuardianPet()) + if( m_caster->IsWithinDistInMap(pet, radius) && pet->getPowerType() == POWER_MANA ) + TagUnitMap.push_back(pet); + } + }*/ } } -void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura) +class PrioritizeManaPlayerWraper +{ + friend struct PrioritizeMana; + + public: + explicit PrioritizeManaPlayerWraper(Player* player) : player(player) + { + uint32 maxmana = player->GetMaxPower(POWER_MANA); + percentMana = maxmana ? player->GetPower(POWER_MANA) * 100 / maxmana : 101; + } + Player* getPlayer() const { return player; } + private: + Player* player; + uint32 percentMana; +}; + +struct PrioritizeMana +{ + int operator()( PrioritizeManaPlayerWraper const& x, PrioritizeManaPlayerWraper const& y ) const + { + return x.percentMana < y.percentMana; + } +}; + +void Spell::prepare(SpellCastTargets const* targets, AuraEffect* triggeredByAura) { if(m_CastItem) m_castItemGUID = m_CastItem->GetGUID(); @@ -2036,6 +2119,13 @@ void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura) m_targets = *targets; + if(!m_targets.getUnitTargetGUID() && m_spellInfo->Targets & TARGET_FLAG_UNIT) + { + if(Unit *target = ObjectAccessor::GetUnit(*m_caster, m_caster->GetUInt64Value(UNIT_FIELD_TARGET))) + if(IsValidSingleTargetSpell(target)) + m_targets.setUnitTarget(target); + } + m_spellState = SPELL_STATE_PREPARING; m_caster->GetPosition(m_castPositionX, m_castPositionY, m_castPositionZ); @@ -2049,7 +2139,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); @@ -2086,13 +2176,13 @@ 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); @@ -2102,13 +2192,14 @@ void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura) // Prepare data for triggers prepareDataForTriggerSystem(); - // 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); // set timer base at cast time ReSetTimer(); - - if(m_IsTriggeredSpell) + //Containers for channeled spells have to be set + //TODO:Apply this to all casted spells if needed + if(m_IsTriggeredSpell && !IsChanneledSpell(m_spellInfo)) cast(true); else { @@ -2154,11 +2245,10 @@ void Spell::cancel() { 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()); + unit->RemoveAurasDueToSpell(m_spellInfo->Id, m_caster->GetGUID(), AURA_REMOVE_BY_CANCEL); } } - - m_caster->RemoveAurasByCasterSpell(m_spellInfo->Id, m_caster->GetGUID()); + m_caster->RemoveAurasDueToSpell(m_spellInfo->Id, m_caster->GetGUID(), AURA_REMOVE_BY_CANCEL); SendChannelUpdate(0); SendInterrupted(0); SendCastResult(SPELL_FAILED_INTERRUPTED); @@ -2182,8 +2272,6 @@ void Spell::cast(bool skipCheck) { SetExecutedCurrently(true); - uint8 castResult = 0; - // update pointers base at GUIDs to prevent access to non-existed already object UpdatePointers(); @@ -2198,23 +2286,12 @@ void Spell::cast(bool skipCheck) if(m_caster->GetTypeId() != TYPEID_PLAYER && m_targets.getUnitTarget() && m_targets.getUnitTarget() != m_caster) m_caster->SetInFront(m_targets.getUnitTarget()); - if(!m_IsTriggeredSpell) - { - castResult = CheckPower(); - if(castResult != 0) - { - SendCastResult(castResult); - finish(false); - SetExecutedCurrently(false); - return; - } - } // 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); @@ -2225,48 +2302,70 @@ 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_IsTriggeredSpell) + if (m_caster->GetTypeId() == TYPEID_PLAYER) { - //TakePower(); - TakeReagents(); // we must remove reagents before HandleEffects to allow place crafted item in same slot - } + if (!m_IsTriggeredSpell && m_CastItem) + ((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM, m_CastItem->GetEntry()); - // 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(); + ((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL, m_spellInfo->Id); + } - //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) + // this is related to combo points so must be done before takepower + // 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 (IsAffectedBy(auraSpellInfo, auraSpellIdx)) + if(SpellEntry const *spellInfo = sSpellStore.LookupEntry(auraSpellInfo->EffectTriggerSpell[auraSpellIdx])) { - 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)->GetStackAmount())); - } + // 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())); } } + // this is related to combo points so must be done before takepower + if(m_customAttr & SPELL_ATTR_CU_DIRECT_DAMAGE) + CalculateDamageDoneForAllTargets(); + + if(!m_IsTriggeredSpell) + { + // Powers have to be taken before SendSpellGo + TakePower(); + TakeReagents(); // we must remove reagents before HandleEffects to allow place crafted item in same slot + } + + // 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_CHARGE) EffectCharge(0); @@ -2288,12 +2387,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)) @@ -2312,9 +2405,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 @@ -2450,93 +2552,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 + if(m_spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) 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) @@ -2690,6 +2719,10 @@ void Spell::finish(bool ok) m_caster->resetAttackTimer(RANGED_ATTACK); } + // potions disabled by client, send event "not in combat" if need + if (m_caster->GetTypeId() == TYPEID_PLAYER) + ((Player*)m_caster)->UpdatePotionCooldown(this); + // 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()) @@ -2700,72 +2733,65 @@ void Spell::finish(bool ok) 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 + 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 + 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(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 (don't must be) + data << uint32(0); + 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); } void Spell::SendSpellStart() @@ -2779,6 +2805,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)); @@ -2788,14 +2817,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); @@ -2815,24 +2862,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); } @@ -2877,15 +2968,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 @@ -2896,6 +3008,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() @@ -2960,30 +3075,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()) @@ -3002,6 +3106,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; } @@ -3015,13 +3126,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); } @@ -3089,10 +3203,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); } @@ -3129,7 +3252,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) { @@ -3145,7 +3268,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); } @@ -3171,7 +3294,7 @@ void Spell::TakeCastItem() void Spell::TakePower() { - if(m_CastItem || m_triggeredByAuraSpell || !m_powerCost) + if(m_CastItem || m_triggeredByAuraSpell) return; bool hit = true; @@ -3184,12 +3307,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) { @@ -3203,8 +3341,6 @@ void Spell::TakePower() return; } - Powers powerType = Powers(m_spellInfo->powerType); - if(hit) m_caster->ModifyPower(powerType, -m_powerCost); else @@ -3215,6 +3351,136 @@ 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); + } +} + +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. @@ -3223,11 +3489,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++) { @@ -3243,7 +3507,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); @@ -3290,14 +3554,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); @@ -3314,7 +3573,7 @@ void Spell::HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTar EffectEnchantItemTmp(i); else { - sLog.outError("SPELL: unknown effect %u spell id %u\n", + sLog.outError("SPELL: unknown effect %u spell id %u", eff, m_spellInfo->Id); } } @@ -3330,11 +3589,14 @@ void Spell::TriggerSpell() } } -uint8 Spell::CanCast(bool strict) +SpellCastResult Spell::CheckCast(bool strict) { // check cooldowns to prevent cheating if(m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->HasSpellCooldown(m_spellInfo->Id)) { + //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 @@ -3351,20 +3613,56 @@ 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; + } + } + + 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 - if(m_spellInfo->CasterAuraState && !m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraState))) + 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))) + 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() ) @@ -3375,18 +3673,23 @@ uint8 Spell::CanCast(bool strict) 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_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_spellInfo->TargetAuraState && !target->HasAuraState(AuraState(m_spellInfo->TargetAuraState), m_spellInfo, m_caster)) return SPELL_FAILED_TARGET_AURASTATE; // Not allow casting on flying player @@ -3399,13 +3702,23 @@ 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_CHAIN_DAMAGE) + { + if (target = m_caster->GetUnit(*m_caster, ((Player *)m_caster)->GetSelection())) + m_targets.setUnitTarget(target); + else + return SPELL_FAILED_BAD_TARGETS; } } @@ -3414,7 +3727,7 @@ uint8 Spell::CanCast(bool strict) { if(m_spellInfo->EffectImplicitTargetA[j] == TARGET_PET) { - target = m_caster->GetPet(); + target = m_caster->GetGuardianPet(); if(!target) { if(m_triggeredByAuraSpell) // not report pet not existence for triggered spells @@ -3458,14 +3771,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; @@ -3480,10 +3794,9 @@ 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(!((Player*)m_caster)->InBattleGround()) @@ -3493,14 +3806,19 @@ 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; + 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 && @@ -3514,11 +3832,14 @@ 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++) { @@ -3553,7 +3874,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); @@ -3591,7 +3912,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); @@ -3654,13 +3975,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; } @@ -3685,7 +4009,7 @@ uint8 Spell::CanCast(bool strict) { // 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 @@ -3699,7 +4023,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; @@ -3711,10 +4035,13 @@ uint8 Spell::CanCast(bool strict) } case SPELL_EFFECT_LEARN_SPELL: { + if (m_caster->GetTypeId() != TYPEID_PLAYER) + return SPELL_FAILED_BAD_TARGETS; + if(m_spellInfo->EffectImplicitTargetA[i] != TARGET_PET) break; - Pet* pet = m_caster->GetPet(); + Pet* pet = ((Player*)m_caster)->GetPet(); if(!pet) return SPELL_FAILED_NO_PET; @@ -3724,21 +4051,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; @@ -3747,31 +4070,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()) @@ -3843,130 +4164,40 @@ 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; + uint32 lockId = 0; if (GameObject* go=m_targets.getGOTarget()) - lockInfo = sLockStore.LookupEntry(go->GetLockId()); + lockId = go->GetLockId(); else if(Item* itm=m_targets.getItemTarget()) - lockInfo = sLockStore.LookupEntry(itm->GetProto()->LockID); + lockId = itm->GetProto()->LockID; - // check lock compatibility - if (lockInfo) - { - // 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; + SkillType skillId =SKILL_NONE; + int32 reqSkillValue = 0; + int32 skillValue = 0; - 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) - return SPELL_FAILED_BAD_TARGETS; - } + // check lock compatibility + SpellCastResult res = CanOpenLock(i,lockId,skillId,reqSkillValue,skillValue); + if(res != SPELL_CAST_OK) + return res; // chance for fail at orange mining/herb/LockPicking gathering attempt - if (!m_selfContainer || ((*m_selfContainer) != this)) - break; - - // 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) + // second check prevent fail at rechecks + if(skillId != SKILL_NONE && (!m_selfContainer || ((*m_selfContainer) != this))) { - 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; + bool canFailAtMax = skillId != SKILL_HERBALISM && skillId != SKILL_MINING; - // 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; - - // get the required lock value - int32 ReqValue=0; - if (lockInfo) - { - // 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; - - 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 > 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; @@ -3975,16 +4206,11 @@ 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]) { - case SUMMON_TYPE_POSESSED: - case SUMMON_TYPE_POSESSED2: - case SUMMON_TYPE_POSESSED3: case SUMMON_TYPE_DEMON: case SUMMON_TYPE_SUMMON: { @@ -3996,13 +4222,25 @@ uint8 Spell::CanCast(bool strict) break; } } + SummonPropertiesEntry const *SummonProperties = sSummonPropertiesStore.LookupEntry(m_spellInfo->EffectMiscValueB[i]); + if(!SummonProperties) + break; + switch(SummonProperties->Category) + { + case SUMMON_CATEGORY_POSSESSED: + { + if(m_caster->GetPetGUID()) + return SPELL_FAILED_ALREADY_HAVE_SUMMON; + + 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; @@ -4016,13 +4254,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; @@ -4058,7 +4294,7 @@ uint8 Spell::CanCast(bool strict) case SPELL_EFFECT_LEAP: case SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER: { - float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + float dis = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); float fx = m_caster->GetPositionX() + dis * cos(m_caster->GetOrientation()); float fy = m_caster->GetPositionY() + dis * sin(m_caster->GetOrientation()); // teleport a bit above terrain level to avoid falling below it @@ -4094,25 +4330,38 @@ 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_targets.getUnitTarget() || m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER) + return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - if (m_targets.getUnitTarget()->getLevel() > m_caster->getLevel()) - return SPELL_FAILED_HIGHLEVEL; + if (m_targets.getUnitTarget()->getLevel() > m_caster->getLevel()) + return SPELL_FAILED_HIGHLEVEL; - // use SMSG_PET_TAME_FAILURE? - if (!((Creature*)m_targets.getUnitTarget())->GetCreatureInfo()->isTameable ()) - return SPELL_FAILED_BAD_TARGETS; + // use SMSG_PET_TAME_FAILURE? + if (!((Creature*)m_targets.getUnitTarget())->GetCreatureInfo()->isTameable ()) + return SPELL_FAILED_BAD_TARGETS; - if(m_caster->GetPetGUID()) - return SPELL_FAILED_ALREADY_HAVE_SUMMON; + if(m_caster->GetPetGUID()) + return SPELL_FAILED_ALREADY_HAVE_SUMMON; - if(m_caster->GetCharmGUID()) - return SPELL_FAILED_ALREADY_HAVE_CHARM; + 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: { @@ -4145,17 +4394,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 +4420,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; @@ -4202,10 +4447,10 @@ 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()) return SPELL_FAILED_CASTER_DEAD; @@ -4253,20 +4498,16 @@ int16 Spell::PetCanCast(Unit* target) 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; + return SPELL_CAST_OK; uint8 school_immune = 0; uint32 mechanic_immune = 0; @@ -4291,21 +4532,22 @@ uint8 Spell::CheckCasterAuras() const } //Check whether the cast should be prevented by any state you might have. - uint8 prevented_reason = 0; + 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) { @@ -4315,37 +4557,43 @@ 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 ) + if( GetAllSpellMechanicMask(itr->second->GetSpellProto()) & school_immune ) continue; if( (1<<(itr->second->GetSpellProto()->Dispel)) & dispel_immune) continue; //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; + } + } } } } @@ -4354,7 +4602,7 @@ uint8 Spell::CheckCasterAuras() const else return prevented_reason; } - return 0; // all ok + return SPELL_CAST_OK; } bool Spell::CanAutoCast(Unit* target) @@ -4367,25 +4615,25 @@ bool Spell::CanAutoCast(Unit* target) { 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) + if( (target->GetAuraEffect(m_spellInfo->Id, j))->GetParentAura()->GetStackAmount() >= m_spellInfo->StackAmount) return false; } } else if ( IsAreaAuraEffect( m_spellInfo->Effect[j] )) { - if( target->HasAura(m_spellInfo->Id, j) ) + 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) @@ -4396,12 +4644,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 @@ -4410,15 +4659,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) @@ -4452,7 +4701,7 @@ uint8 Spell::CheckRange(bool strict) return SPELL_FAILED_TOO_CLOSE; } - return 0; // ok + return SPELL_CAST_OK; } int32 Spell::CalculatePowerCost() @@ -4492,9 +4741,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; @@ -4520,39 +4772,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) @@ -4562,77 +4818,72 @@ uint8 Spell::CheckItems() } else { - itemid = m_CastItem->GetEntry(); + 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++) + 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; + + // 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++) { - if (proto->Spells[i].SpellCharges) + // skip check, pet not required like checks, and for TARGET_PET m_targets.getUnitTarget() is not the real target but the caster + if (m_spellInfo->EffectImplicitTargetA[i] == TARGET_PET) + continue; + + if (m_spellInfo->Effect[i] == SPELL_EFFECT_HEAL) { - if(m_CastItem->GetSpellCharges(i)==0) - return SPELL_FAILED_NO_CHARGES_REMAIN; + if (m_targets.getUnitTarget()->GetHealth() == m_targets.getUnitTarget()->GetMaxHealth()) + { + failReason = SPELL_FAILED_ALREADY_AT_FULL_HEALTH; + continue; + } + else + { + failReason = SPELL_CAST_OK; + break; + } } - } - uint32 ItemClass = proto->Class; - if (ItemClass == ITEM_CLASS_CONSUMABLE && m_targets.getUnitTarget()) - { - // 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++) + // Mana Potion, Rage Potion, Thistle Tea(Rogue), ... + if (m_spellInfo->Effect[i] == SPELL_EFFECT_ENERGIZE) { - // skip check, pet not required like checks, and for TARGET_PET m_targets.getUnitTarget() is not the real target but the caster - if (m_spellInfo->EffectImplicitTargetA[i] == TARGET_PET) + if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS) + { + failReason = SPELL_FAILED_ALREADY_AT_FULL_POWER; continue; + } - if (m_spellInfo->Effect[i] == SPELL_EFFECT_HEAL) + Powers power = Powers(m_spellInfo->EffectMiscValue[i]); + if (m_targets.getUnitTarget()->GetPower(power) == m_targets.getUnitTarget()->GetMaxPower(power)) { - 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_POWER; + continue; } - - // Mana Potion, Rage Potion, Thistle Tea(Rogue), ... - if (m_spellInfo->Effect[i] == SPELL_EFFECT_ENERGIZE) + else { - if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS) - { - failReason = (uint8)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; - } + 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) @@ -4651,6 +4902,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())); @@ -4658,29 +4910,29 @@ 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))) + // check reagents (ignore triggered spells with reagents processed by original spell) and special reagent ignore case. + if (!m_IsTriggeredSpell && !p_caster->CanNoReagentCast(m_spellInfo)) { 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 ) @@ -4688,7 +4940,7 @@ uint8 Spell::CheckItems() ItemPrototype const *proto = m_CastItem->GetProto(); if(!proto) return SPELL_FAILED_ITEM_NOT_READY; - 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); @@ -4700,10 +4952,11 @@ uint8 Spell::CheckItems() } } if( !p_caster->HasItemCount(itemid,itemcount) ) - return (uint8)SPELL_FAILED_ITEM_NOT_READY; //0x54 + return SPELL_FAILED_ITEM_NOT_READY; //0x54 } } + // check totem-item requirements (items presence in inventory) uint32 totems = 2; for(int i=0;i<2;++i) { @@ -4718,9 +4971,9 @@ uint8 Spell::CheckItems() totems -= 1; } if(totems != 0) - return (uint8)SPELL_FAILED_TOTEMS; //0x7C + return SPELL_FAILED_TOTEMS; //0x7C - //Check items for TotemCategory + // Check items for TotemCategory (items presence in inventory) uint32 TotemCategory = 2; for(int i=0;i<2;++i) { @@ -4736,8 +4989,9 @@ uint8 Spell::CheckItems() TotemCategory -= 1; } if(TotemCategory != 0) - return (uint8)SPELL_FAILED_TOTEM_CATEGORY; //0x7B + return SPELL_FAILED_TOTEM_CATEGORY; //0x7B + // special checks for spell effects for(int i = 0; i < 3; i++) { switch (m_spellInfo->Effect[i]) @@ -4757,6 +5011,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 +5095,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: { @@ -4915,7 +5193,7 @@ uint8 Spell::CheckItems() } } - return uint8(0); + return SPELL_CAST_OK; } void Spell::Delayed() // only called in DealDamage() @@ -4926,18 +5204,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 +5243,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) { @@ -4987,11 +5273,8 @@ void Spell::DelayedChannel() 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); } - } } @@ -5022,17 +5305,12 @@ 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) + if(m_spellInfo->SpellFamilyName==SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags.IsEqual(0,0x02,0)) { // not allow cast at player if(target->GetTypeId()==TYPEID_PLAYER) @@ -5066,7 +5344,7 @@ CurrentSpellTypes Spell::GetCurrentContainer() return(CURRENT_GENERIC_SPELL); } -bool Spell::CheckTarget( Unit* target, uint32 eff, bool hitPhase ) +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_SELF) @@ -5075,6 +5353,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 +5419,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; } @@ -5149,14 +5439,13 @@ Unit* Spell::SelectMagnetTarget() 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) + Unit::AuraEffectList const& magnetAuras = target->GetAurasByType(SPELL_AURA_SPELL_MAGNET); + for(Unit::AuraEffectList::const_iterator itr = magnetAuras.begin(); itr != magnetAuras.end(); ++itr) { if(Unit* magnet = (*itr)->GetCaster()) { - if((*itr)->m_procCharges>0) + if((*itr)->GetParentAura()->DropAuraCharge()) { - (*itr)->SetAuraProcCharges((*itr)->m_procCharges-1); target = magnet; m_targets.setUnitTarget(target); AddUnitTarget(target, 0); @@ -5180,7 +5469,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; } @@ -5259,9 +5548,6 @@ 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 { // do the action (pass spell to channeling state) @@ -5364,6 +5650,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; @@ -5376,6 +5670,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) @@ -5434,3 +5750,64 @@ 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; +} |