aboutsummaryrefslogtreecommitdiff
path: root/src/game/Spell.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/Spell.cpp')
-rw-r--r--src/game/Spell.cpp2061
1 files changed, 1219 insertions, 842 deletions
diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp
index 1e9a2a7d60e..8291056e15f 100644
--- a/src/game/Spell.cpp
+++ b/src/game/Spell.cpp
@@ -1,7 +1,7 @@
/*
- * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
- * Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
+ * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,7 +35,6 @@
#include "Unit.h"
#include "Spell.h"
#include "DynamicObject.h"
-#include "SpellAuras.h"
#include "Group.h"
#include "UpdateData.h"
#include "MapManager.h"
@@ -43,14 +42,13 @@
#include "CellImpl.h"
#include "Policies/SingletonImp.h"
#include "SharedDefines.h"
-#include "Tools.h"
#include "LootMgr.h"
#include "VMapFactory.h"
#include "BattleGround.h"
#include "Util.h"
#include "TemporarySummon.h"
-#define SPELL_CHANNEL_UPDATE_INTERVAL 1000
+#define SPELL_CHANNEL_UPDATE_INTERVAL (1*IN_MILISECONDS)
extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS];
@@ -120,6 +118,14 @@ void SpellCastTargets::setDestination(Unit *target, bool send)
m_targetMask |= TARGET_FLAG_DEST_LOCATION;
}
+void SpellCastTargets::setSource(float x, float y, float z)
+{
+ m_srcX = x;
+ m_srcY = y;
+ m_srcZ = z;
+ m_targetMask |= TARGET_FLAG_SOURCE_LOCATION;
+}
+
void SpellCastTargets::setGOTarget(GameObject *target)
{
m_GOTarget = target;
@@ -171,6 +177,8 @@ bool SpellCastTargets::read ( WorldPacket * data, Unit *caster )
if(data->rpos()+4 > data->size())
return false;
+ //data->hexlike();
+
*data >> m_targetMask;
sLog.outDebug("Spell read, target mask = %u", m_targetMask);
@@ -178,19 +186,23 @@ bool SpellCastTargets::read ( WorldPacket * data, Unit *caster )
return true;
// TARGET_FLAG_UNK2 is used for non-combat pets, maybe other?
- if( m_targetMask & (TARGET_FLAG_UNIT|TARGET_FLAG_UNK2) )
- if(!readGUID(*data, m_unitTargetGUID))
+ if( m_targetMask & ( TARGET_FLAG_UNIT | TARGET_FLAG_UNK2 ))
+ if(!data->readPackGUID(m_unitTargetGUID))
return false;
- if( m_targetMask & ( TARGET_FLAG_OBJECT | TARGET_FLAG_OBJECT_UNK ))
- if(!readGUID(*data, m_GOTargetGUID))
+ if( m_targetMask & ( TARGET_FLAG_OBJECT ))
+ if(!data->readPackGUID(m_GOTargetGUID))
return false;
if(( m_targetMask & ( TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM )) && caster->GetTypeId() == TYPEID_PLAYER)
- if(!readGUID(*data, m_itemTargetGUID))
+ if(!data->readPackGUID(m_itemTargetGUID))
return false;
- /*if( m_targetMask & TARGET_FLAG_SOURCE_LOCATION )
+ if( m_targetMask & (TARGET_FLAG_CORPSE | TARGET_FLAG_PVP_CORPSE ) )
+ if(!data->readPackGUID(m_CorpseTargetGUID))
+ return false;
+
+ if( m_targetMask & TARGET_FLAG_SOURCE_LOCATION )
{
if(data->rpos()+4+4+4 > data->size())
return false;
@@ -198,17 +210,23 @@ bool SpellCastTargets::read ( WorldPacket * data, Unit *caster )
*data >> m_srcX >> m_srcY >> m_srcZ;
if(!Trinity::IsValidMapCoord(m_srcX, m_srcY, m_srcZ))
return false;
- }*/
- if( m_targetMask & (TARGET_FLAG_SOURCE_LOCATION | TARGET_FLAG_DEST_LOCATION) )
+ m_hasDest = true;
+ }
+
+ if( m_targetMask & TARGET_FLAG_DEST_LOCATION )
{
- if(data->rpos()+4+4+4 > data->size())
+ if(data->rpos()+1+4+4+4 > data->size())
+ return false;
+
+ if(!data->readPackGUID(m_unitTargetGUID))
return false;
*data >> m_destX >> m_destY >> m_destZ;
- m_hasDest = true;
if(!Trinity::IsValidMapCoord(m_destX, m_destY, m_destZ))
return false;
+
+ m_hasDest = true;
}
if( m_targetMask & TARGET_FLAG_STRING )
@@ -219,10 +237,6 @@ bool SpellCastTargets::read ( WorldPacket * data, Unit *caster )
*data >> m_strTarget;
}
- if( m_targetMask & (TARGET_FLAG_CORPSE | TARGET_FLAG_PVP_CORPSE ) )
- if(!readGUID(*data, m_CorpseTargetGUID))
- return false;
-
// find real units/GOs
Update(caster);
return true;
@@ -242,7 +256,7 @@ void SpellCastTargets::write ( WorldPacket * data )
else
*data << uint8(0);
}
- else if( m_targetMask & ( TARGET_FLAG_OBJECT | TARGET_FLAG_OBJECT_UNK ) )
+ else if( m_targetMask & TARGET_FLAG_OBJECT )
{
if(m_GOTarget)
data->append(m_GOTarget->GetPackGUID());
@@ -267,7 +281,14 @@ void SpellCastTargets::write ( WorldPacket * data )
*data << m_srcX << m_srcY << m_srcZ;
if( m_targetMask & TARGET_FLAG_DEST_LOCATION )
+ {
+ if(m_unitTarget)
+ data->append(m_unitTarget->GetPackGUID());
+ else
+ *data << uint8(0);
+
*data << m_destX << m_destY << m_destZ;
+ }
if( m_targetMask & TARGET_FLAG_STRING )
*data << m_strTarget;
@@ -305,7 +326,7 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi
break;
default:
// Wands
- if (m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_REQ_WAND)
+ if (m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG)
m_attackType = RANGED_ATTACK;
else
m_attackType = BASE_ATTACK;
@@ -320,7 +341,7 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi
if((m_caster->getClassMask() & CLASSMASK_WAND_USERS) != 0 && m_caster->GetTypeId()==TYPEID_PLAYER)
{
if(Item* pItem = ((Player*)m_caster)->GetWeaponForAttack(RANGED_ATTACK))
- m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetProto()->Damage->DamageType);
+ m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetProto()->Damage[0].DamageType);
}
}
// Set health leech amount to zero
@@ -355,14 +376,14 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi
gameObjTarget = NULL;
focusObject = NULL;
m_cast_count = 0;
+ m_glyphIndex = 0;
+ m_preCastSpell = 0;
m_triggeredByAuraSpell = NULL;
- //Auto Shot & Shoot
- if( m_spellInfo->AttributesEx2 == 0x000020 && !triggered )
- m_autoRepeat = true;
- else
- m_autoRepeat = false;
+ //Auto Shot & Shoot (wand)
+ m_autoRepeat = IsAutoRepeatRangedSpell(m_spellInfo);
+ m_runesState = 0;
m_powerCost = 0; // setup to correct value in Spell::prepare, don't must be used before.
m_casttime = 0; // setup to correct value in Spell::prepare, don't must be used before.
m_timer = 0; // will set to castime in prepare
@@ -372,7 +393,7 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi
// determine reflection
m_canReflect = false;
- if(m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && (m_spellInfo->AttributesEx2 & 0x4)==0)
+ if(m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && !(m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_CANT_REFLECTED))
{
for(int j=0;j<3;j++)
{
@@ -457,11 +478,11 @@ void Spell::FillTargetMap()
{
// non-standard target selection
SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex);
- float max_range = GetSpellMaxRange(srange);
+ float max_range = GetSpellMaxRangeForHostile(srange);
WorldObject* result = NULL;
Trinity::CannibalizeObjectCheck u_check(m_caster, max_range);
- Trinity::WorldObjectSearcher<Trinity::CannibalizeObjectCheck > searcher(result, u_check);
+ Trinity::WorldObjectSearcher<Trinity::CannibalizeObjectCheck > searcher(m_caster, result, u_check);
m_caster->VisitNearbyGridObject(max_range, searcher);
if(!result)
m_caster->VisitNearbyWorldObject(max_range, searcher);
@@ -511,12 +532,15 @@ void Spell::FillTargetMap()
case SPELL_EFFECT_CREATE_ITEM:
case SPELL_EFFECT_TRIGGER_SPELL:
case SPELL_EFFECT_SKILL_STEP:
+ case SPELL_EFFECT_PROFICIENCY:
+ case SPELL_EFFECT_SUMMON_OBJECT_WILD:
case SPELL_EFFECT_SELF_RESURRECT:
case SPELL_EFFECT_REPUTATION:
case SPELL_EFFECT_LEARN_SPELL:
if(m_targets.getUnitTarget())
tmpUnitMap.push_back(m_targets.getUnitTarget());
- else
+ // Triggered spells have additional spell targets - cast them even if no explicit unit target is given (required for spell 50516 for example)
+ else if(m_spellInfo->Effect[i] == SPELL_EFFECT_TRIGGER_SPELL)
tmpUnitMap.push_back(m_caster);
break;
case SPELL_EFFECT_SUMMON_PLAYER:
@@ -543,19 +567,22 @@ void Spell::FillTargetMap()
break;
case SPELL_EFFECT_SUMMON_CHANGE_ITEM:
case SPELL_EFFECT_ADD_FARSIGHT:
+ case SPELL_EFFECT_APPLY_GLYPH:
case SPELL_EFFECT_STUCK:
+ case SPELL_EFFECT_FEED_PET:
case SPELL_EFFECT_DESTROY_ALL_TOTEMS:
tmpUnitMap.push_back(m_caster);
break;
case SPELL_EFFECT_LEARN_PET_SPELL:
- if(Pet* pet = m_caster->GetPet())
+ if(Guardian* pet = m_caster->GetGuardianPet())
tmpUnitMap.push_back(pet);
break;
/*case SPELL_EFFECT_ENCHANT_ITEM:
case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY:
+ case SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC:
case SPELL_EFFECT_DISENCHANT:
- case SPELL_EFFECT_FEED_PET:
case SPELL_EFFECT_PROSPECTING:
+ case SPELL_EFFECT_MILLING:
if(m_targets.getItemTarget())
AddItemTarget(m_targets.getItemTarget(), i);
break;*/
@@ -596,13 +623,12 @@ void Spell::FillTargetMap()
}
}
-
if(IsChanneledSpell(m_spellInfo) && !tmpUnitMap.empty())
m_needAliveTargetMask |= (1<<i);
for (std::list<Unit*>::iterator itr = tmpUnitMap.begin() ; itr != tmpUnitMap.end();)
{
- if(!CheckTarget(*itr, i, false ))
+ if(!CheckTarget(*itr, i))
{
itr = tmpUnitMap.erase(itr);
continue;
@@ -633,37 +659,43 @@ void Spell::prepareDataForTriggerSystem()
// Ņan spell trigger another or not ( m_canTrigger )
// Create base triggers flags for Attacker and Victim ( m_procAttacker and m_procVictim)
//==========================================================================================
-
// Fill flag can spell trigger or not
- if (!m_IsTriggeredSpell)
+ // TODO: possible exist spell attribute for this
+ m_canTrigger = false;
+
+ if (m_CastItem)
+ m_canTrigger = false; // Do not trigger from item cast spell
+ else if (!m_IsTriggeredSpell)
m_canTrigger = true; // Normal cast - can trigger
else if (!m_triggeredByAuraSpell)
m_canTrigger = true; // Triggered from SPELL_EFFECT_TRIGGER_SPELL - can trigger
- else // Exceptions (some periodic triggers)
+
+ if (!m_canTrigger) // Exceptions (some periodic triggers)
{
- m_canTrigger = false; // Triggered spells can`t trigger another
switch (m_spellInfo->SpellFamilyName)
{
case SPELLFAMILY_MAGE: // Arcane Missles / Blizzard triggers need do it
- if (m_spellInfo->SpellFamilyFlags & 0x0000000000200080LL) m_canTrigger = true;
+ if (m_spellInfo->SpellFamilyFlags[0] & 0x200080) m_canTrigger = true;
break;
case SPELLFAMILY_WARLOCK: // For Hellfire Effect / Rain of Fire / Seed of Corruption triggers need do it
- if (m_spellInfo->SpellFamilyFlags & 0x0000800000000060LL) m_canTrigger = true;
+ if (m_spellInfo->SpellFamilyFlags[1] & 0x00008000 || m_spellInfo->SpellFamilyFlags[0] & 0x00000060) m_canTrigger = true;
+ break;
+ case SPELLFAMILY_PRIEST: // For Penance heal/damage triggers need do it
+ if (m_spellInfo->SpellFamilyFlags[1] & 0x00018000) m_canTrigger = true;
break;
- case SPELLFAMILY_HUNTER: // Hunter Explosive Trap Effect/Immolation Trap Effect/Frost Trap Aura/Snake Trap Effect
- if (m_spellInfo->SpellFamilyFlags & 0x0000200000000014LL) m_canTrigger = true;
+ case SPELLFAMILY_ROGUE: // For poisons need do it
+ if (m_spellInfo->SpellFamilyFlags[1] & 0x00000010 || m_spellInfo->SpellFamilyFlags[0] & 0x1001E000) m_canTrigger = true;
break;
- case SPELLFAMILY_PALADIN: // For Holy Shock triggers need do it
- if (m_spellInfo->SpellFamilyFlags & 0x0001000000200000LL) m_canTrigger = true;
+ case SPELLFAMILY_HUNTER: // Hunter Rapid Killing/Explosive Trap Effect/Immolation Trap Effect/Frost Trap Aura/Snake Trap Effect/Explosive Shot
+ if (m_spellInfo->SpellFamilyFlags[1] & 0x01002000
+ || m_spellInfo->SpellFamilyFlags[0] & 0x00000214 ||
+ m_spellInfo->SpellFamilyFlags[2] & 0x200) m_canTrigger = true;
break;
- case SPELLFAMILY_ROGUE: // mutilate mainhand + offhand
- if (m_spellInfo->SpellFamilyFlags & 0x600000000LL) m_canTrigger = true;
+ case SPELLFAMILY_PALADIN: // For Judgements (all) / Holy Shock triggers need do it
+ if (m_spellInfo->SpellFamilyFlags[1] & 0x00010009 || m_spellInfo->SpellFamilyFlags[0] & 0x00B80400) m_canTrigger = true;
break;
}
}
- // Do not trigger from item cast spell
- if (m_CastItem)
- m_canTrigger = false;
// Get data for type of attack and fill base info for trigger
switch (m_spellInfo->DmgClass)
@@ -673,21 +705,30 @@ void Spell::prepareDataForTriggerSystem()
m_procVictim = PROC_FLAG_TAKEN_MELEE_SPELL_HIT;
break;
case SPELL_DAMAGE_CLASS_RANGED:
- m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT;
- m_procVictim = PROC_FLAG_TAKEN_RANGED_SPELL_HIT;
+ // Auto attack
+ if (m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG)
+ {
+ m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_HIT;
+ m_procVictim = PROC_FLAG_TAKEN_RANGED_HIT;
+ }
+ else // Ranged spell attack
+ {
+ m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT;
+ m_procVictim = PROC_FLAG_TAKEN_RANGED_SPELL_HIT;
+ }
break;
default:
- if (IsPositiveSpell(m_spellInfo->Id)) // Check for positive spell
+ if (IsPositiveSpell(m_spellInfo->Id)) // Check for positive spell
{
m_procAttacker = PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL;
m_procVictim = PROC_FLAG_TAKEN_POSITIVE_SPELL;
}
- else if (m_spellInfo->Id == 5019) // Wands
+ else if (m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG) // Wands auto attack
{
- m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT;
- m_procVictim = PROC_FLAG_TAKEN_RANGED_SPELL_HIT;
+ m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_HIT;
+ m_procVictim = PROC_FLAG_TAKEN_RANGED_HIT;
}
- else
+ else // Negative spell
{
m_procAttacker = PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT;
m_procVictim = PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT;
@@ -696,7 +737,7 @@ void Spell::prepareDataForTriggerSystem()
}
// Hunter traps spells (for Entrapment trigger)
// Gives your Immolation Trap, Frost Trap, Explosive Trap, and Snake Trap ....
- if (m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && m_spellInfo->SpellFamilyFlags & 0x0000200000000014LL)
+ if (m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && (m_spellInfo->SpellFamilyFlags[1] & 0x00002000 || m_spellInfo->SpellFamilyFlags[0] & 0x1C))
m_procAttacker |= PROC_FLAG_ON_TRAP_ACTIVATION;
}
@@ -705,8 +746,6 @@ void Spell::CleanupTargetList()
m_UniqueTargetInfo.clear();
m_UniqueGOTargetInfo.clear();
m_UniqueItemInfo.clear();
- m_countOfHit = 0;
- m_countOfMiss = 0;
m_delayMoment = 0;
}
@@ -715,6 +754,9 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex)
if( m_spellInfo->Effect[effIndex]==0 )
return;
+ // Check for effect immune skip if immuned
+ bool immuned = pVictim->IsImmunedToSpellEffect(m_spellInfo, effIndex);
+
uint64 targetGUID = pVictim->GetGUID();
// Lookup target in already in list
@@ -722,7 +764,8 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex)
{
if (targetGUID == ihit->targetGUID) // Found in list
{
- ihit->effectMask |= 1<<effIndex; // Add only effect mask
+ if (!immuned)
+ ihit->effectMask |= 1<<effIndex; // Add only effect mask if not immuned
return;
}
}
@@ -732,7 +775,7 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex)
// Get spell hit result on target
TargetInfo target;
target.targetGUID = targetGUID; // Store target GUID
- target.effectMask = 1<<effIndex; // Store index of effect
+ target.effectMask = immuned ? 0 : 1<<effIndex; // Store index of effect if not immuned
target.processed = false; // Effects not apply on target
target.damage = 0;
@@ -746,11 +789,6 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex)
else
target.missCondition = SPELL_MISS_EVADE; //SPELL_MISS_NONE;
- if (target.missCondition == SPELL_MISS_NONE)
- ++m_countOfHit;
- else
- ++m_countOfMiss;
-
// Spell have speed - need calculate incoming time
if (m_spellInfo->speed > 0.0f)
{
@@ -831,8 +869,6 @@ void Spell::AddGOTarget(GameObject* pVictim, uint32 effIndex)
else
target.timeDelay = 0LL;
- ++m_countOfHit;
-
// Add target to list
m_UniqueGOTargetInfo.push_back(target);
}
@@ -866,83 +902,6 @@ void Spell::AddItemTarget(Item* pitem, uint32 effIndex)
target.effectMask = 1<<effIndex;
m_UniqueItemInfo.push_back(target);
}
-/*
-void Spell::doTriggers(SpellMissInfo missInfo, uint32 damage, SpellSchoolMask damageSchoolMask, uint32 block, uint32 absorb, bool crit)
-{
- // Do triggers depends from hit result (triggers on hit do in effects)
- // Set aura states depends from hit result
- if (missInfo!=SPELL_MISS_NONE)
- {
- // Miss/dodge/parry/block only for melee based spells
- // Resist only for magic based spells
- switch (missInfo)
- {
- case SPELL_MISS_MISS:
- if(m_caster->GetTypeId()== TYPEID_PLAYER)
- ((Player*)m_caster)->UpdateWeaponSkill(BASE_ATTACK);
-
- m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_MISS, m_spellInfo, m_IsTriggeredSpell);
- break;
- case SPELL_MISS_RESIST:
- m_caster->ProcDamageAndSpell(unitTarget, PROC_FLAG_TARGET_RESISTS, PROC_FLAG_RESIST_SPELL, 0, damageSchoolMask, m_spellInfo, m_IsTriggeredSpell);
- break;
- case SPELL_MISS_DODGE:
- if(unitTarget->GetTypeId() == TYPEID_PLAYER)
- ((Player*)unitTarget)->UpdateDefense();
-
- // Overpower
- if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->getClass() == CLASS_WARRIOR)
- {
- ((Player*) m_caster)->AddComboPoints(unitTarget, 1);
- m_caster->StartReactiveTimer( REACTIVE_OVERPOWER );
- }
-
- // Riposte
- if (unitTarget->getClass() != CLASS_ROGUE)
- {
- unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true);
- unitTarget->StartReactiveTimer( REACTIVE_DEFENSE );
- }
-
- m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_DODGE, m_spellInfo, m_IsTriggeredSpell);
- break;
- case SPELL_MISS_PARRY:
- // Update victim defense ?
- if(unitTarget->GetTypeId() == TYPEID_PLAYER)
- ((Player*)unitTarget)->UpdateDefense();
- // Mongoose bite - set only Counterattack here
- if (unitTarget->getClass() == CLASS_HUNTER)
- {
- unitTarget->ModifyAuraState(AURA_STATE_HUNTER_PARRY,true);
- unitTarget->StartReactiveTimer( REACTIVE_HUNTER_PARRY );
- }
- else
- {
- unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true);
- unitTarget->StartReactiveTimer( REACTIVE_DEFENSE );
- }
- m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_PARRY, m_spellInfo, m_IsTriggeredSpell);
- break;
- case SPELL_MISS_BLOCK:
- unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true);
- unitTarget->StartReactiveTimer( REACTIVE_DEFENSE );
-
- m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_BLOCK, m_spellInfo, m_IsTriggeredSpell);
- break;
- // Trigger from this events not supported
- case SPELL_MISS_EVADE:
- case SPELL_MISS_IMMUNE:
- case SPELL_MISS_IMMUNE2:
- case SPELL_MISS_DEFLECT:
- case SPELL_MISS_ABSORB:
- // Trigger from reflects need do after get reflect result
- case SPELL_MISS_REFLECT:
- break;
- default:
- break;
- }
- }
-}*/
void Spell::DoAllEffectOnTarget(TargetInfo *target)
{
@@ -952,15 +911,13 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target)
// Get mask of effects for target
uint32 mask = target->effectMask;
- if (mask == 0) // No effects
- return;
Unit* unit = m_caster->GetGUID()==target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster,target->targetGUID);
if (!unit)
return;
// Get original caster (if exist) and calculate damage/healing from him data
- Unit *caster = m_originalCasterGUID ? m_originalCaster : m_caster;
+ Unit *caster = m_originalCaster ? m_originalCaster : m_caster;
// Skip if m_originalCaster not avaiable
if (!caster)
@@ -1008,7 +965,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target)
if (crit)
{
procEx |= PROC_EX_CRITICAL_HIT;
- addhealth = caster->SpellCriticalBonus(m_spellInfo, addhealth, NULL);
+ addhealth = caster->SpellCriticalHealingBonus(m_spellInfo, addhealth, NULL);
}
else
procEx |= PROC_EX_NORMAL_HIT;
@@ -1047,44 +1004,12 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target)
caster->DealSpellDamage(&damageInfo, true);
- // Shadow Word: Death - deals damage equal to damage done to caster if victim is not killed
- if (m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST && m_spellInfo->SpellFamilyFlags&0x0000000200000000LL &&
- caster != unitTarget && unitTarget->isAlive())
- {
- // Redirect damage to caster if victim Alive
- damageInfo.target = caster;
- damageInfo.absorb = 0;
- damageInfo.resist = 0;
- damageInfo.blocked = 0;
- // Send log damage message to client
- caster->SendSpellNonMeleeDamageLog(&damageInfo);
- caster->DealSpellDamage(&damageInfo, true);
- }
// Judgement of Blood
- else if (m_spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN && m_spellInfo->SpellFamilyFlags & 0x0000000800000000LL && m_spellInfo->SpellIconID==153)
+ if (m_spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN && m_spellInfo->SpellFamilyFlags[1] & 0x00000008 && m_spellInfo->SpellIconID==153)
{
int32 damagePoint = damageInfo.damage * 33 / 100;
m_caster->CastCustomSpell(m_caster, 32220, &damagePoint, NULL, NULL, true);
}
- // Bloodthirst
- else if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR && m_spellInfo->SpellFamilyFlags & 0x40000000000LL)
- {
- uint32 BTAura = 0;
- switch(m_spellInfo->Id)
- {
- case 23881: BTAura = 23885; break;
- case 23892: BTAura = 23886; break;
- case 23893: BTAura = 23887; break;
- case 23894: BTAura = 23888; break;
- case 25251: BTAura = 25252; break;
- case 30335: BTAura = 30339; break;
- default:
- sLog.outError("Spell::EffectSchoolDMG: Spell %u not handled in BTAura",m_spellInfo->Id);
- break;
- }
- if (BTAura)
- m_caster->CastSpell(m_caster,BTAura,true);
- }
}
// Passive spell hits/misses or active spells only misses (only triggers)
else
@@ -1097,6 +1022,14 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target)
caster->ProcDamageAndSpell(unit, procAttacker, procVictim, procEx, 0, m_attackType, m_spellInfo);
}
+ // Take combo points after effects handling (combo points are used in effect handling)
+ if(!m_IsTriggeredSpell && !m_CastItem
+ && NeedsComboPoints(m_spellInfo)
+ && m_caster->GetTypeId()==TYPEID_PLAYER
+ && target->targetGUID == m_targets.getUnitTargetGUID()
+ && (missInfo == SPELL_MISS_NONE || missInfo == SPELL_MISS_MISS))
+ ((Player*)m_caster)->ClearComboPoints();
+
// Call scripted function for AI if this spell is casted upon a creature (except pets)
if(IS_CREATURE_GUID(target->targetGUID))
{
@@ -1108,14 +1041,14 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target)
if( !m_caster->IsFriendlyTo(unit) && !IsPositiveSpell(m_spellInfo->Id))
{
- if( !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) )
+ if( !(m_spellInfo->AttributesEx & SPELL_ATTR_EX_NO_INITIAL_AGGRO) )
{
m_caster->CombatStart(unit);
}
else if(m_customAttr & SPELL_ATTR_CU_AURA_CC)
{
if(!unit->IsStandState())
- unit->SetStandState(PLAYER_STATE_NONE);
+ unit->SetStandState(UNIT_STAND_STATE_STAND);
}
}
}
@@ -1128,25 +1061,37 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask)
// Recheck immune (only for delayed spells)
if( m_spellInfo->speed &&
!(m_spellInfo->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY)
- && (unit->IsImmunedToDamage(GetSpellSchoolMask(m_spellInfo),true) ||
- unit->IsImmunedToSpell(m_spellInfo,true) ))
+ && (unit->IsImmunedToDamage(GetSpellSchoolMask(m_spellInfo)) ||
+ unit->IsImmunedToSpell(m_spellInfo)))
{
m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_IMMUNE);
m_damage = 0;
return;
}
+ if (unit->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Player*)unit)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, m_spellInfo->Id);
+ ((Player*)unit)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2, m_spellInfo->Id);
+ }
+
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2, m_spellInfo->Id, 0, unit);
+ }
+
if( m_caster != unit )
{
- if (unit->GetCharmerOrOwnerGUID() != m_caster->GetGUID())
+ // Recheck UNIT_FLAG_NON_ATTACKABLE for delayed spells
+ if (m_spellInfo->speed > 0.0f &&
+ unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE) &&
+ unit->GetCharmerOrOwnerGUID() != m_caster->GetGUID())
{
- if (unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
- {
- m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE);
+ m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE);
m_damage = 0;
- return;
- }
+ return;
}
+
if( !m_caster->IsFriendlyTo(unit) )
{
// for delayed spells ignore not visible explicit target
@@ -1178,7 +1123,7 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask)
m_caster->SetContestedPvP();
//m_caster->UpdatePvP(true);
}
- if( unit->isInCombat() && !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) )
+ if( unit->isInCombat() && !(m_spellInfo->AttributesEx & SPELL_ATTR_EX_NO_INITIAL_AGGRO) )
{
m_caster->SetInCombatState(unit->GetCombatTimer() > 0);
unit->getHostilRefManager().threatAssist(m_caster, 0.0f);
@@ -1196,9 +1141,60 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask)
unit->IncrDiminishing(m_diminishGroup);
}
+ // Apply additional spell effects to target
+ if (m_preCastSpell)
+ {
+ // Special spell id
+ // TODO: Handle all of special spells in one place?
+ if(m_preCastSpell==61988)
+ {
+ //Cast Forbearance
+ m_caster->CastSpell(unit,25771, true, m_CastItem);
+ // Cast Avenging Wrath Marker
+ m_caster->CastSpell(unit,61987, true, m_CastItem);
+ }
+ else
+ m_caster->CastSpell(unit,m_preCastSpell, true, m_CastItem);
+ }
+ uint8 t_effmask=0;
+ for (uint8 i=0;i<3;++i)
+ if (effectMask & (1<<i) && (m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA || IsAreaAuraEffect(m_spellInfo->Effect[i])))
+ t_effmask |=1<<i;
+
+ if (t_effmask)
+ {
+ Unit * caster = m_originalCaster ? m_originalCaster : m_caster;
+ Aura * Aur= new Aura(m_spellInfo, t_effmask, &m_currentBasePoints[0], unit, caster , m_CastItem);
+
+ if (!Aur->IsAreaAura())
+ {
+ // Now Reduce spell duration using data received at spell hit
+ int32 duration = Aur->GetAuraMaxDuration();
+ unit->ApplyDiminishingToDuration(m_diminishGroup,duration,caster,m_diminishLevel);
+ Aur->setDiminishGroup(m_diminishGroup);
+
+ duration = caster->ModSpellDuration(m_spellInfo, unit, duration, Aur->IsPositive());
+
+ //mod duration of channeled aura by spell haste
+ if (IsChanneledSpell(m_spellInfo))
+ caster->ModSpellCastTime(m_spellInfo, duration, this);
+
+ if(duration != Aur->GetAuraMaxDuration())
+ {
+ Aur->SetAuraMaxDuration(duration);
+ Aur->SetAuraDuration(duration);
+ }
+
+ // Prayer of Mending (jump animation), we need formal caster instead original for correct animation
+ if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST && (m_spellInfo->SpellFamilyFlags[1] & 0x000020))
+ m_caster->CastSpell(unit, 41637, true, NULL, NULL, m_originalCasterGUID);
+ }
+ unit->AddAura(Aur);
+ }
+ t_effmask = effectMask& ~t_effmask;
for(uint32 effectNumber=0;effectNumber<3;effectNumber++)
{
- if (effectMask & (1<<effectNumber))
+ if (t_effmask & (1<<effectNumber))
{
HandleEffects(unit,NULL,NULL,effectNumber/*,m_damageMultipliers[effectNumber]*/);
//Only damage and heal spells need this
@@ -1226,20 +1222,23 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask)
int _duration=0;
for(ChanceTriggerSpells::const_iterator i = m_ChanceTriggerSpells.begin(); i != m_ChanceTriggerSpells.end(); ++i)
{
+ // SPELL_AURA_ADD_TARGET_TRIGGER auras shouldn't trigger auras without duration
+ // set duration equal to triggering spell
if(roll_chance_i(i->second))
{
m_caster->CastSpell(unit, i->first, true);
- // SPELL_AURA_ADD_TARGET_TRIGGER auras shouldn't trigger auras without duration
- // set duration equal to triggering spell
- if (GetSpellDuration(i->first)==-1)
+ }
+ if (GetSpellDuration(i->first)==-1)
+ {
+ if (Aura * triggeredAur = unit->GetAura(i->first->Id, m_caster->GetGUID()))
{
// get duration from aura-only once
if (!_duration)
{
- Aura * aur = unit->GetAuraByCasterSpell(m_spellInfo->Id, m_caster->GetGUID());
+ Aura * aur = unit->GetAura(m_spellInfo->Id, m_caster->GetGUID());
_duration = aur ? aur->GetAuraDuration() : -1;
}
- unit->SetAurasDurationByCasterSpell(i->first->Id, m_caster->GetGUID(), _duration);
+ triggeredAur->SetAuraDuration(_duration);
}
}
}
@@ -1373,6 +1372,16 @@ void Spell::SearchChainTarget(std::list<Unit*> &TagUnitMap, float max_range, uin
if(!cur)
return;
+ // Get spell max affected targets
+ /*uint32 unMaxTargets = m_spellInfo->MaxAffectedTargets;
+ Unit::AuraList const& mod = m_caster->GetAurasByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS);
+ for(Unit::AuraList::const_iterator m = mod.begin(); m != mod.end(); ++m)
+ {
+ if (!(*m)->isAffectedOnSpell(m_spellInfo))
+ continue;
+ unMaxTargets+=(*m)->GetAmount();
+ }*/
+
//FIXME: This very like horrible hack and wrong for most spells
if(m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_MELEE)
max_range += num * CHAIN_SPELL_JUMP_RADIUS;
@@ -1484,7 +1493,7 @@ Unit* Spell::SearchNearbyTarget(float radius, SpellTargets TargetType, uint32 en
{
Creature* target = NULL;
Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster, entry, true, radius);
- Trinity::CreatureLastSearcher<Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(target, u_check);
+ Trinity::CreatureLastSearcher<Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(m_caster, target, u_check);
m_caster->VisitNearbyObject(radius, searcher);
return target;
}
@@ -1498,13 +1507,13 @@ Unit* Spell::SearchNearbyTarget(float radius, SpellTargets TargetType, uint32 en
case SPELL_TARGETS_ENEMY:
{
Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(m_caster, m_caster, radius);
- Trinity::UnitLastSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(target, u_check);
+ Trinity::UnitLastSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(m_caster, target, u_check);
m_caster->VisitNearbyObject(radius, searcher);
}break;
case SPELL_TARGETS_ALLY:
{
Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(m_caster, m_caster, radius);
- Trinity::UnitLastSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(target, u_check);
+ Trinity::UnitLastSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(m_caster, target, u_check);
m_caster->VisitNearbyObject(radius, searcher);
}break;
}
@@ -1513,21 +1522,35 @@ Unit* Spell::SearchNearbyTarget(float radius, SpellTargets TargetType, uint32 en
void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
{
- float radius;
+ float radius_h, radius_f;
if (m_spellInfo->EffectRadiusIndex[i])
- radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+ {
+ radius_h = GetSpellRadiusForHostile(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+ radius_f = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+ }
else
- radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex));
+ {
+ radius_h = GetSpellMaxRangeForHostile(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex));
+ radius_f = GetSpellMaxRangeForFriend(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex));
+ }
//Chain: 2, 6, 22, 25, 45, 77
uint32 EffectChainTarget = m_spellInfo->EffectChainTarget[i];
uint32 unMaxTargets = m_spellInfo->MaxAffectedTargets;
+ Unit::AuraEffectList const& Auras = m_caster->GetAurasByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS);
+ for(Unit::AuraEffectList::const_iterator j = Auras.begin();j != Auras.end(); ++j)
+ {
+ if((*j)->isAffectedOnSpell(m_spellInfo))
+ unMaxTargets+=(*j)->GetAmount();
+ }
+
if(m_originalCaster)
{
if(Player* modOwner = m_originalCaster->GetSpellModOwner())
{
- modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius,this);
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius_f,this);
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius_h,this);
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, EffectChainTarget, this);
}
}
@@ -1554,17 +1577,17 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
TagUnitMap.push_back(owner);
break;
case TARGET_UNIT_PET:
- if(Pet* pet = m_caster->GetPet())
+ if(Guardian* pet = m_caster->GetGuardianPet())
TagUnitMap.push_back(pet);
break;
case TARGET_UNIT_PARTY_CASTER:
- m_caster->GetPartyMember(TagUnitMap, radius);
+ m_caster->GetPartyMember(TagUnitMap, radius_f);
break;
case TARGET_UNIT_RAID:
- if(Unit *target = m_targets.getUnitTarget())
- TagUnitMap.push_back(target);
- else
- m_caster->GetRaidMember(TagUnitMap, radius);
+ //if(Unit *target = m_targets.getUnitTarget())
+ // TagUnitMap.push_back(target);
+ //else
+ m_caster->GetRaidMember(TagUnitMap, radius_f);
break;
}
}break;
@@ -1581,30 +1604,29 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
switch(cur)
{
case TARGET_UNIT_MINIPET:
- if( target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->isPet() && ((Pet*)target)->getPetType() == MINI_PET)
+ if(target->GetGUID() == m_caster->m_SummonSlot[4])
TagUnitMap.push_back(target);
break;
case TARGET_UNIT_TARGET_ALLY:
case TARGET_UNIT_TARGET_RAID:
case TARGET_UNIT_TARGET_ANY: // SelectMagnetTarget()?
case TARGET_UNIT_TARGET_PARTY:
- case TARGET_UNIT_SINGLE_UNKNOWN:
TagUnitMap.push_back(target);
break;
case TARGET_UNIT_PARTY_TARGET:
- target->GetPartyMember(TagUnitMap, radius);
+ target->GetPartyMember(TagUnitMap, radius_f);
break;
case TARGET_UNIT_TARGET_ENEMY:
if(EffectChainTarget <= 1)
TagUnitMap.push_back(SelectMagnetTarget());
else if(SelectMagnetTarget()) //TODO: chain target should also use magnet target
- SearchChainTarget(TagUnitMap, radius, EffectChainTarget, SPELL_TARGETS_ENEMY);
+ SearchChainTarget(TagUnitMap, radius_h, EffectChainTarget, SPELL_TARGETS_ENEMY);
break;
case TARGET_UNIT_CHAINHEAL:
if(EffectChainTarget <= 1)
TagUnitMap.push_back(target);
else
- SearchChainTarget(TagUnitMap, radius, EffectChainTarget, SPELL_TARGETS_CHAINHEAL);
+ SearchChainTarget(TagUnitMap, radius_f, EffectChainTarget, SPELL_TARGETS_CHAINHEAL);
break;
}
}break;
@@ -1651,17 +1673,17 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
case TARGET_UNIT_AREA_ENEMY_GROUND:
m_targets.m_targetMask |= TARGET_FLAG_DEST_LOCATION;
case TARGET_UNIT_AREA_ENEMY:
- SearchAreaTarget(TagUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_ENEMY);
+ SearchAreaTarget(TagUnitMap, radius_h, PUSH_DEST_CENTER, SPELL_TARGETS_ENEMY);
break;
case TARGET_UNIT_AREA_ALLY_GROUND:
m_targets.m_targetMask |= TARGET_FLAG_DEST_LOCATION;
case TARGET_UNIT_AREA_ALLY:
- SearchAreaTarget(TagUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_ALLY);
+ SearchAreaTarget(TagUnitMap, radius_f, PUSH_DEST_CENTER, SPELL_TARGETS_ALLY);
break;
case TARGET_UNIT_AREA_PARTY_GROUND:
m_targets.m_targetMask |= TARGET_FLAG_DEST_LOCATION;
case TARGET_UNIT_AREA_PARTY:
- m_caster->GetPartyMember(TagUnitMap, radius);
+ m_caster->GetPartyMember(TagUnitMap, radius_f);
break;
case TARGET_UNIT_AREA_ENTRY_GROUND:
m_targets.m_targetMask |= TARGET_FLAG_DEST_LOCATION;
@@ -1671,7 +1693,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(m_spellInfo->Id);
if(lower==upper)
{
- SearchAreaTarget(TagUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_ENEMY);
+ SearchAreaTarget(TagUnitMap, radius_h, PUSH_DEST_CENTER, SPELL_TARGETS_ENEMY);
sLog.outErrorDb("Spell (ID: %u) (caster Entry: %u) does not have record in `spell_script_target`", m_spellInfo->Id, m_caster->GetEntry());
break;
}
@@ -1683,7 +1705,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
sLog.outError( "SPELL: spell ID %u requires non-creature target\n", m_spellInfo->Id );
continue;
}
- SearchAreaTarget(TagUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_ENTRY, i_spellST->second.targetEntry);
+ SearchAreaTarget(TagUnitMap, radius_f, PUSH_DEST_CENTER, SPELL_TARGETS_ENTRY, i_spellST->second.targetEntry);
}
}
break;
@@ -1727,41 +1749,41 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
m_targets.setDestination(((Player*)m_caster)->m_homebindX,((Player*)m_caster)->m_homebindY,((Player*)m_caster)->m_homebindZ, true, ((Player*)m_caster)->m_homebindMapId);
break;
-
case TARGET_IN_FRONT_OF_CASTER:
case TARGET_UNIT_CONE_ENEMY_UNKNOWN:
if(m_customAttr & SPELL_ATTR_CU_CONE_BACK)
- SearchAreaTarget(TagUnitMap, radius, PUSH_IN_BACK, SPELL_TARGETS_ENEMY);
+ SearchAreaTarget(TagUnitMap, radius_h, PUSH_IN_BACK, SPELL_TARGETS_ENEMY);
else if(m_customAttr & SPELL_ATTR_CU_CONE_LINE)
- SearchAreaTarget(TagUnitMap, radius, PUSH_IN_LINE, SPELL_TARGETS_ENEMY);
+ SearchAreaTarget(TagUnitMap, radius_h, PUSH_IN_LINE, SPELL_TARGETS_ENEMY);
else
- SearchAreaTarget(TagUnitMap, radius, PUSH_IN_FRONT, SPELL_TARGETS_ENEMY);
+ SearchAreaTarget(TagUnitMap, radius_h, PUSH_IN_FRONT, SPELL_TARGETS_ENEMY);
break;
case TARGET_UNIT_CONE_ALLY:
- SearchAreaTarget(TagUnitMap, radius, PUSH_IN_FRONT, SPELL_TARGETS_ALLY);
+ SearchAreaTarget(TagUnitMap, radius_f, PUSH_IN_FRONT, SPELL_TARGETS_ALLY);
break;
// nearby target
case TARGET_UNIT_NEARBY_ALLY:
+ case TARGET_UNIT_NEARBY_ALLY_UNK:
if(!m_targets.getUnitTarget())
- m_targets.setUnitTarget(SearchNearbyTarget(radius, SPELL_TARGETS_ALLY));
+ m_targets.setUnitTarget(SearchNearbyTarget(radius_f, SPELL_TARGETS_ALLY));
if(m_targets.getUnitTarget())
{
if(EffectChainTarget <= 1)
TagUnitMap.push_back(m_targets.getUnitTarget());
else
- SearchChainTarget(TagUnitMap, radius, EffectChainTarget, SPELL_TARGETS_ALLY);
+ SearchChainTarget(TagUnitMap, radius_f, EffectChainTarget, SPELL_TARGETS_ALLY);
}
break;
- case TARGET_RANDOM_ENEMY_CHAIN_IN_AREA:
+ case TARGET_UNIT_NEARBY_ENEMY:
if(!m_targets.getUnitTarget())
- m_targets.setUnitTarget(SearchNearbyTarget(radius, SPELL_TARGETS_ENEMY));
+ m_targets.setUnitTarget(SearchNearbyTarget(radius_h, SPELL_TARGETS_ENEMY));
if(m_targets.getUnitTarget())
{
if(EffectChainTarget <= 1)
TagUnitMap.push_back(m_targets.getUnitTarget());
else
- SearchChainTarget(TagUnitMap, radius, EffectChainTarget, SPELL_TARGETS_ENEMY);
+ SearchChainTarget(TagUnitMap, radius_h, EffectChainTarget, SPELL_TARGETS_ENEMY);
}
break;
case TARGET_SCRIPT:
@@ -1778,7 +1800,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
}
SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex);
- float range = GetSpellMaxRange(srange);
+ float range = GetSpellMaxRangeForHostile(srange);
Creature* creatureScriptTarget = NULL;
GameObject* goScriptTarget = NULL;
@@ -1794,7 +1816,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
if(i_spellST->second.targetEntry)
{
Trinity::NearestGameObjectEntryInObjectRangeCheck go_check(*m_caster,i_spellST->second.targetEntry,range);
- Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck> checker(p_GameObject,go_check);
+ Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck> checker(m_caster, p_GameObject,go_check);
m_caster->VisitNearbyGridObject(range, checker);
if(p_GameObject)
@@ -1824,7 +1846,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
Creature *p_Creature = NULL;
Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster,i_spellST->second.targetEntry,i_spellST->second.type!=SPELL_TARGET_TYPE_DEAD,range);
- Trinity::CreatureLastSearcher<Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(p_Creature, u_check);
+ Trinity::CreatureLastSearcher<Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(m_caster, p_Creature, u_check);
m_caster->VisitNearbyObject(range, searcher);
if(p_Creature )
@@ -1873,7 +1895,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
Player* Target = itr->getSource();
// IsHostileTo check duel and controlled by enemy
- if( Target && targetPlayer->IsWithinDistInMap(Target, radius) &&
+ if( Target && targetPlayer->IsWithinDistInMap(Target, radius_f) &&
targetPlayer->getClass() == Target->getClass() &&
!m_caster->IsHostileTo(Target) )
{
@@ -1904,7 +1926,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
float x, y, z, angle, dist;
float objSize = m_caster->GetObjectSize();
- dist = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+ dist = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
if(dist < objSize)
dist = objSize;
else if(cur == TARGET_DEST_CASTER_RANDOM)
@@ -1947,7 +1969,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
float x, y, z, angle, dist;
float objSize = target->GetObjectSize();
- dist = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+ dist = target->GetSpellRadiusForTarget(target, sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
if(dist < objSize)
dist = objSize;
else if(cur == TARGET_DEST_CASTER_RANDOM)
@@ -1983,7 +2005,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
float x, y, z, angle, dist;
- dist = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+ dist = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
if (cur == TARGET_DEST_DEST_RANDOM)
dist *= rand_norm();
@@ -2024,10 +2046,71 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
advance(itr, urand(0, TagUnitMap.size() - 1));
TagUnitMap.erase(itr);
}
+
+ /*if(m_spellInfo->Id==57669) //Replenishment (special target selection)
+ {
+ if(pGroup)
+ {
+ typedef std::priority_queue<PrioritizeManaPlayerWraper, std::vector<PrioritizeManaPlayerWraper>, PrioritizeMana> Top10;
+ Top10 manaUsers;
+
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL && manaUsers.size() < 10; itr = itr->next())
+ {
+ Player* Target = itr->getSource();
+ if (m_caster->GetGUID() != Target->GetGUID() && Target->getPowerType() == POWER_MANA &&
+ !Target->isDead() && m_caster->IsWithinDistInMap(Target, radius))
+ {
+ PrioritizeManaPlayerWraper WTarget(Target);
+ manaUsers.push(WTarget);
+ }
+ }
+
+ while(!manaUsers.empty())
+ {
+ TagUnitMap.push_back(manaUsers.top().getPlayer());
+ manaUsers.pop();
+ }
+ }
+ else
+ {
+ Unit* ownerOrSelf = pTarget ? pTarget : m_caster->GetCharmerOrOwnerOrSelf();
+ if ((ownerOrSelf==m_caster || m_caster->IsWithinDistInMap(ownerOrSelf, radius)) &&
+ ownerOrSelf->getPowerType() == POWER_MANA)
+ TagUnitMap.push_back(ownerOrSelf);
+
+ if(Pet* pet = ownerOrSelf->GetGuardianPet())
+ if( m_caster->IsWithinDistInMap(pet, radius) && pet->getPowerType() == POWER_MANA )
+ TagUnitMap.push_back(pet);
+ }
+ }*/
}
}
-void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura)
+class PrioritizeManaPlayerWraper
+{
+ friend struct PrioritizeMana;
+
+ public:
+ explicit PrioritizeManaPlayerWraper(Player* player) : player(player)
+ {
+ uint32 maxmana = player->GetMaxPower(POWER_MANA);
+ percentMana = maxmana ? player->GetPower(POWER_MANA) * 100 / maxmana : 101;
+ }
+ Player* getPlayer() const { return player; }
+ private:
+ Player* player;
+ uint32 percentMana;
+};
+
+struct PrioritizeMana
+{
+ int operator()( PrioritizeManaPlayerWraper const& x, PrioritizeManaPlayerWraper const& y ) const
+ {
+ return x.percentMana < y.percentMana;
+ }
+};
+
+void Spell::prepare(SpellCastTargets const* targets, AuraEffect* triggeredByAura)
{
if(m_CastItem)
m_castItemGUID = m_CastItem->GetGUID();
@@ -2036,6 +2119,13 @@ void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura)
m_targets = *targets;
+ if(!m_targets.getUnitTargetGUID() && m_spellInfo->Targets & TARGET_FLAG_UNIT)
+ {
+ if(Unit *target = ObjectAccessor::GetUnit(*m_caster, m_caster->GetUInt64Value(UNIT_FIELD_TARGET)))
+ if(IsValidSingleTargetSpell(target))
+ m_targets.setUnitTarget(target);
+ }
+
m_spellState = SPELL_STATE_PREPARING;
m_caster->GetPosition(m_castPositionX, m_castPositionY, m_castPositionZ);
@@ -2049,7 +2139,7 @@ void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura)
m_caster->m_Events.AddEvent(Event, m_caster->m_Events.CalculateTime(1));
//Prevent casting at cast another spell (ServerSide check)
- if(m_caster->IsNonMeleeSpellCasted(false, true) && m_cast_count)
+ if(m_caster->IsNonMeleeSpellCasted(false, true, true) && m_cast_count)
{
SendCastResult(SPELL_FAILED_SPELL_IN_PROGRESS);
finish(false);
@@ -2086,13 +2176,13 @@ void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura)
// Fill cost data
m_powerCost = CalculatePowerCost();
- uint8 result = CanCast(true);
- if(result != 0 && !IsAutoRepeat()) //always cast autorepeat dummy for triggering
+ SpellCastResult result = CheckCast(true);
+ if(result != SPELL_CAST_OK && !IsAutoRepeat()) //always cast autorepeat dummy for triggering
{
if(triggeredByAura)
{
SendChannelUpdate(0);
- triggeredByAura->SetAuraDuration(0);
+ triggeredByAura->GetParentAura()->SetAuraDuration(0);
}
SendCastResult(result);
finish(false);
@@ -2102,13 +2192,14 @@ void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura)
// Prepare data for triggers
prepareDataForTriggerSystem();
- // calculate cast time (calculated after first CanCast check to prevent charge counting for first CanCast fail)
+ // calculate cast time (calculated after first CheckCast check to prevent charge counting for first CheckCast fail)
m_casttime = GetSpellCastTime(m_spellInfo, this);
// set timer base at cast time
ReSetTimer();
-
- if(m_IsTriggeredSpell)
+ //Containers for channeled spells have to be set
+ //TODO:Apply this to all casted spells if needed
+ if(m_IsTriggeredSpell && !IsChanneledSpell(m_spellInfo))
cast(true);
else
{
@@ -2154,11 +2245,10 @@ void Spell::cancel()
{
Unit* unit = m_caster->GetGUID()==(*ihit).targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID);
if( unit && unit->isAlive() )
- unit->RemoveAurasByCasterSpell(m_spellInfo->Id, m_caster->GetGUID());
+ unit->RemoveAurasDueToSpell(m_spellInfo->Id, m_caster->GetGUID(), AURA_REMOVE_BY_CANCEL);
}
}
-
- m_caster->RemoveAurasByCasterSpell(m_spellInfo->Id, m_caster->GetGUID());
+ m_caster->RemoveAurasDueToSpell(m_spellInfo->Id, m_caster->GetGUID(), AURA_REMOVE_BY_CANCEL);
SendChannelUpdate(0);
SendInterrupted(0);
SendCastResult(SPELL_FAILED_INTERRUPTED);
@@ -2182,8 +2272,6 @@ void Spell::cast(bool skipCheck)
{
SetExecutedCurrently(true);
- uint8 castResult = 0;
-
// update pointers base at GUIDs to prevent access to non-existed already object
UpdatePointers();
@@ -2198,23 +2286,12 @@ void Spell::cast(bool skipCheck)
if(m_caster->GetTypeId() != TYPEID_PLAYER && m_targets.getUnitTarget() && m_targets.getUnitTarget() != m_caster)
m_caster->SetInFront(m_targets.getUnitTarget());
- if(!m_IsTriggeredSpell)
- {
- castResult = CheckPower();
- if(castResult != 0)
- {
- SendCastResult(castResult);
- finish(false);
- SetExecutedCurrently(false);
- return;
- }
- }
// triggered cast called from Spell::prepare where it was already checked
- if(!skipCheck)
+ if(!m_IsTriggeredSpell || !skipCheck)
{
- castResult = CanCast(false);
- if(castResult != 0)
+ SpellCastResult castResult = CheckCast(false);
+ if(castResult != SPELL_CAST_OK)
{
SendCastResult(castResult);
finish(false);
@@ -2225,48 +2302,70 @@ void Spell::cast(bool skipCheck)
FillTargetMap();
- if(m_spellState == SPELL_STATE_FINISHED) // stop cast if spell marked as finish somewhere in Take*/FillTargetMap
+ if(m_spellInfo->SpellFamilyName)
{
- SetExecutedCurrently(false);
- return;
+ if (m_spellInfo->excludeCasterAuraSpell && !IsPositiveSpell(m_spellInfo->excludeCasterAuraSpell))
+ m_preCastSpell = m_spellInfo->excludeCasterAuraSpell;
+ else if (m_spellInfo->excludeTargetAuraSpell && !IsPositiveSpell(m_spellInfo->excludeTargetAuraSpell))
+ m_preCastSpell = m_spellInfo->excludeTargetAuraSpell;
+ }
+ switch (m_spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ {
+ if (m_spellInfo->Mechanic == MECHANIC_BANDAGE) // Bandages
+ m_preCastSpell = 11196; // Recently Bandaged
+ else if(m_spellInfo->SpellIconID == 1662 && m_spellInfo->AttributesEx & 0x20)
+ m_preCastSpell = 23230; // Blood Fury - Healing Reduction
+ break;
+ }
}
-
// traded items have trade slot instead of guid in m_itemTargetGUID
// set to real guid to be sent later to the client
m_targets.updateTradeSlotItem();
- if(!m_IsTriggeredSpell)
+ if (m_caster->GetTypeId() == TYPEID_PLAYER)
{
- //TakePower();
- TakeReagents(); // we must remove reagents before HandleEffects to allow place crafted item in same slot
- }
+ if (!m_IsTriggeredSpell && m_CastItem)
+ ((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM, m_CastItem->GetEntry());
- // CAST SPELL
- SendSpellCooldown();
- //SendCastResult(castResult);
- SendSpellGo(); // we must send smsg_spell_go packet before m_castItem delete in TakeCastItem()...
-
- if(m_customAttr & SPELL_ATTR_CU_DIRECT_DAMAGE)
- CalculateDamageDoneForAllTargets();
+ ((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL, m_spellInfo->Id);
+ }
- //handle SPELL_AURA_ADD_TARGET_TRIGGER auras
- //are there any spells need to be triggered after hit?
- Unit::AuraList const& targetTriggers = m_caster->GetAurasByType(SPELL_AURA_ADD_TARGET_TRIGGER);
- for(Unit::AuraList::const_iterator i = targetTriggers.begin(); i != targetTriggers.end(); ++i)
+ // this is related to combo points so must be done before takepower
+ // are there any spells need to be triggered after hit?
+ // handle SPELL_AURA_ADD_TARGET_TRIGGER auras
+ Unit::AuraEffectList const& targetTriggers = m_caster->GetAurasByType(SPELL_AURA_ADD_TARGET_TRIGGER);
+ for(Unit::AuraEffectList::const_iterator i = targetTriggers.begin(); i != targetTriggers.end(); ++i)
{
+ if (!(*i)->isAffectedOnSpell(m_spellInfo))
+ continue;
SpellEntry const *auraSpellInfo = (*i)->GetSpellProto();
uint32 auraSpellIdx = (*i)->GetEffIndex();
- if (IsAffectedBy(auraSpellInfo, auraSpellIdx))
+ if(SpellEntry const *spellInfo = sSpellStore.LookupEntry(auraSpellInfo->EffectTriggerSpell[auraSpellIdx]))
{
- if(SpellEntry const *spellInfo = sSpellStore.LookupEntry(auraSpellInfo->EffectTriggerSpell[auraSpellIdx]))
- {
- // Calculate chance at that moment (can be depend for example from combo points)
- int32 chance = m_caster->CalculateSpellDamage(auraSpellInfo, auraSpellIdx, (*i)->GetBasePoints(), NULL);
- m_ChanceTriggerSpells.push_back(std::make_pair(spellInfo, chance * (*i)->GetStackAmount()));
- }
+ // Calculate chance at that moment (can be depend for example from combo points)
+ int32 chance = m_caster->CalculateSpellDamage(auraSpellInfo, auraSpellIdx, (*i)->GetBasePoints(), NULL);
+ m_ChanceTriggerSpells.push_back(std::make_pair(spellInfo, chance * (*i)->GetParentAura()->GetStackAmount()));
}
}
+ // this is related to combo points so must be done before takepower
+ if(m_customAttr & SPELL_ATTR_CU_DIRECT_DAMAGE)
+ CalculateDamageDoneForAllTargets();
+
+ if(!m_IsTriggeredSpell)
+ {
+ // Powers have to be taken before SendSpellGo
+ TakePower();
+ TakeReagents(); // we must remove reagents before HandleEffects to allow place crafted item in same slot
+ }
+
+ // CAST SPELL
+ SendSpellCooldown();
+ //SendCastResult(castResult);
+ SendSpellGo(); // we must send smsg_spell_go packet before m_castItem delete in TakeCastItem()...
+
if(m_customAttr & SPELL_ATTR_CU_CHARGE)
EffectCharge(0);
@@ -2288,12 +2387,6 @@ void Spell::cast(bool skipCheck)
handle_immediate();
}
- // combo points should not be taken before SPELL_AURA_ADD_TARGET_TRIGGER auras are handled
- if(!m_IsTriggeredSpell)
- {
- TakePower();
- }
-
if(m_customAttr & SPELL_ATTR_CU_LINK_CAST)
{
if(const std::vector<int32> *spell_triggered = spellmgr.GetSpellLinked(m_spellInfo->Id))
@@ -2312,9 +2405,18 @@ void Spell::handle_immediate()
// start channeling if applicable
if(IsChanneledSpell(m_spellInfo))
{
- m_spellState = SPELL_STATE_CASTING;
- m_caster->AddInterruptMask(m_spellInfo->ChannelInterruptFlags);
- SendChannelStart(GetSpellDuration(m_spellInfo));
+ int32 duration = GetSpellDuration(m_spellInfo);
+ if (duration)
+ {
+ //apply haste mods
+ m_caster->ModSpellCastTime(m_spellInfo, duration, this);
+ // Apply duration mod
+ if(Player* modOwner = m_caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration);
+ m_spellState = SPELL_STATE_CASTING;
+ m_caster->AddInterruptMask(m_spellInfo->ChannelInterruptFlags);
+ SendChannelStart(duration);
+ }
}
// process immediate effects (items, ground, etc.) also initialize some variables
@@ -2450,93 +2552,20 @@ void Spell::SendSpellCooldown()
return;
Player* _player = (Player*)m_caster;
- // Add cooldown for max (disable spell)
- // Cooldown started on SendCooldownEvent call
- if (m_spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE)
- {
- _player->AddSpellCooldown(m_spellInfo->Id, 0, time(NULL) - 1);
- return;
- }
-
- // init cooldown values
- uint32 cat = 0;
- int32 rec = -1;
- int32 catrec = -1;
- // some special item spells without correct cooldown in SpellInfo
- // cooldown information stored in item prototype
- // This used in same way in WorldSession::HandleItemQuerySingleOpcode data sending to client.
-
- if(m_CastItem)
+ // mana/health/etc potions, disabled by client (until combat out as declarate)
+ if (m_CastItem && m_CastItem->IsPotion())
{
- ItemPrototype const* proto = m_CastItem->GetProto();
- if(proto)
- {
- for(int idx = 0; idx < 5; ++idx)
- {
- if(proto->Spells[idx].SpellId == m_spellInfo->Id)
- {
- cat = proto->Spells[idx].SpellCategory;
- rec = proto->Spells[idx].SpellCooldown;
- catrec = proto->Spells[idx].SpellCategoryCooldown;
- break;
- }
- }
- }
- }
-
- // if no cooldown found above then base at DBC data
- if(rec < 0 && catrec < 0)
- {
- cat = m_spellInfo->Category;
- rec = m_spellInfo->RecoveryTime;
- catrec = m_spellInfo->CategoryRecoveryTime;
+ // need in some way provided data for Spell::finish SendCooldownEvent
+ _player->SetLastPotionId(m_CastItem->GetEntry());
+ return;
}
- // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK)
- // prevent 0 cooldowns set by another way
- if (rec <= 0 && catrec <= 0 && (cat == 76 || cat == 351))
- rec = _player->GetAttackTime(RANGED_ATTACK);
-
- // Now we have cooldown data (if found any), time to apply mods
- if(rec > 0)
- _player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COOLDOWN, rec, this);
-
- if(catrec > 0)
- _player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COOLDOWN, catrec, this);
-
- // replace negative cooldowns by 0
- if (rec < 0) rec = 0;
- if (catrec < 0) catrec = 0;
-
- // no cooldown after applying spell mods
- if( rec == 0 && catrec == 0)
+ // have infinity cooldown but set at aura apply
+ if(m_spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE)
return;
- time_t curTime = time(NULL);
-
- time_t catrecTime = catrec ? curTime+catrec/1000 : 0; // in secs
- time_t recTime = rec ? curTime+rec/1000 : catrecTime;// in secs
-
- // self spell cooldown
- if(recTime > 0)
- _player->AddSpellCooldown(m_spellInfo->Id, m_CastItem ? m_CastItem->GetEntry() : 0, recTime);
-
- // category spells
- if (catrec > 0)
- {
- SpellCategoryStore::const_iterator i_scstore = sSpellCategoryStore.find(cat);
- if(i_scstore != sSpellCategoryStore.end())
- {
- for(SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
- {
- if(*i_scset == m_spellInfo->Id) // skip main spell, already handled above
- continue;
-
- _player->AddSpellCooldown(m_spellInfo->Id, m_CastItem ? m_CastItem->GetEntry() : 0, catrecTime);
- }
- }
- }
+ _player->AddSpellAndCategoryCooldowns(m_spellInfo,m_CastItem ? m_CastItem->GetEntry() : 0, this);
}
void Spell::update(uint32 difftime)
@@ -2690,6 +2719,10 @@ void Spell::finish(bool ok)
m_caster->resetAttackTimer(RANGED_ATTACK);
}
+ // potions disabled by client, send event "not in combat" if need
+ if (m_caster->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)m_caster)->UpdatePotionCooldown(this);
+
// call triggered spell only at successful cast (after clear combo points -> for add some if need)
// I assume what he means is that some triggered spells may add combo points
if(!m_TriggerSpells.empty())
@@ -2700,72 +2733,65 @@ void Spell::finish(bool ok)
m_caster->AttackStop();
}
-void Spell::SendCastResult(uint8 result)
+void Spell::SendCastResult(SpellCastResult result)
{
+ if(result == SPELL_CAST_OK)
+ return;
+
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return;
if(((Player*)m_caster)->GetSession()->PlayerLoading()) // don't send cast results at loading time
return;
- if(result != 0)
- {
- WorldPacket data(SMSG_CAST_FAILED, (4+1+1));
- data << uint32(m_spellInfo->Id);
- data << uint8(result); // problem
- data << uint8(m_cast_count); // single cast or multi 2.3 (0/1)
- switch (result)
- {
- case SPELL_FAILED_REQUIRES_SPELL_FOCUS:
- data << uint32(m_spellInfo->RequiresSpellFocus);
- break;
- case SPELL_FAILED_REQUIRES_AREA:
- // hardcode areas limitation case
- switch(m_spellInfo->Id)
- {
- case 41617: // Cenarion Mana Salve
- case 41619: // Cenarion Healing Salve
- data << uint32(3905);
- break;
- case 41618: // Bottled Nethergon Energy
- case 41620: // Bottled Nethergon Vapor
- data << uint32(3842);
- break;
- case 45373: // Bloodberry Elixir
- data << uint32(4075);
- break;
- default: // default case
- data << uint32(m_spellInfo->AreaId);
- break;
- }
- break;
- case SPELL_FAILED_TOTEMS:
- if(m_spellInfo->Totem[0])
- data << uint32(m_spellInfo->Totem[0]);
- if(m_spellInfo->Totem[1])
- data << uint32(m_spellInfo->Totem[1]);
- break;
- case SPELL_FAILED_TOTEM_CATEGORY:
- if(m_spellInfo->TotemCategory[0])
- data << uint32(m_spellInfo->TotemCategory[0]);
- if(m_spellInfo->TotemCategory[1])
- data << uint32(m_spellInfo->TotemCategory[1]);
- break;
- case SPELL_FAILED_EQUIPPED_ITEM_CLASS:
- data << uint32(m_spellInfo->EquippedItemClass);
- data << uint32(m_spellInfo->EquippedItemSubClassMask);
- data << uint32(m_spellInfo->EquippedItemInventoryTypeMask);
- break;
- }
- ((Player*)m_caster)->GetSession()->SendPacket(&data);
- }
- else
+ WorldPacket data(SMSG_CAST_FAILED, (4+1+1));
+ data << uint8(m_cast_count); // single cast or multi 2.3 (0/1)
+ data << uint32(m_spellInfo->Id);
+ data << uint8(result); // problem
+ switch (result)
{
- WorldPacket data(SMSG_CLEAR_EXTRA_AURA_INFO, (8+4));
- data.append(m_caster->GetPackGUID());
- data << uint32(m_spellInfo->Id);
- ((Player*)m_caster)->GetSession()->SendPacket(&data);
+ case SPELL_FAILED_REQUIRES_SPELL_FOCUS:
+ data << uint32(m_spellInfo->RequiresSpellFocus);
+ break;
+ case SPELL_FAILED_REQUIRES_AREA:
+ // hardcode areas limitation case
+ switch(m_spellInfo->Id)
+ {
+ case 41617: // Cenarion Mana Salve
+ case 41619: // Cenarion Healing Salve
+ data << uint32(3905);
+ break;
+ case 41618: // Bottled Nethergon Energy
+ case 41620: // Bottled Nethergon Vapor
+ data << uint32(3842);
+ break;
+ case 45373: // Bloodberry Elixir
+ data << uint32(4075);
+ break;
+ default: // default case (don't must be)
+ data << uint32(0);
+ break;
+ }
+ break;
+ case SPELL_FAILED_TOTEMS:
+ if(m_spellInfo->Totem[0])
+ data << uint32(m_spellInfo->Totem[0]);
+ if(m_spellInfo->Totem[1])
+ data << uint32(m_spellInfo->Totem[1]);
+ break;
+ case SPELL_FAILED_TOTEM_CATEGORY:
+ if(m_spellInfo->TotemCategory[0])
+ data << uint32(m_spellInfo->TotemCategory[0]);
+ if(m_spellInfo->TotemCategory[1])
+ data << uint32(m_spellInfo->TotemCategory[1]);
+ break;
+ case SPELL_FAILED_EQUIPPED_ITEM_CLASS:
+ data << uint32(m_spellInfo->EquippedItemClass);
+ data << uint32(m_spellInfo->EquippedItemSubClassMask);
+ //data << uint32(m_spellInfo->EquippedItemInventoryTypeMask);
+ break;
}
+ ((Player*)m_caster)->GetSession()->SendPacket(&data);
}
void Spell::SendSpellStart()
@@ -2779,6 +2805,9 @@ void Spell::SendSpellStart()
if(IsRangedSpell())
castFlags |= CAST_FLAG_AMMO;
+ if(m_spellInfo->runeCostID)
+ castFlags |= CAST_FLAG_UNKNOWN10;
+
Unit *target = m_targets.getUnitTarget() ? m_targets.getUnitTarget() : m_caster;
WorldPacket data(SMSG_SPELL_START, (8+8+4+4+2));
@@ -2788,14 +2817,32 @@ void Spell::SendSpellStart()
data.append(m_caster->GetPackGUID());
data.append(m_caster->GetPackGUID());
- data << uint32(m_spellInfo->Id);
- data << uint8(m_cast_count); // single cast or multi 2.3 (0/1)
- data << uint16(castFlags);
- data << uint32(m_timer);
+ data << uint8(m_cast_count); // pending spell cast?
+ data << uint32(m_spellInfo->Id); // spellId
+ data << uint32(castFlags); // cast flags
+ data << uint32(m_timer); // delay?
m_targets.write(&data);
- if( castFlags & CAST_FLAG_AMMO )
+ if ( castFlags & CAST_FLAG_UNKNOWN6 ) // predicted power?
+ data << uint32(0);
+
+ if ( castFlags & CAST_FLAG_UNKNOWN7 ) // rune cooldowns list
+ {
+ uint8 v1 = 0;//m_runesState;
+ uint8 v2 = 0;//((Player*)m_caster)->GetRunesState();
+ data << uint8(v1); // runes state before
+ data << uint8(v2); // runes state after
+ for(uint8 i = 0; i < MAX_RUNES; ++i)
+ {
+ uint8 m = (1 << i);
+ if(m & v1) // usable before...
+ if(!(m & v2)) // ...but on cooldown now...
+ data << uint8(0); // some unknown byte (time?)
+ }
+ }
+
+ if ( castFlags & CAST_FLAG_AMMO )
WriteAmmoToPacket(&data);
m_caster->SendMessageToSet(&data, true);
@@ -2815,24 +2862,68 @@ void Spell::SendSpellGo()
if(IsRangedSpell())
castFlags |= CAST_FLAG_AMMO; // arrows/bullets visual
+ if((m_caster->GetTypeId() == TYPEID_PLAYER) && (m_caster->getClass() == CLASS_DEATH_KNIGHT) && m_spellInfo->runeCostID)
+ {
+ castFlags |= CAST_FLAG_UNKNOWN10; // same as in SMSG_SPELL_START
+ castFlags |= CAST_FLAG_UNKNOWN6; // makes cooldowns visible
+ castFlags |= CAST_FLAG_UNKNOWN7; // rune cooldowns list
+ }
+
WorldPacket data(SMSG_SPELL_GO, 50); // guess size
+
if(m_CastItem)
data.append(m_CastItem->GetPackGUID());
else
data.append(m_caster->GetPackGUID());
data.append(m_caster->GetPackGUID());
- data << uint32(m_spellInfo->Id);
- data << uint16(castFlags);
+ data << uint8(m_cast_count); // pending spell cast?
+ data << uint32(m_spellInfo->Id); // spellId
+ data << uint32(castFlags); // cast flags
data << uint32(getMSTime()); // timestamp
WriteSpellGoTargets(&data);
m_targets.write(&data);
- if( castFlags & CAST_FLAG_AMMO )
+ if ( castFlags & CAST_FLAG_UNKNOWN6 ) // unknown wotlk, predicted power?
+ data << uint32(0);
+
+ if ( castFlags & CAST_FLAG_UNKNOWN7 ) // rune cooldowns list
+ {
+ uint8 v1 = m_runesState;
+ uint8 v2 = ((Player*)m_caster)->GetRunesState();
+ data << uint8(v1); // runes state before
+ data << uint8(v2); // runes state after
+ for(uint8 i = 0; i < MAX_RUNES; ++i)
+ {
+ uint8 m = (1 << i);
+ if(m & v1) // usable before...
+ if(!(m & v2)) // ...but on cooldown now...
+ data << uint8(0); // some unknown byte (time?)
+ }
+ }
+
+ if ( castFlags & CAST_FLAG_UNKNOWN4 ) // unknown wotlk
+ {
+ data << float(0);
+ data << uint32(0);
+ }
+
+ if ( castFlags & CAST_FLAG_AMMO )
WriteAmmoToPacket(&data);
+ if ( castFlags & CAST_FLAG_UNKNOWN5 ) // unknown wotlk
+ {
+ data << uint32(0);
+ data << uint32(0);
+ }
+
+ if ( m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION )
+ {
+ data << uint8(0);
+ }
+
m_caster->SendMessageToSet(&data, true);
}
@@ -2877,15 +2968,36 @@ void Spell::WriteAmmoToPacket( WorldPacket * data )
void Spell::WriteSpellGoTargets( WorldPacket * data )
{
- *data << (uint8)m_countOfHit;
+ // This function also fill data for channeled spells:
+ // m_needAliveTargetMask req for stop channelig if one target die
+ uint32 hit = m_UniqueGOTargetInfo.size(); // Always hits on GO
+ uint32 miss = 0;
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ {
+ if ((*ihit).effectMask == 0) // No effect apply - all immuned add state
+ {
+ // possibly SPELL_MISS_IMMUNE2 for this??
+ ihit->missCondition = SPELL_MISS_IMMUNE2;
+ miss++;
+ }
+ else if ((*ihit).missCondition == SPELL_MISS_NONE)
+ hit++;
+ else
+ miss++;
+ }
+
+ *data << (uint8)hit;
for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
if ((*ihit).missCondition == SPELL_MISS_NONE) // Add only hits
+ {
*data << uint64(ihit->targetGUID);
+ m_needAliveTargetMask |=ihit->effectMask;
+ }
for(std::list<GOTargetInfo>::iterator ighit= m_UniqueGOTargetInfo.begin();ighit != m_UniqueGOTargetInfo.end();++ighit)
*data << uint64(ighit->targetGUID); // Always hits
- *data << (uint8)m_countOfMiss;
+ *data << (uint8)miss;
for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
{
if( ihit->missCondition != SPELL_MISS_NONE ) // Add only miss
@@ -2896,6 +3008,9 @@ void Spell::WriteSpellGoTargets( WorldPacket * data )
*data << uint8(ihit->reflectResult);
}
}
+ // Reset m_needAliveTargetMask for non channeled spell
+ if(!IsChanneledSpell(m_spellInfo))
+ m_needAliveTargetMask = 0;
}
void Spell::SendLogExecute()
@@ -2960,30 +3075,19 @@ void Spell::SendLogExecute()
data << uint8(0);
break;
case SPELL_EFFECT_CREATE_ITEM:
+ case SPELL_EFFECT_CREATE_ITEM_2:
data << uint32(m_spellInfo->EffectItemType[0]);
break;
case SPELL_EFFECT_SUMMON:
- case SPELL_EFFECT_SUMMON_WILD:
- case SPELL_EFFECT_SUMMON_GUARDIAN:
case SPELL_EFFECT_TRANS_DOOR:
case SPELL_EFFECT_SUMMON_PET:
- case SPELL_EFFECT_SUMMON_POSSESSED:
- case SPELL_EFFECT_SUMMON_TOTEM:
case SPELL_EFFECT_SUMMON_OBJECT_WILD:
case SPELL_EFFECT_CREATE_HOUSE:
case SPELL_EFFECT_DUEL:
- case SPELL_EFFECT_SUMMON_TOTEM_SLOT1:
- case SPELL_EFFECT_SUMMON_TOTEM_SLOT2:
- case SPELL_EFFECT_SUMMON_TOTEM_SLOT3:
- case SPELL_EFFECT_SUMMON_TOTEM_SLOT4:
- case SPELL_EFFECT_SUMMON_PHANTASM:
- case SPELL_EFFECT_SUMMON_CRITTER:
case SPELL_EFFECT_SUMMON_OBJECT_SLOT1:
case SPELL_EFFECT_SUMMON_OBJECT_SLOT2:
case SPELL_EFFECT_SUMMON_OBJECT_SLOT3:
case SPELL_EFFECT_SUMMON_OBJECT_SLOT4:
- case SPELL_EFFECT_SUMMON_DEMON:
- case SPELL_EFFECT_150:
if(Unit *unit = m_targets.getUnitTarget())
data.append(unit->GetPackGUID());
else if(m_targets.getItemTargetGUID())
@@ -3002,6 +3106,13 @@ void Spell::SendLogExecute()
else
data << uint8(0);
break;
+ case SPELL_EFFECT_RESURRECT:
+ case SPELL_EFFECT_RESURRECT_NEW:
+ if(Unit *unit = m_targets.getUnitTarget())
+ data.append(unit->GetPackGUID());
+ else
+ data << uint8(0);
+ break;
default:
return;
}
@@ -3015,13 +3126,16 @@ void Spell::SendInterrupted(uint8 result)
{
WorldPacket data(SMSG_SPELL_FAILURE, (8+4+1));
data.append(m_caster->GetPackGUID());
- data << m_spellInfo->Id;
- data << result;
+ data << uint8(m_cast_count);
+ data << uint32(m_spellInfo->Id);
+ data << uint8(result);
m_caster->SendMessageToSet(&data, true);
data.Initialize(SMSG_SPELL_FAILED_OTHER, (8+4));
data.append(m_caster->GetPackGUID());
- data << m_spellInfo->Id;
+ data << uint8(m_cast_count);
+ data << uint32(m_spellInfo->Id);
+ data << uint8(result);
m_caster->SendMessageToSet(&data, true);
}
@@ -3089,10 +3203,19 @@ void Spell::SendChannelStart(uint32 duration)
void Spell::SendResurrectRequest(Player* target)
{
- WorldPacket data(SMSG_RESURRECT_REQUEST, (8+4+2+4));
- data << m_caster->GetGUID();
- data << uint32(1) << uint16(0) << uint32(1);
+ // Both players and NPCs can resurrect using spells - have a look at creature 28487 for example
+ // However, the packet structure differs slightly
+
+ const char* sentName = m_caster->GetTypeId()==TYPEID_PLAYER ?"":m_caster->GetNameForLocaleIdx(target->GetSession()->GetSessionDbLocaleIndex());
+
+ WorldPacket data(SMSG_RESURRECT_REQUEST, (8+4+strlen(sentName)+1+1+1));
+ data << uint64(m_caster->GetGUID());
+ data << uint32(strlen(sentName)+1);
+
+ data << sentName;
+ data << uint8(0);
+ data << uint8(m_caster->GetTypeId()==TYPEID_PLAYER ?0:1);
target->GetSession()->SendPacket(&data);
}
@@ -3129,7 +3252,7 @@ void Spell::TakeCastItem()
bool expendable = false;
bool withoutCharges = false;
- for (int i = 0; i<5; i++)
+ for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
{
if (proto->Spells[i].SpellId)
{
@@ -3145,7 +3268,7 @@ void Spell::TakeCastItem()
if (charges)
{
(charges > 0) ? --charges : ++charges; // abs(charges) less at 1 after use
- if (proto->Stackable < 2)
+ if (proto->Stackable == 1)
m_CastItem->SetSpellCharges(i, charges);
m_CastItem->SetState(ITEM_CHANGED, (Player*)m_caster);
}
@@ -3171,7 +3294,7 @@ void Spell::TakeCastItem()
void Spell::TakePower()
{
- if(m_CastItem || m_triggeredByAuraSpell || !m_powerCost)
+ if(m_CastItem || m_triggeredByAuraSpell)
return;
bool hit = true;
@@ -3184,12 +3307,27 @@ void Spell::TakePower()
{
if(ihit->missCondition != SPELL_MISS_NONE && ihit->missCondition != SPELL_MISS_MISS/* && ihit->targetGUID!=m_caster->GetGUID()*/)
hit = false;
+ if (ihit->missCondition != SPELL_MISS_NONE)
+ {
+ //lower spell cost on fail (by talent aura)
+ if(Player *modOwner = ((Player*)m_caster)->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_SPELL_COST_REFUND_ON_FAIL, m_powerCost);
+ }
break;
}
- if(hit && NeedsComboPoints(m_spellInfo))
- ((Player*)m_caster)->ClearComboPoints();
}
+ Powers powerType = Powers(m_spellInfo->powerType);
+
+ if(powerType == POWER_RUNE)
+ {
+ TakeRunePower();
+ return;
+ }
+
+ if (!m_powerCost)
+ return;
+
// health as power used
if(m_spellInfo->powerType == POWER_HEALTH)
{
@@ -3203,8 +3341,6 @@ void Spell::TakePower()
return;
}
- Powers powerType = Powers(m_spellInfo->powerType);
-
if(hit)
m_caster->ModifyPower(powerType, -m_powerCost);
else
@@ -3215,6 +3351,136 @@ void Spell::TakePower()
m_caster->SetLastManaUse(getMSTime());
}
+void Spell::TakeAmmo()
+{
+ if(m_attackType == RANGED_ATTACK && m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ Item *pItem = ((Player*)m_caster)->GetWeaponForAttack( RANGED_ATTACK );
+
+ // wands don't have ammo
+ if(!pItem || pItem->IsBroken() || pItem->GetProto()->SubClass==ITEM_SUBCLASS_WEAPON_WAND)
+ return;
+
+ if( pItem->GetProto()->InventoryType == INVTYPE_THROWN )
+ {
+ if(pItem->GetMaxStackCount()==1)
+ {
+ // decrease durability for non-stackable throw weapon
+ ((Player*)m_caster)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_RANGED);
+ }
+ else
+ {
+ // decrease items amount for stackable throw weapon
+ uint32 count = 1;
+ ((Player*)m_caster)->DestroyItemCount( pItem, count, true);
+ }
+ }
+ else if(uint32 ammo = ((Player*)m_caster)->GetUInt32Value(PLAYER_AMMO_ID))
+ ((Player*)m_caster)->DestroyItemCount(ammo, 1, true);
+ }
+}
+
+SpellCastResult Spell::CheckRuneCost(uint32 runeCostID)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return SPELL_CAST_OK;
+
+ Player *plr = (Player*)m_caster;
+
+ if(plr->getClass() != CLASS_DEATH_KNIGHT)
+ return SPELL_CAST_OK;
+
+ SpellRuneCostEntry const *src = sSpellRuneCostStore.LookupEntry(runeCostID);
+
+ if(!src)
+ return SPELL_CAST_OK;
+
+ if(src->NoRuneCost())
+ return SPELL_CAST_OK;
+
+ int32 runeCost[NUM_RUNE_TYPES]; // blood, frost, unholy, death
+
+ for(uint32 i = 0; i < RUNE_DEATH; ++i)
+ runeCost[i] = src->RuneCost[i];
+
+ runeCost[RUNE_DEATH] = MAX_RUNES; // calculated later
+
+ for(uint32 i = 0; i < MAX_RUNES; ++i)
+ {
+ uint8 rune = plr->GetCurrentRune(i);
+ if((plr->GetRuneCooldown(i) == 0) && (runeCost[rune] > 0))
+ runeCost[rune]--;
+ }
+
+ for(uint32 i = 0; i < RUNE_DEATH; ++i)
+ if(runeCost[i] > 0)
+ runeCost[RUNE_DEATH] += runeCost[i];
+
+ if(runeCost[RUNE_DEATH] > MAX_RUNES)
+ return SPELL_FAILED_NO_POWER; // not sure if result code is correct
+
+ return SPELL_CAST_OK;
+}
+
+void Spell::TakeRunePower()
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *plr = (Player*)m_caster;
+
+ if(plr->getClass() != CLASS_DEATH_KNIGHT)
+ return;
+
+ SpellRuneCostEntry const *src = sSpellRuneCostStore.LookupEntry(m_spellInfo->runeCostID);
+
+ if(!src || (src->NoRuneCost() && src->NoRunicPowerGain()))
+ return;
+ m_runesState = plr->GetRunesState(); // store previous state
+
+ int32 runeCost[NUM_RUNE_TYPES]; // blood, frost, unholy, death
+
+ for(uint32 i = 0; i < RUNE_DEATH; ++i)
+ {
+ runeCost[i] = src->RuneCost[i];
+ }
+
+ runeCost[RUNE_DEATH] = 0; // calculated later
+
+ for(uint32 i = 0; i < MAX_RUNES; ++i)
+ {
+ uint8 rune = plr->GetCurrentRune(i);
+ if((plr->GetRuneCooldown(i) == 0) && (runeCost[rune] > 0))
+ {
+ plr->SetRuneCooldown(i, RUNE_COOLDOWN); // 5*2=10 sec
+ runeCost[rune]--;
+ }
+ }
+
+ runeCost[RUNE_DEATH] = runeCost[RUNE_BLOOD] + runeCost[RUNE_UNHOLY] + runeCost[RUNE_FROST];
+
+ if(runeCost[RUNE_DEATH] > 0)
+ {
+ for(uint32 i = 0; i < MAX_RUNES; ++i)
+ {
+ uint8 rune = plr->GetCurrentRune(i);
+ if((plr->GetRuneCooldown(i) == 0) && (rune == RUNE_DEATH))
+ {
+ plr->SetRuneCooldown(i, RUNE_COOLDOWN); // 5*2=10 sec
+ runeCost[rune]--;
+ plr->ConvertRune(i, plr->GetBaseRune(i));
+ if(runeCost[RUNE_DEATH] == 0)
+ break;
+ }
+ }
+ }
+
+ // you can gain some runic power when use runes
+ float rp = src->runePowerGain;
+ rp *= sWorld.getRate(RATE_POWER_RUNICPOWER_INCOME);
+ plr->ModifyPower(POWER_RUNIC_POWER, (int32)rp);
+}
+
void Spell::TakeReagents()
{
if(m_IsTriggeredSpell) // reagents used in triggered spell removed by original spell or don't must be removed.
@@ -3223,11 +3489,9 @@ void Spell::TakeReagents()
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return;
- if (m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP &&
- m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION))
- return;
-
Player* p_caster = (Player*)m_caster;
+ if (p_caster->CanNoReagentCast(m_spellInfo))
+ return;
for(uint32 x=0;x<8;x++)
{
@@ -3243,7 +3507,7 @@ void Spell::TakeReagents()
ItemPrototype const *proto = m_CastItem->GetProto();
if( proto && proto->ItemId == itemid )
{
- for(int s=0;s<5;s++)
+ for(int s=0;s < MAX_ITEM_PROTO_SPELLS; ++s)
{
// CastItem will be used up and does not count as reagent
int32 charges = m_CastItem->GetSpellCharges(s);
@@ -3290,14 +3554,9 @@ void Spell::HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTar
gameObjTarget = pGOTarget;
uint8 eff = m_spellInfo->Effect[i];
- uint32 mechanic = m_spellInfo->EffectMechanic[i];
sLog.outDebug( "Spell: Effect : %u", eff);
- //Simply return. Do not display "immune" in red text on client
- if(unitTarget && unitTarget->IsImmunedToSpellEffect(eff, mechanic))
- return;
-
//we do not need DamageMultiplier here.
damage = CalculateDamage(i, NULL);
@@ -3314,7 +3573,7 @@ void Spell::HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTar
EffectEnchantItemTmp(i);
else
{
- sLog.outError("SPELL: unknown effect %u spell id %u\n",
+ sLog.outError("SPELL: unknown effect %u spell id %u",
eff, m_spellInfo->Id);
}
}
@@ -3330,11 +3589,14 @@ void Spell::TriggerSpell()
}
}
-uint8 Spell::CanCast(bool strict)
+SpellCastResult Spell::CheckCast(bool strict)
{
// check cooldowns to prevent cheating
if(m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->HasSpellCooldown(m_spellInfo->Id))
{
+ //can cast triggered (by aura only?) spells while have this flag
+ if (!m_IsTriggeredSpell && ((Player*)m_caster)->HasFlag(PLAYER_FLAGS, PLAYER_ALLOW_ONLY_ABILITY))
+ return SPELL_FAILED_SPELL_IN_PROGRESS;
if(m_triggeredByAuraSpell)
return SPELL_FAILED_DONT_REPORT;
else
@@ -3351,20 +3613,56 @@ uint8 Spell::CanCast(bool strict)
// for now, ignore triggered spells
if( strict && !m_IsTriggeredSpell)
{
- // Cannot be used in this stance/form
- if(uint8 shapeError = GetErrorAtShapeshiftedCast(m_spellInfo, m_caster->m_form))
- return shapeError;
+ bool checkForm = true;
+ // Ignore form req aura
+ Unit::AuraEffectList const& ignore = m_caster->GetAurasByType(SPELL_AURA_MOD_IGNORE_SHAPESHIFT);
+ for(Unit::AuraEffectList::const_iterator i = ignore.begin(); i != ignore.end(); ++i)
+ {
+ if (!(*i)->isAffectedOnSpell(m_spellInfo))
+ continue;
+ checkForm = false;
+ break;
+ }
+ if (checkForm)
+ {
+ // Cannot be used in this stance/form
+ SpellCastResult shapeError = GetErrorAtShapeshiftedCast(m_spellInfo, m_caster->m_form);
+ if(shapeError != SPELL_CAST_OK)
+ return shapeError;
- if ((m_spellInfo->Attributes & SPELL_ATTR_ONLY_STEALTHED) && !(m_caster->HasStealthAura()))
- return SPELL_FAILED_ONLY_STEALTHED;
+ if ((m_spellInfo->Attributes & SPELL_ATTR_ONLY_STEALTHED) && !(m_caster->HasStealthAura()))
+ return SPELL_FAILED_ONLY_STEALTHED;
+ }
+ }
+
+ bool reqCombat=true;
+ Unit::AuraEffectList const& stateAuras = m_caster->GetAurasByType(SPELL_AURA_ABILITY_IGNORE_AURASTATE);
+ for(Unit::AuraEffectList::const_iterator j = stateAuras.begin();j != stateAuras.end(); ++j)
+ {
+ if((*j)->isAffectedOnSpell(m_spellInfo))
+ {
+ if ((*j)->GetMiscValue()==1)
+ {
+ reqCombat=false;
+ break;
+ }
+ }
}
// caster state requirements
- if(m_spellInfo->CasterAuraState && !m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraState)))
+ if(m_spellInfo->CasterAuraState && !m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraState), m_spellInfo, m_caster))
return SPELL_FAILED_CASTER_AURASTATE;
- if(m_spellInfo->CasterAuraStateNot && m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraStateNot)))
+ if(m_spellInfo->CasterAuraStateNot && m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraStateNot), m_spellInfo, m_caster))
return SPELL_FAILED_CASTER_AURASTATE;
+ if(m_spellInfo->casterAuraSpell && !m_caster->HasAura(m_spellInfo->casterAuraSpell))
+ return SPELL_FAILED_CASTER_AURASTATE;
+ if(m_spellInfo->excludeCasterAuraSpell && m_caster->HasAura(m_spellInfo->excludeCasterAuraSpell))
+ return SPELL_FAILED_CASTER_AURASTATE;
+
+ if(reqCombat && m_caster->isInCombat() && IsNonCombatSpell(m_spellInfo))
+ return SPELL_FAILED_AFFECTING_COMBAT;
+
// cancel autorepeat spells if cast start when moving
// (not wand currently autorepeat cast delayed to moving stop anyway in spell update code)
if( m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->isMoving() )
@@ -3375,18 +3673,23 @@ uint8 Spell::CanCast(bool strict)
return SPELL_FAILED_MOVING;
}
- Unit *target = m_targets.getUnitTarget();
-
- if(target)
+ if(Unit *target = m_targets.getUnitTarget())
{
+
// target state requirements (not allowed state), apply to self also
- if(m_spellInfo->TargetAuraStateNot && target->HasAuraState(AuraState(m_spellInfo->TargetAuraStateNot)))
+ if(m_spellInfo->TargetAuraStateNot && target->HasAuraState(AuraState(m_spellInfo->TargetAuraStateNot), m_spellInfo, m_caster))
+ return SPELL_FAILED_TARGET_AURASTATE;
+
+ if(m_spellInfo->targetAuraSpell && !target->HasAura(m_spellInfo->targetAuraSpell))
+ return SPELL_FAILED_TARGET_AURASTATE;
+
+ if(m_spellInfo->excludeTargetAuraSpell && target->HasAura(m_spellInfo->excludeTargetAuraSpell))
return SPELL_FAILED_TARGET_AURASTATE;
if(target != m_caster)
{
// target state requirements (apply to non-self only), to allow cast affects to self like Dirty Deeds
- if(m_spellInfo->TargetAuraState && !target->HasAuraState(AuraState(m_spellInfo->TargetAuraState)))
+ if(m_spellInfo->TargetAuraState && !target->HasAuraState(AuraState(m_spellInfo->TargetAuraState), m_spellInfo, m_caster))
return SPELL_FAILED_TARGET_AURASTATE;
// Not allow casting on flying player
@@ -3399,13 +3702,23 @@ uint8 Spell::CanCast(bool strict)
// auto selection spell rank implemented in WorldSession::HandleCastSpellOpcode
// this case can be triggered if rank not found (too low-level target for first rank)
if(m_caster->GetTypeId() == TYPEID_PLAYER && !IsPassiveSpell(m_spellInfo->Id) && !m_CastItem)
- {
for(int i=0;i<3;i++)
- {
if(IsPositiveEffect(m_spellInfo->Id, i) && m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA)
if(target->getLevel() + 10 < m_spellInfo->spellLevel)
return SPELL_FAILED_LOWLEVEL;
- }
+ }
+ else if (m_caster->GetTypeId()==TYPEID_PLAYER) // Target - is player caster
+ {
+ // Additional check for some spells
+ // If 0 spell effect empty - client not send target data (need use selection)
+ // TODO: check it on next client version
+ if (m_targets.m_targetMask == TARGET_FLAG_SELF &&
+ m_spellInfo->EffectImplicitTargetA[1] == TARGET_CHAIN_DAMAGE)
+ {
+ if (target = m_caster->GetUnit(*m_caster, ((Player *)m_caster)->GetSelection()))
+ m_targets.setUnitTarget(target);
+ else
+ return SPELL_FAILED_BAD_TARGETS;
}
}
@@ -3414,7 +3727,7 @@ uint8 Spell::CanCast(bool strict)
{
if(m_spellInfo->EffectImplicitTargetA[j] == TARGET_PET)
{
- target = m_caster->GetPet();
+ target = m_caster->GetGuardianPet();
if(!target)
{
if(m_triggeredByAuraSpell) // not report pet not existence for triggered spells
@@ -3458,14 +3771,15 @@ uint8 Spell::CanCast(bool strict)
}
if(IsPositiveSpell(m_spellInfo->Id))
- {
- if(target->IsImmunedToSpell(m_spellInfo,false))
+ if(target->IsImmunedToSpell(m_spellInfo))
return SPELL_FAILED_TARGET_AURASTATE;
- }
//Must be behind the target.
if( m_spellInfo->AttributesEx2 == 0x100000 && (m_spellInfo->AttributesEx & 0x200) == 0x200 && target->HasInArc(M_PI, m_caster)
- && (m_spellInfo->SpellFamilyName != SPELLFAMILY_DRUID || m_spellInfo->SpellFamilyFlags != 0x0000000000020000LL))
+ //Exclusion for Pounce: Facing Limitation was removed in 2.0.1, but it still uses the same, old Ex-Flags
+ && (!(m_spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && m_spellInfo->SpellFamilyFlags.IsEqual(0x20000,0,0)))
+ //Mutilate no longer requires you be behind the target as of patch 3.0.3
+ && (!(m_spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && m_spellInfo->SpellFamilyFlags[1] & 0x200000)))
{
SendInterrupted(2);
return SPELL_FAILED_NOT_BEHIND;
@@ -3480,10 +3794,9 @@ uint8 Spell::CanCast(bool strict)
// check if target is in combat
if (target != m_caster && (m_spellInfo->AttributesEx & SPELL_ATTR_EX_NOT_IN_COMBAT_TARGET) && target->isInCombat())
- {
return SPELL_FAILED_TARGET_AFFECTING_COMBAT;
- }
}
+
// Spell casted only on battleground
if((m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_BATTLEGROUND) && m_caster->GetTypeId()==TYPEID_PLAYER)
if(!((Player*)m_caster)->InBattleGround())
@@ -3493,14 +3806,19 @@ uint8 Spell::CanCast(bool strict)
// - with greater than 15 min CD without SPELL_ATTR_EX4_USABLE_IN_ARENA flag
// - with SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA flag
if( (m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA) ||
- GetSpellRecoveryTime(m_spellInfo) > 15 * MINUTE * 1000 && !(m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_USABLE_IN_ARENA) )
+ GetSpellRecoveryTime(m_spellInfo) > 15 * MINUTE * IN_MILISECONDS && !(m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_USABLE_IN_ARENA) )
if(MapEntry const* mapEntry = sMapStore.LookupEntry(m_caster->GetMapId()))
if(mapEntry->IsBattleArena())
return SPELL_FAILED_NOT_IN_ARENA;
// zone check
- if(!IsSpellAllowedInLocation(m_spellInfo,m_caster->GetMapId(),m_caster->GetZoneId(),m_caster->GetAreaId()))
- return SPELL_FAILED_REQUIRES_AREA;
+ uint32 zone, area;
+ m_caster->GetZoneAndAreaId(zone,area);
+
+ SpellCastResult locRes= spellmgr.GetSpellAllowedInLocationError(m_spellInfo,m_caster->GetMapId(),zone,area,
+ m_caster->GetTypeId()==TYPEID_PLAYER ? ((Player*)m_caster) : NULL);
+ if(locRes != SPELL_CAST_OK)
+ return locRes;
// not let players cast spells at mount (and let do it to creatures)
if( m_caster->IsMounted() && m_caster->GetTypeId()==TYPEID_PLAYER && !m_IsTriggeredSpell &&
@@ -3514,11 +3832,14 @@ uint8 Spell::CanCast(bool strict)
// always (except passive spells) check items (focus object can be required for any type casts)
if(!IsPassiveSpell(m_spellInfo->Id))
- if(uint8 castResult = CheckItems())
+ {
+ SpellCastResult castResult = CheckItems();
+ if(castResult != SPELL_CAST_OK)
return castResult;
+ }
/*//ImpliciteTargetA-B = 38, If fact there is 0 Spell with ImpliciteTargetB=38
- if(m_UniqueTargetInfo.empty()) // skip second canCast apply (for delayed spells for example)
+ if(m_UniqueTargetInfo.empty()) // skip second CheckCast apply (for delayed spells for example)
{
for(uint8 j = 0; j < 3; j++)
{
@@ -3553,7 +3874,7 @@ uint8 Spell::CanCast(bool strict)
cell.data.Part.reserved = ALL_DISTRICT;
MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*m_caster,i_spellST->second.targetEntry,range);
- MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(p_GameObject,go_check);
+ MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(m_caster, p_GameObject,go_check);
TypeContainerVisitor<MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker);
CellLock<GridReadGuard> cell_lock(cell, p);
@@ -3591,7 +3912,7 @@ uint8 Spell::CanCast(bool strict)
cell.SetNoCreate(); // Really don't know what is that???
MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster,i_spellST->second.targetEntry,i_spellST->second.type!=SPELL_TARGET_TYPE_DEAD,range);
- MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(p_Creature, u_check);
+ MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(m_caster, p_Creature, u_check);
TypeContainerVisitor<MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck>, GridTypeMapContainer > grid_creature_searcher(searcher);
@@ -3654,13 +3975,16 @@ uint8 Spell::CanCast(bool strict)
if(!m_IsTriggeredSpell)
{
- if(uint8 castResult = CheckRange(strict))
+ SpellCastResult castResult = CheckRange(strict);
+ if(castResult != SPELL_CAST_OK)
return castResult;
- if(uint8 castResult = CheckPower())
+ castResult = CheckPower();
+ if(castResult != SPELL_CAST_OK)
return castResult;
- if(uint8 castResult = CheckCasterAuras())
+ castResult = CheckCasterAuras();
+ if(castResult != SPELL_CAST_OK)
return castResult;
}
@@ -3685,7 +4009,7 @@ uint8 Spell::CanCast(bool strict)
{
// spell different for friends and enemies
// hart version required facing
- if(m_targets.getUnitTarget() && !m_caster->IsFriendlyTo(m_targets.getUnitTarget()) && !m_caster->HasInArc( M_PI, target ))
+ if(m_targets.getUnitTarget() && !m_caster->IsFriendlyTo(m_targets.getUnitTarget()) && !m_caster->HasInArc( M_PI, m_targets.getUnitTarget() ))
return SPELL_FAILED_UNIT_NOT_INFRONT;
}
else if (m_spellInfo->Id == 19938) // Awaken Peon
@@ -3699,7 +4023,7 @@ uint8 Spell::CanCast(bool strict)
case SPELL_EFFECT_SCHOOL_DAMAGE:
{
// Hammer of Wrath
- if(m_spellInfo->SpellVisual == 7250)
+ if(m_spellInfo->SpellVisual[0] == 7250)
{
if (!m_targets.getUnitTarget())
return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
@@ -3711,10 +4035,13 @@ uint8 Spell::CanCast(bool strict)
}
case SPELL_EFFECT_LEARN_SPELL:
{
+ if (m_caster->GetTypeId() != TYPEID_PLAYER)
+ return SPELL_FAILED_BAD_TARGETS;
+
if(m_spellInfo->EffectImplicitTargetA[i] != TARGET_PET)
break;
- Pet* pet = m_caster->GetPet();
+ Pet* pet = ((Player*)m_caster)->GetPet();
if(!pet)
return SPELL_FAILED_NO_PET;
@@ -3724,21 +4051,17 @@ uint8 Spell::CanCast(bool strict)
if(!learn_spellproto)
return SPELL_FAILED_NOT_KNOWN;
- if(!pet->CanTakeMoreActiveSpells(learn_spellproto->Id))
- return SPELL_FAILED_TOO_MANY_SKILLS;
-
if(m_spellInfo->spellLevel > pet->getLevel())
return SPELL_FAILED_LOWLEVEL;
- if(!pet->HasTPForSpell(learn_spellproto->Id))
- return SPELL_FAILED_TRAINING_POINTS;
-
break;
}
case SPELL_EFFECT_LEARN_PET_SPELL:
{
- Pet* pet = m_caster->GetPet();
+ if (m_caster->GetTypeId() != TYPEID_PLAYER)
+ return SPELL_FAILED_BAD_TARGETS;
+ Pet* pet = ((Player*)m_caster)->GetPet();
if(!pet)
return SPELL_FAILED_NO_PET;
@@ -3747,31 +4070,29 @@ uint8 Spell::CanCast(bool strict)
if(!learn_spellproto)
return SPELL_FAILED_NOT_KNOWN;
- if(!pet->CanTakeMoreActiveSpells(learn_spellproto->Id))
- return SPELL_FAILED_TOO_MANY_SKILLS;
-
if(m_spellInfo->spellLevel > pet->getLevel())
return SPELL_FAILED_LOWLEVEL;
- if(!pet->HasTPForSpell(learn_spellproto->Id))
- return SPELL_FAILED_TRAINING_POINTS;
-
break;
}
case SPELL_EFFECT_FEED_PET:
{
- if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_targets.getItemTarget() )
+ if (m_caster->GetTypeId() != TYPEID_PLAYER)
+ return SPELL_FAILED_BAD_TARGETS;
+
+ Item* foodItem = m_targets.getItemTarget();
+ if(!foodItem)
return SPELL_FAILED_BAD_TARGETS;
- Pet* pet = m_caster->GetPet();
+ Pet* pet = ((Player*)m_caster)->GetPet();
if(!pet)
return SPELL_FAILED_NO_PET;
- if(!pet->HaveInDiet(m_targets.getItemTarget()->GetProto()))
+ if(!pet->HaveInDiet(foodItem->GetProto()))
return SPELL_FAILED_WRONG_PET_FOOD;
- if(!pet->GetCurrentFoodBenefitLevel(m_targets.getItemTarget()->GetProto()->ItemLevel))
+ if(!pet->GetCurrentFoodBenefitLevel(foodItem->GetProto()->ItemLevel))
return SPELL_FAILED_FOOD_LOWLEVEL;
if(m_caster->isInCombat() || pet->isInCombat())
@@ -3843,130 +4164,40 @@ uint8 Spell::CanCast(bool strict)
// In BattleGround players can use only flags and banners
if( ((Player*)m_caster)->InBattleGround() &&
- !((Player*)m_caster)->isAllowUseBattleGroundObject() )
+ !((Player*)m_caster)->CanUseBattleGroundObject() )
return SPELL_FAILED_TRY_AGAIN;
// get the lock entry
- LockEntry const *lockInfo = NULL;
+ uint32 lockId = 0;
if (GameObject* go=m_targets.getGOTarget())
- lockInfo = sLockStore.LookupEntry(go->GetLockId());
+ lockId = go->GetLockId();
else if(Item* itm=m_targets.getItemTarget())
- lockInfo = sLockStore.LookupEntry(itm->GetProto()->LockID);
+ lockId = itm->GetProto()->LockID;
- // check lock compatibility
- if (lockInfo)
- {
- // check for lock - key pair (checked by client also, just prevent cheating
- bool ok_key = false;
- for(int it = 0; it < 5; ++it)
- {
- switch(lockInfo->keytype[it])
- {
- case LOCK_KEY_NONE:
- break;
- case LOCK_KEY_ITEM:
- {
- if(lockInfo->key[it])
- {
- if(m_CastItem && m_CastItem->GetEntry()==lockInfo->key[it])
- ok_key =true;
- break;
- }
- }
- case LOCK_KEY_SKILL:
- {
- if(uint32(m_spellInfo->EffectMiscValue[i])!=lockInfo->key[it])
- break;
+ SkillType skillId =SKILL_NONE;
+ int32 reqSkillValue = 0;
+ int32 skillValue = 0;
- switch(lockInfo->key[it])
- {
- case LOCKTYPE_HERBALISM:
- if(((Player*)m_caster)->HasSkill(SKILL_HERBALISM))
- ok_key =true;
- break;
- case LOCKTYPE_MINING:
- if(((Player*)m_caster)->HasSkill(SKILL_MINING))
- ok_key =true;
- break;
- default:
- ok_key =true;
- break;
- }
- }
- }
- if(ok_key)
- break;
- }
-
- if(!ok_key)
- return SPELL_FAILED_BAD_TARGETS;
- }
+ // check lock compatibility
+ SpellCastResult res = CanOpenLock(i,lockId,skillId,reqSkillValue,skillValue);
+ if(res != SPELL_CAST_OK)
+ return res;
// chance for fail at orange mining/herb/LockPicking gathering attempt
- if (!m_selfContainer || ((*m_selfContainer) != this))
- break;
-
- // get the skill value of the player
- int32 SkillValue = 0;
- bool canFailAtMax = true;
- if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_HERBALISM)
- {
- SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_HERBALISM);
- canFailAtMax = false;
- }
- else if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_MINING)
+ // second check prevent fail at rechecks
+ if(skillId != SKILL_NONE && (!m_selfContainer || ((*m_selfContainer) != this)))
{
- SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_MINING);
- canFailAtMax = false;
- }
- else if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_PICKLOCK)
- SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_LOCKPICKING);
-
- // castitem check: rogue using skeleton keys. the skill values should not be added in this case.
- if(m_CastItem)
- SkillValue = 0;
+ bool canFailAtMax = skillId != SKILL_HERBALISM && skillId != SKILL_MINING;
- // add the damage modifier from the spell casted (cheat lock / skeleton key etc.) (use m_currentBasePoints, CalculateDamage returns wrong value)
- // TODO: is this a hack?
- SkillValue += m_currentBasePoints[i]+1;
-
- // get the required lock value
- int32 ReqValue=0;
- if (lockInfo)
- {
- // check for lock - key pair
- bool ok = false;
- for(int it = 0; it < 5; ++it)
- {
- if(lockInfo->keytype[it]==LOCK_KEY_ITEM && lockInfo->key[it] && m_CastItem && m_CastItem->GetEntry()==lockInfo->key[it])
- {
- // if so, we're good to go
- ok = true;
- break;
- }
- }
- if(ok)
- break;
-
- if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_PICKLOCK)
- ReqValue = lockInfo->requiredlockskill;
- else
- ReqValue = lockInfo->requiredminingskill;
+ // chance for failure in orange gather / lockpick (gathering skill can't fail at maxskill)
+ if((canFailAtMax || skillValue < sWorld.GetConfigMaxSkillValue()) && reqSkillValue > irand(skillValue-25, skillValue+37))
+ return SPELL_FAILED_TRY_AGAIN;
}
-
- // skill doesn't meet the required value
- if (ReqValue > SkillValue)
- return SPELL_FAILED_LOW_CASTLEVEL;
-
- // chance for failure in orange gather / lockpick (gathering skill can't fail at maxskill)
- if((canFailAtMax || SkillValue < sWorld.GetConfigMaxSkillValue()) && ReqValue > irand(SkillValue-25, SkillValue+37))
- return SPELL_FAILED_TRY_AGAIN;
-
break;
}
case SPELL_EFFECT_SUMMON_DEAD_PET:
{
- Creature *pet = m_caster->GetPet();
+ Creature *pet = m_caster->GetGuardianPet();
if(!pet)
return SPELL_FAILED_NO_PET;
@@ -3975,16 +4206,11 @@ uint8 Spell::CanCast(bool strict)
break;
}
- // This is generic summon effect now and don't make this check for summon types similar
- // SPELL_EFFECT_SUMMON_CRITTER, SPELL_EFFECT_SUMMON_WILD or SPELL_EFFECT_SUMMON_GUARDIAN.
- // These won't show up in m_caster->GetPetGUID()
+ // This is generic summon effect
case SPELL_EFFECT_SUMMON:
{
switch(m_spellInfo->EffectMiscValueB[i])
{
- case SUMMON_TYPE_POSESSED:
- case SUMMON_TYPE_POSESSED2:
- case SUMMON_TYPE_POSESSED3:
case SUMMON_TYPE_DEMON:
case SUMMON_TYPE_SUMMON:
{
@@ -3996,13 +4222,25 @@ uint8 Spell::CanCast(bool strict)
break;
}
}
+ SummonPropertiesEntry const *SummonProperties = sSummonPropertiesStore.LookupEntry(m_spellInfo->EffectMiscValueB[i]);
+ if(!SummonProperties)
+ break;
+ switch(SummonProperties->Category)
+ {
+ case SUMMON_CATEGORY_POSSESSED:
+ {
+ if(m_caster->GetPetGUID())
+ return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+
+ if(m_caster->GetCharmGUID())
+ return SPELL_FAILED_ALREADY_HAVE_CHARM;
+ break;
+ }
+ }
break;
}
- // Don't make this check for SPELL_EFFECT_SUMMON_CRITTER, SPELL_EFFECT_SUMMON_WILD or SPELL_EFFECT_SUMMON_GUARDIAN.
- // These won't show up in m_caster->GetPetGUID()
- case SPELL_EFFECT_SUMMON_POSSESSED:
+ // Not used for summon?
case SPELL_EFFECT_SUMMON_PHANTASM:
- case SPELL_EFFECT_SUMMON_DEMON:
{
if(m_caster->GetPetGUID())
return SPELL_FAILED_ALREADY_HAVE_SUMMON;
@@ -4016,13 +4254,11 @@ uint8 Spell::CanCast(bool strict)
{
if(m_caster->GetPetGUID()) //let warlock do a replacement summon
{
-
- Pet* pet = ((Player*)m_caster)->GetPet();
-
if (m_caster->GetTypeId()==TYPEID_PLAYER && m_caster->getClass()==CLASS_WARLOCK)
{
if (strict) //starting cast, trigger pet stun (cast by pet so it doesn't attack player)
- pet->CastSpell(pet, 32752, true, NULL, NULL, pet->GetGUID());
+ if(Pet* pet = ((Player*)m_caster)->GetPet())
+ pet->CastSpell(pet, 32752, true, NULL, NULL, pet->GetGUID());
}
else
return SPELL_FAILED_ALREADY_HAVE_SUMMON;
@@ -4058,7 +4294,7 @@ uint8 Spell::CanCast(bool strict)
case SPELL_EFFECT_LEAP:
case SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER:
{
- float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+ float dis = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
float fx = m_caster->GetPositionX() + dis * cos(m_caster->GetOrientation());
float fy = m_caster->GetPositionY() + dis * sin(m_caster->GetOrientation());
// teleport a bit above terrain level to avoid falling below it
@@ -4094,25 +4330,38 @@ uint8 Spell::CanCast(bool strict)
{
case SPELL_AURA_DUMMY:
{
- if(m_spellInfo->Id == 1515)
+ //custom check
+ switch(m_spellInfo->Id)
{
- if (!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER)
- return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
+ case 61336:
+ if(m_caster->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_caster)->IsInFeralForm())
+ return SPELL_FAILED_ONLY_SHAPESHIFT;
+ break;
+ case 1515:
+ {
+ if (!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER)
+ return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
- if (m_targets.getUnitTarget()->getLevel() > m_caster->getLevel())
- return SPELL_FAILED_HIGHLEVEL;
+ if (m_targets.getUnitTarget()->getLevel() > m_caster->getLevel())
+ return SPELL_FAILED_HIGHLEVEL;
- // use SMSG_PET_TAME_FAILURE?
- if (!((Creature*)m_targets.getUnitTarget())->GetCreatureInfo()->isTameable ())
- return SPELL_FAILED_BAD_TARGETS;
+ // use SMSG_PET_TAME_FAILURE?
+ if (!((Creature*)m_targets.getUnitTarget())->GetCreatureInfo()->isTameable ())
+ return SPELL_FAILED_BAD_TARGETS;
- if(m_caster->GetPetGUID())
- return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+ if(m_caster->GetPetGUID())
+ return SPELL_FAILED_ALREADY_HAVE_SUMMON;
- if(m_caster->GetCharmGUID())
- return SPELL_FAILED_ALREADY_HAVE_CHARM;
+ if(m_caster->GetCharmGUID())
+ return SPELL_FAILED_ALREADY_HAVE_CHARM;
+
+ break;
+ }
+ default:
+ break;
}
- }break;
+ break;
+ }
case SPELL_AURA_MOD_POSSESS:
case SPELL_AURA_MOD_CHARM:
{
@@ -4145,17 +4394,14 @@ uint8 Spell::CanCast(bool strict)
return SPELL_FAILED_NO_MOUNTS_ALLOWED;
// Ignore map check if spell have AreaId. AreaId already checked and this prevent special mount spells
- if (m_caster->GetTypeId()==TYPEID_PLAYER && !sMapStore.LookupEntry(m_caster->GetMapId())->IsMountAllowed() && !m_IsTriggeredSpell && !m_spellInfo->AreaId)
- return SPELL_FAILED_NO_MOUNTS_ALLOWED;
-
- if (m_caster->GetAreaId()==35)
+ if (m_caster->GetTypeId()==TYPEID_PLAYER && !sMapStore.LookupEntry(m_caster->GetMapId())->IsMountAllowed() && !m_IsTriggeredSpell && !m_spellInfo->AreaGroupId)
return SPELL_FAILED_NO_MOUNTS_ALLOWED;
ShapeshiftForm form = m_caster->m_form;
if( form == FORM_CAT || form == FORM_TREE || form == FORM_TRAVEL ||
form == FORM_AQUA || form == FORM_BEAR || form == FORM_DIREBEAR ||
form == FORM_CREATUREBEAR || form == FORM_GHOSTWOLF || form == FORM_FLIGHT ||
- form == FORM_FLIGHT_EPIC || form == FORM_MOONKIN )
+ form == FORM_FLIGHT_EPIC || form == FORM_MOONKIN || form == FORM_METAMORPHOSIS )
return SPELL_FAILED_NOT_SHAPESHIFT;
break;
@@ -4174,11 +4420,10 @@ uint8 Spell::CanCast(bool strict)
case SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED:
case SPELL_AURA_FLY:
{
- // not allow cast fly spells at old maps by players (all spells is self target)
+ // not allow cast fly spells at old maps by players (all spells is self target)
if(m_caster->GetTypeId()==TYPEID_PLAYER)
{
- if( !((Player*)m_caster)->isGameMaster() &&
- GetVirtualMapForMapAndZone(m_caster->GetMapId(),m_caster->GetZoneId()) != 530)
+ if( !((Player*)m_caster)->IsAllowUseFlyMountsHere() )
return SPELL_FAILED_NOT_HERE;
}
break;
@@ -4202,10 +4447,10 @@ uint8 Spell::CanCast(bool strict)
}
// all ok
- return 0;
+ return SPELL_CAST_OK;
}
-int16 Spell::PetCanCast(Unit* target)
+SpellCastResult Spell::CheckPetCast(Unit* target)
{
if(!m_caster->isAlive())
return SPELL_FAILED_CASTER_DEAD;
@@ -4253,20 +4498,16 @@ int16 Spell::PetCanCast(Unit* target)
return SPELL_FAILED_NOT_READY;
}
- uint16 result = CanCast(true);
- if(result != 0)
- return result;
- else
- return -1; //this allows to check spell fail 0, in combat
+ return CheckCast(true);
}
-uint8 Spell::CheckCasterAuras() const
+SpellCastResult Spell::CheckCasterAuras() const
{
// Flag drop spells totally immuned to caster auras
// FIXME: find more nice check for all totally immuned spells
// AttributesEx3 & 0x10000000?
if(m_spellInfo->Id==23336 || m_spellInfo->Id==23334 || m_spellInfo->Id==34991)
- return 0;
+ return SPELL_CAST_OK;
uint8 school_immune = 0;
uint32 mechanic_immune = 0;
@@ -4291,21 +4532,22 @@ uint8 Spell::CheckCasterAuras() const
}
//Check whether the cast should be prevented by any state you might have.
- uint8 prevented_reason = 0;
+ SpellCastResult prevented_reason = SPELL_CAST_OK;
// Have to check if there is a stun aura. Otherwise will have problems with ghost aura apply while logging out
- if(!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED) && m_caster->HasAuraType(SPELL_AURA_MOD_STUN))
+ uint32 unitflag = m_caster->GetUInt32Value(UNIT_FIELD_FLAGS); // Get unit state
+ if(unitflag & UNIT_FLAG_STUNNED && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED))
prevented_reason = SPELL_FAILED_STUNNED;
- else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED) && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED))
+ else if(unitflag & UNIT_FLAG_CONFUSED && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED))
prevented_reason = SPELL_FAILED_CONFUSED;
- else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING) && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED))
+ else if(unitflag & UNIT_FLAG_FLEEING && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED))
prevented_reason = SPELL_FAILED_FLEEING;
- else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED) && m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_SILENCE)
+ else if(unitflag & UNIT_FLAG_SILENCED && m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_SILENCE)
prevented_reason = SPELL_FAILED_SILENCED;
- else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED) && m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_PACIFY)
+ else if(unitflag & UNIT_FLAG_PACIFIED && m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_PACIFY)
prevented_reason = SPELL_FAILED_PACIFIED;
// Attr must make flag drop spell totally immune from all effects
- if(prevented_reason)
+ if(prevented_reason != SPELL_CAST_OK)
{
if(school_immune || mechanic_immune || dispel_immune)
{
@@ -4315,37 +4557,43 @@ uint8 Spell::CheckCasterAuras() const
{
if(itr->second)
{
- if( GetSpellMechanicMask(itr->second->GetSpellProto(), itr->second->GetEffIndex()) & mechanic_immune )
+ if( GetAllSpellMechanicMask(itr->second->GetSpellProto()) & mechanic_immune )
continue;
- if( GetSpellSchoolMask(itr->second->GetSpellProto()) & school_immune )
+ if( GetAllSpellMechanicMask(itr->second->GetSpellProto()) & school_immune )
continue;
if( (1<<(itr->second->GetSpellProto()->Dispel)) & dispel_immune)
continue;
//Make a second check for spell failed so the right SPELL_FAILED message is returned.
//That is needed when your casting is prevented by multiple states and you are only immune to some of them.
- switch(itr->second->GetModifier()->m_auraname)
+ for (uint8 i=0;i<MAX_SPELL_EFFECTS;++i)
{
- case SPELL_AURA_MOD_STUN:
- if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED))
- return SPELL_FAILED_STUNNED;
- break;
- case SPELL_AURA_MOD_CONFUSE:
- if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED))
- return SPELL_FAILED_CONFUSED;
- break;
- case SPELL_AURA_MOD_FEAR:
- if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED))
- return SPELL_FAILED_FLEEING;
- break;
- case SPELL_AURA_MOD_SILENCE:
- case SPELL_AURA_MOD_PACIFY:
- case SPELL_AURA_MOD_PACIFY_SILENCE:
- if( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_PACIFY)
- return SPELL_FAILED_PACIFIED;
- else if ( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_SILENCE)
- return SPELL_FAILED_SILENCED;
- break;
+ if (AuraEffect * part = itr->second->GetPartAura(i))
+ {
+ switch(part->GetAuraName())
+ {
+ case SPELL_AURA_MOD_STUN:
+ if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED))
+ return SPELL_FAILED_STUNNED;
+ break;
+ case SPELL_AURA_MOD_CONFUSE:
+ if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED))
+ return SPELL_FAILED_CONFUSED;
+ break;
+ case SPELL_AURA_MOD_FEAR:
+ if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED))
+ return SPELL_FAILED_FLEEING;
+ break;
+ case SPELL_AURA_MOD_SILENCE:
+ case SPELL_AURA_MOD_PACIFY:
+ case SPELL_AURA_MOD_PACIFY_SILENCE:
+ if( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_PACIFY)
+ return SPELL_FAILED_PACIFIED;
+ else if ( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_SILENCE)
+ return SPELL_FAILED_SILENCED;
+ break;
+ }
+ }
}
}
}
@@ -4354,7 +4602,7 @@ uint8 Spell::CheckCasterAuras() const
else
return prevented_reason;
}
- return 0; // all ok
+ return SPELL_CAST_OK;
}
bool Spell::CanAutoCast(Unit* target)
@@ -4367,25 +4615,25 @@ bool Spell::CanAutoCast(Unit* target)
{
if( m_spellInfo->StackAmount <= 1)
{
- if( target->HasAura(m_spellInfo->Id, j) )
+ if( target->HasAuraEffect(m_spellInfo->Id, j) )
return false;
}
else
{
- if( target->GetAuras().count(Unit::spellEffectPair(m_spellInfo->Id, j)) >= m_spellInfo->StackAmount)
+ if( (target->GetAuraEffect(m_spellInfo->Id, j))->GetParentAura()->GetStackAmount() >= m_spellInfo->StackAmount)
return false;
}
}
else if ( IsAreaAuraEffect( m_spellInfo->Effect[j] ))
{
- if( target->HasAura(m_spellInfo->Id, j) )
+ if( target->HasAuraEffect(m_spellInfo->Id, j) )
return false;
}
}
- int16 result = PetCanCast(target);
+ SpellCastResult result = CheckPetCast(target);
- if(result == -1 || result == SPELL_FAILED_UNIT_NOT_INFRONT)
+ if(result == SPELL_CAST_OK || result == SPELL_FAILED_UNIT_NOT_INFRONT)
{
FillTargetMap();
//check if among target units, our WANTED target is as well (->only self cast spells return false)
@@ -4396,12 +4644,13 @@ bool Spell::CanAutoCast(Unit* target)
return false; //target invalid
}
-uint8 Spell::CheckRange(bool strict)
+SpellCastResult Spell::CheckRange(bool strict)
{
//float range_mod;
// self cast doesn't need range checking -- also for Starshards fix
- if (m_spellInfo->rangeIndex == 1) return 0;
+ if (m_spellInfo->rangeIndex == 1)
+ return SPELL_CAST_OK;
// i do not know why we need this
/*if (strict) //add radius of caster
@@ -4410,15 +4659,15 @@ uint8 Spell::CheckRange(bool strict)
range_mod = 6.25;*/
SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex);
- float max_range = GetSpellMaxRange(srange); // + range_mod;
- float min_range = GetSpellMinRange(srange);
+
+ Unit *target = m_targets.getUnitTarget();
+ float max_range = m_caster->GetSpellMaxRangeForTarget(target, srange); // + range_mod;
+ float min_range = m_caster->GetSpellMinRangeForTarget(target, srange);
uint32 range_type = GetSpellRangeType(srange);
if(Player* modOwner = m_caster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, max_range, this);
- Unit *target = m_targets.getUnitTarget();
-
if(target && target != m_caster)
{
if(range_type == SPELL_RANGE_MELEE)
@@ -4452,7 +4701,7 @@ uint8 Spell::CheckRange(bool strict)
return SPELL_FAILED_TOO_CLOSE;
}
- return 0; // ok
+ return SPELL_CAST_OK;
}
int32 Spell::CalculatePowerCost()
@@ -4492,9 +4741,12 @@ int32 Spell::CalculatePowerCost()
case POWER_FOCUS:
case POWER_ENERGY:
case POWER_HAPPINESS:
- // case POWER_RUNES:
powerCost += m_spellInfo->ManaCostPercentage * m_caster->GetMaxPower(Powers(m_spellInfo->powerType)) / 100;
break;
+ case POWER_RUNE:
+ case POWER_RUNIC_POWER:
+ sLog.outDebug("Spell::CalculateManaCost: Not implemented yet!");
+ break;
default:
sLog.outError("Spell::CalculateManaCost: Unknown power type '%d' in spell %d", m_spellInfo->powerType, m_spellInfo->Id);
return 0;
@@ -4520,39 +4772,43 @@ int32 Spell::CalculatePowerCost()
return powerCost;
}
-uint8 Spell::CheckPower()
+SpellCastResult Spell::CheckPower()
{
// item cast not used power
if(m_CastItem)
- return 0;
+ return SPELL_CAST_OK;
// health as power used - need check health amount
if(m_spellInfo->powerType == POWER_HEALTH)
{
if(m_caster->GetHealth() <= m_powerCost)
return SPELL_FAILED_CASTER_AURASTATE;
- return 0;
+ return SPELL_CAST_OK;
}
// Check valid power type
if( m_spellInfo->powerType >= MAX_POWERS )
{
- sLog.outError("Spell::CheckMana: Unknown power type '%d'", m_spellInfo->powerType);
+ sLog.outError("Spell::CheckPower: Unknown power type '%d'", m_spellInfo->powerType);
return SPELL_FAILED_UNKNOWN;
}
+
+ SpellCastResult failReason = CheckRuneCost(m_spellInfo->runeCostID);
+ if(failReason != SPELL_CAST_OK)
+ return failReason;
+
// Check power amount
Powers powerType = Powers(m_spellInfo->powerType);
if(m_caster->GetPower(powerType) < m_powerCost)
return SPELL_FAILED_NO_POWER;
else
- return 0;
+ return SPELL_CAST_OK;
}
-uint8 Spell::CheckItems()
+SpellCastResult Spell::CheckItems()
{
if (m_caster->GetTypeId() != TYPEID_PLAYER)
- return 0;
+ return SPELL_CAST_OK;
- uint32 itemid, itemcount;
Player* p_caster = (Player*)m_caster;
if(!m_CastItem)
@@ -4562,77 +4818,72 @@ uint8 Spell::CheckItems()
}
else
{
- itemid = m_CastItem->GetEntry();
+ uint32 itemid = m_CastItem->GetEntry();
if( !p_caster->HasItemCount(itemid,1) )
return SPELL_FAILED_ITEM_NOT_READY;
- else
- {
- ItemPrototype const *proto = m_CastItem->GetProto();
- if(!proto)
- return SPELL_FAILED_ITEM_NOT_READY;
- for (int i = 0; i<5; i++)
+ ItemPrototype const *proto = m_CastItem->GetProto();
+ if(!proto)
+ return SPELL_FAILED_ITEM_NOT_READY;
+
+ for (int i = 0; i<5; i++)
+ if (proto->Spells[i].SpellCharges)
+ if(m_CastItem->GetSpellCharges(i)==0)
+ return SPELL_FAILED_NO_CHARGES_REMAIN;
+
+ // consumable cast item checks
+ if (proto->Class == ITEM_CLASS_CONSUMABLE && m_targets.getUnitTarget())
+ {
+ // such items should only fail if there is no suitable effect at all - see Rejuvenation Potions for example
+ SpellCastResult failReason = SPELL_CAST_OK;
+ for (int i = 0; i < 3; i++)
{
- if (proto->Spells[i].SpellCharges)
+ // skip check, pet not required like checks, and for TARGET_PET m_targets.getUnitTarget() is not the real target but the caster
+ if (m_spellInfo->EffectImplicitTargetA[i] == TARGET_PET)
+ continue;
+
+ if (m_spellInfo->Effect[i] == SPELL_EFFECT_HEAL)
{
- if(m_CastItem->GetSpellCharges(i)==0)
- return SPELL_FAILED_NO_CHARGES_REMAIN;
+ if (m_targets.getUnitTarget()->GetHealth() == m_targets.getUnitTarget()->GetMaxHealth())
+ {
+ failReason = SPELL_FAILED_ALREADY_AT_FULL_HEALTH;
+ continue;
+ }
+ else
+ {
+ failReason = SPELL_CAST_OK;
+ break;
+ }
}
- }
- uint32 ItemClass = proto->Class;
- if (ItemClass == ITEM_CLASS_CONSUMABLE && m_targets.getUnitTarget())
- {
- // such items should only fail if there is no suitable effect at all - see Rejuvenation Potions for example
- uint8 failReason = 0;
- for (int i = 0; i < 3; i++)
+ // Mana Potion, Rage Potion, Thistle Tea(Rogue), ...
+ if (m_spellInfo->Effect[i] == SPELL_EFFECT_ENERGIZE)
{
- // skip check, pet not required like checks, and for TARGET_PET m_targets.getUnitTarget() is not the real target but the caster
- if (m_spellInfo->EffectImplicitTargetA[i] == TARGET_PET)
+ if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS)
+ {
+ failReason = SPELL_FAILED_ALREADY_AT_FULL_POWER;
continue;
+ }
- if (m_spellInfo->Effect[i] == SPELL_EFFECT_HEAL)
+ Powers power = Powers(m_spellInfo->EffectMiscValue[i]);
+ if (m_targets.getUnitTarget()->GetPower(power) == m_targets.getUnitTarget()->GetMaxPower(power))
{
- if (m_targets.getUnitTarget()->GetHealth() == m_targets.getUnitTarget()->GetMaxHealth())
- {
- failReason = (uint8)SPELL_FAILED_ALREADY_AT_FULL_HEALTH;
- continue;
- }
- else
- {
- failReason = 0;
- break;
- }
+ failReason = SPELL_FAILED_ALREADY_AT_FULL_POWER;
+ continue;
}
-
- // Mana Potion, Rage Potion, Thistle Tea(Rogue), ...
- if (m_spellInfo->Effect[i] == SPELL_EFFECT_ENERGIZE)
+ else
{
- if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS)
- {
- failReason = (uint8)SPELL_FAILED_ALREADY_AT_FULL_POWER;
- continue;
- }
-
- Powers power = Powers(m_spellInfo->EffectMiscValue[i]);
- if (m_targets.getUnitTarget()->GetPower(power) == m_targets.getUnitTarget()->GetMaxPower(power))
- {
- failReason = (uint8)SPELL_FAILED_ALREADY_AT_FULL_POWER;
- continue;
- }
- else
- {
- failReason = 0;
- break;
- }
+ failReason = SPELL_CAST_OK;
+ break;
}
}
- if (failReason)
- return failReason;
}
+ if (failReason != SPELL_CAST_OK)
+ return failReason;
}
}
+ // check target item
if(m_targets.getItemTargetGUID())
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
@@ -4651,6 +4902,7 @@ uint8 Spell::CheckItems()
return SPELL_FAILED_EQUIPPED_ITEM_CLASS;
}
+ // check spell focus object
if(m_spellInfo->RequiresSpellFocus)
{
CellPair p(Trinity::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
@@ -4658,29 +4910,29 @@ uint8 Spell::CheckItems()
cell.data.Part.reserved = ALL_DISTRICT;
GameObject* ok = NULL;
- Trinity::GameObjectFocusCheck go_check(m_caster,m_spellInfo->RequiresSpellFocus);
- Trinity::GameObjectSearcher<Trinity::GameObjectFocusCheck> checker(ok,go_check);
+ MaNGOS::GameObjectFocusCheck go_check(m_caster,m_spellInfo->RequiresSpellFocus);
+ MaNGOS::GameObjectSearcher<MaNGOS::GameObjectFocusCheck> checker(m_caster,ok,go_check);
TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectFocusCheck>, GridTypeMapContainer > object_checker(checker);
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
if(!ok)
- return (uint8)SPELL_FAILED_REQUIRES_SPELL_FOCUS;
+ return SPELL_FAILED_REQUIRES_SPELL_FOCUS;
focusObject = ok; // game object found in range
}
- if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP &&
- m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION)))
+ // check reagents (ignore triggered spells with reagents processed by original spell) and special reagent ignore case.
+ if (!m_IsTriggeredSpell && !p_caster->CanNoReagentCast(m_spellInfo))
{
for(uint32 i=0;i<8;i++)
{
if(m_spellInfo->Reagent[i] <= 0)
continue;
- itemid = m_spellInfo->Reagent[i];
- itemcount = m_spellInfo->ReagentCount[i];
+ uint32 itemid = m_spellInfo->Reagent[i];
+ uint32 itemcount = m_spellInfo->ReagentCount[i];
// if CastItem is also spell reagent
if( m_CastItem && m_CastItem->GetEntry() == itemid )
@@ -4688,7 +4940,7 @@ uint8 Spell::CheckItems()
ItemPrototype const *proto = m_CastItem->GetProto();
if(!proto)
return SPELL_FAILED_ITEM_NOT_READY;
- for(int s=0;s<5;s++)
+ for(int s=0; s < MAX_ITEM_PROTO_SPELLS; ++s)
{
// CastItem will be used up and does not count as reagent
int32 charges = m_CastItem->GetSpellCharges(s);
@@ -4700,10 +4952,11 @@ uint8 Spell::CheckItems()
}
}
if( !p_caster->HasItemCount(itemid,itemcount) )
- return (uint8)SPELL_FAILED_ITEM_NOT_READY; //0x54
+ return SPELL_FAILED_ITEM_NOT_READY; //0x54
}
}
+ // check totem-item requirements (items presence in inventory)
uint32 totems = 2;
for(int i=0;i<2;++i)
{
@@ -4718,9 +4971,9 @@ uint8 Spell::CheckItems()
totems -= 1;
}
if(totems != 0)
- return (uint8)SPELL_FAILED_TOTEMS; //0x7C
+ return SPELL_FAILED_TOTEMS; //0x7C
- //Check items for TotemCategory
+ // Check items for TotemCategory (items presence in inventory)
uint32 TotemCategory = 2;
for(int i=0;i<2;++i)
{
@@ -4736,8 +4989,9 @@ uint8 Spell::CheckItems()
TotemCategory -= 1;
}
if(TotemCategory != 0)
- return (uint8)SPELL_FAILED_TOTEM_CATEGORY; //0x7B
+ return SPELL_FAILED_TOTEM_CATEGORY; //0x7B
+ // special checks for spell effects
for(int i = 0; i < 3; i++)
{
switch (m_spellInfo->Effect[i])
@@ -4757,6 +5011,7 @@ uint8 Spell::CheckItems()
break;
}
case SPELL_EFFECT_ENCHANT_ITEM:
+ case SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC:
{
Item* targetItem = m_targets.getItemTarget();
if(!targetItem)
@@ -4840,13 +5095,36 @@ uint8 Spell::CheckItems()
return SPELL_FAILED_LOW_CASTLEVEL;
//make sure the player has the required ores in inventory
if(m_targets.getItemTarget()->GetCount() < 5)
- return SPELL_FAILED_PROSPECT_NEED_MORE;
+ return SPELL_FAILED_NEED_MORE_ITEMS;
if(!LootTemplates_Prospecting.HaveLootFor(m_targets.getItemTargetEntry()))
return SPELL_FAILED_CANT_BE_PROSPECTED;
break;
}
+ case SPELL_EFFECT_MILLING:
+ {
+ if(!m_targets.getItemTarget())
+ return SPELL_FAILED_CANT_BE_MILLED;
+ //ensure item is a millable herb
+ if(!(m_targets.getItemTarget()->GetProto()->BagFamily & BAG_FAMILY_MASK_HERBS) || m_targets.getItemTarget()->GetProto()->Class != ITEM_CLASS_TRADE_GOODS)
+ return SPELL_FAILED_CANT_BE_MILLED;
+ //prevent milling in trade slot
+ if( m_targets.getItemTarget()->GetOwnerGUID() != m_caster->GetGUID() )
+ return SPELL_FAILED_CANT_BE_MILLED;
+ //Check for enough skill in inscription
+ uint32 item_millingskilllevel = m_targets.getItemTarget()->GetProto()->RequiredSkillRank;
+ if(item_millingskilllevel >p_caster->GetSkillValue(SKILL_INSCRIPTION))
+ return SPELL_FAILED_LOW_CASTLEVEL;
+ //make sure the player has the required herbs in inventory
+ if(m_targets.getItemTarget()->GetCount() < 5)
+ return SPELL_FAILED_NEED_MORE_ITEMS;
+
+ if(!LootTemplates_Milling.HaveLootFor(m_targets.getItemTargetEntry()))
+ return SPELL_FAILED_CANT_BE_MILLED;
+
+ break;
+ }
case SPELL_EFFECT_WEAPON_DAMAGE:
case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
{
@@ -4915,7 +5193,7 @@ uint8 Spell::CheckItems()
}
}
- return uint8(0);
+ return SPELL_CAST_OK;
}
void Spell::Delayed() // only called in DealDamage()
@@ -4926,18 +5204,22 @@ void Spell::Delayed() // only called in DealDamage()
//if (m_spellState == SPELL_STATE_DELAYED)
// return; // spell is active and can't be time-backed
+ if(isDelayableNoMore()) // Spells may only be delayed twice
+ return;
+
// spells not loosing casting time ( slam, dynamites, bombs.. )
//if(!(m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_DAMAGE))
// return;
- //check resist chance
- int32 resistChance = 100; //must be initialized to 100 for percent modifiers
- ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME,resistChance, this);
- resistChance += m_caster->GetTotalAuraModifier(SPELL_AURA_RESIST_PUSHBACK) - 100;
- if (roll_chance_i(resistChance))
+ //check pushback reduce
+ int32 delaytime = 500; // spellcasting delay is normally 500ms
+ int32 delayReduce = 100; // must be initialized to 100 for percent modifiers
+ ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this);
+ delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100;
+ if(delayReduce >= 100)
return;
- int32 delaytime = GetNextDelayAtDamageMsTime();
+ delaytime = delaytime * (100 - delayReduce) / 100;
if(int32(m_timer) + delaytime > m_casttime)
{
@@ -4961,14 +5243,18 @@ void Spell::DelayedChannel()
if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER || getState() != SPELL_STATE_CASTING)
return;
- //check resist chance
- int32 resistChance = 100; //must be initialized to 100 for percent modifiers
- ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME,resistChance, this);
- resistChance += m_caster->GetTotalAuraModifier(SPELL_AURA_RESIST_PUSHBACK) - 100;
- if (roll_chance_i(resistChance))
+ if(isDelayableNoMore()) // Spells may only be delayed twice
return;
- int32 delaytime = GetNextDelayAtDamageMsTime();
+ //check pushback reduce
+ int32 delaytime = GetSpellDuration(m_spellInfo) * 25 / 100; // channeling delay is normally 25% of its time per hit
+ int32 delayReduce = 100; // must be initialized to 100 for percent modifiers
+ ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME,delayReduce, this);
+ delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100;
+ if(delayReduce >= 100)
+ return;
+
+ delaytime = delaytime * (100 - delayReduce) / 100;
if(int32(m_timer) < delaytime)
{
@@ -4987,11 +5273,8 @@ void Spell::DelayedChannel()
Unit* unit = m_caster->GetGUID()==ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID);
if (unit)
{
- for (int j=0;j<3;j++)
- if( ihit->effectMask & (1<<j) )
- unit->DelayAura(m_spellInfo->Id, j, delaytime);
+ unit->DelayAura(m_spellInfo->Id, m_caster->GetGUID(), delaytime);
}
-
}
}
@@ -5022,17 +5305,12 @@ void Spell::UpdatePointers()
m_targets.Update(m_caster);
}
-bool Spell::IsAffectedBy(SpellEntry const *spellInfo, uint32 effectId)
-{
- return spellmgr.IsAffectedBySpell(m_spellInfo,spellInfo->Id,effectId,spellInfo->EffectItemType[effectId]);
-}
-
bool Spell::CheckTargetCreatureType(Unit* target) const
{
uint32 spellCreatureTargetMask = m_spellInfo->TargetCreatureType;
// Curse of Doom : not find another way to fix spell target check :/
- if(m_spellInfo->SpellFamilyName==SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags == 0x0200000000LL)
+ if(m_spellInfo->SpellFamilyName==SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags.IsEqual(0,0x02,0))
{
// not allow cast at player
if(target->GetTypeId()==TYPEID_PLAYER)
@@ -5066,7 +5344,7 @@ CurrentSpellTypes Spell::GetCurrentContainer()
return(CURRENT_GENERIC_SPELL);
}
-bool Spell::CheckTarget( Unit* target, uint32 eff, bool hitPhase )
+bool Spell::CheckTarget( Unit* target, uint32 eff )
{
// Check targets for creature type mask and remove not appropriate (skip explicit self target case, maybe need other explicit targets)
if(m_spellInfo->EffectImplicitTargetA[eff]!=TARGET_SELF)
@@ -5075,6 +5353,12 @@ bool Spell::CheckTarget( Unit* target, uint32 eff, bool hitPhase )
return false;
}
+ // Check Aura spell req (need for AoE spells)
+ if(m_spellInfo->targetAuraSpell && !target->HasAura(m_spellInfo->targetAuraSpell))
+ return false;
+ if (m_spellInfo->excludeTargetAuraSpell && target->HasAura(m_spellInfo->excludeTargetAuraSpell))
+ return false;
+
// Check targets for not_selectable unit flag and remove
// A player can cast spells on his pet (or other controlled unit) though in any state
if (target != m_caster && target->GetCharmerOrOwnerGUID() != m_caster->GetGUID())
@@ -5135,7 +5419,13 @@ bool Spell::CheckTarget( Unit* target, uint32 eff, bool hitPhase )
// all ok by some way or another, skip normal check
break;
default: // normal case
- if(target!=m_caster && !target->IsWithinLOSInMap(m_caster))
+ // Get GO cast coordinates if original caster -> GO
+ WorldObject *caster = NULL;
+ if (m_originalCasterGUID)
+ caster = ObjectAccessor::GetGameObject(*m_caster, m_originalCasterGUID);
+ if (!caster)
+ caster = m_caster;
+ if(target!=m_caster && !target->IsWithinLOSInMap(caster))
return false;
break;
}
@@ -5149,14 +5439,13 @@ Unit* Spell::SelectMagnetTarget()
if(target && m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && target->HasAuraType(SPELL_AURA_SPELL_MAGNET)) //Attributes & 0x10 what is this?
{
- Unit::AuraList const& magnetAuras = target->GetAurasByType(SPELL_AURA_SPELL_MAGNET);
- for(Unit::AuraList::const_iterator itr = magnetAuras.begin(); itr != magnetAuras.end(); ++itr)
+ Unit::AuraEffectList const& magnetAuras = target->GetAurasByType(SPELL_AURA_SPELL_MAGNET);
+ for(Unit::AuraEffectList::const_iterator itr = magnetAuras.begin(); itr != magnetAuras.end(); ++itr)
{
if(Unit* magnet = (*itr)->GetCaster())
{
- if((*itr)->m_procCharges>0)
+ if((*itr)->GetParentAura()->DropAuraCharge())
{
- (*itr)->SetAuraProcCharges((*itr)->m_procCharges-1);
target = magnet;
m_targets.setUnitTarget(target);
AddUnitTarget(target, 0);
@@ -5180,7 +5469,7 @@ Unit* Spell::SelectMagnetTarget()
bool Spell::IsNeedSendToClient() const
{
- return m_spellInfo->SpellVisual!=0 || IsChanneledSpell(m_spellInfo) ||
+ return m_spellInfo->SpellVisual[0] || m_spellInfo->SpellVisual[1] || IsChanneledSpell(m_spellInfo) ||
m_spellInfo->speed > 0.0f || !m_triggeredByAuraSpell && !m_IsTriggeredSpell;
}
@@ -5259,9 +5548,6 @@ bool SpellEvent::Execute(uint64 e_time, uint32 p_time)
// another non-melee non-delayed spell is casted now, abort
m_Spell->cancel();
}
- // Check if target of channeled spell still in range
- else if (m_Spell->CheckRange(false))
- m_Spell->cancel();
else
{
// do the action (pass spell to channeling state)
@@ -5364,6 +5650,14 @@ void Spell::CalculateDamageDoneForAllTargets()
}
}
+ bool usesAmmo=true;
+ Unit::AuraEffectList const& Auras = m_caster->GetAurasByType(SPELL_AURA_ABILITY_CONSUME_NO_AMMO);
+ for(Unit::AuraEffectList::const_iterator j = Auras.begin();j != Auras.end(); ++j)
+ {
+ if((*j)->isAffectedOnSpell(m_spellInfo))
+ usesAmmo=false;
+ }
+
for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
{
TargetInfo &target = *ihit;
@@ -5376,6 +5670,28 @@ void Spell::CalculateDamageDoneForAllTargets()
if (!unit)
continue;
+ if (usesAmmo)
+ {
+ bool ammoTaken=false;
+ for (uint8 i=0;i<3;i++)
+ {
+ if (!(mask & 1<<i))
+ continue;
+ switch (m_spellInfo->Effect[i])
+ {
+ case SPELL_EFFECT_SCHOOL_DAMAGE:
+ case SPELL_EFFECT_WEAPON_DAMAGE:
+ case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
+ case SPELL_EFFECT_NORMALIZED_WEAPON_DMG:
+ case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE:
+ ammoTaken=true;
+ TakeAmmo();
+ }
+ if (ammoTaken)
+ break;
+ }
+ }
+
if (target.missCondition==SPELL_MISS_NONE) // In case spell hit target, do all effect on that target
target.damage += CalculateDamageDone(unit, mask, multiplier);
else if (target.missCondition == SPELL_MISS_REFLECT) // In case spell reflect from target, do all effect on caster (if hit)
@@ -5434,3 +5750,64 @@ int32 Spell::CalculateDamageDone(Unit *unit, const uint32 effectMask, float *mul
return damageDone;
}
+SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& skillId, int32& reqSkillValue, int32& skillValue)
+{
+ if(!lockId) // possible case for GO and maybe for items.
+ return SPELL_CAST_OK;
+
+ // Get LockInfo
+ LockEntry const *lockInfo = sLockStore.LookupEntry(lockId);
+
+ if (!lockInfo)
+ return SPELL_FAILED_BAD_TARGETS;
+
+ bool reqKey = false; // some locks not have reqs
+
+ for(int j = 0; j < 8; ++j)
+ {
+ switch(lockInfo->Type[j])
+ {
+ // check key item (many fit cases can be)
+ case LOCK_KEY_ITEM:
+ if(lockInfo->Index[j] && m_CastItem && m_CastItem->GetEntry()==lockInfo->Index[j])
+ return SPELL_CAST_OK;
+ reqKey = true;
+ break;
+ // check key skill (only single first fit case can be)
+ case LOCK_KEY_SKILL:
+ {
+ reqKey = true;
+
+ // wrong locktype, skip
+ if(uint32(m_spellInfo->EffectMiscValue[effIndex]) != lockInfo->Index[j])
+ continue;
+
+ skillId = SkillByLockType(LockType(lockInfo->Index[j]));
+
+ if ( skillId != SKILL_NONE )
+ {
+ // skill bonus provided by casting spell (mostly item spells)
+ // add the damage modifier from the spell casted (cheat lock / skeleton key etc.) (use m_currentBasePoints, CalculateDamage returns wrong value)
+ uint32 spellSkillBonus = uint32(m_currentBasePoints[effIndex]+1);
+ reqSkillValue = lockInfo->Skill[j];
+
+ // castitem check: rogue using skeleton keys. the skill values should not be added in this case.
+ skillValue = m_CastItem || m_caster->GetTypeId()!= TYPEID_PLAYER ?
+ 0 : ((Player*)m_caster)->GetSkillValue(skillId);
+
+ skillValue += spellSkillBonus;
+
+ if (skillValue < reqSkillValue)
+ return SPELL_FAILED_LOW_CASTLEVEL;
+ }
+
+ return SPELL_CAST_OK;
+ }
+ }
+ }
+
+ if(reqKey)
+ return SPELL_FAILED_BAD_TARGETS;
+
+ return SPELL_CAST_OK;
+}