diff options
Diffstat (limited to 'src/game/Spell.cpp')
| -rw-r--r-- | src/game/Spell.cpp | 1906 | 
1 files changed, 1123 insertions, 783 deletions
diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index 1e9a2a7d60e..0068e31010b 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,6 +532,8 @@ void Spell::FillTargetMap()                  case SPELL_EFFECT_CREATE_ITEM:                  case SPELL_EFFECT_TRIGGER_SPELL:                  case SPELL_EFFECT_SKILL_STEP: +                case SPELL_EFFECT_PROFICIENCY: +                case SPELL_EFFECT_SUMMON_OBJECT_WILD:                  case SPELL_EFFECT_SELF_RESURRECT:                  case SPELL_EFFECT_REPUTATION:                  case SPELL_EFFECT_LEARN_SPELL: @@ -543,7 +566,9 @@ void Spell::FillTargetMap()                      break;                  case SPELL_EFFECT_SUMMON_CHANGE_ITEM:                  case SPELL_EFFECT_ADD_FARSIGHT: +                case SPELL_EFFECT_APPLY_GLYPH:                  case SPELL_EFFECT_STUCK: +                case SPELL_EFFECT_FEED_PET:                  case SPELL_EFFECT_DESTROY_ALL_TOTEMS:                      tmpUnitMap.push_back(m_caster);                      break; @@ -553,9 +578,10 @@ void Spell::FillTargetMap()                      break;                  /*case SPELL_EFFECT_ENCHANT_ITEM:                  case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY: +                case SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC:                  case SPELL_EFFECT_DISENCHANT: -                case SPELL_EFFECT_FEED_PET:                  case SPELL_EFFECT_PROSPECTING: +                case SPELL_EFFECT_MILLING:                      if(m_targets.getItemTarget())                          AddItemTarget(m_targets.getItemTarget(), i);                      break;*/ @@ -596,13 +622,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 +658,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 +704,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 +736,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 +745,6 @@ void Spell::CleanupTargetList()      m_UniqueTargetInfo.clear();      m_UniqueGOTargetInfo.clear();      m_UniqueItemInfo.clear(); -    m_countOfHit = 0; -    m_countOfMiss = 0;      m_delayMoment = 0;  } @@ -715,6 +753,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 +763,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 +774,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 +788,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 +868,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 +901,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 +910,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 +964,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 +1003,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 +1021,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 +1040,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 +1060,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 +1122,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,6 +1140,23 @@ 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); +    } + +      for(uint32 effectNumber=0;effectNumber<3;effectNumber++)      {          if (effectMask & (1<<effectNumber)) @@ -1373,6 +1334,16 @@ void Spell::SearchChainTarget(std::list<Unit*> &TagUnitMap, float max_range, uin      if(!cur)          return; +    // Get spell max affected targets +    /*uint32 unMaxTargets = m_spellInfo->MaxAffectedTargets; +    Unit::AuraList const& mod = m_caster->GetAurasByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS); +    for(Unit::AuraList::const_iterator m = mod.begin(); m != mod.end(); ++m) +    { +        if (!(*m)->isAffectedOnSpell(m_spellInfo)) +            continue; +        unMaxTargets+=(*m)->GetModifier()->m_amount; +    }*/ +      //FIXME: This very like horrible hack and wrong for most spells      if(m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_MELEE)          max_range += num * CHAIN_SPELL_JUMP_RADIUS; @@ -1484,7 +1455,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 +1469,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 +1484,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::AuraList const& Auras = m_caster->GetAurasByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS); +    for(Unit::AuraList::const_iterator j = Auras.begin();j != Auras.end(); ++j) +    { +        if((*j)->isAffectedOnSpell(m_spellInfo)) +            unMaxTargets+=(*j)->GetModifier()->m_amount; +    } +      if(m_originalCaster)      {          if(Player* modOwner = m_originalCaster->GetSpellModOwner())          { -            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);          }      } @@ -1558,13 +1543,13 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)                          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 +1566,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_TotemSlot[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 +1635,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 +1655,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 +1667,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 +1711,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 +1762,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 +1778,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 +1808,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 +1857,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 +1888,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 +1931,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 +1967,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 +2008,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->GetPet()) +                    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, Aura* triggeredByAura)  {      if(m_CastItem)          m_castItemGUID = m_CastItem->GetGUID(); @@ -2036,6 +2081,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 +2101,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,8 +2138,8 @@ 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)          { @@ -2102,13 +2154,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      { @@ -2182,8 +2235,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 +2249,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 +2265,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 -    } - -    // CAST SPELL -    SendSpellCooldown(); -    //SendCastResult(castResult); -    SendSpellGo();                                          // we must send smsg_spell_go packet before m_castItem delete in TakeCastItem()... +        if (m_CastItem) +            ((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM, m_CastItem->GetEntry()); -    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? +    // 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::AuraList const& targetTriggers = m_caster->GetAurasByType(SPELL_AURA_ADD_TARGET_TRIGGER);      for(Unit::AuraList::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)->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 +2350,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 +2368,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 +2515,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 +2682,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 +2696,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 +2768,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 +2780,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 +2825,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 +2931,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 +2971,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 +3038,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 +3069,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 +3089,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 +3166,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 +3215,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 +3231,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 +3257,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 +3270,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 +3304,6 @@ void Spell::TakePower()          return;      } -    Powers powerType = Powers(m_spellInfo->powerType); -      if(hit)          m_caster->ModifyPower(powerType, -m_powerCost);      else @@ -3215,6 +3314,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 +3452,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 +3470,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 +3517,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 +3536,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 +3552,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 +3576,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::AuraList const& ignore = m_caster->GetAurasByType(SPELL_AURA_MOD_IGNORE_SHAPESHIFT); +        for(Unit::AuraList::const_iterator i = ignore.begin(); i != ignore.end(); ++i) +        { +            if (!(*i)->isAffectedOnSpell(m_spellInfo)) +                continue; +            checkForm = false; +            break; +        } +        if (checkForm) +        { +            // Cannot be used in this stance/form +            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::AuraList const& stateAuras = m_caster->GetAurasByType(SPELL_AURA_ABILITY_IGNORE_AURASTATE); +    for(Unit::AuraList::const_iterator j = stateAuras.begin();j != stateAuras.end(); ++j) +    { +        if((*j)->isAffectedOnSpell(m_spellInfo)) +        { +            if ((*j)->GetModifier()->m_miscvalue==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), 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->CasterAuraStateNot && m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraStateNot))) +    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 +3636,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 +3665,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;              }          } @@ -3458,14 +3734,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 +3757,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 +3769,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 +3795,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 +3837,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 +3875,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 +3938,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 +3972,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 +3986,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; @@ -3724,15 +4011,9 @@ uint8 Spell::CanCast(bool strict)                  if(!learn_spellproto)                      return SPELL_FAILED_NOT_KNOWN; -                if(!pet->CanTakeMoreActiveSpells(learn_spellproto->Id)) -                    return SPELL_FAILED_TOO_MANY_SKILLS; -                  if(m_spellInfo->spellLevel > pet->getLevel())                      return SPELL_FAILED_LOWLEVEL; -                if(!pet->HasTPForSpell(learn_spellproto->Id)) -                    return SPELL_FAILED_TRAINING_POINTS; -                  break;              }              case SPELL_EFFECT_LEARN_PET_SPELL: @@ -3747,20 +4028,18 @@ uint8 Spell::CanCast(bool strict)                  if(!learn_spellproto)                      return SPELL_FAILED_NOT_KNOWN; -                if(!pet->CanTakeMoreActiveSpells(learn_spellproto->Id)) -                    return SPELL_FAILED_TOO_MANY_SKILLS; -                  if(m_spellInfo->spellLevel > pet->getLevel())                      return SPELL_FAILED_LOWLEVEL; -                if(!pet->HasTPForSpell(learn_spellproto->Id)) -                    return SPELL_FAILED_TRAINING_POINTS; -                  break;              }              case SPELL_EFFECT_FEED_PET:              { -                if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_targets.getItemTarget() ) +                if (m_caster->GetTypeId() != TYPEID_PLAYER) +                    return SPELL_FAILED_BAD_TARGETS; + +                Item* foodItem = m_targets.getItemTarget(); +                if(!foodItem)                      return SPELL_FAILED_BAD_TARGETS;                  Pet* pet = m_caster->GetPet(); @@ -3768,10 +4047,10 @@ uint8 Spell::CanCast(bool strict)                  if(!pet)                      return SPELL_FAILED_NO_PET; -                if(!pet->HaveInDiet(m_targets.getItemTarget()->GetProto())) +                if(!pet->HaveInDiet(foodItem->GetProto()))                      return SPELL_FAILED_WRONG_PET_FOOD; -                if(!pet->GetCurrentFoodBenefitLevel(m_targets.getItemTarget()->GetProto()->ItemLevel)) +                if(!pet->GetCurrentFoodBenefitLevel(foodItem->GetProto()->ItemLevel))                      return SPELL_FAILED_FOOD_LOWLEVEL;                  if(m_caster->isInCombat() || pet->isInCombat()) @@ -3843,125 +4122,35 @@ 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; - -                                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; -                    } +                SkillType skillId =SKILL_NONE; +                int32 reqSkillValue = 0; +                int32 skillValue = 0; -                    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; - -                // 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; +                    bool canFailAtMax = skillId != SKILL_HERBALISM && skillId != SKILL_MINING; -                // 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: @@ -3975,16 +4164,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 +4180,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 +4212,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 = m_caster->GetPet()) +                                pet->CastSpell(pet, 32752, true, NULL, NULL, pet->GetGUID());                      }                      else                          return SPELL_FAILED_ALREADY_HAVE_SUMMON; @@ -4058,7 +4252,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 +4288,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 +4352,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 +4378,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 +4405,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 +4456,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 +4490,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)          { @@ -4354,7 +4554,7 @@ uint8 Spell::CheckCasterAuras() const          else              return prevented_reason;      } -    return 0;                                               // all ok +    return SPELL_CAST_OK;  }  bool Spell::CanAutoCast(Unit* target) @@ -4383,9 +4583,9 @@ bool Spell::CanAutoCast(Unit* target)          }      } -    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 +4596,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 +4611,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 +4653,7 @@ uint8 Spell::CheckRange(bool strict)              return SPELL_FAILED_TOO_CLOSE;      } -    return 0;                                               // ok +    return SPELL_CAST_OK;  }  int32 Spell::CalculatePowerCost() @@ -4492,9 +4693,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,18 +4724,18 @@ 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 ) @@ -4539,20 +4743,24 @@ uint8 Spell::CheckPower()          sLog.outError("Spell::CheckMana: 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 +4770,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 +4854,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 +4862,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 +    if (!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 +4892,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 +4904,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 +4923,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 +4941,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 +4963,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 +5047,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 +5145,7 @@ uint8 Spell::CheckItems()          }      } -    return uint8(0); +    return SPELL_CAST_OK;  }  void Spell::Delayed() // only called in DealDamage() @@ -4926,18 +5156,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 +5195,18 @@ void Spell::DelayedChannel()      if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER || getState() != SPELL_STATE_CASTING)          return; -    //check resist chance -    int32 resistChance = 100;                               //must be initialized to 100 for percent modifiers -    ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME,resistChance, this); -    resistChance += m_caster->GetTotalAuraModifier(SPELL_AURA_RESIST_PUSHBACK) - 100; -    if (roll_chance_i(resistChance)) +    if(isDelayableNoMore())                                 // Spells may only be delayed twice +        return; + +    //check pushback reduce +    int32 delaytime = GetSpellDuration(m_spellInfo) * 25 / 100; // channeling delay is normally 25% of its time per hit +    int32 delayReduce = 100;                               // must be initialized to 100 for percent modifiers +    ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME,delayReduce, this); +    delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; +    if(delayReduce >= 100)          return; -    int32 delaytime = GetNextDelayAtDamageMsTime(); +    delaytime = delaytime * (100 - delayReduce) / 100;      if(int32(m_timer) < delaytime)      { @@ -5022,9 +5260,9 @@ void Spell::UpdatePointers()      m_targets.Update(m_caster);  } -bool Spell::IsAffectedBy(SpellEntry const *spellInfo, uint32 effectId) +bool Spell::IsAffectedByAura(Aura *aura)  { -    return spellmgr.IsAffectedBySpell(m_spellInfo,spellInfo->Id,effectId,spellInfo->EffectItemType[effectId]); +    return spellmgr.IsAffectedByMod(m_spellInfo, aura->getAuraSpellMod());  }  bool Spell::CheckTargetCreatureType(Unit* target) const @@ -5032,7 +5270,7 @@ bool Spell::CheckTargetCreatureType(Unit* target) const      uint32 spellCreatureTargetMask = m_spellInfo->TargetCreatureType;      // Curse of Doom : not find another way to fix spell target check :/ -    if(m_spellInfo->SpellFamilyName==SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags == 0x0200000000LL) +    if(m_spellInfo->SpellFamilyName==SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags.IsEqual(0,0x02,0))      {          // not allow cast at player          if(target->GetTypeId()==TYPEID_PLAYER) @@ -5066,7 +5304,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 +5313,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 +5379,13 @@ bool Spell::CheckTarget( Unit* target, uint32 eff, bool hitPhase )              // all ok by some way or another, skip normal check              break;          default:                                            // normal case -            if(target!=m_caster && !target->IsWithinLOSInMap(m_caster)) +            // Get GO cast coordinates if original caster -> GO +            WorldObject *caster = NULL; +            if (m_originalCasterGUID) +                caster = ObjectAccessor::GetGameObject(*m_caster, m_originalCasterGUID); +            if (!caster) +                caster = m_caster; +            if(target!=m_caster && !target->IsWithinLOSInMap(caster))                  return false;              break;      } @@ -5154,9 +5404,8 @@ Unit* Spell::SelectMagnetTarget()          {              if(Unit* magnet = (*itr)->GetCaster())              { -                if((*itr)->m_procCharges>0) +                if((*itr)->DropAuraCharge())                  { -                    (*itr)->SetAuraProcCharges((*itr)->m_procCharges-1);                      target = magnet;                      m_targets.setUnitTarget(target);                      AddUnitTarget(target, 0); @@ -5180,7 +5429,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;  } @@ -5364,6 +5613,14 @@ void Spell::CalculateDamageDoneForAllTargets()          }      } +    bool usesAmmo=true; +    Unit::AuraList const& Auras = m_caster->GetAurasByType(SPELL_AURA_ABILITY_CONSUME_NO_AMMO); +    for(Unit::AuraList::const_iterator j = Auras.begin();j != Auras.end(); ++j) +    { +        if((*j)->isAffectedOnSpell(m_spellInfo)) +            usesAmmo=false; +    } +      for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)      {          TargetInfo &target = *ihit; @@ -5376,6 +5633,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 +5713,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; +}  | 
