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