aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Spells/Spell.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/game/Spells/Spell.cpp')
-rw-r--r--src/server/game/Spells/Spell.cpp549
1 files changed, 279 insertions, 270 deletions
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 93e14154302..4f8562ce5ec 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -53,6 +53,7 @@
#include "SpellScript.h"
#include "InstanceScript.h"
#include "SpellInfo.h"
+#include "DB2Stores.h"
#include "Battlefield.h"
#include "BattlefieldMgr.h"
@@ -532,7 +533,7 @@ m_caster((info->AttributesEx6 & SPELL_ATTR6_CAST_BY_CHARMER && caster->GetCharme
// wand case
if ((m_caster->getClassMask() & CLASSMASK_WAND_USERS) != 0 && m_caster->GetTypeId() == TYPEID_PLAYER)
if (Item* pItem = m_caster->ToPlayer()->GetWeaponForAttack(RANGED_ATTACK))
- m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetTemplate()->Damage[0].DamageType);
+ m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetTemplate()->DamageType);
if (originalCasterGUID)
m_originalCasterGUID = originalCasterGUID;
@@ -795,7 +796,10 @@ void Spell::SelectSpellTargets()
else if (m_spellInfo->Speed > 0.0f)
{
float dist = m_caster->GetDistance(*m_targets.GetDstPos());
- m_delayMoment = (uint64) floor(dist / m_spellInfo->Speed * 1000.0f);
+ if (!(m_spellInfo->AttributesEx9 & SPELL_ATTR9_SPECIAL_DELAY_CALCULATION))
+ m_delayMoment = uint64(floor(dist / m_spellInfo->Speed * 1000.0f));
+ else
+ m_delayMoment = uint64(m_spellInfo->Speed * 1000.0f);
}
}
}
@@ -1067,14 +1071,7 @@ void Spell::SelectImplicitConeTargets(SpellEffIndex effIndex, SpellImplicitTarge
{
// Other special target selection goes here
if (uint32 maxTargets = m_spellValue->MaxAffectedTargets)
- {
- Unit::AuraEffectList const& Auras = m_caster->GetAuraEffectsByType(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();
-
Trinity::Containers::RandomResizeList(targets, maxTargets);
- }
// for compability with older code - add only unit and go targets
/// @todo remove this
@@ -1349,14 +1346,7 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge
// Other special target selection goes here
if (uint32 maxTargets = m_spellValue->MaxAffectedTargets)
- {
- Unit::AuraEffectList const& Auras = m_caster->GetAuraEffectsByType(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();
-
Trinity::Containers::RandomResizeList(unitTargets, maxTargets);
- }
for (std::list<Unit*>::iterator itr = unitTargets.begin(); itr != unitTargets.end(); ++itr)
AddUnitTarget(*itr, effMask, false);
@@ -1365,14 +1355,7 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge
if (!gObjTargets.empty())
{
if (uint32 maxTargets = m_spellValue->MaxAffectedTargets)
- {
- Unit::AuraEffectList const& Auras = m_caster->GetAuraEffectsByType(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();
-
Trinity::Containers::RandomResizeList(gObjTargets, maxTargets);
- }
for (std::list<GameObject*>::iterator itr = gObjTargets.begin(); itr != gObjTargets.end(); ++itr)
AddGOTarget(*itr, effMask);
@@ -1747,6 +1730,9 @@ void Spell::SelectImplicitTrajTargets()
trajDst.Relocate(x, y, z, m_caster->GetOrientation());
m_targets.ModDst(trajDst);
}
+
+ if (Vehicle* veh = m_caster->GetVehicleKit())
+ veh->SetLastShootPos(*m_targets.GetDstPos());
}
void Spell::SelectEffectTypeImplicitTargets(uint8 effIndex)
@@ -2181,7 +2167,11 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*=
if (dist < 5.0f)
dist = 5.0f;
- targetInfo.timeDelay = (uint64) floor(dist / m_spellInfo->Speed * 1000.0f);
+
+ if (!(m_spellInfo->AttributesEx9 & SPELL_ATTR9_SPECIAL_DELAY_CALCULATION))
+ targetInfo.timeDelay = uint64(floor(dist / m_spellInfo->Speed * 1000.0f));
+ else
+ targetInfo.timeDelay = uint64(m_spellInfo->Speed * 1000.0f);
// Calculate minimum incoming time
if (m_delayMoment == 0 || m_delayMoment > targetInfo.timeDelay)
@@ -2260,7 +2250,12 @@ void Spell::AddGOTarget(GameObject* go, uint32 effectMask)
float dist = m_caster->GetDistance(go->GetPositionX(), go->GetPositionY(), go->GetPositionZ());
if (dist < 5.0f)
dist = 5.0f;
- target.timeDelay = uint64(floor(dist / m_spellInfo->Speed * 1000.0f));
+
+ if (!(m_spellInfo->AttributesEx9 & SPELL_ATTR9_SPECIAL_DELAY_CALCULATION))
+ target.timeDelay = uint64(floor(dist / m_spellInfo->Speed * 1000.0f));
+ else
+ target.timeDelay = uint64(m_spellInfo->Speed * 1000.0f);
+
if (m_delayMoment == 0 || m_delayMoment > target.timeDelay)
m_delayMoment = target.timeDelay;
}
@@ -2504,6 +2499,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
}
+
m_damage = damageInfo.damage;
caster->DealSpellDamage(&damageInfo, true);
@@ -2577,25 +2573,9 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
// disable effects to which unit is immune
SpellMissInfo returnVal = SPELL_MISS_IMMUNE;
for (uint32 effectNumber = 0; effectNumber < MAX_SPELL_EFFECTS; ++effectNumber)
- {
if (effectMask & (1 << effectNumber))
- {
if (unit->IsImmunedToSpellEffect(m_spellInfo, effectNumber))
effectMask &= ~(1 << effectNumber);
- else if (m_spellInfo->Effects[effectNumber].IsAura() && !m_spellInfo->IsPositiveEffect(effectNumber))
- {
- int32 debuff_resist_chance = unit->GetMaxPositiveAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, int32(m_spellInfo->Dispel));
- debuff_resist_chance += unit->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, int32(m_spellInfo->Dispel));
-
- if (debuff_resist_chance > 0)
- if (irand(0, 10000) <= (debuff_resist_chance * 100))
- {
- effectMask &= ~(1 << effectNumber);
- returnVal = SPELL_MISS_RESIST;
- }
- }
- }
- }
if (!effectMask)
return returnVal;
@@ -2606,14 +2586,14 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
if (Player* player = unit->ToPlayer())
{
player->StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_SPELL_TARGET, m_spellInfo->Id);
- player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, m_spellInfo->Id, 0, m_caster);
+ player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, m_spellInfo->Id, 0, 0, m_caster);
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2, m_spellInfo->Id);
}
if (Player* player = m_caster->ToPlayer())
{
player->StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_SPELL_CASTER, m_spellInfo->Id);
- player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2, m_spellInfo->Id, 0, unit);
+ player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2, m_spellInfo->Id, 0, 0, unit);
}
if (m_caster != unit)
@@ -2730,15 +2710,28 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
duration = m_originalCaster->ModSpellDuration(aurSpellInfo, unit, duration, positive, effectMask);
- // Haste modifies duration of channeled spells
- if (m_spellInfo->IsChanneled())
+ if (duration > 0)
{
- if (m_spellInfo->AttributesEx5 & SPELL_ATTR5_HASTE_AFFECT_DURATION)
- m_originalCaster->ModSpellCastTime(aurSpellInfo, duration, this);
+ // Haste modifies duration of channeled spells
+ if (m_spellInfo->IsChanneled())
+ {
+ if (m_spellInfo->AttributesEx5 & SPELL_ATTR5_HASTE_AFFECT_DURATION)
+ m_originalCaster->ModSpellCastTime(aurSpellInfo, duration, this);
+ }
+ else if (m_spellInfo->AttributesEx5 & SPELL_ATTR5_HASTE_AFFECT_DURATION)
+ {
+ int32 origDuration = duration;
+ duration = 0;
+ for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ if (AuraEffect const* eff = m_spellAura->GetEffect(i))
+ if (int32 amplitude = eff->GetAmplitude()) // amplitude is hastened by UNIT_MOD_CAST_SPEED
+ duration = std::max(std::max(origDuration / amplitude, 1) * amplitude, duration);
+
+ // if there is no periodic effect
+ if (!duration)
+ duration = int32(origDuration * m_originalCaster->GetFloatValue(UNIT_MOD_CAST_SPEED));
+ }
}
- // and duration of auras affected by SPELL_AURA_PERIODIC_HASTE
- else if (m_originalCaster->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, aurSpellInfo) || m_spellInfo->AttributesEx5 & SPELL_ATTR5_HASTE_AFFECT_DURATION)
- duration = int32(duration * m_originalCaster->GetFloatValue(UNIT_MOD_CAST_SPEED));
if (duration != m_spellAura->GetMaxDuration())
{
@@ -2819,11 +2812,15 @@ void Spell::DoTriggersOnSpellHit(Unit* unit, uint8 effMask)
// trigger linked auras remove/apply
/// @todo remove/cleanup this, as this table is not documented and people are doing stupid things with it
if (std::vector<int32> const* spellTriggered = sSpellMgr->GetSpellLinked(m_spellInfo->Id + SPELL_LINK_HIT))
+ {
for (std::vector<int32>::const_iterator i = spellTriggered->begin(); i != spellTriggered->end(); ++i)
+ {
if (*i < 0)
unit->RemoveAurasDueToSpell(-(*i));
else
unit->CastSpell(unit, *i, true, 0, 0, m_caster->GetGUID());
+ }
+ }
}
void Spell::DoAllEffectOnTarget(GOTargetInfo* target)
@@ -3035,7 +3032,9 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered
// don't allow channeled spells / spells with cast time to be casted while moving
// (even if they are interrupted on moving, spells with almost immediate effect get to have their effect processed before movement interrupter kicks in)
- if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->isMoving() && m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT)
+ // don't cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect
+ if (((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->isMoving() &&
+ m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) && !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo))
{
SendCastResult(SPELL_FAILED_MOVING);
finish(false);
@@ -3382,7 +3381,7 @@ void Spell::handle_immediate()
// Remove used for cast item if need (it can be already NULL after TakeReagents call
TakeCastItem();
- // handle ammo consumption for Hunter's volley spell
+ // handle ammo consumption for thrown weapons
if (m_spellInfo->IsRangedWeaponSpell() && m_spellInfo->IsChanneled())
TakeAmmo();
@@ -3512,6 +3511,9 @@ void Spell::_handle_finish_phase()
// Real add combo points from effects
if (m_comboPointGain)
m_caster->m_movedPlayer->GainSpellComboPoints(m_comboPointGain);
+
+ if (m_spellInfo->PowerType == POWER_HOLY_POWER && m_caster->m_movedPlayer->getClass() == CLASS_PALADIN)
+ HandleHolyPower(m_caster->m_movedPlayer);
}
if (m_caster->m_extraAttacks && GetSpellInfo()->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS))
@@ -3555,9 +3557,11 @@ void Spell::update(uint32 difftime)
}
// check if the player caster has moved before the spell finished
+ // with the exception of spells affected with SPELL_AURA_CAST_WHILE_WALKING effect
if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) &&
m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) &&
- (m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR)))
+ (m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR)) &&
+ !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo))
{
// don't cancel for melee, autorepeat, triggered and instant spells
if (!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered())
@@ -3698,7 +3702,7 @@ void Spell::finish(bool ok)
Unit::AuraEffectList const& vIgnoreReset = m_caster->GetAuraEffectsByType(SPELL_AURA_IGNORE_MELEE_RESET);
for (Unit::AuraEffectList::const_iterator i = vIgnoreReset.begin(); i != vIgnoreReset.end(); ++i)
{
- if ((*i)->IsAffectedOnSpell(m_spellInfo))
+ if ((*i)->IsAffectingSpell(m_spellInfo))
{
found = true;
break;
@@ -3759,6 +3763,9 @@ void Spell::SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 cas
data << uint8(result); // problem
switch (result)
{
+ case SPELL_FAILED_NOT_READY:
+ data << uint32(0); // unknown (value 1 update cooldowns on client flag)
+ break;
case SPELL_FAILED_REQUIRES_SPELL_FOCUS:
data << uint32(spellInfo->RequiresSpellFocus); // SpellFocusObject.dbc id
break;
@@ -3811,9 +3818,29 @@ void Spell::SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 cas
data << uint32(proto->ItemLimitCategory);
break;
}
+ case SPELL_FAILED_PREVENTED_BY_MECHANIC:
+ data << uint32(spellInfo->GetAllEffectsMechanicMask()); // SpellMechanic.dbc id
+ break;
+ case SPELL_FAILED_NEED_EXOTIC_AMMO:
+ data << uint32(spellInfo->EquippedItemSubClassMask); // seems correct...
+ break;
+ case SPELL_FAILED_NEED_MORE_ITEMS:
+ data << uint32(0); // Item id
+ data << uint32(0); // Item count?
+ break;
+ case SPELL_FAILED_MIN_SKILL:
+ data << uint32(0); // SkillLine.dbc id
+ data << uint32(0); // required skill value
+ break;
+ case SPELL_FAILED_FISHING_TOO_LOW:
+ data << uint32(0); // required fishing skill
+ break;
case SPELL_FAILED_CUSTOM_ERROR:
data << uint32(customError);
break;
+ case SPELL_FAILED_SILENCED:
+ data << uint32(0); // Unknown
+ break;
case SPELL_FAILED_REAGENTS:
{
uint32 missingItem = 0;
@@ -3835,23 +3862,7 @@ void Spell::SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 cas
data << uint32(missingItem); // first missing item
break;
}
- case SPELL_FAILED_PREVENTED_BY_MECHANIC:
- data << uint32(spellInfo->Mechanic);
- break;
- case SPELL_FAILED_NEED_EXOTIC_AMMO:
- data << uint32(spellInfo->EquippedItemSubClassMask);
- break;
- case SPELL_FAILED_NEED_MORE_ITEMS:
- data << uint32(0); // Item entry
- data << uint32(0); // Count
- break;
- case SPELL_FAILED_MIN_SKILL:
- data << uint32(0); // SkillLine.dbc Id
- data << uint32(0); // Amount
- break;
- case SPELL_FAILED_FISHING_TOO_LOW:
- data << uint32(0); // Skill level
- break;
+ // TODO: SPELL_FAILED_NOT_STANDING
default:
break;
}
@@ -3865,19 +3876,17 @@ void Spell::SendSpellStart()
//sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Sending SMSG_SPELL_START id=%u", m_spellInfo->Id);
- uint32 castFlags = CAST_FLAG_UNKNOWN_2;
+ uint32 castFlags = CAST_FLAG_HAS_TRAJECTORY;
if ((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell)
castFlags |= CAST_FLAG_PENDING;
- if (m_spellInfo->Attributes & SPELL_ATTR0_REQ_AMMO)
- castFlags |= CAST_FLAG_AMMO;
if ((m_caster->GetTypeId() == TYPEID_PLAYER ||
(m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->isPet()))
&& m_spellInfo->PowerType != POWER_HEALTH)
castFlags |= CAST_FLAG_POWER_LEFT_SELF;
- if (m_spellInfo->RuneCostID && m_spellInfo->PowerType == POWER_RUNE)
+ if (m_spellInfo->RuneCostID && m_spellInfo->PowerType == POWER_RUNES)
castFlags |= CAST_FLAG_UNKNOWN_19;
WorldPacket data(SMSG_SPELL_START, (8+8+4+4+2));
@@ -3890,20 +3899,56 @@ void Spell::SendSpellStart()
data << uint8(m_cast_count); // pending spell cast?
data << uint32(m_spellInfo->Id); // spellId
data << uint32(castFlags); // cast flags
- data << int32(m_timer); // delay?
+ data << uint32(m_timer); // delay?
+ data << uint32(m_casttime);
m_targets.Write(data);
if (castFlags & CAST_FLAG_POWER_LEFT_SELF)
data << uint32(m_caster->GetPower((Powers)m_spellInfo->PowerType));
- if (castFlags & CAST_FLAG_AMMO)
- WriteAmmoToPacket(&data);
+ if (castFlags & CAST_FLAG_RUNE_LIST) // rune cooldowns list
+ {
+ //TODO: There is a crash caused by a spell with CAST_FLAG_RUNE_LIST casted by a creature
+ //The creature is the mover of a player, so HandleCastSpellOpcode uses it as the caster
+ if (Player* player = m_caster->ToPlayer())
+ {
+ data << uint8(m_runesState); // runes state before
+ data << uint8(player->GetRunesState()); // runes state after
+ for (uint8 i = 0; i < MAX_RUNES; ++i)
+ {
+ // float casts ensure the division is performed on floats as we need float result
+ float baseCd = float(player->GetRuneBaseCooldown(i));
+ data << uint8((baseCd - float(player->GetRuneCooldown(i))) / baseCd * 255); // rune cooldown passed
+ }
+ }
+ else
+ {
+ data << uint8(0);
+ data << uint8(0);
+ for (uint8 i = 0; i < MAX_RUNES; ++i)
+ data << uint8(0);
+ }
+ }
- if (castFlags & CAST_FLAG_UNKNOWN_23)
+ if (castFlags & CAST_FLAG_PROJECTILE)
{
+ data << uint32(0); // Ammo display ID
+ data << uint32(0); // Inventory Type
+ }
+
+ if (castFlags & CAST_FLAG_IMMUNITY)
+ {
+ data << uint32(0);
data << uint32(0);
+ }
+
+ if (castFlags & CAST_FLAG_HEAL_PREDICTION)
+ {
data << uint32(0);
+ data << uint8(0); // unkByte
+ // if (unkByte == 2)
+ // data.append(0);
}
m_caster->SendMessageToSet(&data, true);
@@ -3923,9 +3968,6 @@ void Spell::SendSpellGo()
if ((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell)
castFlags |= CAST_FLAG_PENDING;
- if (m_spellInfo->Attributes & SPELL_ATTR0_REQ_AMMO)
- castFlags |= CAST_FLAG_AMMO; // arrows/bullets visual
-
if ((m_caster->GetTypeId() == TYPEID_PLAYER ||
(m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->isPet()))
&& m_spellInfo->PowerType != POWER_HEALTH)
@@ -3934,7 +3976,7 @@ void Spell::SendSpellGo()
if ((m_caster->GetTypeId() == TYPEID_PLAYER)
&& (m_caster->getClass() == CLASS_DEATH_KNIGHT)
&& m_spellInfo->RuneCostID
- && m_spellInfo->PowerType == POWER_RUNE)
+ && m_spellInfo->PowerType == POWER_RUNES)
{
castFlags |= CAST_FLAG_UNKNOWN_19; // same as in SMSG_SPELL_START
castFlags |= CAST_FLAG_RUNE_LIST; // rune cooldowns list
@@ -3960,6 +4002,7 @@ void Spell::SendSpellGo()
data << uint8(m_cast_count); // pending spell cast?
data << uint32(m_spellInfo->Id); // spellId
data << uint32(castFlags); // cast flags
+ data << uint32(m_timer);
data << uint32(getMSTime()); // timestamp
WriteSpellGoTargets(&data);
@@ -3975,30 +4018,28 @@ void Spell::SendSpellGo()
//The creature is the mover of a player, so HandleCastSpellOpcode uses it as the caster
if (Player* player = m_caster->ToPlayer())
{
- uint8 runeMaskInitial = m_runesState;
- uint8 runeMaskAfterCast = player->GetRunesState();
- data << uint8(runeMaskInitial); // runes state before
- data << uint8(runeMaskAfterCast); // runes state after
+ data << uint8(m_runesState); // runes state before
+ data << uint8(player->GetRunesState()); // runes state after
for (uint8 i = 0; i < MAX_RUNES; ++i)
{
- uint8 mask = (1 << i);
- if (mask & runeMaskInitial && !(mask & runeMaskAfterCast)) // usable before andon cooldown now...
- {
- // float casts ensure the division is performed on floats as we need float result
- float baseCd = float(player->GetRuneBaseCooldown(i));
- data << uint8((baseCd - float(player->GetRuneCooldown(i))) / baseCd * 255); // rune cooldown passed
- }
+ // float casts ensure the division is performed on floats as we need float result
+ float baseCd = float(player->GetRuneBaseCooldown(i));
+ data << uint8((baseCd - float(player->GetRuneCooldown(i))) / baseCd * 255); // rune cooldown passed
}
}
}
+
if (castFlags & CAST_FLAG_ADJUST_MISSILE)
{
data << m_targets.GetElevation();
data << uint32(m_delayMoment);
}
- if (castFlags & CAST_FLAG_AMMO)
- WriteAmmoToPacket(&data);
+ if (castFlags & CAST_FLAG_PROJECTILE)
+ {
+ data << uint32(0); // Ammo display ID
+ data << uint32(0); // Inventory Type
+ }
if (castFlags & CAST_FLAG_VISUAL_CHAIN)
{
@@ -4011,79 +4052,21 @@ void Spell::SendSpellGo()
data << uint8(0);
}
- m_caster->SendMessageToSet(&data, true);
-}
-
-void Spell::WriteAmmoToPacket(WorldPacket* data)
-{
- uint32 ammoInventoryType = 0;
- uint32 ammoDisplayID = 0;
-
- if (m_caster->GetTypeId() == TYPEID_PLAYER)
+ if (m_targets.GetTargetMask() & TARGET_FLAG_EXTRA_TARGETS)
{
- Item* pItem = m_caster->ToPlayer()->GetWeaponForAttack(RANGED_ATTACK);
- if (pItem)
+ data << uint32(0); // Extra targets count
+ /*
+ for (uint8 i = 0; i < count; ++i)
{
- ammoInventoryType = pItem->GetTemplate()->InventoryType;
- if (ammoInventoryType == INVTYPE_THROWN)
- ammoDisplayID = pItem->GetTemplate()->DisplayInfoID;
- else
- {
- uint32 ammoID = m_caster->ToPlayer()->GetUInt32Value(PLAYER_AMMO_ID);
- if (ammoID)
- {
- ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(ammoID);
- if (pProto)
- {
- ammoDisplayID = pProto->DisplayInfoID;
- ammoInventoryType = pProto->InventoryType;
- }
- }
- else if (m_caster->HasAura(46699)) // Requires No Ammo
- {
- ammoDisplayID = 5996; // normal arrow
- ammoInventoryType = INVTYPE_AMMO;
- }
- }
- }
- }
- 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 << float(0); // Target Position X
+ data << float(0); // Target Position Y
+ data << float(0); // Target Position Z
+ data << uint64(0); // Target Guid
}
+ */
}
- *data << uint32(ammoDisplayID);
- *data << uint32(ammoInventoryType);
+ m_caster->SendMessageToSet(&data, true);
}
/// Writes miss and hit targets for a SMSG_SPELL_GO packet
@@ -4289,7 +4272,25 @@ void Spell::SendChannelStart(uint32 duration)
data.append(m_caster->GetPackGUID());
data << uint32(m_spellInfo->Id);
data << uint32(duration);
-
+ data << uint8(0); // immunity (castflag & 0x04000000)
+ /*
+ if (immunity)
+ {
+ data << uint32(); // CastSchoolImmunities
+ data << uint32(); // CastImmunities
+ }
+ */
+ data << uint8(0); // healPrediction (castflag & 0x40000000)
+ /*
+ if (healPrediction)
+ {
+ data.appendPackGUID(channelTarget); // target packguid
+ data << uint32(); // spellid
+ data << uint8(0); // unk3
+ if (unk3 == 2)
+ data.append(); // unk packed guid (unused ?)
+ }
+ */
m_caster->SendMessageToSet(&data, true);
m_timer = duration;
@@ -4316,8 +4317,8 @@ void Spell::SendResurrectRequest(Player* target)
data << uint8(m_caster->GetTypeId() == TYPEID_PLAYER ? 0 : 1); // "you'll be afflicted with resurrection sickness"
// override delay sent with SMSG_CORPSE_RECLAIM_DELAY, set instant resurrection for spells with this attribute
- if (m_spellInfo->AttributesEx3 & SPELL_ATTR3_IGNORE_RESURRECTION_TIMER)
- data << uint32(0);
+ // 4.2.2 edit : id of the spell used to resurect. (used client-side for Mass Resurect)
+ data << uint32(m_spellInfo->Id);
target->GetSession()->SendPacket(&data);
}
@@ -4399,7 +4400,7 @@ void Spell::TakePower()
bool hit = true;
if (m_caster->GetTypeId() == TYPEID_PLAYER)
{
- if (powerType == POWER_RAGE || powerType == POWER_ENERGY || powerType == POWER_RUNE)
+ if (powerType == POWER_RAGE || powerType == POWER_ENERGY || powerType == POWER_RUNES)
if (uint64 targetGUID = m_targets.GetUnitTargetGUID())
for (std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
if (ihit->targetGUID == targetGUID)
@@ -4415,7 +4416,7 @@ void Spell::TakePower()
}
}
- if (powerType == POWER_RUNE)
+ if (powerType == POWER_RUNES)
{
TakeRunePower(hit);
return;
@@ -4441,10 +4442,6 @@ void Spell::TakePower()
m_caster->ModifyPower(powerType, -m_powerCost);
else
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()
@@ -4471,14 +4468,12 @@ void Spell::TakeAmmo()
m_caster->ToPlayer()->DestroyItemCount(pItem, count, true);
}
}
- else if (uint32 ammo = m_caster->ToPlayer()->GetUInt32Value(PLAYER_AMMO_ID))
- m_caster->ToPlayer()->DestroyItemCount(ammo, 1, true);
}
}
SpellCastResult Spell::CheckRuneCost(uint32 runeCostID)
{
- if (m_spellInfo->PowerType != POWER_RUNE || !runeCostID)
+ if (m_spellInfo->PowerType != POWER_RUNES || !runeCostID)
return SPELL_CAST_OK;
if (m_caster->GetTypeId() != TYPEID_PLAYER)
@@ -4687,6 +4682,41 @@ void Spell::HandleThreatSpells()
sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Spell %u, added an additional %f threat for %s %u target(s)", m_spellInfo->Id, threat, m_spellInfo->_IsPositiveSpell() ? "assisting" : "harming", uint32(m_UniqueTargetInfo.size()));
}
+void Spell::HandleHolyPower(Player* caster)
+{
+ if (!caster)
+ return;
+
+ bool hit = true;
+ Player* modOwner = caster->GetSpellModOwner();
+
+ m_powerCost = caster->GetPower(POWER_HOLY_POWER); // Always use all the holy power we have
+
+ if (!m_powerCost || !modOwner)
+ return;
+
+ if (uint64 targetGUID = m_targets.GetUnitTargetGUID())
+ {
+ for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
+ {
+ if (ihit->targetGUID == targetGUID)
+ {
+ if (ihit->missCondition != SPELL_MISS_NONE && ihit->missCondition != SPELL_MISS_MISS)
+ hit = false;
+
+ break;
+ }
+ }
+
+ // The spell did hit the target, apply aura cost mods if there are any.
+ if (hit)
+ {
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, m_powerCost);
+ m_caster->ModifyPower(POWER_HOLY_POWER, -m_powerCost);
+ }
+ }
+}
+
void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOTarget, uint32 i, SpellEffectHandleMode mode)
{
effectHandleMode = mode;
@@ -4699,8 +4729,7 @@ void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOT
sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Spell: %u Effect : %u", m_spellInfo->Id, eff);
- // we do not need DamageMultiplier here.
- damage = CalculateDamage(i, NULL);
+ damage = CalculateDamage(i, unitTarget);
bool preventDefault = CallScriptEffectHandlers((SpellEffIndex)i, mode);
@@ -4768,7 +4797,7 @@ SpellCastResult Spell::CheckCast(bool strict)
Unit::AuraEffectList const& ignore = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_IGNORE_SHAPESHIFT);
for (Unit::AuraEffectList::const_iterator i = ignore.begin(); i != ignore.end(); ++i)
{
- if (!(*i)->IsAffectedOnSpell(m_spellInfo))
+ if (!(*i)->IsAffectingSpell(m_spellInfo))
continue;
checkForm = false;
break;
@@ -4794,7 +4823,7 @@ SpellCastResult Spell::CheckCast(bool strict)
Unit::AuraEffectList const& stateAuras = m_caster->GetAuraEffectsByType(SPELL_AURA_ABILITY_IGNORE_AURASTATE);
for (Unit::AuraEffectList::const_iterator j = stateAuras.begin(); j != stateAuras.end(); ++j)
{
- if ((*j)->IsAffectedOnSpell(m_spellInfo))
+ if ((*j)->IsAffectingSpell(m_spellInfo))
{
m_needComboPoints = false;
if ((*j)->GetMiscValue() == 1)
@@ -4826,7 +4855,8 @@ SpellCastResult Spell::CheckCast(bool strict)
// 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 && m_caster->ToPlayer()->isMoving())
+ // Do not cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect
+ if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving() && !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo))
{
// skip stuck spell to allow use it in falling case and apply spell limitations at movement
if ((!m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR) || m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK) &&
@@ -4927,14 +4957,14 @@ SpellCastResult Spell::CheckCast(bool strict)
if (!m_caster->ToPlayer()->InBattleground())
return SPELL_FAILED_ONLY_BATTLEGROUNDS;
- // do not allow spells to be cast in arenas
- // - with greater than 10 min CD without SPELL_ATTR4_USABLE_IN_ARENA flag
- // - with SPELL_ATTR4_NOT_USABLE_IN_ARENA flag
- if ((m_spellInfo->AttributesEx4 & SPELL_ATTR4_NOT_USABLE_IN_ARENA) ||
- (m_spellInfo->GetRecoveryTime() > 10 * MINUTE * IN_MILLISECONDS && !(m_spellInfo->AttributesEx4 & SPELL_ATTR4_USABLE_IN_ARENA)))
- if (MapEntry const* mapEntry = sMapStore.LookupEntry(m_caster->GetMapId()))
- if (mapEntry->IsBattleArena())
- return SPELL_FAILED_NOT_IN_ARENA;
+ // do not allow spells to be cast in arenas or rated battlegrounds
+ if (Player* player = m_caster->ToPlayer())
+ if (player->InArena()/* || player->InRatedBattleGround() NYI*/)
+ {
+ SpellCastResult castResult = CheckArenaAndRatedBattlegroundCastRules();
+ if (castResult != SPELL_CAST_OK)
+ return castResult;
+ }
// zone check
if (m_caster->GetTypeId() == TYPEID_UNIT || !m_caster->ToPlayer()->isGameMaster())
@@ -4997,6 +5027,7 @@ SpellCastResult Spell::CheckCast(bool strict)
bool hasNonDispelEffect = false;
uint32 dispelMask = 0;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ {
if (m_spellInfo->Effects[i].Effect == SPELL_EFFECT_DISPEL)
{
if (m_spellInfo->Effects[i].IsTargetingArea() || m_spellInfo->AttributesEx & SPELL_ATTR1_MELEE_COMBAT_START)
@@ -5012,16 +5043,6 @@ SpellCastResult Spell::CheckCast(bool strict)
hasNonDispelEffect = true;
break;
}
-
- if (!hasNonDispelEffect && !hasDispellableAura && dispelMask && !IsTriggered())
- {
- if (Unit* target = m_targets.GetUnitTarget())
- {
- DispelChargesList dispelList;
- target->GetDispellableAuraList(m_caster, dispelMask, dispelList);
- if (dispelList.empty())
- return SPELL_FAILED_NOTHING_TO_DISPEL;
- }
}
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
@@ -5029,6 +5050,26 @@ SpellCastResult Spell::CheckCast(bool strict)
// for effects of spells that have only one target
switch (m_spellInfo->Effects[i].Effect)
{
+ case SPELL_EFFECT_DUMMY:
+ {
+ if (m_spellInfo->Id == 19938) // Awaken Peon
+ {
+ Unit* unit = m_targets.GetUnitTarget();
+ if (!unit || !unit->HasAura(17743))
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ else if (m_spellInfo->Id == 31789) // Righteous Defense
+ {
+ if (m_caster->GetTypeId() != TYPEID_PLAYER)
+ return SPELL_FAILED_DONT_REPORT;
+
+ Unit* target = m_targets.GetUnitTarget();
+ if (!target || !target->IsFriendlyTo(m_caster) || target->getAttackers().empty())
+ return SPELL_FAILED_BAD_TARGETS;
+
+ }
+ break;
+ }
case SPELL_EFFECT_LEARN_SPELL:
{
if (m_caster->GetTypeId() != TYPEID_PLAYER)
@@ -5596,8 +5637,9 @@ SpellCastResult Spell::CheckCasterAuras() const
bool usableInStun = m_spellInfo->AttributesEx5 & SPELL_ATTR5_USABLE_WHILE_STUNNED;
// Glyph of Pain Suppression
+ // Allow Pain Suppression and Guardian Spirit to be cast while stunned
// there is no other way to handle it
- if (m_spellInfo->Id == 33206 && !m_caster->HasAura(63248))
+ if ((m_spellInfo->Id == 33206 || m_spellInfo->Id == 47788) && !m_caster->HasAura(63248))
usableInStun = false;
// Check whether the cast should be prevented by any state you might have.
@@ -5693,6 +5735,37 @@ SpellCastResult Spell::CheckCasterAuras() const
return SPELL_CAST_OK;
}
+SpellCastResult Spell::CheckArenaAndRatedBattlegroundCastRules()
+{
+ bool isRatedBattleground = false; // NYI
+ bool isArena = !isRatedBattleground;
+
+ // check USABLE attributes
+ // USABLE takes precedence over NOT_USABLE
+ if (isRatedBattleground && m_spellInfo->AttributesEx9 & SPELL_ATTR9_USABLE_IN_RATED_BATTLEGROUNDS)
+ return SPELL_CAST_OK;
+
+ if (isArena && m_spellInfo->AttributesEx4 & SPELL_ATTR4_USABLE_IN_ARENA)
+ return SPELL_CAST_OK;
+
+ // check NOT_USABLE attributes
+ if (m_spellInfo->AttributesEx4 & SPELL_ATTR4_NOT_USABLE_IN_ARENA_OR_RATED_BG)
+ return isArena ? SPELL_FAILED_NOT_IN_ARENA : SPELL_FAILED_NOT_IN_RATED_BATTLEGROUND;
+
+ if (isArena && m_spellInfo->AttributesEx9 & SPELL_ATTR9_NOT_USABLE_IN_ARENA)
+ return SPELL_FAILED_NOT_IN_ARENA;
+
+ // check cooldowns
+ uint32 spellCooldown = m_spellInfo->GetRecoveryTime();
+ if (isArena && spellCooldown > 10 * MINUTE * IN_MILLISECONDS) // not sure if still needed
+ return SPELL_FAILED_NOT_IN_ARENA;
+
+ if (isRatedBattleground && spellCooldown > 15 * MINUTE * IN_MILLISECONDS)
+ return SPELL_FAILED_NOT_IN_RATED_BATTLEGROUND;
+
+ return SPELL_CAST_OK;
+}
+
bool Spell::CanAutoCast(Unit* target)
{
uint64 targetguid = target->GetGUID();
@@ -5813,8 +5886,8 @@ SpellCastResult Spell::CheckPower()
return SPELL_FAILED_UNKNOWN;
}
- //check rune cost only if a spell has PowerType == POWER_RUNE
- if (m_spellInfo->PowerType == POWER_RUNE)
+ //check rune cost only if a spell has PowerType == POWER_RUNES
+ if (m_spellInfo->PowerType == POWER_RUNES)
{
SpellCastResult failReason = CheckRuneCost(m_spellInfo->RuneCostID);
if (failReason != SPELL_CAST_OK)
@@ -6006,25 +6079,7 @@ SpellCastResult Spell::CheckItems()
totems -= 1;
}
if (totems != 0)
- return SPELL_FAILED_TOTEMS; //0x7C
-
- // Check items for TotemCategory (items presence in inventory)
- uint32 TotemCategory = 2;
- for (int i= 0; i < 2; ++i)
- {
- if (m_spellInfo->TotemCategory[i] != 0)
- {
- if (p_caster->HasItemTotemCategory(m_spellInfo->TotemCategory[i]))
- {
- TotemCategory -= 1;
- continue;
- }
- }
- else
- TotemCategory -= 1;
- }
- if (TotemCategory != 0)
- return SPELL_FAILED_TOTEM_CATEGORY; //0x7B
+ return SPELL_FAILED_TOTEMS;
}
// special checks for spell effects
@@ -6064,7 +6119,7 @@ SpellCastResult Spell::CheckItems()
}
case SPELL_EFFECT_ENCHANT_ITEM:
if (m_spellInfo->Effects[i].ItemType && m_targets.GetItemTarget()
- && (m_targets.GetItemTarget()->IsWeaponVellum() || m_targets.GetItemTarget()->IsArmorVellum()))
+ && (m_targets.GetItemTarget()->IsVellum()))
{
// cannot enchant vellum for other player
if (m_targets.GetItemTarget()->GetOwner() != m_caster)
@@ -6238,46 +6293,6 @@ SpellCastResult Spell::CheckItems()
case ITEM_SUBCLASS_WEAPON_GUN:
case ITEM_SUBCLASS_WEAPON_BOW:
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
- {
- uint32 ammo = m_caster->ToPlayer()->GetUInt32Value(PLAYER_AMMO_ID);
- if (!ammo)
- {
- // Requires No Ammo
- if (m_caster->HasAura(46699))
- break; // skip other checks
-
- return SPELL_FAILED_NO_AMMO;
- }
-
- ItemTemplate const* ammoProto = sObjectMgr->GetItemTemplate(ammo);
- if (!ammoProto)
- return SPELL_FAILED_NO_AMMO;
-
- if (ammoProto->Class != ITEM_CLASS_PROJECTILE)
- return SPELL_FAILED_NO_AMMO;
-
- // check ammo ws. weapon compatibility
- switch (pItem->GetTemplate()->SubClass)
- {
- case ITEM_SUBCLASS_WEAPON_BOW:
- case ITEM_SUBCLASS_WEAPON_CROSSBOW:
- if (ammoProto->SubClass != ITEM_SUBCLASS_ARROW)
- return SPELL_FAILED_NO_AMMO;
- break;
- case ITEM_SUBCLASS_WEAPON_GUN:
- if (ammoProto->SubClass != ITEM_SUBCLASS_BULLET)
- return SPELL_FAILED_NO_AMMO;
- break;
- default:
- return SPELL_FAILED_NO_AMMO;
- }
-
- if (!m_caster->ToPlayer()->HasItemCount(ammo))
- {
- m_caster->ToPlayer()->SetUInt32Value(PLAYER_AMMO_ID, 0);
- return SPELL_FAILED_NO_AMMO;
- }
- }; break;
case ITEM_SUBCLASS_WEAPON_WAND:
break;
default:
@@ -6554,7 +6569,7 @@ bool Spell::IsAutoActionResetSpell() const
bool Spell::IsNeedSendToClient() const
{
return m_spellInfo->SpellVisual[0] || m_spellInfo->SpellVisual[1] || m_spellInfo->IsChanneled() ||
- m_spellInfo->Speed > 0.0f || (!m_triggeredByAuraSpell && !IsTriggered());
+ (m_spellInfo->AttributesEx8 & SPELL_ATTR8_AURA_SEND_AMOUNT) || m_spellInfo->Speed > 0.0f || (!m_triggeredByAuraSpell && !IsTriggered());
}
bool Spell::HaveTargetsForEffect(uint8 effect) const
@@ -6722,12 +6737,6 @@ void Spell::HandleLaunchPhase()
multiplier[i] = m_spellInfo->Effects[i].CalcDamageMultiplier(m_originalCaster, this);
bool usesAmmo = m_spellInfo->AttributesCu & SPELL_ATTR0_CU_DIRECT_DAMAGE;
- Unit::AuraEffectList const& Auras = m_caster->GetAuraEffectsByType(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)
{
@@ -7200,7 +7209,7 @@ void Spell::PrepareTriggersExecutedOnHit()
Unit::AuraEffectList const& targetTriggers = m_caster->GetAuraEffectsByType(SPELL_AURA_ADD_TARGET_TRIGGER);
for (Unit::AuraEffectList::const_iterator i = targetTriggers.begin(); i != targetTriggers.end(); ++i)
{
- if (!(*i)->IsAffectedOnSpell(m_spellInfo))
+ if (!(*i)->IsAffectingSpell(m_spellInfo))
continue;
SpellInfo const* auraSpellInfo = (*i)->GetSpellInfo();
uint32 auraSpellIdx = (*i)->GetEffIndex();