diff options
| author | ariel- <ariel-@users.noreply.github.com> | 2018-02-10 16:43:01 -0300 |
|---|---|---|
| committer | Ariel Silva <ariel-@users.noreply.github.com> | 2018-03-09 14:41:28 -0300 |
| commit | 45c5e1b9d63796d168339a44f63418f220cf2403 (patch) | |
| tree | c5c2ef9917147e38779d1f94e6ffc38386b9b251 /src/server/game/Spells/Spell.cpp | |
| parent | 080d2c6cd439acb2059adc4e24a279de98aa0db6 (diff) | |
Core/Spells: rework part 5: GameObject casting
Closes #21330
Closes #18885
Ref #18752
Diffstat (limited to 'src/server/game/Spells/Spell.cpp')
| -rw-r--r-- | src/server/game/Spells/Spell.cpp | 1262 |
1 files changed, 724 insertions, 538 deletions
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 145c385dcd0..8604a7f67ca 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -60,7 +60,7 @@ #include "WorldPacket.h" #include "WorldSession.h" -extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS]; +extern SpellEffectHandlerFn SpellEffectHandlers[TOTAL_SPELL_EFFECTS]; SpellDestination::SpellDestination() { @@ -454,9 +454,9 @@ bool SpellCastTargets::HasDst() const return (GetTargetMask() & TARGET_FLAG_DEST_LOCATION) != 0; } -void SpellCastTargets::Update(Unit* caster) +void SpellCastTargets::Update(WorldObject* caster) { - m_objectTarget = m_objectTargetGUID ? ((m_objectTargetGUID == caster->GetGUID()) ? caster : ObjectAccessor::GetWorldObject(*caster, m_objectTargetGUID)) : nullptr; + m_objectTarget = (m_objectTargetGUID == caster->GetGUID()) ? caster : ObjectAccessor::GetWorldObject(*caster, m_objectTargetGUID); m_itemTarget = nullptr; if (caster->GetTypeId() == TYPEID_PLAYER) @@ -493,28 +493,6 @@ void SpellCastTargets::Update(Unit* caster) } } -void SpellCastTargets::OutDebug() const -{ - if (!m_targetMask) - TC_LOG_DEBUG("spells", "No targets"); - - TC_LOG_DEBUG("spells", "target mask: %u", m_targetMask); - if (m_targetMask & (TARGET_FLAG_UNIT_MASK | TARGET_FLAG_CORPSE_MASK | TARGET_FLAG_GAMEOBJECT_MASK)) - TC_LOG_DEBUG("spells", "Object target: %s", m_objectTargetGUID.ToString().c_str()); - if (m_targetMask & TARGET_FLAG_ITEM) - TC_LOG_DEBUG("spells", "Item target: %s", m_itemTargetGUID.ToString().c_str()); - if (m_targetMask & TARGET_FLAG_TRADE_ITEM) - TC_LOG_DEBUG("spells", "Trade item target: %s", m_itemTargetGUID.ToString().c_str()); - if (m_targetMask & TARGET_FLAG_SOURCE_LOCATION) - TC_LOG_DEBUG("spells", "Source location: transport guid:%s trans offset: %s position: %s", m_src._transportGUID.ToString().c_str(), m_src._transportOffset.ToString().c_str(), m_src._position.ToString().c_str()); - if (m_targetMask & TARGET_FLAG_DEST_LOCATION) - TC_LOG_DEBUG("spells", "Destination location: transport guid:%s trans offset: %s position: %s", m_dst._transportGUID.ToString().c_str(), m_dst._transportOffset.ToString().c_str(), m_dst._position.ToString().c_str()); - if (m_targetMask & TARGET_FLAG_STRING) - TC_LOG_DEBUG("spells", "String: %s", m_strTarget.c_str()); - TC_LOG_DEBUG("spells", "speed: %f", m_speed); - TC_LOG_DEBUG("spells", "elevation: %f", m_elevation); -} - SpellValue::SpellValue(SpellInfo const* proto) { for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) @@ -529,7 +507,7 @@ class TC_GAME_API SpellEvent : public BasicEvent { public: SpellEvent(Spell* spell); - virtual ~SpellEvent(); + ~SpellEvent(); bool Execute(uint64 e_time, uint32 p_time) override; void Abort(uint64 e_time) override; @@ -539,7 +517,7 @@ class TC_GAME_API SpellEvent : public BasicEvent Spell* m_Spell; }; -Spell::Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, ObjectGuid originalCasterGUID) : +Spell::Spell(WorldObject* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, ObjectGuid originalCasterGUID) : m_spellInfo(sSpellMgr->GetSpellForDifficultyFromSpell(info, caster)), m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerOrOwner()) ? caster->GetCharmerOrOwner() : caster) , m_spellValue(new SpellValue(m_spellInfo)), _spellEvent(nullptr) @@ -563,11 +541,14 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO m_spellSchoolMask = info->GetSchoolMask(); // Can be override for some spell (wand shoot for example) - if (m_attackType == RANGED_ATTACK) + if (Player const* playerCaster = m_caster->ToPlayer()) + { // 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); + if (m_attackType == RANGED_ATTACK) + if ((playerCaster->getClassMask() & CLASSMASK_WAND_USERS) != 0) + if (Item* pItem = playerCaster->GetWeaponForAttack(RANGED_ATTACK)) + m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetTemplate()->Damage[0].DamageType); + } if (originalCasterGUID) m_originalCasterGUID = originalCasterGUID; @@ -575,7 +556,7 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO m_originalCasterGUID = m_caster->GetGUID(); if (m_originalCasterGUID == m_caster->GetGUID()) - m_originalCaster = m_caster; + m_originalCaster = m_caster->ToUnit(); else { m_originalCaster = ObjectAccessor::GetUnit(*m_caster, m_originalCasterGUID); @@ -609,6 +590,7 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO m_glyphIndex = 0; m_preCastSpell = 0; m_triggeredByAuraSpell = nullptr; + unitCaster = nullptr; _spellAura = nullptr; _dynObjAura = nullptr; @@ -626,7 +608,7 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO // Determine if spell can be reflected back to the caster // Patch 1.2 notes: Spell Reflection no longer reflects abilities - m_canReflect = m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && !m_spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) + m_canReflect = caster->isType(TYPEMASK_UNIT) && m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && !m_spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) && !m_spellInfo->HasAttribute(SPELL_ATTR1_CANT_BE_REFLECTED) && !m_spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) && !m_spellInfo->IsPassive(); @@ -697,11 +679,11 @@ void Spell::InitExplicitTargets(SpellCastTargets const& targets) } // try to use attacked unit as a target else if ((m_caster->GetTypeId() == TYPEID_UNIT) && neededTargets & (TARGET_FLAG_UNIT_ENEMY | TARGET_FLAG_UNIT)) - unit = m_caster->GetVictim(); + unit = m_caster->ToCreature()->GetVictim(); // didn't find anything - let's use self as target if (!unit && neededTargets & (TARGET_FLAG_UNIT_RAID | TARGET_FLAG_UNIT_PARTY | TARGET_FLAG_UNIT_ALLY)) - unit = m_caster; + unit = m_caster->ToUnit(); m_targets.SetUnitTarget(unit); } @@ -739,10 +721,10 @@ void Spell::SelectExplicitTargets() if (Unit* target = m_targets.GetUnitTarget()) { // check for explicit target redirection, for Grounding Totem for example - if (m_spellInfo->GetExplicitTargetMask() & TARGET_FLAG_UNIT_ENEMY - || (m_spellInfo->GetExplicitTargetMask() & TARGET_FLAG_UNIT && !m_caster->IsFriendlyTo(target))) + if ((m_spellInfo->GetExplicitTargetMask() & TARGET_FLAG_UNIT_ENEMY) || + ((m_spellInfo->GetExplicitTargetMask() & TARGET_FLAG_UNIT) && !m_caster->IsFriendlyTo(target))) { - Unit* redirect; + Unit* redirect = nullptr; switch (m_spellInfo->DmgClass) { case SPELL_DAMAGE_CLASS_MAGIC: @@ -750,10 +732,10 @@ void Spell::SelectExplicitTargets() break; case SPELL_DAMAGE_CLASS_MELEE: case SPELL_DAMAGE_CLASS_RANGED: - redirect = m_caster->GetMeleeHitRedirectTarget(target, m_spellInfo); + // should gameobjects cast damagetype melee/ranged spells this needs to be changed + redirect = ASSERT_NOTNULL(m_caster->ToUnit())->GetMeleeHitRedirectTarget(target, m_spellInfo); break; default: - redirect = nullptr; break; } if (redirect && (redirect != target)) @@ -1200,7 +1182,8 @@ void Spell::SelectImplicitConeTargets(SpellEffIndex effIndex, SpellImplicitTarge // Other special target selection goes here if (uint32 maxTargets = m_spellValue->MaxAffectedTargets) { - maxTargets += m_caster->GetTotalAuraModifierByAffectMask(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS, m_spellInfo); + if (Unit* unitCaster = m_caster->ToUnit()) + maxTargets += unitCaster->GetTotalAuraModifierByAffectMask(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS, m_spellInfo); Trinity::Containers::RandomResize(targets, maxTargets); } @@ -1217,7 +1200,7 @@ void Spell::SelectImplicitConeTargets(SpellEffIndex effIndex, SpellImplicitTarge void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType, uint32 effMask) { - Unit* referer = nullptr; + WorldObject* referer = nullptr; switch (targetType.GetReferenceType()) { case TARGET_REFERENCE_TYPE_SRC: @@ -1283,7 +1266,8 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge // Other special target selection goes here if (uint32 maxTargets = m_spellValue->MaxAffectedTargets) { - maxTargets += m_caster->GetTotalAuraModifierByAffectMask(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS, m_spellInfo); + if (Unit* unitCaster = m_caster->ToUnit()) + maxTargets += unitCaster->GetTotalAuraModifierByAffectMask(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS, m_spellInfo); Trinity::Containers::RandomResize(targets, maxTargets); } @@ -1361,15 +1345,19 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici } case TARGET_DEST_CASTER_FRONT_LEAP: { - float dist = m_spellInfo->Effects[effIndex].CalcRadius(m_caster); + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + break; + + float dist = m_spellInfo->Effects[effIndex].CalcRadius(unitCaster); float angle = targetType.CalcDirectionAngle(); Position pos = dest._position; - m_caster->MovePositionToFirstCollision(pos, dist, angle); + unitCaster->MovePositionToFirstCollision(pos, dist, angle); // Generate path to that point. if (!m_preGeneratedPath) - m_preGeneratedPath = std::make_unique<PathGenerator>(m_caster); + m_preGeneratedPath = std::make_unique<PathGenerator>(unitCaster); m_preGeneratedPath->SetPathLengthLimit(dist); @@ -1511,14 +1499,17 @@ void Spell::SelectImplicitCasterObjectTargets(SpellEffIndex effIndex, SpellImpli target = m_caster->GetCharmerOrOwner(); break; case TARGET_UNIT_PET: - target = m_caster->GetGuardianPet(); + if (Unit* unitCaster = m_caster->ToUnit()) + target = unitCaster->GetGuardianPet(); break; case TARGET_UNIT_SUMMONER: - if (m_caster->IsSummon()) - target = m_caster->ToTempSummon()->GetSummoner(); + if (Unit* unitCaster = m_caster->ToUnit()) + if (unitCaster->IsSummon()) + target = unitCaster->ToTempSummon()->GetSummoner(); break; case TARGET_UNIT_VEHICLE: - target = m_caster->GetVehicleBase(); + if (Unit* unitCaster = m_caster->ToUnit()) + target = unitCaster->GetVehicleBase(); break; case TARGET_UNIT_PASSENGER_0: case TARGET_UNIT_PASSENGER_1: @@ -1528,8 +1519,9 @@ void Spell::SelectImplicitCasterObjectTargets(SpellEffIndex effIndex, SpellImpli case TARGET_UNIT_PASSENGER_5: case TARGET_UNIT_PASSENGER_6: case TARGET_UNIT_PASSENGER_7: - if (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsVehicle()) - target = m_caster->GetVehicleKit()->GetPassenger(targetType.GetTarget() - TARGET_UNIT_PASSENGER_0); + if (Creature* vehicleBase = m_caster->ToCreature()) + if (vehicleBase->IsVehicle()) + target = vehicleBase->GetVehicleKit()->GetPassenger(targetType.GetTarget() - TARGET_UNIT_PASSENGER_0); break; default: break; @@ -1537,8 +1529,13 @@ void Spell::SelectImplicitCasterObjectTargets(SpellEffIndex effIndex, SpellImpli CallScriptObjectTargetSelectHandlers(target, effIndex, targetType); - if (target && target->ToUnit()) - AddUnitTarget(target->ToUnit(), 1 << effIndex, checkIfValid); + if (target) + { + if (Unit* unit = target->ToUnit()) + AddUnitTarget(unit, 1 << effIndex, checkIfValid); + else if (GameObject* go = target->ToGameObject()) + AddGOTarget(go, 1 << effIndex); + } } void Spell::SelectImplicitTargetObjectTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType) @@ -1627,7 +1624,7 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex, SpellImplicitTarge float b = tangent(m_targets.GetElevation()); float a = (srcToDestDelta - dist2d * b) / (dist2d * dist2d); if (a > -0.0001f) - a = 0; + a = 0.f; // We should check if triggered spell has greater range (which is true in many cases, and initial spell has too short max range) // limit max range to 300 yards, sometimes triggered spells can have 50000yds @@ -1635,15 +1632,16 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex, SpellImplicitTarge if (SpellInfo const* triggerSpellInfo = sSpellMgr->GetSpellInfo(m_spellInfo->Effects[effIndex].TriggerSpell)) bestDist = std::min(std::max(bestDist, triggerSpellInfo->GetMaxRange(false)), std::min(dist2d, 300.0f)); - std::list<WorldObject*>::const_iterator itr = targets.begin(); - for (; itr != targets.end(); ++itr) + // GameObjects don't cast traj + Unit* unitCaster = ASSERT_NOTNULL(m_caster->ToUnit()); + for (auto itr = targets.begin(); itr != targets.end(); ++itr) { - if (m_spellInfo->CheckTarget(m_caster, *itr, true) != SPELL_CAST_OK) + if (m_spellInfo->CheckTarget(unitCaster, *itr, true) != SPELL_CAST_OK) continue; if (Unit* unit = (*itr)->ToUnit()) { - if (m_caster == *itr || m_caster->IsOnVehicle(unit) || unit->GetVehicle()) + if (unitCaster == *itr || unitCaster->IsOnVehicle(unit) || unit->GetVehicle()) continue; if (Creature* creatureTarget = unit->ToCreature()) @@ -1653,14 +1651,14 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex, SpellImplicitTarge } } - const float size = std::max((*itr)->GetCombatReach(), 1.0f); - const float objDist2d = srcPos.GetExactDist2d(*itr); - const float dz = (*itr)->GetPositionZ() - srcPos.m_positionZ; + float const size = std::max((*itr)->GetCombatReach(), 1.0f); + float const objDist2d = srcPos.GetExactDist2d(*itr); + float const dz = (*itr)->GetPositionZ() - srcPos.m_positionZ; - const float horizontalDistToTraj = std::fabs(objDist2d * std::sin(srcPos.GetRelativeAngle(*itr))); - const float sizeFactor = std::cos((horizontalDistToTraj / size) * (M_PI / 2.0f)); - const float distToHitPoint = std::max(objDist2d * std::cos(srcPos.GetRelativeAngle(*itr)) - size * sizeFactor, 0.0f); - const float height = distToHitPoint * (a * distToHitPoint + b); + float const horizontalDistToTraj = std::fabs(objDist2d * std::sin(srcPos.GetRelativeAngle(*itr))); + float const sizeFactor = std::cos((horizontalDistToTraj / size) * (M_PI / 2.0f)); + float const distToHitPoint = std::max(objDist2d * std::cos(srcPos.GetRelativeAngle(*itr)) - size * sizeFactor, 0.0f); + float const height = distToHitPoint * (a * distToHitPoint + b); if (fabs(dz - height) > size + b / 2.0f + TRAJECTORY_MISSILE_SIZE) continue; @@ -1674,11 +1672,11 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex, SpellImplicitTarge if (dist2d > bestDist) { - float x = m_targets.GetSrcPos()->m_positionX + std::cos(m_caster->GetOrientation()) * bestDist; - float y = m_targets.GetSrcPos()->m_positionY + std::sin(m_caster->GetOrientation()) * bestDist; + float x = m_targets.GetSrcPos()->m_positionX + std::cos(unitCaster->GetOrientation()) * bestDist; + float y = m_targets.GetSrcPos()->m_positionY + std::sin(unitCaster->GetOrientation()) * bestDist; float z = m_targets.GetSrcPos()->m_positionZ + bestDist * (a * bestDist + b); - SpellDestination dest(x, y, z, m_caster->GetOrientation()); + SpellDestination dest(x, y, z, unitCaster->GetOrientation()); CallScriptDestinationTargetSelectHandlers(dest, effIndex, targetType); m_targets.ModDst(dest); } @@ -1691,9 +1689,9 @@ void Spell::SelectEffectTypeImplicitTargets(uint8 effIndex) { case SPELL_EFFECT_SUMMON_RAF_FRIEND: case SPELL_EFFECT_SUMMON_PLAYER: - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->GetTarget()) + if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->GetTarget()) { - WorldObject* target = ObjectAccessor::FindPlayer(m_caster->GetTarget()); + WorldObject* target = ObjectAccessor::FindPlayer(m_caster->ToPlayer()->GetTarget()); CallScriptObjectTargetSelectHandlers(target, SpellEffIndex(effIndex), SpellImplicitTargetInfo()); // scripts may modify the target - recheck @@ -1821,7 +1819,7 @@ uint32 Spell::GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionConta } template<class SEARCHER> -void Spell::SearchTargets(SEARCHER& searcher, uint32 containerMask, Unit* referer, Position const* pos, float radius) +void Spell::SearchTargets(SEARCHER& searcher, uint32 containerMask, WorldObject* referer, Position const* pos, float radius) { if (!containerMask) return; @@ -1857,7 +1855,7 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargetObjectTypes objec return target; } -void Spell::SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList) +void Spell::SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, WorldObject* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList) { uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList); if (!containerTypeMask) @@ -1968,7 +1966,7 @@ GameObject* Spell::SearchSpellFocus() GameObject* focus = nullptr; Trinity::GameObjectFocusCheck check(m_caster, m_spellInfo->RequiresSpellFocus); Trinity::GameObjectSearcher<Trinity::GameObjectFocusCheck> searcher(m_caster, focus, check); - SearchTargets<Trinity::GameObjectSearcher<Trinity::GameObjectFocusCheck> > (searcher, GRID_MAP_TYPE_MASK_GAMEOBJECT, m_caster, m_caster, m_caster->GetVisibilityRange()); + SearchTargets(searcher, GRID_MAP_TYPE_MASK_GAMEOBJECT, m_caster, m_caster, m_caster->GetVisibilityRange()); return focus; } @@ -2082,7 +2080,7 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= return; if (checkIfValid) - if (m_spellInfo->CheckTarget(m_caster, target, implicit || m_caster->GetEntry() == WORLD_TRIGGER) != SPELL_CAST_OK) // skip stealth checks for GO casts + if (m_spellInfo->CheckTarget(m_caster, target, implicit) != SPELL_CAST_OK) // skip stealth checks for AOE return; // Check for effect immune skip if immuned @@ -2127,10 +2125,8 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= } // Calculate hit result - if (m_originalCaster) - targetInfo.MissCondition = m_originalCaster->SpellHitResult(target, m_spellInfo, m_canReflect && !(IsPositive() && m_caster->IsFriendlyTo(target))); - else - targetInfo.MissCondition = SPELL_MISS_EVADE; //SPELL_MISS_NONE; + WorldObject* caster = m_originalCaster ? m_originalCaster : m_caster; + targetInfo.MissCondition = caster->SpellHitResult(target, m_spellInfo, m_canReflect && !(IsPositive() && m_caster->IsFriendlyTo(target))); // Spell have speed - need calculate incoming time // Incoming time is zero for self casts. At least I think so. @@ -2154,8 +2150,9 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= // If target reflect spell back to caster if (targetInfo.MissCondition == SPELL_MISS_REFLECT) { - // Calculate reflected spell result on caster - targetInfo.ReflectResult = m_caster->SpellHitResult(m_caster, m_spellInfo, false); // can't reflect twice + // Calculate reflected spell result on caster (shouldn't be able to reflect gameobject spells) + Unit* unitCaster = ASSERT_NOTNULL(m_caster->ToUnit()); + targetInfo.ReflectResult = unitCaster->SpellHitResult(unitCaster, m_spellInfo, false); // can't reflect twice // Proc spell reflect aura when missile hits the original target target->m_Events.AddEvent(new ProcReflectDelayed(target, m_originalCasterGUID), target->m_Events.CalculateTime(targetInfo.TimeDelay)); @@ -2265,7 +2262,7 @@ void Spell::AddDestTarget(SpellDestination const& dest, uint32 effIndex) void Spell::TargetInfo::PreprocessTarget(Spell* spell) { - Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID); + Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster->ToUnit() : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID); if (!unit) return; @@ -2280,7 +2277,7 @@ void Spell::TargetInfo::PreprocessTarget(Spell* spell) if (MissCondition == SPELL_MISS_NONE) _spellHitTarget = unit; else if (MissCondition == SPELL_MISS_REFLECT && ReflectResult == SPELL_MISS_NONE) - _spellHitTarget = spell->m_caster; + _spellHitTarget = spell->m_caster->ToUnit(); _enablePVP = false; // need to check PvP state before spell effects, but act on it afterwards if (_spellHitTarget) @@ -2293,7 +2290,7 @@ void Spell::TargetInfo::PreprocessTarget(Spell* spell) if (missInfo != SPELL_MISS_NONE) { if (missInfo != SPELL_MISS_MISS) - spell->m_caster->SendSpellMiss(unit, spell->m_spellInfo->Id, missInfo); + spell->m_caster->ToUnit()->SendSpellMiss(unit, spell->m_spellInfo->Id, missInfo); spell->m_damage = 0; _spellHitTarget = nullptr; } @@ -2308,7 +2305,7 @@ void Spell::TargetInfo::PreprocessTarget(Spell* spell) void Spell::TargetInfo::DoTargetSpellHit(Spell* spell, uint8 effIndex) { - Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID); + Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster->ToUnit() : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID); if (!unit) return; @@ -2337,7 +2334,7 @@ void Spell::TargetInfo::DoTargetSpellHit(Spell* spell, uint8 effIndex) void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) { - Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID); + Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster->ToUnit() : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID); if (!unit) return; @@ -2352,7 +2349,7 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) // Get original caster (if exist) and calculate damage/healing from him data // Skip if m_originalCaster not available - Unit* caster = spell->m_originalCaster ? spell->m_originalCaster : spell->m_caster; + Unit* caster = spell->m_originalCaster ? spell->m_originalCaster : spell->m_caster->ToUnit(); if (!caster) return; @@ -2497,8 +2494,9 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) // Failed Pickpocket, reveal rogue if (MissCondition == SPELL_MISS_RESIST && spell->m_spellInfo->HasAttribute(SPELL_ATTR0_CU_PICKPOCKET) && spell->unitTarget->GetTypeId() == TYPEID_UNIT) { - spell->m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK); - spell->unitTarget->ToCreature()->EngageWithTarget(spell->m_caster); + Unit* unitCaster = ASSERT_NOTNULL(spell->m_caster->ToUnit()); + unitCaster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK); + spell->unitTarget->ToCreature()->EngageWithTarget(unitCaster); } } @@ -2526,7 +2524,8 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) // _spellHitTarget can be null if spell is missed in DoSpellHitOnUnit if (MissCondition != SPELL_MISS_EVADE && _spellHitTarget && !spell->m_caster->IsFriendlyTo(unit) && (!spell->IsPositive() || spell->m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL))) { - spell->m_caster->AttackedTarget(unit, spell->m_spellInfo->HasInitialAggro()); + if (Unit* unitCaster = spell->m_caster->ToUnit()) + unitCaster->AttackedTarget(unit, spell->m_spellInfo->HasInitialAggro()); if (!unit->IsStandState()) unit->SetStandState(UNIT_STAND_STATE_STAND); @@ -2540,11 +2539,20 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) { //AI functions if (_spellHitTarget->GetTypeId() == TYPEID_UNIT) + { if (_spellHitTarget->ToCreature()->IsAIEnabled) - _spellHitTarget->ToCreature()->AI()->SpellHit(spell->m_caster, spell->m_spellInfo); + { + if (spell->m_caster->GetTypeId() == TYPEID_GAMEOBJECT) + _spellHitTarget->ToCreature()->AI()->SpellHit(spell->m_caster->ToGameObject(), spell->m_spellInfo); + else + _spellHitTarget->ToCreature()->AI()->SpellHit(spell->m_caster->ToUnit(), spell->m_spellInfo); + } + } if (spell->m_caster->GetTypeId() == TYPEID_UNIT && spell->m_caster->ToCreature()->IsAIEnabled) spell->m_caster->ToCreature()->AI()->SpellHitTarget(_spellHitTarget, spell->m_spellInfo); + else if (spell->m_caster->GetTypeId() == TYPEID_GAMEOBJECT && spell->m_caster->ToGameObject()->AI()) + spell->m_caster->ToGameObject()->AI()->SpellHitTarget(_spellHitTarget, spell->m_spellInfo); // Needs to be called after dealing damage/healing to not remove breaking on damage auras spell->DoTriggersOnSpellHit(_spellHitTarget, EffectMask); @@ -2558,7 +2566,7 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) void Spell::GOTargetInfo::DoTargetSpellHit(Spell* spell, uint8 effIndex) { - GameObject* go = ObjectAccessor::GetGameObject(*spell->m_caster, TargetGUID); + GameObject* go = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster->ToGameObject() : ObjectAccessor::GetGameObject(*spell->m_caster, TargetGUID); if (!go) return; @@ -2566,8 +2574,19 @@ void Spell::GOTargetInfo::DoTargetSpellHit(Spell* spell, uint8 effIndex) spell->HandleEffects(nullptr, nullptr, go, effIndex, SPELL_EFFECT_HANDLE_HIT_TARGET); + //AI functions if (go->AI()) - go->AI()->SpellHit(spell->m_caster, spell->m_spellInfo); + { + if (spell->m_caster->GetTypeId() == TYPEID_GAMEOBJECT) + go->AI()->SpellHit(spell->m_caster->ToGameObject(), spell->m_spellInfo); + else + go->AI()->SpellHit(spell->m_caster->ToUnit(), spell->m_spellInfo); + } + + if (spell->m_caster->GetTypeId() == TYPEID_UNIT && spell->m_caster->ToCreature()->IsAIEnabled) + spell->m_caster->ToCreature()->AI()->SpellHitTarget(go, spell->m_spellInfo); + else if (spell->m_caster->GetTypeId() == TYPEID_GAMEOBJECT && spell->m_caster->ToGameObject()->AI()) + spell->m_caster->ToGameObject()->AI()->SpellHitTarget(go, spell->m_spellInfo); spell->CallScriptOnHitHandlers(); spell->CallScriptAfterHitHandlers(); @@ -2635,17 +2654,23 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, bool scaleAura, TargetInfo& playerOwner->UpdatePvP(true); } } - if (unit->IsInCombat() && m_spellInfo->HasInitialAggro()) + + if (m_originalCaster && unit->IsInCombat() && m_spellInfo->HasInitialAggro()) { - if (m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) // only do explicit combat forwarding for PvP enabled units - m_caster->GetCombatManager().InheritCombatStatesFrom(unit); // for creature v creature combat, the threat forward does it for us - unit->GetThreatManager().ForwardThreatForAssistingMe(m_caster, 0.0f, nullptr, true); + if (m_originalCaster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) // only do explicit combat forwarding for PvP enabled units + m_originalCaster->GetCombatManager().InheritCombatStatesFrom(unit); // for creature v creature combat, the threat forward does it for us + unit->GetThreatManager().ForwardThreatForAssistingMe(m_originalCaster, 0.0f, nullptr, true); } } } + // original caster for auras + WorldObject* origCaster = m_caster; + if (m_originalCaster) + origCaster = m_originalCaster; + // check immunity due to diminishing returns - if (m_originalCaster && Aura::BuildEffectMaskForOwner(m_spellInfo, MAX_EFFECT_MASK, unit)) + if (Aura::BuildEffectMaskForOwner(m_spellInfo, MAX_EFFECT_MASK, unit)) { // Select rank for aura with level requirements only in specific cases // Unit has to be target only of aura effect, both caster and target have to be players, target has to be other than unit target @@ -2683,7 +2708,7 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, bool scaleAura, TargetInfo& // Now Reduce spell duration using data received at spell hit // check whatever effects we're going to apply, diminishing returns only apply to negative aura effects hitInfo.Positive = true; - if (m_originalCaster == unit || !m_originalCaster->IsFriendlyTo(unit)) + if (origCaster == unit || !origCaster->IsFriendlyTo(unit)) { for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { @@ -2701,7 +2726,7 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, bool scaleAura, TargetInfo& hitInfo.AuraDuration = hitInfo.AuraSpellInfo->GetMaxDuration(); // unit is immune to aura if it was diminished to 0 duration - if (!hitInfo.Positive && !unit->ApplyDiminishingToDuration(hitInfo.AuraSpellInfo, triggered, hitInfo.AuraDuration, m_originalCaster, diminishLevel)) + if (!hitInfo.Positive && !unit->ApplyDiminishingToDuration(hitInfo.AuraSpellInfo, triggered, hitInfo.AuraDuration, origCaster, diminishLevel)) if (std::all_of(std::begin(hitInfo.AuraSpellInfo->Effects), std::end(hitInfo.AuraSpellInfo->Effects), [](SpellEffectInfo const& effInfo) { return !effInfo.IsEffect() || effInfo.Effect == SPELL_EFFECT_APPLY_AURA; })) return SPELL_MISS_IMMUNE; } @@ -2711,10 +2736,13 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, bool scaleAura, TargetInfo& void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) { - uint8 aura_effmask = Aura::BuildEffectMaskForOwner(m_spellInfo, 1 << effIndex, unit); - if (aura_effmask) + if (uint8 aura_effmask = Aura::BuildEffectMaskForOwner(m_spellInfo, 1 << effIndex, unit)) { + WorldObject* caster = m_caster; if (m_originalCaster) + caster = m_originalCaster; + + if (caster) { bool refresh = false; @@ -2728,7 +2756,7 @@ void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) AuraCreateInfo createInfo(hitInfo.AuraSpellInfo, allAuraEffectMask, unit); createInfo - .SetCaster(m_originalCaster) + .SetCasterGUID(caster->GetGUID()) .SetBaseAmount(bp) .SetCastItem(m_CastItem) .SetPeriodicReset(resetPeriodicTimer) @@ -2754,13 +2782,13 @@ void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) _spellAura->SetDiminishGroup(hitInfo.DRGroup); - hitInfo.AuraDuration = m_originalCaster->ModSpellDuration(hitInfo.AuraSpellInfo, unit, hitInfo.AuraDuration, hitInfo.Positive, _spellAura->GetEffectMask()); + hitInfo.AuraDuration = caster->ModSpellDuration(hitInfo.AuraSpellInfo, unit, hitInfo.AuraDuration, hitInfo.Positive, _spellAura->GetEffectMask()); // Haste modifies duration of channeled spells if (m_spellInfo->IsChanneled()) - m_originalCaster->ModSpellDurationTime(hitInfo.AuraSpellInfo, hitInfo.AuraDuration, this); + caster->ModSpellDurationTime(hitInfo.AuraSpellInfo, hitInfo.AuraDuration, this); // and duration of auras affected by SPELL_AURA_PERIODIC_HASTE - else if (m_originalCaster->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, hitInfo.AuraSpellInfo) || m_spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION)) + else if (m_originalCaster && (m_originalCaster->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, hitInfo.AuraSpellInfo) || m_spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION))) hitInfo.AuraDuration = int32(hitInfo.AuraDuration * m_originalCaster->GetFloatValue(UNIT_MOD_CAST_SPEED)); if (hitInfo.AuraDuration != _spellAura->GetMaxDuration()) @@ -2795,9 +2823,10 @@ void Spell::DoTriggersOnSpellHit(Unit* unit, uint8 effMask) // Cast the serverside immunity shield marker m_caster->CastSpell(unit, 61988, true); - if (sSpellMgr->GetSpellInfo(m_preCastSpell)) - // Blizz seems to just apply aura without bothering to cast - m_caster->AddAura(m_preCastSpell, unit); + // Blizz seems to just apply aura without bothering to cast + if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(m_preCastSpell)) + if (Unit* unitCaster = m_caster->ToUnit()) + unitCaster->AddAura(spellInfo, MAX_EFFECT_MASK, unit); } // handle SPELL_AURA_ADD_TARGET_TRIGGER auras @@ -2876,7 +2905,7 @@ bool Spell::UpdateChanneledTargetList() { if (targetInfo.MissCondition == SPELL_MISS_NONE && (channelTargetEffectMask & targetInfo.EffectMask)) { - Unit* unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID); + Unit* unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster->ToUnit() : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID); if (!unit) continue; @@ -2922,20 +2951,23 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered InitExplicitTargets(targets); // Fill aura scaling information - if (m_caster->IsControlledByPlayer() && !m_spellInfo->IsPassive() && m_spellInfo->SpellLevel && !m_spellInfo->IsChanneled() && !(_triggeredCastFlags & TRIGGERED_IGNORE_AURA_SCALING)) + if (Unit* unitCaster = m_caster->ToUnit()) { - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (unitCaster->IsControlledByPlayer() && !m_spellInfo->IsPassive() && m_spellInfo->SpellLevel && !m_spellInfo->IsChanneled() && !(_triggeredCastFlags & TRIGGERED_IGNORE_AURA_SCALING)) { - if (m_spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { - // Change aura with ranks only if basepoints are taken from spellInfo and aura is positive - if (m_spellInfo->IsPositiveEffect(i)) + if (m_spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) { - m_auraScaleMask |= (1 << i); - if (m_spellValue->EffectBasePoints[i] != m_spellInfo->Effects[i].BasePoints) + // Change aura with ranks only if basepoints are taken from spellInfo and aura is positive + if (m_spellInfo->IsPositiveEffect(i)) { - m_auraScaleMask = 0; - break; + m_auraScaleMask |= (1 << i); + if (m_spellValue->EffectBasePoints[i] != m_spellInfo->Effects[i].BasePoints) + { + m_auraScaleMask = 0; + break; + } } } } @@ -2951,20 +2983,22 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered _spellEvent = new SpellEvent(this); m_caster->m_Events.AddEvent(_spellEvent, m_caster->m_Events.CalculateTime(1)); - //Prevent casting at cast another spell (ServerSide check) - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CAST_IN_PROGRESS) && m_caster->IsNonMeleeSpellCast(false, true, true, m_spellInfo->Id == 75) && m_cast_count) + // check disables + if (DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, m_caster)) { - SendCastResult(SPELL_FAILED_SPELL_IN_PROGRESS); + SendCastResult(SPELL_FAILED_SPELL_UNAVAILABLE); finish(false); return; } - if (DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, m_caster)) + // Prevent casting at cast another spell (ServerSide check) + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CAST_IN_PROGRESS) && m_caster->ToUnit() && m_caster->ToUnit()->IsNonMeleeSpellCast(false, true, true, m_spellInfo->Id == 75) && m_cast_count) { - SendCastResult(SPELL_FAILED_SPELL_UNAVAILABLE); + SendCastResult(SPELL_FAILED_SPELL_IN_PROGRESS); finish(false); return; } + LoadScripts(); // Fill cost data (do not use power for item casts) @@ -3016,7 +3050,7 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered // don't allow channeled spells / spells with cast time to be cast while moving // exception are only channeled spells that have no casttime and SPELL_ATTR5_CAN_CHANNEL_WHEN_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->IsCharmed() && m_caster->GetCharmerGUID().IsCreature()) && m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT)) + if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && !(m_caster->ToPlayer()->IsCharmed() && m_caster->ToPlayer()->GetCharmerGUID().IsCreature()) && m_caster->ToPlayer()->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT)) { // 1. Has casttime, 2. Or doesn't have flag to allow movement during channel if (m_casttime || !m_spellInfo->IsMoveAllowedChannel()) @@ -3051,20 +3085,25 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered 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 (!(_triggeredCastFlags & TRIGGERED_IGNORE_AURA_INTERRUPT_FLAGS) && m_spellInfo->IsBreakingStealth()) + if (Unit* unitCaster = m_caster->ToUnit()) { - m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CAST); - for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (m_spellInfo->Effects[i].GetUsedTargetObjectType() == TARGET_OBJECT_TYPE_UNIT) + // 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 (!(_triggeredCastFlags & TRIGGERED_IGNORE_AURA_INTERRUPT_FLAGS) && m_spellInfo->IsBreakingStealth()) + { + unitCaster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CAST); + for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) { - m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_SPELL_ATTACK); - break; + if (m_spellInfo->Effects[i].GetUsedTargetObjectType() == TARGET_OBJECT_TYPE_UNIT) + { + unitCaster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_SPELL_ATTACK); + break; + } } - } + } - m_caster->SetCurrentCastSpell(this); + unitCaster->SetCurrentCastSpell(this); + } SendSpellStart(); if (!(_triggeredCastFlags & TRIGGERED_IGNORE_GCD)) @@ -3101,7 +3140,7 @@ void Spell::cancel() case SPELL_STATE_CASTING: for (TargetInfo const& targetInfo : m_UniqueTargetInfo) if (targetInfo.MissCondition == SPELL_MISS_NONE) - if (Unit* unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID)) + if (Unit* unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster->ToUnit() : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID)) unit->RemoveOwnedAura(m_spellInfo->Id, m_originalCasterGUID, 0, AURA_REMOVE_BY_CANCEL); SendChannelUpdate(0); @@ -3119,9 +3158,13 @@ void Spell::cancel() if (m_selfContainer && *m_selfContainer == this) *m_selfContainer = nullptr; - m_caster->RemoveDynObject(m_spellInfo->Id); - if (m_spellInfo->IsChanneled()) // if not channeled then the object for the current cast wasn't summoned yet - m_caster->RemoveGameObject(m_spellInfo->Id, true); + // originalcaster handles gameobjects/dynobjects for gob caster + if (m_originalCaster) + { + m_originalCaster->RemoveDynObject(m_spellInfo->Id); + if (m_spellInfo->IsChanneled()) // if not channeled then the object for the current cast wasn't summoned yet + m_originalCaster->RemoveGameObject(m_spellInfo->Id, true); + } //set state back so finish will be processed m_spellState = oldState; @@ -3184,7 +3227,7 @@ void Spell::_cast(bool skipCheck) if (!(_triggeredCastFlags & TRIGGERED_IGNORE_SET_FACING)) if (m_caster->GetTypeId() == TYPEID_UNIT && m_targets.GetObjectTarget() && m_caster != m_targets.GetObjectTarget()) - m_caster->SetInFront(m_targets.GetObjectTarget()); + m_caster->ToCreature()->SetInFront(m_targets.GetObjectTarget()); // Should this be done for original caster? Player* modOwner = m_caster->GetSpellModOwner(); @@ -3255,11 +3298,13 @@ void Spell::_cast(bool skipCheck) DiminishingReturnsType type = m_spellInfo->GetDiminishingReturnsGroupType(triggered); if (type == DRTYPE_ALL || (type == DRTYPE_PLAYER && target->IsAffectedByDiminishingReturns())) { - Unit* caster = m_originalCaster ? m_originalCaster : m_caster; - if (target->HasStrongerAuraWithDR(m_spellInfo, caster, triggered)) + if (Unit* caster = m_originalCaster ? m_originalCaster : m_caster->ToUnit()) { - cleanupSpell(SPELL_FAILED_AURA_BOUNCED); - return; + if (target->HasStrongerAuraWithDR(m_spellInfo, caster, triggered)) + { + cleanupSpell(SPELL_FAILED_AURA_BOUNCED); + return; + } } } } @@ -3272,7 +3317,7 @@ void Spell::_cast(bool skipCheck) if (m_caster->GetTypeId() == TYPEID_UNIT && !m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) if (!m_spellInfo->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST)) if (WorldObject* objTarget = m_targets.GetObjectTarget()) - m_caster->SetInFront(objTarget); + m_caster->ToCreature()->SetInFront(objTarget); SelectSpellTargets(); @@ -3289,9 +3334,10 @@ void Spell::_cast(bool skipCheck) return; } - if (m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET)) - if (Creature* pet = ObjectAccessor::GetCreature(*m_caster, m_caster->GetPetGUID())) - pet->DespawnOrUnsummon(); + if (Unit* unitCaster = m_caster->ToUnit()) + if (m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET)) + if (Creature* pet = ObjectAccessor::GetCreature(*m_caster, unitCaster->GetPetGUID())) + pet->DespawnOrUnsummon(); PrepareTriggersExecutedOnHit(); @@ -3349,8 +3395,9 @@ void Spell::_cast(bool skipCheck) m_spellState = SPELL_STATE_DELAYED; SetDelayStart(0); - if (m_caster->HasUnitState(UNIT_STATE_CASTING) && !m_caster->IsNonMeleeSpellCast(false, false, true)) - m_caster->ClearUnitState(UNIT_STATE_CASTING); + if (Unit* unitCaster = m_caster->ToUnit()) + if (unitCaster->HasUnitState(UNIT_STATE_CASTING) && !unitCaster->IsNonMeleeSpellCast(false, false, true)) + unitCaster->ClearUnitState(UNIT_STATE_CASTING); } else { @@ -3365,7 +3412,10 @@ void Spell::_cast(bool skipCheck) for (int32 id : *spell_triggered) { if (id < 0) - m_caster->RemoveAurasDueToSpell(-id); + { + if (Unit* unitCaster = m_caster->ToUnit()) + unitCaster->RemoveAurasDueToSpell(-id); + } else m_caster->CastSpell(m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : m_caster, id, true); } @@ -3376,8 +3426,8 @@ void Spell::_cast(bool skipCheck) modOwner->SetSpellModTakingSpell(this, false); //Clear spell cooldowns after every spell is cast if .cheat cooldown is enabled. - if (modOwner->GetCommandStatus(CHEAT_COOLDOWN)) - m_caster->GetSpellHistory()->ResetCooldown(m_spellInfo->Id, true); + if (m_originalCaster && modOwner->GetCommandStatus(CHEAT_COOLDOWN)) + m_originalCaster->GetSpellHistory()->ResetCooldown(m_spellInfo->Id, true); } SetExecutedCurrently(false); @@ -3433,17 +3483,16 @@ void Spell::handle_immediate() // Apply haste mods m_caster->ModSpellDurationTime(m_spellInfo, duration, this); - m_spellState = SPELL_STATE_CASTING; - m_caster->AddInterruptMask(m_spellInfo->ChannelInterruptFlags); m_channeledDuration = duration; SendChannelStart(duration); } else if (duration == -1) - { - m_spellState = SPELL_STATE_CASTING; - m_caster->AddInterruptMask(m_spellInfo->ChannelInterruptFlags); SendChannelStart(duration); - } + + m_spellState = SPELL_STATE_CASTING; + + // GameObjects shouldn't cast channeled spells + ASSERT_NOTNULL(m_caster->ToUnit())->AddInterruptMask(m_spellInfo->ChannelInterruptFlags); } PrepareTargetProcessing(); @@ -3589,20 +3638,23 @@ void Spell::_handle_immediate_phase() void Spell::_handle_finish_phase() { - // Take for real after all targets are processed - if (m_needComboPoints) - m_caster->ClearComboPoints(); + if (Unit* unitCaster = m_caster->ToUnit()) + { + // Take for real after all targets are processed + if (m_needComboPoints) + unitCaster->ClearComboPoints(); - // Real add combo points from effects - if (m_comboTarget && m_comboPointGain) - m_caster->AddComboPoints(m_comboTarget, m_comboPointGain); + // Real add combo points from effects + if (m_comboTarget && m_comboPointGain) + unitCaster->AddComboPoints(m_comboTarget, m_comboPointGain); - if (m_caster->m_extraAttacks && m_spellInfo->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS)) - { - if (Unit* victim = ObjectAccessor::GetUnit(*m_caster, m_targets.GetOrigUnitTargetGUID())) - m_caster->HandleProcExtraAttackFor(victim); - else - m_caster->m_extraAttacks = 0; + if (unitCaster->m_extraAttacks && m_spellInfo->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS)) + { + if (Unit* victim = ObjectAccessor::GetUnit(*unitCaster, m_targets.GetOrigUnitTargetGUID())) + unitCaster->HandleProcExtraAttackFor(victim); + else + unitCaster->m_extraAttacks = 0; + } } // Handle procs on finish @@ -3623,7 +3675,10 @@ void Spell::_handle_finish_phase() void Spell::SendSpellCooldown() { - m_caster->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_CastItem, this); + if (m_caster->GetTypeId() == TYPEID_GAMEOBJECT) + return; + + m_caster->ToUnit()->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_CastItem, this); } void Spell::update(uint32 difftime) @@ -3644,9 +3699,9 @@ void Spell::update(uint32 difftime) } // check if the player caster has moved before the spell finished - 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)))) + if (m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0 && + m_caster->ToPlayer()->isMoving() && m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT && + (m_spellInfo->Effects[EFFECT_0].Effect != SPELL_EFFECT_STUCK || !m_caster->ToPlayer()->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR))) { // don't cancel for melee, autorepeat, triggered and instant spells if (!m_spellInfo->IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered() && !(IsChannelActive() && m_spellInfo->IsMoveAllowedChannel())) @@ -3654,7 +3709,7 @@ void Spell::update(uint32 difftime) // if charmed by creature, trust the AI not to cheat and allow the cast to proceed // @todo this is a hack, "creature" movesplines don't differentiate turning/moving right now // however, checking what type of movement the spline is for every single spline would be really expensive - if (!m_caster->GetCharmerGUID().IsCreature()) + if (!m_caster->ToPlayer()->GetCharmerGUID().IsCreature()) cancel(); } } @@ -3688,7 +3743,7 @@ void Spell::update(uint32 difftime) // Also remove applied auras for (TargetInfo const& target : m_UniqueTargetInfo) - if (Unit* unit = m_caster->GetGUID() == target.TargetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target.TargetGUID)) + if (Unit* unit = m_caster->GetGUID() == target.TargetGUID ? m_caster->ToUnit() : ObjectAccessor::GetUnit(*m_caster, target.TargetGUID)) unit->RemoveOwnedAura(m_spellInfo->Id, m_originalCasterGUID, 0, AURA_REMOVE_BY_CANCEL); } @@ -3715,44 +3770,48 @@ void Spell::update(uint32 difftime) void Spell::finish(bool ok) { + if (m_spellState == SPELL_STATE_FINISHED) + return; + m_spellState = SPELL_STATE_FINISHED; + if (!m_caster) return; - if (m_spellState == SPELL_STATE_FINISHED) + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) return; - m_spellState = SPELL_STATE_FINISHED; if (m_spellInfo->IsChanneled()) - m_caster->UpdateInterruptMask(); + unitCaster->UpdateInterruptMask(); - if (m_caster->HasUnitState(UNIT_STATE_CASTING) && !m_caster->IsNonMeleeSpellCast(false, false, true)) - m_caster->ClearUnitState(UNIT_STATE_CASTING); + if (unitCaster->HasUnitState(UNIT_STATE_CASTING) && !unitCaster->IsNonMeleeSpellCast(false, false, true)) + unitCaster->ClearUnitState(UNIT_STATE_CASTING); // Unsummon summon as possessed creatures on spell cancel - if (m_spellInfo->IsChanneled() && m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_spellInfo->IsChanneled() && unitCaster->GetTypeId() == TYPEID_PLAYER) { - if (Unit* charm = m_caster->GetCharm()) + if (Unit* charm = unitCaster->GetCharm()) if (charm->GetTypeId() == TYPEID_UNIT && charm->ToCreature()->HasUnitTypeMask(UNIT_MASK_PUPPET) && charm->GetUInt32Value(UNIT_CREATED_BY_SPELL) == m_spellInfo->Id) ((Puppet*)charm)->UnSummon(); } - if (Creature* creatureCaster = m_caster->ToCreature()) + if (Creature* creatureCaster = unitCaster->ToCreature()) creatureCaster->ReleaseFocus(this); if (!ok) return; - if (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsSummon()) + if (unitCaster->GetTypeId() == TYPEID_UNIT && unitCaster->IsSummon()) { // Unsummon statue - uint32 spell = m_caster->GetUInt32Value(UNIT_CREATED_BY_SPELL); + uint32 spell = unitCaster->GetUInt32Value(UNIT_CREATED_BY_SPELL); SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell); if (spellInfo && spellInfo->SpellIconID == 2056) { - TC_LOG_DEBUG("spells", "Statue %d is unsummoned in spell %d finish", m_caster->GetGUID().GetCounter(), m_spellInfo->Id); - m_caster->setDeathState(JUST_DIED); + TC_LOG_DEBUG("spells", "Statue %d is unsummoned in spell %d finish", unitCaster->GetGUID().GetCounter(), m_spellInfo->Id); + unitCaster->setDeathState(JUST_DIED); return; } } @@ -3760,7 +3819,7 @@ void Spell::finish(bool ok) if (IsAutoActionResetSpell()) { bool found = false; - Unit::AuraEffectList const& vIgnoreReset = m_caster->GetAuraEffectsByType(SPELL_AURA_IGNORE_MELEE_RESET); + Unit::AuraEffectList const& vIgnoreReset = unitCaster->GetAuraEffectsByType(SPELL_AURA_IGNORE_MELEE_RESET); for (Unit::AuraEffectList::const_iterator i = vIgnoreReset.begin(); i != vIgnoreReset.end(); ++i) { if ((*i)->IsAffectedOnSpell(m_spellInfo)) @@ -3772,23 +3831,23 @@ void Spell::finish(bool ok) if (!found && !m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS)) { - m_caster->resetAttackTimer(BASE_ATTACK); - if (m_caster->haveOffhandWeapon()) - m_caster->resetAttackTimer(OFF_ATTACK); - m_caster->resetAttackTimer(RANGED_ATTACK); + unitCaster->resetAttackTimer(BASE_ATTACK); + if (unitCaster->haveOffhandWeapon()) + unitCaster->resetAttackTimer(OFF_ATTACK); + unitCaster->resetAttackTimer(RANGED_ATTACK); } } // potions disabled by client, send event "not in combat" if need - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) { if (!m_triggeredByAuraSpell) - m_caster->ToPlayer()->UpdatePotionCooldown(this); + unitCaster->ToPlayer()->UpdatePotionCooldown(this); } // Stop Attack for some spells if (m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET)) - m_caster->AttackStop(); + unitCaster->AttackStop(); } void Spell::WriteCastResultInfo(WorldPacket& data, Player* caster, SpellInfo const* spellInfo, uint8 castCount, SpellCastResult result, SpellCustomErrors customError, uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/) @@ -4024,8 +4083,14 @@ void Spell::SendSpellStart() //TC_LOG_DEBUG("spells", "Sending SMSG_SPELL_START id=%u", m_spellInfo->Id); uint32 castFlags = CAST_FLAG_UNKNOWN_2; - uint32 schoolImmunityMask = m_caster->GetSchoolImmunityMask(); - uint32 mechanicImmunityMask = m_caster->GetMechanicImmunityMask(); + uint32 schoolImmunityMask = 0; + uint32 mechanicImmunityMask = 0; + if (Unit* unitCaster = m_caster->ToUnit()) + { + schoolImmunityMask = unitCaster->GetSchoolImmunityMask(); + mechanicImmunityMask = unitCaster->GetMechanicImmunityMask(); + } + if (schoolImmunityMask || mechanicImmunityMask) castFlags |= CAST_FLAG_IMMUNITY; @@ -4035,7 +4100,7 @@ void Spell::SendSpellStart() if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA)) castFlags |= CAST_FLAG_AMMO; if ((m_caster->GetTypeId() == TYPEID_PLAYER || - (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsPet())) + (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsPet())) && m_spellInfo->PowerType != POWER_HEALTH) castFlags |= CAST_FLAG_POWER_LEFT_SELF; @@ -4057,7 +4122,7 @@ void Spell::SendSpellStart() m_targets.Write(data); if (castFlags & CAST_FLAG_POWER_LEFT_SELF) - data << uint32(m_caster->GetPower((Powers)m_spellInfo->PowerType)); + data << uint32(ASSERT_NOTNULL(m_caster->ToUnit())->GetPower((Powers)m_spellInfo->PowerType)); if (castFlags & CAST_FLAG_AMMO) WriteAmmoToPacket(&data); @@ -4089,12 +4154,12 @@ void Spell::SendSpellGo() castFlags |= CAST_FLAG_AMMO; // arrows/bullets visual if ((m_caster->GetTypeId() == TYPEID_PLAYER || - (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsPet())) + (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->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 + castFlags |= CAST_FLAG_POWER_LEFT_SELF; if ((m_caster->GetTypeId() == TYPEID_PLAYER) - && (m_caster->getClass() == CLASS_DEATH_KNIGHT) + && (m_caster->ToPlayer()->getClass() == CLASS_DEATH_KNIGHT) && m_spellInfo->RuneCostID && m_spellInfo->PowerType == POWER_RUNE && !(_triggeredCastFlags & TRIGGERED_IGNORE_POWER_AND_REAGENT_COST)) @@ -4113,7 +4178,6 @@ void Spell::SendSpellGo() castFlags |= CAST_FLAG_NO_GCD; WorldPacket data(SMSG_SPELL_GO, 50); // guess size - if (m_CastItem) data << m_CastItem->GetPackGUID(); else @@ -4130,7 +4194,7 @@ void Spell::SendSpellGo() m_targets.Write(data); if (castFlags & CAST_FLAG_POWER_LEFT_SELF) - data << uint32(m_caster->GetPower((Powers)m_spellInfo->PowerType)); + data << uint32(ASSERT_NOTNULL(m_caster->ToUnit())->GetPower((Powers)m_spellInfo->PowerType)); if (castFlags & CAST_FLAG_RUNE_LIST) // rune cooldowns list { @@ -4174,7 +4238,14 @@ void Spell::SendSpellGo() data << uint8(0); } - m_caster->SendMessageToSet(&data, true); + // should be sent to self only + if (castFlags & CAST_FLAG_POWER_LEFT_SELF) + { + if (Player* player = m_caster->GetAffectingPlayer()) + player->SendDirectMessage(&data); + } + else + m_caster->SendMessageToSet(&data, true); } void Spell::WriteAmmoToPacket(WorldPacket* data) @@ -4202,7 +4273,7 @@ void Spell::WriteAmmoToPacket(WorldPacket* data) ammoInventoryType = pProto->InventoryType; } } - else if (m_caster->HasAura(46699)) // Requires No Ammo + else if (m_caster->ToPlayer()->HasAura(46699)) // Requires No Ammo { ammoDisplayID = 5996; // normal arrow ammoInventoryType = INVTYPE_AMMO; @@ -4210,9 +4281,9 @@ void Spell::WriteAmmoToPacket(WorldPacket* data) } } } - else + else if (m_caster->GetTypeId() == TYPEID_UNIT) { - for (uint8 i = 0; i < 3; ++i) + for (uint8 i = BASE_ATTACK; i < MAX_ATTACK; ++i) { if (uint32 item_id = m_caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i)) { @@ -4428,59 +4499,70 @@ void Spell::SendInterrupted(uint8 result) void Spell::SendChannelUpdate(uint32 time) { + // GameObjects don't channel + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return; + if (time == 0) { - m_caster->SetChannelObjectGuid(ObjectGuid::Empty); - m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL, 0); + unitCaster->SetChannelObjectGuid(ObjectGuid::Empty); + unitCaster->SetUInt32Value(UNIT_CHANNEL_SPELL, 0); } WorldPacket data(MSG_CHANNEL_UPDATE, 8+4); - data << m_caster->GetPackGUID(); + data << unitCaster->GetPackGUID(); data << uint32(time); - m_caster->SendMessageToSet(&data, true); + unitCaster->SendMessageToSet(&data, true); } void Spell::SendChannelStart(uint32 duration) { + // GameObjects don't channel + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return; + ObjectGuid channelTarget = m_targets.GetObjectTargetGUID(); if (!channelTarget && !m_spellInfo->NeedsExplicitUnitTarget()) if (m_UniqueTargetInfo.size() + m_UniqueGOTargetInfo.size() == 1) // this is for TARGET_SELECT_CATEGORY_NEARBY channelTarget = !m_UniqueTargetInfo.empty() ? m_UniqueTargetInfo.front().TargetGUID : m_UniqueGOTargetInfo.front().TargetGUID; WorldPacket data(MSG_CHANNEL_START, (8+4+4)); - data << m_caster->GetPackGUID(); + data << unitCaster->GetPackGUID(); data << uint32(m_spellInfo->Id); data << uint32(duration); - m_caster->SendMessageToSet(&data, true); + unitCaster->SendMessageToSet(&data, true); m_timer = duration; if (channelTarget) { - m_caster->SetChannelObjectGuid(channelTarget); + unitCaster->SetChannelObjectGuid(channelTarget); - if (channelTarget != m_caster->GetGUID()) - if (Creature* creatureCaster = m_caster->ToCreature()) + if (channelTarget != unitCaster->GetGUID()) + if (Creature* creatureCaster = unitCaster->ToCreature()) if (!creatureCaster->IsFocusing(this)) creatureCaster->FocusTarget(this, ObjectAccessor::GetWorldObject(*creatureCaster, channelTarget)); } - m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL, m_spellInfo->Id); + unitCaster->SetUInt32Value(UNIT_CHANNEL_SPELL, m_spellInfo->Id); } void Spell::SendResurrectRequest(Player* target) { // get resurrector name for creature resurrections, otherwise packet will be not accepted // for player resurrections the name is looked up by guid - std::string const sentName(m_caster->GetTypeId() == TYPEID_PLAYER ? - "" : m_caster->GetNameForLocaleIdx(target->GetSession()->GetSessionDbLocaleIndex())); + std::string sentName; + if (m_caster->GetTypeId() != TYPEID_PLAYER) + sentName = m_caster->GetNameForLocaleIdx(target->GetSession()->GetSessionDbLocaleIndex()); WorldPacket data(SMSG_RESURRECT_REQUEST, 8 + 4 + sentName.size() + 1 + 1 + 1); data << uint64(m_caster->GetGUID()); data << uint32(sentName.size() + 1); data << sentName; - data << uint8(m_caster->IsSpiritHealer()); // "you'll be afflicted with resurrection sickness" + data << uint8(m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsSpiritHealer()); // "you'll be afflicted with resurrection sickness" // override delay sent with SMSG_CORPSE_RECLAIM_DELAY, set instant resurrection for spells with this attribute data << uint8(!m_spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_RESURRECTION_TIMER)); target->SendDirectMessage(&data); @@ -4556,19 +4638,24 @@ void Spell::TakeCastItem() void Spell::TakePower() { + // GameObjects don't use power + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return; + if (m_CastItem || m_triggeredByAuraSpell) return; //Don't take power if the spell is cast while .cheat power is enabled. - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) { - if (m_caster->ToPlayer()->GetCommandStatus(CHEAT_POWER)) + if (unitCaster->ToPlayer()->GetCommandStatus(CHEAT_POWER)) return; } Powers powerType = Powers(m_spellInfo->PowerType); bool hit = true; - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) { if (powerType == POWER_RAGE || powerType == POWER_ENERGY || powerType == POWER_RUNE) { @@ -4579,7 +4666,7 @@ void Spell::TakePower() { hit = false; //lower spell cost on fail (by talent aura) - if (Player* modOwner = m_caster->ToPlayer()->GetSpellModOwner()) + if (Player* modOwner = unitCaster->ToPlayer()->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_SPELL_COST_REFUND_ON_FAIL, m_powerCost); } } @@ -4598,7 +4685,7 @@ void Spell::TakePower() // health as power used if (powerType == POWER_HEALTH) { - m_caster->ModifyHealth(-(int32)m_powerCost); + unitCaster->ModifyHealth(-(int32)m_powerCost); return; } @@ -4608,40 +4695,45 @@ void Spell::TakePower() return; } - m_caster->ModifyPower(powerType, -m_powerCost); + unitCaster->ModifyPower(powerType, -m_powerCost); // Set the five second timer if (powerType == POWER_MANA && m_powerCost > 0) - m_caster->SetLastManaUse(GameTime::GetGameTimeMS()); + unitCaster->SetLastManaUse(GameTime::GetGameTimeMS()); } void Spell::TakeAmmo() { - if (m_attackType == RANGED_ATTACK && m_caster->GetTypeId() == TYPEID_PLAYER) - { - Item* pItem = m_caster->ToPlayer()->GetWeaponForAttack(RANGED_ATTACK); + // Only players use ammo + Player* player = m_caster->ToPlayer(); + if (!player) + return; - // wands don't have ammo - if (!pItem || pItem->IsBroken() || pItem->GetTemplate()->SubClass == ITEM_SUBCLASS_WEAPON_WAND) - return; + // only ranged + if (m_attackType != RANGED_ATTACK) + return; + + // wands don't have ammo + Item* item = player->GetWeaponForAttack(RANGED_ATTACK); + if (!item || item->IsBroken() || item->GetTemplate()->SubClass == ITEM_SUBCLASS_WEAPON_WAND) + return; - if (pItem->GetTemplate()->InventoryType == INVTYPE_THROWN) + if (item->GetTemplate()->InventoryType == INVTYPE_THROWN) + { + if (item->GetMaxStackCount() == 1) { - if (pItem->GetMaxStackCount() == 1) - { - // decrease durability for non-stackable throw weapon - m_caster->ToPlayer()->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_RANGED); - } - else - { - // decrease items amount for stackable throw weapon - uint32 count = 1; - m_caster->ToPlayer()->DestroyItemCount(pItem, count, true); - } + // decrease durability for non-stackable throw weapon + player->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_RANGED); + } + else + { + // decrease items amount for stackable throw weapon + uint32 count = 1; + player->DestroyItemCount(item, count, true); } - else if (uint32 ammo = m_caster->ToPlayer()->GetUInt32Value(PLAYER_AMMO_ID)) - m_caster->ToPlayer()->DestroyItemCount(ammo, 1, true); } + else if (uint32 ammo = player->GetUInt32Value(PLAYER_AMMO_ID)) + player->DestroyItemCount(ammo, 1, true); } SpellCastResult Spell::CheckRuneCost(uint32 runeCostID) const @@ -4693,7 +4785,7 @@ SpellCastResult Spell::CheckRuneCost(uint32 runeCostID) const void Spell::TakeRunePower(bool didHit) { - if (m_caster->GetTypeId() != TYPEID_PLAYER || m_caster->getClass() != CLASS_DEATH_KNIGHT) + if (m_caster->GetTypeId() != TYPEID_PLAYER || m_caster->ToPlayer()->getClass() != CLASS_DEATH_KNIGHT) return; SpellRuneCostEntry const* runeCostData = sSpellRuneCostStore.LookupEntry(m_spellInfo->RuneCostID); @@ -4833,6 +4925,11 @@ void Spell::TakeReagents() void Spell::HandleThreatSpells() { + // wild GameObject spells don't cause threat + Unit* unitCaster = (m_originalCaster ? m_originalCaster : m_caster->ToUnit()); + if (!unitCaster) + return; + if (m_UniqueTargetInfo.empty()) return; @@ -4843,7 +4940,7 @@ void Spell::HandleThreatSpells() if (SpellThreatEntry const* threatEntry = sSpellMgr->GetSpellThreatEntry(m_spellInfo->Id)) { if (threatEntry->apPctMod != 0.0f) - threat += threatEntry->apPctMod * m_caster->GetTotalAttackPowerValue(BASE_ATTACK); + threat += threatEntry->apPctMod * unitCaster->GetTotalAttackPowerValue(BASE_ATTACK); threat += threatEntry->flatMod; } @@ -4863,20 +4960,20 @@ void Spell::HandleThreatSpells() if (ihit->MissCondition != SPELL_MISS_NONE) threatToAdd = 0.0f; - Unit* target = ObjectAccessor::GetUnit(*m_caster, ihit->TargetGUID); + Unit* target = ObjectAccessor::GetUnit(*unitCaster, ihit->TargetGUID); if (!target) continue; // positive spells distribute threat among all units that are in combat with target, like healing if (IsPositive()) - target->GetThreatManager().ForwardThreatForAssistingMe(m_caster, threatToAdd, m_spellInfo); + target->GetThreatManager().ForwardThreatForAssistingMe(unitCaster, threatToAdd, m_spellInfo); // for negative spells threat gets distributed among affected targets else { if (!target->CanHaveThreatList()) continue; - target->GetThreatManager().AddThreat(m_caster, threatToAdd, m_spellInfo, true); + target->GetThreatManager().AddThreat(unitCaster, threatToAdd, m_spellInfo, true); } } TC_LOG_DEBUG("spells", "Spell %u, added an additional %f threat for %s %u target(s)", m_spellInfo->Id, threat, IsPositive() ? "assisting" : "harming", uint32(m_UniqueTargetInfo.size())); @@ -4889,26 +4986,25 @@ void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOT itemTarget = pItemTarget; gameObjTarget = pGOTarget; destTarget = &m_destTargets[i]._position; + unitCaster = m_originalCaster ? m_originalCaster : m_caster->ToUnit(); - uint8 eff = m_spellInfo->Effects[i].Effect; - - TC_LOG_DEBUG("spells", "Spell: %u Effect : %u", m_spellInfo->Id, eff); + uint8 effect = m_spellInfo->Effects[i].Effect; + ASSERT(effect < TOTAL_SPELL_EFFECTS); // checked at startup // we do not need DamageMultiplier here. - damage = CalculateDamage(i, nullptr); + damage = CalculateDamage(i); - bool preventDefault = CallScriptEffectHandlers((SpellEffIndex)i, mode); + SpellEffIndex effIndex = static_cast<SpellEffIndex>(i); + bool preventDefault = CallScriptEffectHandlers(effIndex, mode); - if (!preventDefault && eff < TOTAL_SPELL_EFFECTS) - { - (this->*SpellEffects[eff])((SpellEffIndex)i); - } + if (!preventDefault) + (this->*SpellEffectHandlers[effect])(effIndex); } SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/) { // check death state - if (!m_caster->IsAlive() && !m_spellInfo->IsPassive() && !(m_spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD) || (IsTriggered() && !m_triggeredByAuraSpell))) + if (m_caster->ToUnit() && !m_caster->ToUnit()->IsAlive() && !m_spellInfo->IsPassive() && !(m_spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD) || (IsTriggered() && !m_triggeredByAuraSpell))) return SPELL_FAILED_CASTER_DEAD; // check cooldowns to prevent cheating @@ -4925,7 +5021,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_NOT_READY; } - if (!m_caster->GetSpellHistory()->IsReady(m_spellInfo, m_castItemEntry, IsIgnoringCooldowns())) + if (m_caster->ToUnit() && !m_caster->ToUnit()->GetSpellHistory()->IsReady(m_spellInfo, m_castItemEntry, IsIgnoringCooldowns())) { if (m_triggeredByAuraSpell) return SPELL_FAILED_DONT_REPORT; @@ -4953,95 +5049,98 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (m_caster->GetTypeId() == TYPEID_PLAYER && VMAP::VMapFactory::createOrGetVMapManager()->isLineOfSightCalcEnabled()) { if (m_spellInfo->HasAttribute(SPELL_ATTR0_OUTDOORS_ONLY) && - !m_caster->IsOutdoors()) + !m_caster->IsOutdoors()) return SPELL_FAILED_ONLY_OUTDOORS; if (m_spellInfo->HasAttribute(SPELL_ATTR0_INDOORS_ONLY) && - m_caster->IsOutdoors()) + m_caster->IsOutdoors()) return SPELL_FAILED_ONLY_INDOORS; } - // only check at first call, Stealth auras are already removed at second call - // for now, ignore triggered spells - if (strict && !(_triggeredCastFlags & TRIGGERED_IGNORE_SHAPESHIFT)) + if (Unit* unitCaster = m_caster->ToUnit()) { - bool checkForm = true; - // Ignore form req aura - Unit::AuraEffectList const& ignore = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_IGNORE_SHAPESHIFT); - for (AuraEffect const* aurEff : ignore) + // only check at first call, Stealth auras are already removed at second call + // for now, ignore triggered spells + if (strict && !(_triggeredCastFlags & TRIGGERED_IGNORE_SHAPESHIFT)) { - if (!aurEff->IsAffectedOnSpell(m_spellInfo)) - continue; + bool checkForm = true; + // Ignore form req aura + Unit::AuraEffectList const& ignore = unitCaster->GetAuraEffectsByType(SPELL_AURA_MOD_IGNORE_SHAPESHIFT); + for (AuraEffect const* aurEff : ignore) + { + if (!aurEff->IsAffectedOnSpell(m_spellInfo)) + continue; - checkForm = false; - break; - } + checkForm = false; + break; + } - if (checkForm) - { - // Cannot be used in this stance/form - SpellCastResult shapeError = m_spellInfo->CheckShapeshift(m_caster->GetShapeshiftForm()); - if (shapeError != SPELL_CAST_OK) - return shapeError; + if (checkForm) + { + // Cannot be used in this stance/form + SpellCastResult shapeError = m_spellInfo->CheckShapeshift(unitCaster->GetShapeshiftForm()); + if (shapeError != SPELL_CAST_OK) + return shapeError; - if (m_spellInfo->HasAttribute(SPELL_ATTR0_ONLY_STEALTHED) && !(m_caster->HasStealthAura())) - return SPELL_FAILED_ONLY_STEALTHED; + if (m_spellInfo->HasAttribute(SPELL_ATTR0_ONLY_STEALTHED) && !(unitCaster->HasStealthAura())) + return SPELL_FAILED_ONLY_STEALTHED; + } } - } - if (m_caster->HasAuraTypeWithMiscvalue(SPELL_AURA_BLOCK_SPELL_FAMILY, m_spellInfo->SpellFamilyName)) - return SPELL_FAILED_SPELL_UNAVAILABLE; + if (unitCaster->HasAuraTypeWithMiscvalue(SPELL_AURA_BLOCK_SPELL_FAMILY, m_spellInfo->SpellFamilyName)) + return SPELL_FAILED_SPELL_UNAVAILABLE; - bool reqCombat = true; - 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)) + bool reqCombat = true; + Unit::AuraEffectList const& stateAuras = unitCaster->GetAuraEffectsByType(SPELL_AURA_ABILITY_IGNORE_AURASTATE); + for (Unit::AuraEffectList::const_iterator j = stateAuras.begin(); j != stateAuras.end(); ++j) { - m_needComboPoints = false; - if ((*j)->GetMiscValue() == 1) + if ((*j)->IsAffectedOnSpell(m_spellInfo)) { - reqCombat = false; - break; + m_needComboPoints = false; + if ((*j)->GetMiscValue() == 1) + { + reqCombat = false; + break; + } } } - } - // caster state requirements - // not for triggered spells (needed by execute) - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURASTATE)) - { - if (m_spellInfo->CasterAuraState && !m_caster->HasAuraState(AuraStateType(m_spellInfo->CasterAuraState), m_spellInfo, m_caster)) - return SPELL_FAILED_CASTER_AURASTATE; - if (m_spellInfo->CasterAuraStateNot && m_caster->HasAuraState(AuraStateType(m_spellInfo->CasterAuraStateNot), m_spellInfo, m_caster)) - return SPELL_FAILED_CASTER_AURASTATE; + // caster state requirements + // not for triggered spells (needed by execute) + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURASTATE)) + { + if (m_spellInfo->CasterAuraState && !unitCaster->HasAuraState(AuraStateType(m_spellInfo->CasterAuraState), m_spellInfo, unitCaster)) + return SPELL_FAILED_CASTER_AURASTATE; + if (m_spellInfo->CasterAuraStateNot && unitCaster->HasAuraState(AuraStateType(m_spellInfo->CasterAuraStateNot), m_spellInfo, unitCaster)) + return SPELL_FAILED_CASTER_AURASTATE; - // Note: spell 62473 requres casterAuraSpell = triggering spell - if (m_spellInfo->CasterAuraSpell && !m_caster->HasAura(sSpellMgr->GetSpellIdForDifficulty(m_spellInfo->CasterAuraSpell, m_caster))) - return SPELL_FAILED_CASTER_AURASTATE; - if (m_spellInfo->ExcludeCasterAuraSpell && m_caster->HasAura(sSpellMgr->GetSpellIdForDifficulty(m_spellInfo->ExcludeCasterAuraSpell, m_caster))) - return SPELL_FAILED_CASTER_AURASTATE; + // Note: spell 62473 requres casterAuraSpell = triggering spell + if (m_spellInfo->CasterAuraSpell && !unitCaster->HasAura(sSpellMgr->GetSpellIdForDifficulty(m_spellInfo->CasterAuraSpell, unitCaster))) + return SPELL_FAILED_CASTER_AURASTATE; + if (m_spellInfo->ExcludeCasterAuraSpell && unitCaster->HasAura(sSpellMgr->GetSpellIdForDifficulty(m_spellInfo->ExcludeCasterAuraSpell, unitCaster))) + return SPELL_FAILED_CASTER_AURASTATE; - if (reqCombat && m_caster->IsInCombat() && !m_spellInfo->CanBeUsedInCombat()) - return SPELL_FAILED_AFFECTING_COMBAT; - } + if (reqCombat && unitCaster->IsInCombat() && !m_spellInfo->CanBeUsedInCombat()) + 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 && m_caster->ToPlayer()->isMoving() && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature())) - { - // 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) && - (IsAutoRepeat() || (m_spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) != 0)) - return SPELL_FAILED_MOVING; - } + // cancel autorepeat spells if cast start when moving + // (not wand currently autorepeat cast delayed to moving stop anyway in spell update code) + if (unitCaster->GetTypeId() == TYPEID_PLAYER && unitCaster->ToPlayer()->isMoving() && (!unitCaster->IsCharmed() || !unitCaster->GetCharmerGUID().IsCreature())) + { + // skip stuck spell to allow use it in falling case and apply spell limitations at movement + if ((!unitCaster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR) || m_spellInfo->Effects[EFFECT_0].Effect != SPELL_EFFECT_STUCK) && + (IsAutoRepeat() || (m_spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) != 0)) + return SPELL_FAILED_MOVING; + } - // Check vehicle flags - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE)) - { - SpellCastResult vehicleCheck = m_spellInfo->CheckVehicle(m_caster); - if (vehicleCheck != SPELL_CAST_OK) - return vehicleCheck; + // Check vehicle flags + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE)) + { + SpellCastResult vehicleCheck = m_spellInfo->CheckVehicle(unitCaster); + if (vehicleCheck != SPELL_CAST_OK) + return vehicleCheck; + } } // check spell cast conditions from database @@ -5070,8 +5169,8 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (!(m_spellInfo->IsPassive() && (!m_targets.GetUnitTarget() || m_targets.GetUnitTarget() == m_caster))) { // Check explicit target for m_originalCaster - todo: get rid of such workarounds - Unit* caster = m_caster; - if (m_originalCaster && m_caster->GetEntry() != WORLD_TRIGGER) // Do a simplified check for gameobject casts + WorldObject* caster = m_caster; + if (m_originalCaster) caster = m_originalCaster; SpellCastResult castResult = m_spellInfo->CheckExplicitTarget(caster, m_targets.GetObjectTarget(), m_targets.GetItemTarget()); @@ -5081,7 +5180,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (Unit* target = m_targets.GetUnitTarget()) { - SpellCastResult castResult = m_spellInfo->CheckTarget(m_caster, target, m_caster->GetEntry() == WORLD_TRIGGER); // skip stealth checks for GO casts + SpellCastResult castResult = m_spellInfo->CheckTarget(m_caster, target, m_caster->GetTypeId() == TYPEID_GAMEOBJECT); // skip stealth checks for GO casts if (castResult != SPELL_CAST_OK) return castResult; @@ -5095,11 +5194,12 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (m_spellInfo->HasAttribute(SPELL_ATTR0_CU_REQ_TARGET_FACING_CASTER) && !target->HasInArc(static_cast<float>(M_PI), m_caster)) return SPELL_FAILED_NOT_INFRONT; - if (m_caster->GetEntry() != WORLD_TRIGGER) // Ignore LOS for gameobjects casts (wrongly cast by a trigger) + // Ignore LOS for gameobjects casts + if (m_caster->GetTypeId() != TYPEID_GAMEOBJECT) { WorldObject* losTarget = m_caster; if (IsTriggered() && m_triggeredByAuraSpell) - if (DynamicObject* dynObj = m_caster->GetDynObject(m_triggeredByAuraSpell->Id)) + if (DynamicObject* dynObj = m_caster->ToUnit()->GetDynObject(m_triggeredByAuraSpell->Id)) losTarget = dynObj; if (!m_spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && !m_spellInfo->HasAttribute(SPELL_ATTR5_SKIP_CHECKCAST_LOS_CHECK) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, nullptr, SPELL_DISABLE_LOS) && !target->IsWithinLOSInMap(losTarget, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) @@ -5119,23 +5219,26 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } // check pet presence - for (int j = 0; j < MAX_SPELL_EFFECTS; ++j) + if (Unit* unitCaster = m_caster->ToUnit()) { - if (m_spellInfo->Effects[j].TargetA.GetTarget() == TARGET_UNIT_PET) + for (uint8 j = EFFECT_0; j < MAX_SPELL_EFFECTS; ++j) { - if (!m_caster->GetGuardianPet()) + if (m_spellInfo->Effects[j].TargetA.GetTarget() == TARGET_UNIT_PET) { - if (m_triggeredByAuraSpell) // not report pet not existence for triggered spells - return SPELL_FAILED_DONT_REPORT; - else - return SPELL_FAILED_NO_PET; + if (!unitCaster->GetGuardianPet()) + { + if (m_triggeredByAuraSpell) // not report pet not existence for triggered spells + return SPELL_FAILED_DONT_REPORT; + else + return SPELL_FAILED_NO_PET; + } + break; } - break; } } // Spell cast only in battleground - if (m_spellInfo->HasAttribute(SPELL_ATTR3_BATTLEGROUND) && m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_spellInfo->HasAttribute(SPELL_ATTR3_BATTLEGROUND) && m_caster->GetTypeId() == TYPEID_PLAYER) if (!m_caster->ToPlayer()->InBattleground()) return SPELL_FAILED_ONLY_BATTLEGROUNDS; @@ -5149,7 +5252,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_NOT_IN_ARENA; // zone check - if (m_caster->GetTypeId() == TYPEID_UNIT || !m_caster->ToPlayer()->IsGameMaster()) + if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_caster->ToPlayer()->IsGameMaster()) { uint32 zone, area; m_caster->GetZoneAndAreaId(zone, area); @@ -5160,13 +5263,15 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } // not let players cast spells at mount (and let do it to creatures) - if (m_caster->IsMounted() && m_caster->GetTypeId() == TYPEID_PLAYER && !(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE) && - !m_spellInfo->IsPassive() && !m_spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_MOUNTED)) + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE)) { - if (m_caster->IsInFlight()) - return SPELL_FAILED_NOT_ON_TAXI; - else - return SPELL_FAILED_NOT_MOUNTED; + if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->IsMounted() && !m_spellInfo->IsPassive() && !m_spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_MOUNTED)) + { + if (m_caster->ToPlayer()->IsInFlight()) + return SPELL_FAILED_NOT_ON_TAXI; + else + return SPELL_FAILED_NOT_MOUNTED; + } } // check spell focus object @@ -5238,22 +5343,25 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint { if (Unit* target = m_targets.GetUnitTarget()) { - // do not allow to cast on hostile targets in sanctuary - if (!m_caster->IsFriendlyTo(target)) + if (Unit* unitCaster = m_caster->ToUnit()) { - if (m_caster->IsInSanctuary() || target->IsInSanctuary()) + // do not allow to cast on hostile targets in sanctuary + if (!unitCaster->IsFriendlyTo(target)) { - // fix for duels - Player* player = m_caster->ToPlayer(); - if (!player || !player->duel || target != player->duel->opponent) - return SPELL_FAILED_NOTHING_TO_DISPEL; + if (unitCaster->IsInSanctuary() || target->IsInSanctuary()) + { + // fix for duels + Player* player = unitCaster->ToPlayer(); + if (!player || !player->duel || target != player->duel->opponent) + return SPELL_FAILED_NOTHING_TO_DISPEL; + } } - } - DispelChargesList dispelList; - target->GetDispellableAuraList(m_caster, dispelMask, dispelList); - if (dispelList.empty()) - return SPELL_FAILED_NOTHING_TO_DISPEL; + DispelChargesList dispelList; + target->GetDispellableAuraList(unitCaster, dispelMask, dispelList); + if (dispelList.empty()) + return SPELL_FAILED_NOTHING_TO_DISPEL; + } } } @@ -5271,12 +5379,10 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint break; Pet* pet = m_caster->ToPlayer()->GetPet(); - if (!pet) return SPELL_FAILED_NO_PET; SpellInfo const* learn_spellproto = sSpellMgr->GetSpellInfo(m_spellInfo->Effects[i].TriggerSpell); - if (!learn_spellproto) return SPELL_FAILED_NOT_KNOWN; @@ -5309,9 +5415,12 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } case SPELL_EFFECT_APPLY_GLYPH: { + if (m_caster->GetTypeId() != TYPEID_PLAYER) + return SPELL_FAILED_BAD_TARGETS; + uint32 glyphId = m_spellInfo->Effects[i].MiscValue; if (GlyphPropertiesEntry const* gp = sGlyphPropertiesStore.LookupEntry(glyphId)) - if (m_caster->HasAura(gp->SpellId)) + if (m_caster->ToPlayer()->HasAura(gp->SpellId)) return SPELL_FAILED_UNIQUE_GLYPH; break; } @@ -5325,7 +5434,6 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_BAD_TARGETS; Pet* pet = m_caster->ToPlayer()->GetPet(); - if (!pet) return SPELL_FAILED_NO_PET; @@ -5335,7 +5443,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (!pet->GetCurrentFoodBenefitLevel(foodItem->GetTemplate()->ItemLevel)) return SPELL_FAILED_FOOD_LOWLEVEL; - if (m_caster->IsInCombat() || pet->IsInCombat()) + if (m_caster->ToPlayer()->IsInCombat() || pet->IsInCombat()) return SPELL_FAILED_AFFECTING_COMBAT; break; @@ -5352,14 +5460,18 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } case SPELL_EFFECT_CHARGE: { + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR) { // Warbringer - can't be handled in proc system - should be done before checkcast root check and charge effect process - if (strict && m_caster->IsScriptOverriden(m_spellInfo, 6953)) - m_caster->RemoveMovementImpairingAuras(true); + if (strict && unitCaster->IsScriptOverriden(m_spellInfo, 6953)) + unitCaster->RemoveMovementImpairingAuras(true); } - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURAS) && m_caster->HasUnitState(UNIT_STATE_ROOT)) + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURAS) && unitCaster->HasUnitState(UNIT_STATE_ROOT)) return SPELL_FAILED_ROOTED; if (GetSpellInfo()->NeedsExplicitUnitTarget()) @@ -5369,13 +5481,13 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_DONT_REPORT; // first we must check to see if the target is in LoS. A path can usually be built but LoS matters for charge spells - if (!target->IsWithinLOSInMap(m_caster)) //Do full LoS/Path check. Don't exclude m2 + if (!target->IsWithinLOSInMap(unitCaster)) //Do full LoS/Path check. Don't exclude m2 return SPELL_FAILED_LINE_OF_SIGHT; float objSize = target->GetCombatReach(); - float range = m_spellInfo->GetMaxRange(true, m_caster, this) * 1.5f + objSize; // can't be overly strict + float range = m_spellInfo->GetMaxRange(true, unitCaster, this) * 1.5f + objSize; // can't be overly strict - m_preGeneratedPath = Trinity::make_unique<PathGenerator>(m_caster); + m_preGeneratedPath = Trinity::make_unique<PathGenerator>(unitCaster); m_preGeneratedPath->SetPathLengthLimit(range); // first try with raycast, if it fails fall back to normal path float targetObjectSize = std::min(target->GetCombatReach(), 4.0f); @@ -5487,27 +5599,34 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } case SPELL_EFFECT_RESURRECT_PET: { - Creature* pet = m_caster->GetGuardianPet(); + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + Creature* pet = unitCaster->GetGuardianPet(); if (pet && pet->IsAlive()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; - break; } // This is generic summon effect case SPELL_EFFECT_SUMMON: { + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + break; + SummonPropertiesEntry const* SummonProperties = sSummonPropertiesStore.LookupEntry(m_spellInfo->Effects[i].MiscValueB); if (!SummonProperties) break; + switch (SummonProperties->Category) { case SUMMON_CATEGORY_PET: - if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && m_caster->GetPetGUID()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && unitCaster->GetPetGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; - // intentional missing break, check both GetPetGUID() and GetCharmGUID for SUMMON_CATEGORY_PET + // intentional missing break, check both GetPetGUID() and GetCharmGUID for SUMMON_CATEGORY_PET case SUMMON_CATEGORY_PUPPET: - if (m_caster->GetCharmGUID()) + if (unitCaster->GetCharmGUID()) return SPELL_FAILED_ALREADY_HAVE_CHARM; break; } @@ -5526,19 +5645,23 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } case SPELL_EFFECT_SUMMON_PET: { - if (m_caster->GetPetGUID()) //let warlock do a replacement summon + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + + if (unitCaster->GetPetGUID()) //let warlock do a replacement summon { - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) { if (strict) //starting cast, trigger pet stun (cast by pet so it doesn't attack player) - if (Pet* pet = m_caster->ToPlayer()->GetPet()) + if (Pet* pet = unitCaster->ToPlayer()->GetPet()) pet->CastSpell(pet, 32752, pet->GetGUID()); } else if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET)) return SPELL_FAILED_ALREADY_HAVE_SUMMON; } - if (m_caster->GetCharmGUID()) + if (unitCaster->GetCharmGUID()) return SPELL_FAILED_ALREADY_HAVE_CHARM; break; } @@ -5546,7 +5669,8 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint { if (m_caster->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_BAD_TARGETS; - if (!m_caster->GetTarget()) + + if (!m_caster->ToPlayer()->GetTarget()) return SPELL_FAILED_BAD_TARGETS; Player* target = ObjectAccessor::FindPlayer(m_caster->ToPlayer()->GetTarget()); @@ -5583,22 +5707,19 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_BAD_TARGETS; Player* playerCaster = m_caster->ToPlayer(); - // - if (!(playerCaster->GetTarget())) + if (!playerCaster->GetTarget()) return SPELL_FAILED_BAD_TARGETS; Player* target = playerCaster->GetSelectedPlayer(); - if (!target || !(target->GetSession()->GetRecruiterId() == playerCaster->GetSession()->GetAccountId() || target->GetSession()->GetAccountId() == playerCaster->GetSession()->GetRecruiterId())) return SPELL_FAILED_BAD_TARGETS; - break; } case SPELL_EFFECT_LEAP: case SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER: { - //Do not allow to cast it before BG starts. + //Do not allow to cast it before BG starts. if (m_caster->GetTypeId() == TYPEID_PLAYER) if (Battleground const* bg = m_caster->ToPlayer()->GetBattleground()) if (bg->GetStatus() != STATUS_IN_PROGRESS) @@ -5636,9 +5757,13 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } case SPELL_EFFECT_LEAP_BACK: { - if (m_caster->HasUnitState(UNIT_STATE_ROOT)) + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + + if (unitCaster->HasUnitState(UNIT_STATE_ROOT)) { - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) return SPELL_FAILED_ROOTED; else return SPELL_FAILED_DONT_REPORT; @@ -5648,7 +5773,11 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint case SPELL_EFFECT_JUMP: case SPELL_EFFECT_JUMP_DEST: { - if (m_caster->HasUnitState(UNIT_STATE_ROOT)) + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + + if (unitCaster->HasUnitState(UNIT_STATE_ROOT)) return SPELL_FAILED_ROOTED; break; } @@ -5685,16 +5814,20 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint case SPELL_AURA_MOD_CHARM: case SPELL_AURA_AOE_CHARM: { - if (m_caster->GetCharmerGUID()) + Unit* unitCaster = (m_originalCaster ? m_originalCaster : m_caster->ToUnit()); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + + if (unitCaster->GetCharmerGUID()) return SPELL_FAILED_CHARMED; if (m_spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_CHARM || m_spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_POSSESS) { - if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && m_caster->GetPetGUID()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && unitCaster->GetPetGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; - if (m_caster->GetCharmGUID()) + if (unitCaster->GetCharmGUID()) return SPELL_FAILED_ALREADY_HAVE_CHARM; } @@ -5712,7 +5845,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (target->GetOwner() && target->GetOwner()->GetTypeId() == TYPEID_PLAYER) return SPELL_FAILED_TARGET_IS_PLAYER_CONTROLLED; - int32 value = CalculateDamage(i, target); + int32 value = CalculateDamage(i); if (value && int32(target->getLevel()) > value) return SPELL_FAILED_HIGHLEVEL; } @@ -5721,20 +5854,23 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } case SPELL_AURA_MOUNTED: { - if (m_caster->IsInWater() && m_spellInfo->HasAura(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED)) + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + + if (unitCaster->IsInWater() && m_spellInfo->HasAura(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED)) return SPELL_FAILED_ONLY_ABOVEWATER; // Ignore map check if spell have AreaId. AreaId already checked and this prevent special mount spells - bool allowMount = !m_caster->GetMap()->IsDungeon() || m_caster->GetMap()->IsBattlegroundOrArena(); - InstanceTemplate const* it = sObjectMgr->GetInstanceTemplate(m_caster->GetMapId()); + bool allowMount = !unitCaster->GetMap()->IsDungeon() || unitCaster->GetMap()->IsBattlegroundOrArena(); + InstanceTemplate const* it = sObjectMgr->GetInstanceTemplate(unitCaster->GetMapId()); if (it) allowMount = it->AllowMount; - if (m_caster->GetTypeId() == TYPEID_PLAYER && !allowMount && !m_spellInfo->AreaGroupId) + if (unitCaster->GetTypeId() == TYPEID_PLAYER && !allowMount && !m_spellInfo->AreaGroupId) return SPELL_FAILED_NO_MOUNTS_ALLOWED; - if (m_caster->IsInDisallowedMountForm()) + if (unitCaster->IsInDisallowedMountForm()) return SPELL_FAILED_NOT_SHAPESHIFT; - break; } case SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS: @@ -5745,7 +5881,6 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint // can be cast at non-friendly unit or own pet/charm if (m_caster->IsFriendlyTo(m_targets.GetUnitTarget())) return SPELL_FAILED_TARGET_FRIENDLY; - break; } case SPELL_AURA_FLY: @@ -5775,7 +5910,6 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (m_targets.GetUnitTarget()->GetPowerType() != POWER_MANA) return SPELL_FAILED_BAD_TARGETS; - break; } default: @@ -5793,7 +5927,6 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_NOT_TRADING; TradeData* my_trade = m_caster->ToPlayer()->GetTradeData(); - if (!my_trade) return SPELL_FAILED_NOT_TRADING; @@ -5810,15 +5943,18 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint // check if caster has at least 1 combo point on target for spells that require combo points if (m_needComboPoints) { - if (m_spellInfo->NeedsExplicitUnitTarget()) + if (Unit* unitCaster = m_caster->ToUnit()) { - if (!m_caster->GetComboPoints(m_targets.GetUnitTarget())) - return SPELL_FAILED_NO_COMBO_POINTS; - } - else - { - if (!m_caster->GetComboPoints()) - return SPELL_FAILED_NO_COMBO_POINTS; + if (m_spellInfo->NeedsExplicitUnitTarget()) + { + if (!unitCaster->GetComboPoints(m_targets.GetUnitTarget())) + return SPELL_FAILED_NO_COMBO_POINTS; + } + else + { + if (!unitCaster->GetComboPoints()) + return SPELL_FAILED_NO_COMBO_POINTS; + } } } @@ -5828,7 +5964,8 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint SpellCastResult Spell::CheckPetCast(Unit* target) { - if (m_caster->HasUnitState(UNIT_STATE_CASTING) && !(_triggeredCastFlags & TRIGGERED_IGNORE_CAST_IN_PROGRESS)) //prevent spellcast interruption by another spellcast + Unit* unitCaster = m_caster->ToUnit(); + if (unitCaster && unitCaster->HasUnitState(UNIT_STATE_CASTING) && !(_triggeredCastFlags & TRIGGERED_IGNORE_CAST_IN_PROGRESS)) //prevent spellcast interruption by another spellcast return SPELL_FAILED_SPELL_IN_PROGRESS; // dead owner (pets still alive when owners ressed?) @@ -5860,7 +5997,7 @@ SpellCastResult Spell::CheckPetCast(Unit* target) // Check if spell is affected by GCD if (m_spellInfo->StartRecoveryCategory > 0) - if (m_caster->GetCharmInfo() && m_caster->GetSpellHistory()->HasGlobalCooldown(m_spellInfo)) + if (unitCaster && unitCaster->GetCharmInfo() && unitCaster->GetSpellHistory()->HasGlobalCooldown(m_spellInfo)) return SPELL_FAILED_NOT_READY; return CheckCast(true); @@ -5868,6 +6005,10 @@ SpellCastResult Spell::CheckPetCast(Unit* target) SpellCastResult Spell::CheckCasterAuras(uint32* param1) const { + Unit* unitCaster = (m_originalCaster ? m_originalCaster : m_caster->ToUnit()); + if (!unitCaster) + return SPELL_CAST_OK; + // spells totally immuned to caster auras (wsg flag drop, give marks etc) if (m_spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CASTER_AURAS)) return SPELL_CAST_OK; @@ -5886,22 +6027,22 @@ SpellCastResult Spell::CheckCasterAuras(uint32* param1) const // Glyph of Pain Suppression // there is no other way to handle it - if (m_spellInfo->Id == 33206 && !m_caster->HasAura(63248)) + if (m_spellInfo->Id == 33206 && !unitCaster->HasAura(63248)) usableWhileStunned = false; // Check whether the cast should be prevented by any state you might have. SpellCastResult result = SPELL_CAST_OK; // Get unit state - uint32 const unitflag = m_caster->GetUInt32Value(UNIT_FIELD_FLAGS); + uint32 const unitflag = unitCaster->GetUInt32Value(UNIT_FIELD_FLAGS); // this check should only be done when player does cast directly // (ie not when it's called from a script) Breaks for example PlayerAI when charmed /* - if (m_caster->GetCharmerGUID()) + if (unitCaster->GetCharmerGUID()) { - if (Unit* charmer = m_caster->GetCharmer()) - if (charmer->GetUnitBeingMoved() != m_caster && !CheckSpellCancelsCharm(param1)) + if (Unit* charmer = unitCaster->GetCharmer()) + if (charmer->GetUnitBeingMoved() != unitCaster && !CheckSpellCancelsCharm(param1)) result = SPELL_FAILED_CHARMED; } */ @@ -5910,7 +6051,7 @@ SpellCastResult Spell::CheckCasterAuras(uint32* param1) const auto mechanicCheck = [&](AuraType type) -> SpellCastResult { bool foundNotMechanic = false; - Unit::AuraEffectList const& auras = m_caster->GetAuraEffectsByType(type); + Unit::AuraEffectList const& auras = unitCaster->GetAuraEffectsByType(type); for (AuraEffect const* aurEff : auras) { uint32 const mechanicMask = aurEff->GetSpellInfo()->GetAllEffectsMechanicMask(); @@ -5996,8 +6137,12 @@ SpellCastResult Spell::CheckCasterAuras(uint32* param1) const bool Spell::CheckSpellCancelsAuraEffect(AuraType auraType, uint32* param1) const { + Unit* unitCaster = (m_originalCaster ? m_originalCaster : m_caster->ToUnit()); + if (!unitCaster) + return false; + // Checking auras is needed now, because you are prevented by some state but the spell grants immunity. - Unit::AuraEffectList const& auraEffects = m_caster->GetAuraEffectsByType(auraType); + Unit::AuraEffectList const& auraEffects = unitCaster->GetAuraEffectsByType(auraType); if (auraEffects.empty()) return true; @@ -6055,9 +6200,9 @@ bool Spell::CheckSpellCancelsConfuse(uint32* param1) const return CheckSpellCancelsAuraEffect(SPELL_AURA_MOD_CONFUSE, param1); } -int32 Spell::CalculateDamage(uint8 i, Unit const* target) const +int32 Spell::CalculateDamage(uint8 effIndex) const { - return m_caster->CalculateSpellDamage(target, m_spellInfo, i, &m_spellValue->EffectBasePoints[i]); + return m_caster->CalculateSpellDamage(m_spellInfo, effIndex, m_spellValue->EffectBasePoints + effIndex); } bool Spell::CanAutoCast(Unit* target) @@ -6175,24 +6320,29 @@ std::pair<float, float> Spell::GetMinMaxRange(bool strict) const float rangeMod = 0.0f; float minRange = 0.0f; float maxRange = 0.0f; + if (strict && m_spellInfo->IsNextMeleeSwingSpell()) - { - maxRange = 100.0f; - return std::pair<float, float>(minRange, maxRange); - } + return { 0.0f, 100.0f }; + Unit* unitCaster = m_caster->ToUnit(); if (m_spellInfo->RangeEntry) { Unit* target = m_targets.GetUnitTarget(); if (m_spellInfo->RangeEntry->type & SPELL_RANGE_MELEE) { - rangeMod = m_caster->GetMeleeRange(target ? target : m_caster); // when the target is not a unit, take the caster's combat reach as the target's combat reach. + // when the target is not a unit, take the caster's combat reach as the target's combat reach. + if (unitCaster) + rangeMod = unitCaster->GetMeleeRange(target ? target : unitCaster); } else { float meleeRange = 0.0f; if (m_spellInfo->RangeEntry->type & SPELL_RANGE_RANGED) - meleeRange = m_caster->GetMeleeRange(target ? target : m_caster); // when the target is not a unit, take the caster's combat reach as the target's combat reach. + { + // when the target is not a unit, take the caster's combat reach as the target's combat reach. + if (unitCaster) + meleeRange = unitCaster->GetMeleeRange(target ? target : unitCaster); + } minRange = m_caster->GetSpellMinRangeForTarget(target, m_spellInfo) + meleeRange; maxRange = m_caster->GetSpellMaxRangeForTarget(target, m_spellInfo); @@ -6201,14 +6351,13 @@ std::pair<float, float> Spell::GetMinMaxRange(bool strict) const { rangeMod = m_caster->GetCombatReach() + (target ? target->GetCombatReach() : m_caster->GetCombatReach()); - if (minRange > 0.0f && !(m_spellInfo->RangeEntry->type & SPELL_RANGE_RANGED)) minRange += rangeMod; } } - if (target && m_caster->isMoving() && target->isMoving() && !m_caster->IsWalking() && !target->IsWalking() && - (m_spellInfo->RangeEntry->type & SPELL_RANGE_MELEE || target->GetTypeId() == TYPEID_PLAYER)) + if (target && unitCaster && unitCaster->isMoving() && target->isMoving() && !unitCaster->IsWalking() && !target->IsWalking() && + ((m_spellInfo->RangeEntry->type & SPELL_RANGE_MELEE) || target->GetTypeId() == TYPEID_PLAYER)) rangeMod += 8.0f / 3.0f; } @@ -6221,11 +6370,15 @@ std::pair<float, float> Spell::GetMinMaxRange(bool strict) const maxRange += rangeMod; - return std::pair<float, float>(minRange, maxRange); + return { minRange, maxRange }; } SpellCastResult Spell::CheckPower() const { + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_CAST_OK; + // item cast not used power if (m_CastItem) return SPELL_CAST_OK; @@ -6233,7 +6386,7 @@ SpellCastResult Spell::CheckPower() const // health as power used - need check health amount if (m_spellInfo->PowerType == POWER_HEALTH) { - if (int32(m_caster->GetHealth()) <= m_powerCost) + if (int32(unitCaster->GetHealth()) <= m_powerCost) return SPELL_FAILED_CASTER_AURASTATE; return SPELL_CAST_OK; } @@ -6254,7 +6407,7 @@ SpellCastResult Spell::CheckPower() const // Check power amount Powers powerType = Powers(m_spellInfo->PowerType); - if (int32(m_caster->GetPower(powerType)) < m_powerCost) + if (int32(unitCaster->GetPower(powerType)) < m_powerCost) return SPELL_FAILED_NO_POWER; else return SPELL_CAST_OK; @@ -6363,7 +6516,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / // Not own traded item (in trader trade slot) requires reagents even if triggered spell if (!checkReagents) if (Item* targetItem = m_targets.GetItemTarget()) - if (targetItem->GetOwnerGUID() != m_caster->GetGUID()) + if (targetItem->GetOwnerGUID() != player->GetGUID()) checkReagents = true; // check reagents (ignore triggered spells with reagents processed by original spell) and special reagent ignore case. @@ -6449,24 +6602,24 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / case SPELL_EFFECT_CREATE_ITEM_2: { // m_targets.GetUnitTarget() means explicit cast, otherwise we dont check for possible equip error - Unit* target = m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : m_caster; - if (target && target->GetTypeId() == TYPEID_PLAYER && !IsTriggered() && m_spellInfo->Effects[i].ItemType) + Unit* target = m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : player; + if (target->GetTypeId() == TYPEID_PLAYER && !IsTriggered() && m_spellInfo->Effects[i].ItemType) { ItemPosCountVec dest; - InventoryResult msg = target->ToPlayer()->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, m_spellInfo->Effects[i].ItemType, 1); if (msg != EQUIP_ERR_OK) { - ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(m_spellInfo->Effects[i].ItemType); + ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(m_spellInfo->Effects[i].ItemType); /// @todo Needs review - if (pProto && !(pProto->ItemLimitCategory)) + if (itemTemplate && !itemTemplate->ItemLimitCategory) { player->SendEquipError(msg, nullptr, nullptr, m_spellInfo->Effects[i].ItemType); return SPELL_FAILED_DONT_REPORT; } else { - if (!(m_spellInfo->SpellFamilyName == SPELLFAMILY_MAGE && (m_spellInfo->SpellFamilyFlags[0] & 0x40000000))) + // Conjure Food/Water/Refreshment spells + if (m_spellInfo->SpellFamilyName != SPELLFAMILY_MAGE || (!(m_spellInfo->SpellFamilyFlags[0] & 0x40000000))) return SPELL_FAILED_TOO_MANY_OF_ITEM; else if (!(target->ToPlayer()->HasItemCount(m_spellInfo->Effects[i].ItemType))) { @@ -6474,7 +6627,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / return SPELL_FAILED_DONT_REPORT; } else - player->CastSpell(m_caster, m_spellInfo->Effects[EFFECT_1].CalcValue(), false); // move this to anywhere + player->CastSpell(player, m_spellInfo->Effects[EFFECT_1].CalcValue(), false); // move this to anywhere return SPELL_FAILED_DONT_REPORT; } } @@ -6486,7 +6639,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / && (m_targets.GetItemTarget()->IsWeaponVellum() || m_targets.GetItemTarget()->IsArmorVellum())) { // cannot enchant vellum for other player - if (m_targets.GetItemTarget()->GetOwner() != m_caster) + if (m_targets.GetItemTarget()->GetOwner() != player) return SPELL_FAILED_NOT_TRADEABLE; // do not allow to enchant vellum from scroll made by vellum-prevent exploit if (m_CastItem && m_CastItem->GetTemplate()->Flags & ITEM_FLAG_NO_REAGENT_COST) @@ -6551,7 +6704,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / } // Not allow enchant in trade slot for some enchant type - if (targetItem->GetOwner() != m_caster) + if (targetItem->GetOwner() != player) { if (!enchantEntry) return SPELL_FAILED_ERROR; @@ -6566,7 +6719,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / if (!item) return SPELL_FAILED_ITEM_NOT_FOUND; // Not allow enchant in trade slot for some enchant type - if (item->GetOwner() != m_caster) + if (item->GetOwner() != player) { uint32 enchant_id = m_spellInfo->Effects[i].MiscValue; SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); @@ -6595,7 +6748,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / return SPELL_FAILED_CANT_BE_DISENCHANTED; // prevent disenchanting in trade slot - if (m_targets.GetItemTarget()->GetOwnerGUID() != m_caster->GetGUID()) + if (m_targets.GetItemTarget()->GetOwnerGUID() != player->GetGUID()) return SPELL_FAILED_CANT_BE_DISENCHANTED; ItemTemplate const* itemProto = m_targets.GetItemTarget()->GetTemplate(); @@ -6626,7 +6779,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / if (!(item->GetTemplate()->Flags & ITEM_FLAG_IS_PROSPECTABLE)) return SPELL_FAILED_CANT_BE_PROSPECTED; //prevent prospecting in trade slot - if (item->GetOwnerGUID() != m_caster->GetGUID()) + if (item->GetOwnerGUID() != player->GetGUID()) return SPELL_FAILED_CANT_BE_PROSPECTED; //Check for enough skill in jewelcrafting uint32 item_prospectingskilllevel = item->GetTemplate()->RequiredSkillRank; @@ -6657,7 +6810,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / if (!(item->GetTemplate()->Flags & ITEM_FLAG_IS_MILLABLE)) return SPELL_FAILED_CANT_BE_MILLED; //prevent milling in trade slot - if (item->GetOwnerGUID() != m_caster->GetGUID()) + if (item->GetOwnerGUID() != player->GetGUID()) return SPELL_FAILED_CANT_BE_MILLED; //Check for enough skill in inscription uint32 item_millingskilllevel = item->GetTemplate()->RequiredSkillRank; @@ -6685,15 +6838,15 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / if (m_attackType != RANGED_ATTACK) break; - Item* pItem = player->GetWeaponForAttack(m_attackType); - if (!pItem || pItem->IsBroken()) + Item* item = player->GetWeaponForAttack(m_attackType); + if (!item || item->IsBroken()) return SPELL_FAILED_EQUIPPED_ITEM; - switch (pItem->GetTemplate()->SubClass) + switch (item->GetTemplate()->SubClass) { case ITEM_SUBCLASS_WEAPON_THROWN: { - uint32 ammo = pItem->GetEntry(); + uint32 const ammo = item->GetEntry(); if (!player->HasItemCount(ammo)) return SPELL_FAILED_NO_AMMO; break; @@ -6702,11 +6855,11 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / case ITEM_SUBCLASS_WEAPON_BOW: case ITEM_SUBCLASS_WEAPON_CROSSBOW: { - uint32 ammo = player->GetUInt32Value(PLAYER_AMMO_ID); + uint32 const ammo = player->GetUInt32Value(PLAYER_AMMO_ID); if (!ammo) { // Requires No Ammo - if (m_caster->HasAura(46699)) + if (player->HasAura(46699)) break; // skip other checks return SPELL_FAILED_NO_AMMO; @@ -6720,7 +6873,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / return SPELL_FAILED_NO_AMMO; // check ammo ws. weapon compatibility - switch (pItem->GetTemplate()->SubClass) + switch (item->GetTemplate()->SubClass) { case ITEM_SUBCLASS_WEAPON_BOW: case ITEM_SUBCLASS_WEAPON_CROSSBOW: @@ -6773,9 +6926,9 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / // check weapon presence in slots for main/offhand weapons if (!(_triggeredCastFlags & TRIGGERED_IGNORE_EQUIPPED_ITEM_REQUIREMENT) && m_spellInfo->EquippedItemClass >= 0) { - auto weaponCheck = [this](WeaponAttackType attackType) -> SpellCastResult + auto weaponCheck = [&](WeaponAttackType attackType) -> SpellCastResult { - Item const* item = m_caster->ToPlayer()->GetWeaponForAttack(attackType); + Item const* item = player->GetWeaponForAttack(attackType); // skip spell if no weapon in slot or broken if (!item || item->IsBroken()) @@ -6808,24 +6961,24 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / void Spell::Delayed() // only called in DealDamage() { - if (!m_caster)// || m_caster->GetTypeId() != TYPEID_PLAYER) + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) return; - //if (m_spellState == SPELL_STATE_DELAYED) - // return; // spell is active and can't be time-backed - - if (isDelayableNoMore()) // Spells may only be delayed twice + // spells not losing casting time + if (!(m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_PUSH_BACK)) return; - // spells not loosing casting time (slam, dynamites, bombs..) - //if (!(m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_DAMAGE)) - // return; + if (IsDelayableNoMore()) // Spells may only be delayed twice + return; //check pushback reduce int32 delaytime = 500; // spellcasting delay is normally 500ms + int32 delayReduce = 100; // must be initialized to 100 for percent modifiers - m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); - delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; + if (Player* player = unitCaster->GetSpellModOwner()) + player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); + delayReduce += unitCaster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; if (delayReduce >= 100) return; @@ -6839,21 +6992,27 @@ void Spell::Delayed() // only called in DealDamage() else m_timer += delaytime; - TC_LOG_DEBUG("spells", "Spell %u partially interrupted for (%d) ms at damage", m_spellInfo->Id, delaytime); - WorldPacket data(SMSG_SPELL_DELAYED, 8+4); - data << m_caster->GetPackGUID(); + data << unitCaster->GetPackGUID(); data << uint32(delaytime); - m_caster->SendMessageToSet(&data, true); + unitCaster->SendMessageToSet(&data, true); } void Spell::DelayedChannel() { - if (!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER || getState() != SPELL_STATE_CASTING) + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return; + + if (m_spellState != SPELL_STATE_CASTING) return; - if (isDelayableNoMore()) // Spells may only be delayed twice + // spells not losing channeling time + if (!(m_spellInfo->ChannelInterruptFlags & CHANNEL_FLAG_DELAY)) + return; + + if (IsDelayableNoMore()) // Spells may only be delayed twice return; //check pushback reduce @@ -6861,9 +7020,11 @@ void Spell::DelayedChannel() int32 duration = ((m_channeledDuration > 0) ? m_channeledDuration : m_spellInfo->GetDuration()); int32 delaytime = CalculatePct(duration, 25); // channeling delay is normally 25% of its time per hit + int32 delayReduce = 100; // must be initialized to 100 for percent modifiers - m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); - delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; + if (Player* player = unitCaster->GetSpellModOwner()) + player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); + delayReduce += unitCaster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; if (delayReduce >= 100) return; @@ -6877,15 +7038,13 @@ void Spell::DelayedChannel() else m_timer -= delaytime; - TC_LOG_DEBUG("spells", "Spell %u partially interrupted for %i ms, new duration: %u ms", m_spellInfo->Id, delaytime, m_timer); - for (TargetInfo const& targetInfo : m_UniqueTargetInfo) if (targetInfo.MissCondition == SPELL_MISS_NONE) - if (Unit* unit = (m_caster->GetGUID() == targetInfo.TargetGUID) ? m_caster : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID)) + if (Unit* unit = (unitCaster->GetGUID() == targetInfo.TargetGUID) ? unitCaster : ObjectAccessor::GetUnit(*unitCaster, targetInfo.TargetGUID)) unit->DelayOwnedAuras(m_spellInfo->Id, m_originalCasterGUID, delaytime); // partially interrupt persistent area auras - if (DynamicObject* dynObj = m_caster->GetDynObject(m_spellInfo->Id)) + if (DynamicObject* dynObj = unitCaster->GetDynObject(m_spellInfo->Id)) dynObj->Delay(delaytime); SendChannelUpdate(m_timer); @@ -6894,7 +7053,7 @@ void Spell::DelayedChannel() bool Spell::UpdatePointers() { if (m_originalCasterGUID == m_caster->GetGUID()) - m_originalCaster = m_caster; + m_originalCaster = m_caster->ToUnit(); else { m_originalCaster = ObjectAccessor::GetUnit(*m_caster, m_originalCasterGUID); @@ -6969,7 +7128,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff, Position const* lo return false; if (target->GetCharmerGUID()) return false; - if (int32 value = CalculateDamage(eff, target)) + if (int32 value = CalculateDamage(eff)) if ((int32)target->getLevel() > value) return false; break; @@ -7247,12 +7406,18 @@ void Spell::HandleLaunchPhase() HandleEffects(nullptr, nullptr, nullptr, i, SPELL_EFFECT_HANDLE_LAUNCH); } - bool usesAmmo = m_spellInfo->HasAttribute(SPELL_ATTR0_CU_DIRECT_DAMAGE); - if (m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_ABILITY_CONSUME_NO_AMMO, m_spellInfo)) - usesAmmo = false; + bool usesAmmo; + if (Player* player = m_caster->ToPlayer()) + { + usesAmmo = m_spellInfo->HasAttribute(SPELL_ATTR0_CU_DIRECT_DAMAGE); + if (player->HasAuraTypeWithAffectMask(SPELL_AURA_ABILITY_CONSUME_NO_AMMO, m_spellInfo)) + usesAmmo = false; - // do not consume ammo anymore for Hunter's volley spell - if (IsTriggered() && m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && m_spellInfo->IsTargetingArea()) + // do not consume ammo anymore for Hunter's volley spell + if (IsTriggered() && m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && m_spellInfo->IsTargetingArea()) + usesAmmo = false; + } + else usesAmmo = false; PrepareTargetProcessing(); @@ -7308,29 +7473,30 @@ void Spell::DoEffectOnLaunchTarget(TargetInfo& targetInfo, float multiplier, uin Unit* unit = nullptr; // In case spell hit target, do all effect on that target if (targetInfo.MissCondition == SPELL_MISS_NONE) - unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID); + unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster->ToUnit() : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID); // In case spell reflect from target, do all effect on caster (if hit) else if (targetInfo.MissCondition == SPELL_MISS_REFLECT && targetInfo.ReflectResult == SPELL_MISS_NONE) - unit = m_caster; + unit = m_caster->ToUnit(); + if (!unit) return; // This will only cause combat - the target will engage once the projectile hits (in DoAllEffectOnTarget) - if (targetInfo.MissCondition != SPELL_MISS_EVADE && !m_caster->IsFriendlyTo(unit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) && (m_spellInfo->HasInitialAggro() || unit->IsEngaged())) - m_caster->SetInCombatWith(unit); + if (m_originalCaster && targetInfo.MissCondition != SPELL_MISS_EVADE && !m_originalCaster->IsFriendlyTo(unit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) && (m_spellInfo->HasInitialAggro() || unit->IsEngaged())) + m_originalCaster->SetInCombatWith(unit); m_damage = 0; m_healing = 0; HandleEffects(unit, nullptr, nullptr, effIndex, SPELL_EFFECT_HANDLE_LAUNCH_TARGET); - if (m_damage > 0) + if (m_originalCaster && m_damage > 0) { if (m_spellInfo->Effects[effIndex].IsTargetingArea() || m_spellInfo->Effects[effIndex].IsAreaAuraEffect() || m_spellInfo->Effects[effIndex].IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA)) { - m_damage = unit->CalculateAOEAvoidance(m_damage, m_spellInfo->SchoolMask, m_caster->GetGUID()); + m_damage = unit->CalculateAOEAvoidance(m_damage, m_spellInfo->SchoolMask, m_originalCaster->GetGUID()); - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_originalCaster->GetTypeId() == TYPEID_PLAYER) { // cap damage of player AOE uint32 targetAmount = m_UniqueTargetInfo.size(); @@ -7352,9 +7518,14 @@ void Spell::DoEffectOnLaunchTarget(TargetInfo& targetInfo, float multiplier, uin targetInfo.Healing += m_healing; float critChance = m_spellValue->CriticalChance; - if (!critChance) - critChance = m_caster->SpellCritChanceDone(m_spellInfo, m_spellSchoolMask, m_attackType); - targetInfo.IsCrit = roll_chance_f(unit->SpellCritChanceTaken(m_caster, m_spellInfo, m_spellSchoolMask, critChance, m_attackType)); + if (m_originalCaster) + { + if (!critChance) + critChance = m_originalCaster->SpellCritChanceDone(m_spellInfo, m_spellSchoolMask, m_attackType); + critChance = unit->SpellCritChanceTaken(m_originalCaster, m_spellInfo, m_spellSchoolMask, critChance, m_attackType); + } + + targetInfo.IsCrit = roll_chance_f(critChance); } SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& skillId, int32& reqSkillValue, int32& skillValue) @@ -7753,10 +7924,14 @@ void Spell::PrepareTriggersExecutedOnHit() } } + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return; + // handle SPELL_AURA_ADD_TARGET_TRIGGER auras: // save auras which were present on spell caster on cast, to prevent triggered auras from affecting caster // and to correctly calculate proc chance when combopoints are present - Unit::AuraEffectList const& targetTriggers = m_caster->GetAuraEffectsByType(SPELL_AURA_ADD_TARGET_TRIGGER); + Unit::AuraEffectList const& targetTriggers = unitCaster->GetAuraEffectsByType(SPELL_AURA_ADD_TARGET_TRIGGER); for (AuraEffect const* aurEff : targetTriggers) { if (!aurEff->IsAffectedOnSpell(m_spellInfo)) @@ -7770,7 +7945,7 @@ void Spell::PrepareTriggersExecutedOnHit() // this possibly needs fixing int32 auraBaseAmount = aurEff->GetBaseAmount(); // proc chance is stored in effect amount - int32 chance = m_caster->CalculateSpellDamage(nullptr, auraSpellInfo, auraSpellIdx, &auraBaseAmount); + int32 chance = unitCaster->CalculateSpellDamage(auraSpellInfo, auraSpellIdx, &auraBaseAmount); chance *= aurEff->GetBase()->GetStackAmount(); // build trigger and add to the list @@ -7786,23 +7961,30 @@ enum GCDLimits MAX_GCD = 1500 }; -bool Spell::HasGlobalCooldown() const +bool CanHaveGlobalCooldown(WorldObject const* caster) { // Only players or controlled units have global cooldown - if (m_caster->GetTypeId() != TYPEID_PLAYER && !m_caster->GetCharmInfo()) + if (caster->GetTypeId() != TYPEID_PLAYER && (caster->GetTypeId() != TYPEID_UNIT || !const_cast<WorldObject*>(caster)->ToCreature()->GetCharmInfo())) return false; - return m_caster->GetSpellHistory()->HasGlobalCooldown(m_spellInfo); + return true; +} + +bool Spell::HasGlobalCooldown() const +{ + if (!CanHaveGlobalCooldown(m_caster)) + return false; + + return m_caster->ToUnit()->GetSpellHistory()->HasGlobalCooldown(m_spellInfo); } void Spell::TriggerGlobalCooldown() { - int32 gcd = m_spellInfo->StartRecoveryTime; - if (!gcd) + if (!CanHaveGlobalCooldown(m_caster)) return; - // Only players or controlled units have global cooldown - if (m_caster->GetTypeId() != TYPEID_PLAYER && !m_caster->GetCharmInfo()) + int32 gcd = m_spellInfo->StartRecoveryTime; + if (!gcd) return; if (m_caster->GetTypeId() == TYPEID_PLAYER) @@ -7823,47 +8005,44 @@ void Spell::TriggerGlobalCooldown() RoundToInterval<int32>(gcd, MIN_GCD, MAX_GCD); } - m_caster->GetSpellHistory()->AddGlobalCooldown(m_spellInfo, gcd); + m_caster->ToUnit()->GetSpellHistory()->AddGlobalCooldown(m_spellInfo, gcd); } void Spell::CancelGlobalCooldown() { - if (!m_spellInfo->StartRecoveryTime) + if (!CanHaveGlobalCooldown(m_caster)) return; - // Cancel global cooldown when interrupting current cast - if (m_caster->GetCurrentSpell(CURRENT_GENERIC_SPELL) != this) + if (!m_spellInfo->StartRecoveryTime) return; - // Only players or controlled units have global cooldown - if (m_caster->GetTypeId() != TYPEID_PLAYER && !m_caster->GetCharmInfo()) + // Cancel global cooldown when interrupting current cast + if (m_caster->ToUnit()->GetCurrentSpell(CURRENT_GENERIC_SPELL) != this) return; - m_caster->GetSpellHistory()->CancelGlobalCooldown(m_spellInfo); + m_caster->ToUnit()->GetSpellHistory()->CancelGlobalCooldown(m_spellInfo); } namespace Trinity { -WorldObjectSpellTargetCheck::WorldObjectSpellTargetCheck(Unit* caster, Unit* referer, SpellInfo const* spellInfo, - SpellTargetCheckTypes selectionType, ConditionContainer* condList) : _caster(caster), _referer(referer), _spellInfo(spellInfo), - _targetSelectionType(selectionType), _condList(condList) +WorldObjectSpellTargetCheck::WorldObjectSpellTargetCheck(WorldObject* caster, WorldObject* referer, SpellInfo const* spellInfo, + SpellTargetCheckTypes selectionType, ConditionContainer const* condList) : _caster(caster), _referer(referer), _spellInfo(spellInfo), + _targetSelectionType(selectionType), _condSrcInfo(nullptr), _condList(condList) { if (condList) - _condSrcInfo = new ConditionSourceInfo(nullptr, caster); - else - _condSrcInfo = nullptr; + _condSrcInfo = Trinity::make_unique<ConditionSourceInfo>(nullptr, caster); } WorldObjectSpellTargetCheck::~WorldObjectSpellTargetCheck() { - delete _condSrcInfo; } -bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) +bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) const { if (_spellInfo->CheckTarget(_caster, target, true) != SPELL_CAST_OK) return false; + Unit* unitTarget = target->ToUnit(); if (Corpse* corpseTarget = target->ToCorpse()) { @@ -7874,6 +8053,7 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) return false; } + Unit* refUnit = _referer->ToUnit(); if (unitTarget) { // do only faction checks here @@ -7882,7 +8062,7 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) case TARGET_CHECK_ENEMY: if (unitTarget->IsTotem()) return false; - if (!_caster->IsValidAttackTarget(unitTarget, _spellInfo, nullptr, false)) + if (!_caster->IsValidAttackTarget(unitTarget, _spellInfo, false)) return false; break; case TARGET_CHECK_ALLY: @@ -7892,23 +8072,29 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) return false; break; case TARGET_CHECK_PARTY: + if (!refUnit) + return false; if (unitTarget->IsTotem()) return false; if (!_caster->IsValidAssistTarget(unitTarget, _spellInfo, false)) return false; - if (!_referer->IsInPartyWith(unitTarget)) + if (!refUnit->IsInPartyWith(unitTarget)) return false; break; case TARGET_CHECK_RAID_CLASS: - if (_referer->getClass() != unitTarget->getClass()) + if (!refUnit) + return false; + if (refUnit->getClass() != unitTarget->getClass()) return false; // nobreak; case TARGET_CHECK_RAID: + if (!refUnit) + return false; if (unitTarget->IsTotem()) return false; if (!_caster->IsValidAssistTarget(unitTarget, _spellInfo, false)) return false; - if (!_referer->IsInRaidWith(unitTarget)) + if (!refUnit->IsInRaidWith(unitTarget)) return false; break; default: @@ -7935,8 +8121,8 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) return sConditionMgr->IsObjectMeetToConditions(*_condSrcInfo, *_condList); } -WorldObjectSpellNearbyTargetCheck::WorldObjectSpellNearbyTargetCheck(float range, Unit* caster, SpellInfo const* spellInfo, - SpellTargetCheckTypes selectionType, ConditionContainer* condList) +WorldObjectSpellNearbyTargetCheck::WorldObjectSpellNearbyTargetCheck(float range, WorldObject* caster, SpellInfo const* spellInfo, + SpellTargetCheckTypes selectionType, ConditionContainer const* condList) : WorldObjectSpellTargetCheck(caster, caster, spellInfo, selectionType, condList), _range(range), _position(caster) { } bool WorldObjectSpellNearbyTargetCheck::operator()(WorldObject* target) @@ -7950,11 +8136,11 @@ bool WorldObjectSpellNearbyTargetCheck::operator()(WorldObject* target) return false; } -WorldObjectSpellAreaTargetCheck::WorldObjectSpellAreaTargetCheck(float range, Position const* position, Unit* caster, - Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList) +WorldObjectSpellAreaTargetCheck::WorldObjectSpellAreaTargetCheck(float range, Position const* position, WorldObject* caster, + WorldObject* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList) : WorldObjectSpellTargetCheck(caster, referer, spellInfo, selectionType, condList), _range(range), _position(position) { } -bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) +bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) const { if (target->ToGameObject()) { @@ -7973,11 +8159,11 @@ bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) return WorldObjectSpellTargetCheck::operator ()(target); } -WorldObjectSpellConeTargetCheck::WorldObjectSpellConeTargetCheck(float coneAngle, float range, Unit* caster, - SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList) +WorldObjectSpellConeTargetCheck::WorldObjectSpellConeTargetCheck(float coneAngle, float range, WorldObject* caster, + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList) : WorldObjectSpellAreaTargetCheck(range, caster, caster, caster, spellInfo, selectionType, condList), _coneAngle(coneAngle) { } -bool WorldObjectSpellConeTargetCheck::operator()(WorldObject* target) +bool WorldObjectSpellConeTargetCheck::operator()(WorldObject* target) const { if (_spellInfo->HasAttribute(SPELL_ATTR0_CU_CONE_BACK)) { @@ -7997,10 +8183,10 @@ bool WorldObjectSpellConeTargetCheck::operator()(WorldObject* target) return WorldObjectSpellAreaTargetCheck::operator ()(target); } -WorldObjectSpellTrajTargetCheck::WorldObjectSpellTrajTargetCheck(float range, Position const* position, Unit* caster, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList) +WorldObjectSpellTrajTargetCheck::WorldObjectSpellTrajTargetCheck(float range, Position const* position, WorldObject* caster, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList) : WorldObjectSpellTargetCheck(caster, caster, spellInfo, selectionType, condList), _range(range), _position(position) { } -bool WorldObjectSpellTrajTargetCheck::operator()(WorldObject* target) +bool WorldObjectSpellTrajTargetCheck::operator()(WorldObject* target) const { // return all targets on missile trajectory (0 - size of a missile) if (!_caster->HasInLine(target, target->GetCombatReach(), TRAJECTORY_MISSILE_SIZE)) |
