From 962f6d7988b9003e550f6745be7cff812e9d8efa Mon Sep 17 00:00:00 2001 From: ariel- Date: Sat, 10 Feb 2018 16:43:01 -0300 Subject: Core/Spells: rework part 5: GameObject casting Closes #21330 Closes #18885 Ref #18752 (cherry picked from commit 45c5e1b9d63796d168339a44f63418f220cf2403) --- src/server/game/Spells/Spell.cpp | 1196 ++++++++++++++++++++++---------------- 1 file changed, 679 insertions(+), 517 deletions(-) (limited to 'src/server/game/Spells/Spell.cpp') diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index de06b241220..968c0e0ca1e 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -57,7 +57,7 @@ #include "WorldSession.h" #include -extern NonDefaultConstructible SpellEffects[TOTAL_SPELL_EFFECTS]; +extern NonDefaultConstructible SpellEffectHandlers[TOTAL_SPELL_EFFECTS]; SpellDestination::SpellDestination() { @@ -429,9 +429,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.IsEmpty() ? ((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) @@ -470,29 +470,7 @@ 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", "pitch: %f", m_pitch); -} - -SpellValue::SpellValue(SpellInfo const* proto, Unit const* caster) +SpellValue::SpellValue(SpellInfo const* proto, WorldObject const* caster) { memset(EffectBasePoints, 0, sizeof(EffectBasePoints)); for (SpellEffectInfo const* effect : proto->GetEffects()) @@ -511,7 +489,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; @@ -522,7 +500,7 @@ protected: 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(info), m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerOrOwner()) ? caster->GetCharmerOrOwner() : caster), m_spellValue(new SpellValue(m_spellInfo, caster)), _spellEvent(nullptr) { @@ -544,11 +522,14 @@ m_spellValue(new SpellValue(m_spellInfo, caster)), _spellEvent(nullptr) 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()->GetDamageType()); + 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()->GetDamageType()); + } if (Player const* modOwner = caster->GetSpellModOwner()) modOwner->ApplySpellMod(info, SpellModOp::Doses, m_spellValue->AuraStackAmount, this); @@ -559,7 +540,7 @@ m_spellValue(new SpellValue(m_spellInfo, caster)), _spellEvent(nullptr) 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); @@ -597,6 +578,7 @@ m_spellValue(new SpellValue(m_spellInfo, caster)), _spellEvent(nullptr) memset(m_misc.Raw.Data, 0, sizeof(m_misc.Raw.Data)); m_SpellVisual.SpellXSpellVisualID = caster->GetCastSpellXSpellVisualId(m_spellInfo); m_triggeredByAuraSpell = nullptr; + unitCaster = nullptr; _spellAura = nullptr; _dynObjAura = nullptr; @@ -614,7 +596,7 @@ m_spellValue(new SpellValue(m_spellInfo, caster)), _spellEvent(nullptr) // 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->IsUnit() && 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(); @@ -681,11 +663,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->ToUnit()->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); } @@ -723,10 +705,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: @@ -734,10 +716,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)) @@ -1203,7 +1185,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: @@ -1264,7 +1246,7 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge case TARGET_UNIT_TARGET_ALLY_OR_RAID: if (Unit* targetedUnit = m_targets.GetUnitTarget()) { - if (!m_caster->IsInRaidWith(targetedUnit)) + if (!m_caster->IsUnit() || !m_caster->ToUnit()->IsInRaidWith(targetedUnit)) { targets.push_back(m_targets.GetUnitTarget()); @@ -1384,15 +1366,19 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici } case TARGET_DEST_CASTER_FRONT_LEAP: { - float dist = m_spellInfo->GetEffect(effIndex)->CalcRadius(m_caster); + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + break; + + float dist = m_spellInfo->GetEffect(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(m_caster); + m_preGeneratedPath = std::make_unique(unitCaster); m_preGeneratedPath->SetPathLengthLimit(dist); @@ -1412,9 +1398,10 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici m_caster->UpdateAllowedPositionZ(dest._position.GetPositionX(), dest._position.GetPositionY(), dest._position.m_positionZ); break; case TARGET_DEST_SUMMONER: - if (TempSummon const* casterSummon = m_caster->ToTempSummon()) - if (Unit const* summoner = casterSummon->GetSummoner()) - dest = SpellDestination(*summoner); + if (Unit const* unitCaster = m_caster->ToUnit()) + if (TempSummon const* casterSummon = unitCaster->ToTempSummon()) + if (Unit const* summoner = casterSummon->GetSummoner()) + dest = SpellDestination(*summoner); break; default: { @@ -1552,14 +1539,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: @@ -1569,11 +1559,13 @@ 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; case TARGET_UNIT_OWN_CRITTER: - target = ObjectAccessor::GetCreatureOrPetOrVehicle(*m_caster, m_caster->GetCritterGUID()); + if (Unit const* unitCaster = m_caster->ToUnit()) + target = ObjectAccessor::GetCreatureOrPetOrVehicle(*m_caster, unitCaster->GetCritterGUID()); break; default: break; @@ -1581,8 +1573,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) @@ -1676,7 +1673,7 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex, SpellImplicitTarge float b = tangent(m_targets.GetPitch()); 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 @@ -1684,15 +1681,16 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex, SpellImplicitTarge if (SpellInfo const* triggerSpellInfo = sSpellMgr->GetSpellInfo(effect->TriggerSpell, GetCastDifficulty())) bestDist = std::min(std::max(bestDist, triggerSpellInfo->GetMaxRange(false)), std::min(dist2d, 300.0f)); - std::list::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()) @@ -1702,14 +1700,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; @@ -1723,17 +1721,14 @@ 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); } - - if (Vehicle* veh = m_caster->GetVehicleKit()) - veh->SetLastShootPos(*m_targets.GetDstPos()); } void Spell::SelectImplicitLineTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType, uint32 effMask) @@ -1809,9 +1804,9 @@ void Spell::SelectEffectTypeImplicitTargets(uint32 effIndex) { case SPELL_EFFECT_SUMMON_RAF_FRIEND: case SPELL_EFFECT_SUMMON_PLAYER: - if (m_caster->GetTypeId() == TYPEID_PLAYER && !m_caster->GetTarget().IsEmpty()) + if (m_caster->GetTypeId() == TYPEID_PLAYER && !m_caster->ToPlayer()->GetTarget().IsEmpty()) { - 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 @@ -1939,7 +1934,7 @@ uint32 Spell::GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionConta } template -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; @@ -1979,7 +1974,7 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargetObjectTypes objec return target; } -void Spell::SearchAreaTargets(std::list& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList) +void Spell::SearchAreaTargets(std::list& targets, float range, Position const* position, WorldObject* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList) { uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList); if (!containerTypeMask) @@ -2093,7 +2088,7 @@ GameObject* Spell::SearchSpellFocus() GameObject* focus = nullptr; Trinity::GameObjectFocusCheck check(m_caster, m_spellInfo->RequiresSpellFocus); Trinity::GameObjectSearcher searcher(m_caster, focus, check); - SearchTargets > (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; } @@ -2210,7 +2205,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 @@ -2241,10 +2236,8 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= targetInfo.IsCrit = false; // 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. @@ -2269,8 +2262,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)); @@ -2385,7 +2379,7 @@ int64 Spell::GetUnitTargetCountForEffect(SpellEffIndex effect) const { return std::count_if(m_UniqueTargetInfo.begin(), m_UniqueTargetInfo.end(), [effect](TargetInfo const& targetInfo) { - return targetInfo.EffectMask & (1 << effect); + return targetInfo.MissCondition == SPELL_MISS_MISS && targetInfo.EffectMask & (1 << effect); }); } @@ -2407,7 +2401,7 @@ int64 Spell::GetItemTargetCountForEffect(SpellEffIndex effect) const 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; @@ -2422,7 +2416,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) @@ -2435,7 +2429,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; } @@ -2450,7 +2444,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; @@ -2479,7 +2473,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; @@ -2494,7 +2488,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; @@ -2640,8 +2634,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(SpellAuraInterruptFlags::Interacting); - spell->unitTarget->ToCreature()->EngageWithTarget(spell->m_caster); + Unit* unitCaster = ASSERT_NOTNULL(spell->m_caster->ToUnit()); + unitCaster->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Interacting); + spell->unitTarget->ToCreature()->EngageWithTarget(unitCaster); } } @@ -2669,7 +2664,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); @@ -2683,11 +2679,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); @@ -2701,7 +2706,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; @@ -2709,8 +2714,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(); @@ -2778,17 +2794,23 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, TargetInfo& hitInfo) playerOwner->UpdatePvP(true); } } - if (unit->IsInCombat() && m_spellInfo->HasInitialAggro()) + + if (m_originalCaster && unit->IsInCombat() && m_spellInfo->HasInitialAggro()) { - if (m_caster->HasUnitFlag(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->HasUnitFlag(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)) { for (SpellEffectInfo const* auraSpellEffect : m_spellInfo->GetEffects()) if (auraSpellEffect) @@ -2812,7 +2834,7 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, TargetInfo& hitInfo) // 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 (SpellEffectInfo const* effect : m_spellInfo->GetEffects()) { @@ -2831,7 +2853,7 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, TargetInfo& hitInfo) hitInfo.AuraDuration = m_spellInfo->GetMaxDuration(); // unit is immune to aura if it was diminished to 0 duration - if (!hitInfo.Positive && !unit->ApplyDiminishingToDuration(m_spellInfo, hitInfo.AuraDuration, m_originalCaster, diminishLevel)) + if (!hitInfo.Positive && !unit->ApplyDiminishingToDuration(m_spellInfo, hitInfo.AuraDuration, origCaster, diminishLevel)) if (std::all_of(std::begin(m_spellInfo->GetEffects()), std::end(m_spellInfo->GetEffects()), [](SpellEffectInfo const* effInfo) { return !effInfo || !effInfo->IsEffect() || effInfo->IsEffect(SPELL_EFFECT_APPLY_AURA); })) return SPELL_MISS_IMMUNE; } @@ -2841,10 +2863,13 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, TargetInfo& hitInfo) void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) { - uint8 aura_effmask = Aura::BuildEffectMaskForOwner(m_spellInfo, 1 << effIndex, unit); - if (aura_effmask) + if (uint32 aura_effmask = Aura::BuildEffectMaskForOwner(m_spellInfo, 1 << effIndex, unit)) { + WorldObject* caster = m_caster; if (m_originalCaster) + caster = m_originalCaster; + + if (caster) { bool refresh = false; @@ -2855,7 +2880,7 @@ void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) AuraCreateInfo createInfo(m_castId, m_spellInfo, GetCastDifficulty(), allAuraEffectMask, unit); createInfo - .SetCaster(m_originalCaster) + .SetCasterGUID(caster->GetGUID()) .SetBaseAmount(&hitInfo.AuraBasePoints[0]) .SetCastItem(m_castItemGUID, m_castItemEntry, m_castItemLevel) .SetPeriodicReset(resetPeriodicTimer) @@ -2881,7 +2906,7 @@ void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) _spellAura->SetDiminishGroup(hitInfo.DRGroup); - hitInfo.AuraDuration = m_originalCaster->ModSpellDuration(m_spellInfo, unit, hitInfo.AuraDuration, hitInfo.Positive, _spellAura->GetEffectMask()); + hitInfo.AuraDuration = caster->ModSpellDuration(m_spellInfo, unit, hitInfo.AuraDuration, hitInfo.Positive, _spellAura->GetEffectMask()); if (hitInfo.AuraDuration > 0) { @@ -2889,7 +2914,7 @@ void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) // Haste modifies duration of channeled spells if (m_spellInfo->IsChanneled()) - m_originalCaster->ModSpellDurationTime(m_spellInfo, hitInfo.AuraDuration, this); + caster->ModSpellDurationTime(m_spellInfo, hitInfo.AuraDuration, this); else if (m_spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION)) { int32 origDuration = hitInfo.AuraDuration; @@ -2996,10 +3021,11 @@ 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) { - m_caster->RemoveChannelObject(targetInfo.TargetGUID); + if (Unit* unitCaster =m_caster->ToUnit()) + unitCaster->RemoveChannelObject(targetInfo.TargetGUID); continue; } @@ -3013,13 +3039,15 @@ bool Spell::UpdateChanneledTargetList() { targetInfo.EffectMask &= ~aurApp->GetEffectMask(); unit->RemoveAura(aurApp); - m_caster->RemoveChannelObject(targetInfo.TargetGUID); + if (Unit* unitCaster = m_caster->ToUnit()) + unitCaster->RemoveChannelObject(targetInfo.TargetGUID); continue; } } else // aura is dispelled { - m_caster->RemoveChannelObject(targetInfo.TargetGUID); + if (Unit* unitCaster = m_caster->ToUnit()) + unitCaster->RemoveChannelObject(targetInfo.TargetGUID); continue; } } @@ -3066,20 +3094,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_castId.IsEmpty()) + // 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_castId.IsEmpty()) { - 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) @@ -3087,7 +3117,7 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered m_powerCost = m_spellInfo->CalcPowerCost(m_caster, m_spellSchoolMask, this); // Set combo point requirement - if ((_triggeredCastFlags & TRIGGERED_IGNORE_COMBO_POINTS) || m_CastItem || !m_caster->m_playerMovingMe) + if ((_triggeredCastFlags & TRIGGERED_IGNORE_COMBO_POINTS) || m_CastItem) m_needComboPoints = false; uint32 param1 = 0, param2 = 0; @@ -3137,8 +3167,8 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered // 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) // don't cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect - if (((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && !(m_caster->IsCharmed() && m_caster->GetCharmerGUID().IsCreature()) && m_caster->isMoving() && - m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement)) && !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo)) + 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.HasFlag(SpellInterruptFlags::Movement)) && !m_caster->ToPlayer()->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo)) { // 1. Has casttime, 2. Or doesn't have flag to allow movement during channel if (m_casttime || !m_spellInfo->IsMoveAllowedChannel()) @@ -3150,7 +3180,7 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered } // focus if not controlled creature - if (m_caster->GetTypeId() == TYPEID_UNIT && !m_caster->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED)) + if (m_caster->GetTypeId() == TYPEID_UNIT && !m_caster->ToUnit()->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED)) { if (!(m_spellInfo->IsNextMeleeSwingSpell() || IsAutoRepeat())) { @@ -3176,12 +3206,15 @@ 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() && !m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_ACTION_AURA_INTERRUPT_FLAGS)) - m_caster->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Action); + if (Unit* unitCaster = m_caster->ToUnit()) + { + // 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() && !m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_ACTION_AURA_INTERRUPT_FLAGS)) + unitCaster->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Action); - m_caster->SetCurrentCastSpell(this); + unitCaster->SetCurrentCastSpell(this); + } SendSpellStart(); if (!(_triggeredCastFlags & TRIGGERED_IGNORE_GCD)) @@ -3218,7 +3251,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); @@ -3236,9 +3269,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; @@ -3301,7 +3338,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(); @@ -3371,11 +3408,13 @@ void Spell::_cast(bool skipCheck) DiminishingReturnsType type = m_spellInfo->GetDiminishingReturnsGroupType(); if (type == DRTYPE_ALL || (type == DRTYPE_PLAYER && target->IsAffectedByDiminishingReturns())) { - Unit* caster = m_originalCaster ? m_originalCaster : m_caster; - if (target->HasStrongerAuraWithDR(m_spellInfo, caster)) + if (Unit* caster = m_originalCaster ? m_originalCaster : m_caster->ToUnit()) { - cleanupSpell(SPELL_FAILED_AURA_BOUNCED); - return; + if (target->HasStrongerAuraWithDR(m_spellInfo, caster)) + { + cleanupSpell(SPELL_FAILED_AURA_BOUNCED); + return; + } } } } @@ -3385,10 +3424,10 @@ void Spell::_cast(bool skipCheck) // if the spell allows the creature to turn while casting, then adjust server-side orientation to face the target now // client-side orientation is handled by the client itself, as the cast target is targeted due to Creature::FocusTarget - if (m_caster->GetTypeId() == TYPEID_UNIT && !m_caster->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED)) + if (m_caster->GetTypeId() == TYPEID_UNIT && !m_caster->ToUnit()->HasUnitFlag(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(); @@ -3407,9 +3446,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(); @@ -3472,8 +3512,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 { @@ -3488,7 +3529,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); } @@ -3499,10 +3543,10 @@ 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)) + if (m_originalCaster && modOwner->GetCommandStatus(CHEAT_COOLDOWN)) { - m_caster->GetSpellHistory()->ResetCooldown(m_spellInfo->Id, true); - m_caster->GetSpellHistory()->RestoreCharge(m_spellInfo->ChargeCategoryId); + m_originalCaster->GetSpellHistory()->ResetCooldown(m_spellInfo->Id, true); + m_originalCaster->GetSpellHistory()->RestoreCharge(m_spellInfo->ChargeCategoryId); } } @@ -3569,17 +3613,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_spellInfo->ChannelInterruptFlags2); m_channeledDuration = duration; SendChannelStart(duration); } else if (duration == -1) - { - m_spellState = SPELL_STATE_CASTING; - m_caster->AddInterruptMask(m_spellInfo->ChannelInterruptFlags, m_spellInfo->ChannelInterruptFlags2); SendChannelStart(duration); - } + + m_spellState = SPELL_STATE_CASTING; + + // GameObjects shouldn't cast channeled spells + ASSERT_NOTNULL(m_caster->ToUnit())->AddInterruptMask(m_spellInfo->ChannelInterruptFlags, m_spellInfo->ChannelInterruptFlags2); } PrepareTargetProcessing(); @@ -3744,23 +3787,23 @@ void Spell::_handle_immediate_phase() void Spell::_handle_finish_phase() { - if (m_caster->m_playerMovingMe) + if (Unit* unitCaster = m_caster->ToUnit()) { // Take for real after all targets are processed if (m_needComboPoints) - m_caster->m_playerMovingMe->ClearComboPoints(); + unitCaster->ClearComboPoints(); // Real add combo points from effects if (m_comboPointGain) - m_caster->m_playerMovingMe->GainSpellComboPoints(m_comboPointGain); - } + unitCaster->AddComboPoints(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 @@ -3781,10 +3824,13 @@ void Spell::_handle_finish_phase() void Spell::SendSpellCooldown() { + if (!m_caster->IsUnit()) + return; + if (m_CastItem) - m_caster->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_CastItem, this); + m_caster->ToUnit()->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_CastItem, this); else - m_caster->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_castItemEntry, this); + m_caster->ToUnit()->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_castItemEntry, this); } void Spell::update(uint32 difftime) @@ -3808,9 +3854,9 @@ void Spell::update(uint32 difftime) // with the exception of spells affected with SPELL_AURA_CAST_WHILE_WALKING effect SpellEffectInfo const* effect = m_spellInfo->GetEffect(EFFECT_0); if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) && - m_caster->isMoving() && (m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement)) && - ((effect && effect->Effect != SPELL_EFFECT_STUCK) || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR)) && - !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo)) + m_caster->ToPlayer()->isMoving() && (m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement)) && + ((effect && effect->Effect != SPELL_EFFECT_STUCK) || !m_caster->ToPlayer()->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR)) && + !m_caster->ToPlayer()->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo)) { // don't cancel for melee, autorepeat, triggered and instant spells if (!m_spellInfo->IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered() && !(IsChannelActive() && m_spellInfo->IsMoveAllowedChannel())) @@ -3818,7 +3864,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(); } } @@ -3852,7 +3898,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); } @@ -3879,44 +3925,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->m_unitData->CreatedBySpell == int32(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->m_unitData->CreatedBySpell; + uint32 spell = unitCaster->m_unitData->CreatedBySpell; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell, GetCastDifficulty()); if (spellInfo && spellInfo->IconFileDataId == 134230) { - TC_LOG_DEBUG("spells", "Statue %s is unsummoned in spell %d finish", m_caster->GetGUID().ToString().c_str(), m_spellInfo->Id); - m_caster->setDeathState(JUST_DIED); + TC_LOG_DEBUG("spells", "Statue %s is unsummoned in spell %d finish", unitCaster->GetGUID().ToString().c_str(), m_spellInfo->Id); + unitCaster->setDeathState(JUST_DIED); return; } } @@ -3925,23 +3975,23 @@ void Spell::finish(bool ok) { if (!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(); } template @@ -4211,8 +4261,14 @@ void Spell::SendSpellStart() TC_LOG_DEBUG("spells", "Sending SMSG_SPELL_START id=%u", m_spellInfo->Id); uint32 castFlags = CAST_FLAG_HAS_TRAJECTORY; - 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; @@ -4223,7 +4279,7 @@ void Spell::SendSpellStart() castFlags |= CAST_FLAG_PROJECTILE; if ((m_caster->GetTypeId() == TYPEID_PLAYER || - (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsPet())) + (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsPet())) && std::find_if(m_powerCost.begin(), m_powerCost.end(), [](SpellPowerCost const& cost) { return cost.Power != POWER_HEALTH; }) != m_powerCost.end()) castFlags |= CAST_FLAG_POWER_LEFT_SELF; @@ -4255,7 +4311,7 @@ void Spell::SendSpellStart() { WorldPackets::Spells::SpellPowerData powerData; powerData.Type = cost.Power; - powerData.Cost = m_caster->GetPower(cost.Power); + powerData.Cost = ASSERT_NOTNULL(m_caster->ToUnit())->GetPower(cost.Power); castData.RemainingPower.push_back(powerData); } } @@ -4324,12 +4380,12 @@ void Spell::SendSpellGo() castFlags |= CAST_FLAG_PROJECTILE; // 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())) && std::find_if(m_powerCost.begin(), m_powerCost.end(), [](SpellPowerCost const& cost) { return cost.Power != POWER_HEALTH; }) != m_powerCost.end()) - 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) && HasPowerTypeCost(POWER_RUNES) && !(_triggeredCastFlags & TRIGGERED_IGNORE_POWER_AND_REAGENT_COST)) { @@ -4345,7 +4401,6 @@ void Spell::SendSpellGo() WorldPackets::Spells::SpellGo packet; WorldPackets::Spells::SpellCastData& castData = packet.Cast; - if (m_CastItem) castData.CasterGUID = m_CastItem->GetGUID(); else @@ -4370,7 +4425,7 @@ void Spell::SendSpellGo() { WorldPackets::Spells::SpellPowerData powerData; powerData.Type = cost.Power; - powerData.Cost = m_caster->GetPower(cost.Power); + powerData.Cost = ASSERT_NOTNULL(m_caster->ToUnit())->GetPower(cost.Power); castData.RemainingPower.push_back(powerData); } } @@ -4450,26 +4505,26 @@ void Spell::UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& ammo) uint32 ammoInventoryType = 0; uint32 ammoDisplayID = 0; - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (Player const* playerCaster = m_caster->ToPlayer()) { - Item* pItem = m_caster->ToPlayer()->GetWeaponForAttack(RANGED_ATTACK); + Item* pItem = playerCaster->GetWeaponForAttack(RANGED_ATTACK); if (pItem) { ammoInventoryType = pItem->GetTemplate()->GetInventoryType(); if (ammoInventoryType == INVTYPE_THROWN) - ammoDisplayID = pItem->GetDisplayId(m_caster->ToPlayer()); - else if (m_caster->HasAura(46699)) // Requires No Ammo + ammoDisplayID = pItem->GetDisplayId(playerCaster); + else if (playerCaster->HasAura(46699)) // Requires No Ammo { ammoDisplayID = 5996; // normal arrow ammoInventoryType = INVTYPE_AMMO; } } } - else + else if (Unit const* unitCaster = m_caster->ToUnit()) { - for (uint8 i = 0; i < 3; ++i) + for (uint8 i = BASE_ATTACK; i < MAX_ATTACK; ++i) { - if (uint32 item_id = m_caster->GetVirtualItemId(i)) + if (uint32 item_id = unitCaster->GetVirtualItemId(i)) { if (ItemEntry const* itemEntry = sItemStore.LookupEntry(item_id)) { @@ -4478,7 +4533,7 @@ void Spell::UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& ammo) switch (itemEntry->SubclassID) { case ITEM_SUBCLASS_WEAPON_THROWN: - ammoDisplayID = sDB2Manager.GetItemDisplayId(item_id, m_caster->GetVirtualItemAppearanceMod(i)); + ammoDisplayID = sDB2Manager.GetItemDisplayId(item_id, unitCaster->GetVirtualItemAppearanceMod(i)); ammoInventoryType = itemEntry->InventoryType; break; case ITEM_SUBCLASS_WEAPON_BOW: @@ -4532,6 +4587,8 @@ void Spell::SendSpellExecuteLog() spellLogEffect.FeedPetTargets = std::move(_feedPetTargets[effect->EffectIndex]); } + spellExecuteLog.LogData.Initialize(this); + if (!spellExecuteLog.Effects.empty()) m_caster->SendCombatLogMessage(&spellExecuteLog); } @@ -4647,30 +4704,40 @@ 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->ClearChannelObjects(); - m_caster->SetChannelSpellId(0); - m_caster->SetChannelVisual({}); + unitCaster->ClearChannelObjects(); + unitCaster->SetChannelSpellId(0); + unitCaster->SetChannelVisual({}); } WorldPackets::Spells::SpellChannelUpdate spellChannelUpdate; - spellChannelUpdate.CasterGUID = m_caster->GetGUID(); + spellChannelUpdate.CasterGUID = unitCaster->GetGUID(); spellChannelUpdate.TimeRemaining = time; - m_caster->SendMessageToSet(spellChannelUpdate.Write(), true); + unitCaster->SendMessageToSet(spellChannelUpdate.Write(), true); } void Spell::SendChannelStart(uint32 duration) { + // GameObjects don't channel + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return; + WorldPackets::Spells::SpellChannelStart spellChannelStart; - spellChannelStart.CasterGUID = m_caster->GetGUID(); + spellChannelStart.CasterGUID = unitCaster->GetGUID(); spellChannelStart.SpellID = m_spellInfo->Id; spellChannelStart.Visual = m_SpellVisual; spellChannelStart.ChannelDuration = duration; - m_caster->SendMessageToSet(spellChannelStart.Write(), true); + unitCaster->SendMessageToSet(spellChannelStart.Write(), true); - uint32 schoolImmunityMask = m_caster->GetSchoolImmunityMask(); - uint32 mechanicImmunityMask = m_caster->GetMechanicImmunityMask(); + uint32 schoolImmunityMask = unitCaster->GetSchoolImmunityMask(); + uint32 mechanicImmunityMask = unitCaster->GetMechanicImmunityMask(); if (schoolImmunityMask || mechanicImmunityMask) { @@ -4701,35 +4768,36 @@ void Spell::SendChannelStart(uint32 duration) for (TargetInfo const& target : m_UniqueTargetInfo) { if (target.EffectMask & channelAuraMask) - m_caster->AddChannelObject(target.TargetGUID); + unitCaster->AddChannelObject(target.TargetGUID); if (m_UniqueTargetInfo.size() == 1 && m_UniqueGOTargetInfo.empty()) - if(target.TargetGUID != m_caster->GetGUID()) - if (Creature* creatureCaster = m_caster->ToCreature()) + if(target.TargetGUID != unitCaster->GetGUID()) + if (Creature* creatureCaster = unitCaster->ToCreature()) if (!creatureCaster->IsFocusing(this)) creatureCaster->FocusTarget(this, ObjectAccessor::GetWorldObject(*creatureCaster, target.TargetGUID)); } for (GOTargetInfo const& target : m_UniqueGOTargetInfo) if (target.EffectMask & channelAuraMask) - m_caster->AddChannelObject(target.TargetGUID); + unitCaster->AddChannelObject(target.TargetGUID); - m_caster->SetChannelSpellId(m_spellInfo->Id); - m_caster->SetChannelVisual(m_SpellVisual); + unitCaster->SetChannelSpellId(m_spellInfo->Id); + unitCaster->SetChannelVisual(m_SpellVisual); } 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()); WorldPackets::Spells::ResurrectRequest resurrectRequest; resurrectRequest.ResurrectOffererGUID = m_caster->GetGUID(); resurrectRequest.ResurrectOffererVirtualRealmAddress = GetVirtualRealmAddress(); resurrectRequest.Name = sentName; - resurrectRequest.Sickness = m_caster->GetTypeId() != TYPEID_PLAYER && m_caster->IsSpiritHealer(); // "you'll be afflicted with resurrection sickness" + resurrectRequest.Sickness = m_caster->IsUnit() && m_caster->ToUnit()->IsSpiritHealer(); // "you'll be afflicted with resurrection sickness" resurrectRequest.UseTimer = !m_spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_RESURRECTION_TIMER); if (Pet* pet = target->GetPet()) if (CharmInfo* charmInfo = pet->GetCharmInfo()) @@ -4809,13 +4877,18 @@ 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; } @@ -4823,7 +4896,7 @@ void Spell::TakePower() { Powers powerType = Powers(cost.Power); bool hit = true; - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) { if (powerType == POWER_RAGE || powerType == POWER_ENERGY || powerType == POWER_RUNES) { @@ -4835,7 +4908,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->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo, SpellModOp::PowerCostOnMiss, cost.Amount); } } @@ -4854,7 +4927,7 @@ void Spell::TakePower() // health as power used if (powerType == POWER_HEALTH) { - m_caster->ModifyHealth(-cost.Amount); + unitCaster->ModifyHealth(-cost.Amount); continue; } @@ -4864,7 +4937,7 @@ void Spell::TakePower() continue; } - m_caster->ModifyPower(powerType, -cost.Amount); + unitCaster->ModifyPower(powerType, -cost.Amount); } } @@ -4898,7 +4971,7 @@ SpellCastResult Spell::CheckRuneCost() 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; Player* player = m_caster->ToPlayer(); @@ -4972,6 +5045,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; @@ -4982,7 +5060,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; } @@ -5002,20 +5080,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())); @@ -5028,6 +5106,7 @@ 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(); effectInfo = m_spellInfo->GetEffect(i); if (!effectInfo) @@ -5035,18 +5114,15 @@ void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOT TC_LOG_ERROR("spells", "Spell: %u HandleEffects at EffectIndex: %u missing effect", m_spellInfo->Id, i); return; } - uint32 eff = effectInfo->Effect; - - TC_LOG_DEBUG("spells", "Spell: %u Effect: %u", m_spellInfo->Id, eff); + uint32 effect = effectInfo->Effect; damage = CalculateDamage(i, unitTarget, &variance); - bool preventDefault = CallScriptEffectHandlers((SpellEffIndex)i, mode); + SpellEffIndex effIndex = static_cast(i); + bool preventDefault = CallScriptEffectHandlers(effIndex, mode); - if (!preventDefault && eff < TOTAL_SPELL_EFFECTS) - { - (this->*SpellEffects[eff].Value)((SpellEffIndex)i); - } + if (!preventDefault) + (this->*SpellEffectHandlers[effect].Value)(effIndex); } /*static*/ Spell const* Spell::ExtractSpellFromEvent(BasicEvent* event) @@ -5060,28 +5136,28 @@ void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOT 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 if (!m_spellInfo->IsPassive()) { - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (Player const* playerCaster = m_caster->ToPlayer()) { //can cast triggered (by aura only?) spells while have this flag if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURASTATE)) { // These two auras check SpellFamilyName defined by db2 class data instead of current spell SpellFamilyName - if (m_caster->HasAuraType(SPELL_AURA_DISABLE_CASTING_EXCEPT_ABILITIES) + if (playerCaster->HasAuraType(SPELL_AURA_DISABLE_CASTING_EXCEPT_ABILITIES) && !m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) && !m_spellInfo->HasEffect(SPELL_EFFECT_ATTACK) && !m_spellInfo->HasAttribute(SPELL_ATTR12_IGNORE_CASTING_DISABLED) - && !m_caster->HasAuraTypeWithFamilyFlags(SPELL_AURA_DISABLE_CASTING_EXCEPT_ABILITIES, sChrClassesStore.AssertEntry(m_caster->getClass())->SpellClassSet, m_spellInfo->SpellFamilyFlags)) + && !playerCaster->HasAuraTypeWithFamilyFlags(SPELL_AURA_DISABLE_CASTING_EXCEPT_ABILITIES, sChrClassesStore.AssertEntry(playerCaster->getClass())->SpellClassSet, m_spellInfo->SpellFamilyFlags)) return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; - if (m_caster->HasAuraType(SPELL_AURA_DISABLE_ATTACKING_EXCEPT_ABILITIES)) + if (playerCaster->HasAuraType(SPELL_AURA_DISABLE_ATTACKING_EXCEPT_ABILITIES)) { - if (!m_caster->HasAuraTypeWithFamilyFlags(SPELL_AURA_DISABLE_ATTACKING_EXCEPT_ABILITIES, sChrClassesStore.AssertEntry(m_caster->getClass())->SpellClassSet, m_spellInfo->SpellFamilyFlags)) + if (!playerCaster->HasAuraTypeWithFamilyFlags(SPELL_AURA_DISABLE_ATTACKING_EXCEPT_ABILITIES, sChrClassesStore.AssertEntry(playerCaster->getClass())->SpellClassSet, m_spellInfo->SpellFamilyFlags)) { if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->IsNextMeleeSwingSpell() @@ -5098,11 +5174,11 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } // check if we are using a potion in combat for the 2nd+ time. Cooldown is added only after caster gets out of combat - if (!IsIgnoringCooldowns() && m_caster->ToPlayer()->GetLastPotionId() && m_CastItem && (m_CastItem->IsPotion() || m_spellInfo->IsCooldownStartedOnEvent())) + if (!IsIgnoringCooldowns() && playerCaster->GetLastPotionId() && m_CastItem && (m_CastItem->IsPotion() || m_spellInfo->IsCooldownStartedOnEvent())) 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; @@ -5111,7 +5187,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } } - if (m_spellInfo->HasAttribute(SPELL_ATTR7_IS_CHEAT_SPELL) && !m_caster->HasUnitFlag2(UNIT_FLAG2_ALLOW_CHEAT_SPELLS)) + if (m_spellInfo->HasAttribute(SPELL_ATTR7_IS_CHEAT_SPELL) && m_caster->IsUnit() && !m_caster->ToUnit()->HasUnitFlag2(UNIT_FLAG2_ALLOW_CHEAT_SPELLS)) { m_customError = SPELL_CUSTOM_ERROR_GM_ONLY; return SPELL_FAILED_CUSTOM_ERROR; @@ -5130,94 +5206,97 @@ 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->IsAffectingSpell(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->IsAffectingSpell(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; + } } - } - 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)->IsAffectingSpell(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)->IsAffectingSpell(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->ExcludeCasterAuraState && m_caster->HasAuraState(AuraStateType(m_spellInfo->ExcludeCasterAuraState), 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->ExcludeCasterAuraState && unitCaster->HasAuraState(AuraStateType(m_spellInfo->ExcludeCasterAuraState), m_spellInfo, unitCaster)) + return SPELL_FAILED_CASTER_AURASTATE; - // Note: spell 62473 requres casterAuraSpell = triggering spell - if (m_spellInfo->CasterAuraSpell && !m_caster->HasAura(m_spellInfo->CasterAuraSpell)) - return SPELL_FAILED_CASTER_AURASTATE; - if (m_spellInfo->ExcludeCasterAuraSpell && m_caster->HasAura(m_spellInfo->ExcludeCasterAuraSpell)) - return SPELL_FAILED_CASTER_AURASTATE; + // Note: spell 62473 requres casterAuraSpell = triggering spell + if (m_spellInfo->CasterAuraSpell && !unitCaster->HasAura(m_spellInfo->CasterAuraSpell)) + return SPELL_FAILED_CASTER_AURASTATE; + if (m_spellInfo->ExcludeCasterAuraSpell && unitCaster->HasAura(m_spellInfo->ExcludeCasterAuraSpell)) + 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) - // Do not cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving() && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature()) && !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo)) - { - // skip stuck spell to allow use it in falling case and apply spell limitations at movement - SpellEffectInfo const* effect = m_spellInfo->GetEffect(EFFECT_0); - if ((!m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR) || (effect && effect->Effect != SPELL_EFFECT_STUCK)) && - (IsAutoRepeat() || m_spellInfo->HasAuraInterruptFlag(SpellAuraInterruptFlags::Standing))) - 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) + // Do not cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect + if (unitCaster->GetTypeId() == TYPEID_PLAYER && unitCaster->ToPlayer()->isMoving() && (!unitCaster->IsCharmed() || !unitCaster->GetCharmerGUID().IsCreature()) && !unitCaster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo)) + { + // skip stuck spell to allow use it in falling case and apply spell limitations at movement + SpellEffectInfo const* effect = m_spellInfo->GetEffect(EFFECT_0); + if ((!unitCaster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR) || (effect && effect->Effect != SPELL_EFFECT_STUCK)) && + (IsAutoRepeat() || m_spellInfo->HasAuraInterruptFlag(SpellAuraInterruptFlags::Standing))) + 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 @@ -5246,8 +5325,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()); @@ -5257,20 +5336,23 @@ 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; // If it's not a melee spell, check if vision is obscured by SPELL_AURA_INTERFERE_TARGETTING if (m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_MELEE) { - for (auto const& itr : m_caster->GetAuraEffectsByType(SPELL_AURA_INTERFERE_TARGETTING)) - if (!m_caster->IsFriendlyTo(itr->GetCaster()) && !target->HasAura(itr->GetId(), itr->GetCasterGUID())) - return SPELL_FAILED_VISION_OBSCURED; + if (Unit const* unitCaster = m_caster->ToUnit()) + { + for (AuraEffect const* auraEff : unitCaster->GetAuraEffectsByType(SPELL_AURA_INTERFERE_TARGETTING)) + if (!unitCaster->IsFriendlyTo(auraEff->GetCaster()) && !target->HasAura(auraEff->GetId(), auraEff->GetCasterGUID())) + return SPELL_FAILED_VISION_OBSCURED; - for (auto const& itr : target->GetAuraEffectsByType(SPELL_AURA_INTERFERE_TARGETTING)) - if (!m_caster->IsFriendlyTo(itr->GetCaster()) && (!target->HasAura(itr->GetId(), itr->GetCasterGUID()) || !m_caster->HasAura(itr->GetId(), itr->GetCasterGUID()))) - return SPELL_FAILED_VISION_OBSCURED; + for (AuraEffect const* auraEff : target->GetAuraEffectsByType(SPELL_AURA_INTERFERE_TARGETTING)) + if (!unitCaster->IsFriendlyTo(auraEff->GetCaster()) && (!target->HasAura(auraEff->GetId(), auraEff->GetCasterGUID()) || !unitCaster->HasAura(auraEff->GetId(), auraEff->GetCasterGUID()))) + return SPELL_FAILED_VISION_OBSCURED; + } } if (target != m_caster) @@ -5283,11 +5365,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(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_ALWAYS_AOE_LINE_OF_SIGHT) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, nullptr, SPELL_DISABLE_LOS) && !target->IsWithinLOSInMap(losTarget, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) @@ -5307,23 +5390,26 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } // check pet presence - for (SpellEffectInfo const* effect : m_spellInfo->GetEffects()) + if (Unit* unitCaster = m_caster->ToUnit()) { - if (effect && effect->TargetA.GetTarget() == TARGET_UNIT_PET) + for (SpellEffectInfo const* effect : m_spellInfo->GetEffects()) { - if (!m_caster->GetGuardianPet()) + if (effect && effect->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; @@ -5337,7 +5423,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } // 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); @@ -5348,19 +5434,21 @@ 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 if (m_spellInfo->RequiresSpellFocus) { - if (!m_caster->HasAuraTypeWithMiscvalue(SPELL_AURA_PROVIDE_SPELL_FOCUS, m_spellInfo->RequiresSpellFocus)) + if (!m_caster->IsUnit() || !m_caster->ToUnit()->HasAuraTypeWithMiscvalue(SPELL_AURA_PROVIDE_SPELL_FOCUS, m_spellInfo->RequiresSpellFocus)) { focusObject = SearchSpellFocus(); if (!focusObject) @@ -5436,12 +5524,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(effect->TriggerSpell, DIFFICULTY_NONE); - if (!learn_spellproto) return SPELL_FAILED_NOT_KNOWN; @@ -5549,7 +5635,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; @@ -5559,14 +5644,18 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (foodItem->GetTemplate()->GetBaseItemLevel() + 30 <= pet->getLevel()) return SPELL_FAILED_FOOD_LOWLEVEL; - if (m_caster->IsInCombat() || pet->IsInCombat()) + if (m_caster->ToPlayer()->IsInCombat() || pet->IsInCombat()) return SPELL_FAILED_AFFECTING_COMBAT; break; } case SPELL_EFFECT_CHARGE: { - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURAS) && m_caster->HasUnitState(UNIT_STATE_ROOT)) + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURAS) && unitCaster->HasUnitState(UNIT_STATE_ROOT)) return SPELL_FAILED_ROOTED; if (GetSpellInfo()->NeedsExplicitUnitTarget()) @@ -5576,13 +5665,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 = std::make_unique(m_caster); + m_preGeneratedPath = std::make_unique(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); @@ -5683,28 +5772,35 @@ 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(effect->MiscValueB); if (!SummonProperties) break; + switch (SummonProperties->Control) { case SUMMON_CATEGORY_PET: - if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && !m_caster->GetPetGUID().IsEmpty()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && !unitCaster->GetPetGUID().IsEmpty()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; /* fallthrough */ // intentional, check both GetPetGUID() and GetCharmGUID for SUMMON_CATEGORY_PET case SUMMON_CATEGORY_PUPPET: - if (!m_caster->GetCharmGUID().IsEmpty()) + if (!unitCaster->GetCharmGUID().IsEmpty()) return SPELL_FAILED_ALREADY_HAVE_CHARM; break; } @@ -5723,19 +5819,23 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } case SPELL_EFFECT_SUMMON_PET: { - if (!m_caster->GetPetGUID().IsEmpty()) //let warlock do a replacement summon + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + + if (!unitCaster->GetPetGUID().IsEmpty()) //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().IsEmpty()) + if (!unitCaster->GetCharmGUID().IsEmpty()) return SPELL_FAILED_ALREADY_HAVE_CHARM; break; } @@ -5743,7 +5843,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()); @@ -5780,22 +5881,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) @@ -5810,9 +5908,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; @@ -5822,7 +5924,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; } @@ -5833,7 +5939,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (!player) return SPELL_FAILED_TARGET_NOT_PLAYER; - if (!spec || (spec->ClassID != m_caster->getClass() && !spec->IsPetSpecialization())) + if (!spec || (spec->ClassID != player->getClass() && !spec->IsPetSpecialization())) return SPELL_FAILED_NO_SPEC; if (spec->IsPetSpecialization()) @@ -5851,12 +5957,15 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } case SPELL_EFFECT_REMOVE_TALENT: { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + Player* playerCaster = m_caster->ToPlayer(); + if (!playerCaster) return SPELL_FAILED_BAD_TARGETS; + TalentEntry const* talent = sTalentStore.LookupEntry(m_misc.TalentId); if (!talent) return SPELL_FAILED_DONT_REPORT; - if (m_caster->GetSpellHistory()->HasCooldown(talent->SpellID)) + + if (playerCaster->GetSpellHistory()->HasCooldown(talent->SpellID)) { if (param1) *param1 = talent->SpellID; @@ -5867,14 +5976,18 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint case SPELL_EFFECT_GIVE_ARTIFACT_POWER: case SPELL_EFFECT_GIVE_ARTIFACT_POWER_NO_BONUS: { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + Player* playerCaster = m_caster->ToPlayer(); + if (!playerCaster) return SPELL_FAILED_BAD_TARGETS; - Aura* artifactAura = m_caster->GetAura(ARTIFACTS_ALL_WEAPONS_GENERAL_WEAPON_EQUIPPED_PASSIVE); + + Aura* artifactAura = playerCaster->GetAura(ARTIFACTS_ALL_WEAPONS_GENERAL_WEAPON_EQUIPPED_PASSIVE); if (!artifactAura) return SPELL_FAILED_NO_ARTIFACT_EQUIPPED; - Item* artifact = m_caster->ToPlayer()->GetItemByGuid(artifactAura->GetCastItemGUID()); + + Item* artifact = playerCaster->GetItemByGuid(artifactAura->GetCastItemGUID()); if (!artifact) return SPELL_FAILED_NO_ARTIFACT_EQUIPPED; + if (effect->Effect == SPELL_EFFECT_GIVE_ARTIFACT_POWER) { ArtifactEntry const* artifactEntry = sArtifactStore.LookupEntry(artifact->GetTemplate()->GetArtifactID()); @@ -5911,16 +6024,20 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint case SPELL_AURA_MOD_CHARM: case SPELL_AURA_AOE_CHARM: { - if (!m_caster->GetCharmerGUID().IsEmpty()) + Unit* unitCaster = (m_originalCaster ? m_originalCaster : m_caster->ToUnit()); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + + if (!unitCaster->GetCharmerGUID().IsEmpty()) return SPELL_FAILED_CHARMED; if (effect->ApplyAuraName == SPELL_AURA_MOD_CHARM || effect->ApplyAuraName == SPELL_AURA_MOD_POSSESS) { - if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && !m_caster->GetPetGUID().IsEmpty()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && !unitCaster->GetPetGUID().IsEmpty()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; - if (!m_caster->GetCharmGUID().IsEmpty()) + if (!unitCaster->GetCharmGUID().IsEmpty()) return SPELL_FAILED_ALREADY_HAVE_CHARM; } @@ -5947,23 +6064,26 @@ 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->RequiredAreasID) + if (unitCaster->GetTypeId() == TYPEID_PLAYER && !allowMount && !m_spellInfo->RequiredAreasID) return SPELL_FAILED_NO_MOUNTS_ALLOWED; - if (m_caster->IsInDisallowedMountForm()) + if (unitCaster->IsInDisallowedMountForm()) { SendMountResult(MountResult::Shapeshifted); // mount result gets sent before the cast result return SPELL_FAILED_DONT_REPORT; } - break; } case SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS: @@ -5974,7 +6094,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: @@ -6004,7 +6123,6 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (m_targets.GetUnitTarget()->GetPowerType() != POWER_MANA) return SPELL_FAILED_BAD_TARGETS; - break; } default: @@ -6022,7 +6140,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; @@ -6046,7 +6163,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?) @@ -6071,7 +6189,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); @@ -6079,6 +6197,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; @@ -6099,15 +6221,15 @@ SpellCastResult Spell::CheckCasterAuras(uint32* param1) const SpellCastResult result = SPELL_CAST_OK; // Get unit state - uint32 const unitflag = m_caster->m_unitData->Flags; + uint32 const unitflag = unitCaster->m_unitData->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().IsEmpty()) + if (!unitCaster->GetCharmerGUID().IsEmpty()) { - 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; } */ @@ -6116,7 +6238,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(); @@ -6192,7 +6314,7 @@ SpellCastResult Spell::CheckCasterAuras(uint32* param1) const else if (!CheckSpellCancelsConfuse(param1)) result = SPELL_FAILED_CONFUSED; } - else if (m_caster->HasUnitFlag2(UNIT_FLAG2_NO_ACTIONS) && m_spellInfo->PreventionType & SPELL_PREVENTION_TYPE_NO_ACTIONS && !CheckSpellCancelsNoActions(param1)) + else if (unitCaster->HasUnitFlag2(UNIT_FLAG2_NO_ACTIONS) && m_spellInfo->PreventionType & SPELL_PREVENTION_TYPE_NO_ACTIONS && !CheckSpellCancelsNoActions(param1)) result = SPELL_FAILED_NO_ACTIONS; // Attr must make flag drop spell totally immune from all effects @@ -6204,8 +6326,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; @@ -6298,10 +6424,10 @@ SpellCastResult Spell::CheckArenaAndRatedBattlegroundCastRules() return SPELL_CAST_OK; } -int32 Spell::CalculateDamage(uint8 i, Unit const* target, float* var /*= nullptr*/) const +int32 Spell::CalculateDamage(uint8 effIndex, Unit const* target, float* var /*= nullptr*/) const { - bool needRecalculateBasePoints = !(m_spellValue->CustomBasePointsMask & (1 << i)); - return m_caster->CalculateSpellDamage(target, m_spellInfo, i, needRecalculateBasePoints ? nullptr : &m_spellValue->EffectBasePoints[i], var, m_castItemEntry, m_castItemLevel); + bool needRecalculateBasePoints = !(m_spellValue->CustomBasePointsMask & (1 << effIndex)); + return m_caster->CalculateSpellDamage(target, m_spellInfo, effIndex, needRecalculateBasePoints ? nullptr : &m_spellValue->EffectBasePoints[effIndex], var, m_castItemEntry, m_castItemLevel); } bool Spell::CanAutoCast(Unit* target) @@ -6403,7 +6529,7 @@ SpellCastResult Spell::CheckRange(bool strict) const if (m_caster->GetTypeId() == TYPEID_PLAYER && (((m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc(static_cast(M_PI), target)) - && !m_caster->IsWithinBoundaryRadius(target))) + && !m_caster->ToPlayer()->IsWithinBoundaryRadius(target))) return SPELL_FAILED_UNIT_NOT_INFRONT; } @@ -6423,24 +6549,29 @@ std::pair 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(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->Flags & 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->Flags & 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); @@ -6454,8 +6585,8 @@ std::pair Spell::GetMinMaxRange(bool strict) const } } - if (target && m_caster->isMoving() && target->isMoving() && !m_caster->IsWalking() && !target->IsWalking() && - (m_spellInfo->RangeEntry->Flags & SPELL_RANGE_MELEE || target->GetTypeId() == TYPEID_PLAYER)) + if (target && unitCaster && unitCaster->isMoving() && target->isMoving() && !unitCaster->IsWalking() && !target->IsWalking() && + ((m_spellInfo->RangeEntry->Flags & SPELL_RANGE_MELEE) || target->GetTypeId() == TYPEID_PLAYER)) rangeMod += 8.0f / 3.0f; } @@ -6468,11 +6599,15 @@ std::pair Spell::GetMinMaxRange(bool strict) const maxRange += rangeMod; - return std::pair(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; @@ -6482,7 +6617,7 @@ SpellCastResult Spell::CheckPower() const // health as power used - need check health amount if (cost.Power == POWER_HEALTH) { - if (int32(m_caster->GetHealth()) <= cost.Amount) + if (int64(unitCaster->GetHealth()) <= cost.Amount) return SPELL_FAILED_CASTER_AURASTATE; continue; } @@ -6502,7 +6637,7 @@ SpellCastResult Spell::CheckPower() const } // Check power amount - if (int32(m_caster->GetPower(cost.Power)) < cost.Amount) + if (int32(unitCaster->GetPower(cost.Power)) < cost.Amount) return SPELL_FAILED_NO_POWER; } @@ -6612,7 +6747,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. @@ -6707,24 +6842,24 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / case SPELL_EFFECT_CREATE_LOOT: { // 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() && effect->ItemType) + Unit* target = m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : player; + if (target->GetTypeId() == TYPEID_PLAYER && !IsTriggered() && effect->ItemType) { ItemPosCountVec dest; - InventoryResult msg = target->ToPlayer()->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, effect->ItemType, 1); if (msg != EQUIP_ERR_OK) { - ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(effect->ItemType); + ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(effect->ItemType); /// @todo Needs review - if (pProto && !(pProto->GetItemLimitCategory())) + if (itemTemplate && !(itemTemplate->GetItemLimitCategory())) { player->SendEquipError(msg, nullptr, nullptr, effect->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(effect->ItemType))) { @@ -6732,7 +6867,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / return SPELL_FAILED_DONT_REPORT; } else if (SpellEffectInfo const* efi = m_spellInfo->GetEffect(EFFECT_1)) - player->CastSpell(m_caster, efi->CalcValue(), false); // move this to anywhere + player->CastSpell(player, efi->CalcValue(), false); // move this to anywhere return SPELL_FAILED_DONT_REPORT; } } @@ -6744,7 +6879,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / && (m_targets.GetItemTarget()->IsVellum())) { // 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()->GetFlags() & ITEM_FLAG_NO_REAGENT_COST) @@ -6806,7 +6941,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; @@ -6821,7 +6956,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 = effect->MiscValue; SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); @@ -6851,7 +6986,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / return SPELL_FAILED_CANT_BE_DISENCHANTED; // prevent disenchanting in trade slot - if (item->GetOwnerGUID() != m_caster->GetGUID()) + if (item->GetOwnerGUID() != player->GetGUID()) return SPELL_FAILED_CANT_BE_DISENCHANTED; ItemTemplate const* itemProto = item->GetTemplate(); @@ -6874,7 +7009,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / if (!(item->GetTemplate()->GetFlags() & 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()->GetRequiredSkillRank(); @@ -6905,7 +7040,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / if (!(item->GetTemplate()->GetFlags() & 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()->GetRequiredSkillRank(); @@ -6933,15 +7068,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()->GetSubClass()) + switch (item->GetTemplate()->GetSubClass()) { case ITEM_SUBCLASS_WEAPON_THROWN: { - uint32 ammo = pItem->GetEntry(); + uint32 const ammo = item->GetEntry(); if (!player->HasItemCount(ammo)) return SPELL_FAILED_NO_AMMO; break; @@ -7011,9 +7146,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()) @@ -7046,20 +7181,20 @@ 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 + 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, SpellModOp::ResistPushback, delayReduce, this); - delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; + if (Player* player = unitCaster->GetSpellModOwner()) + player->ApplySpellMod(m_spellInfo, SpellModOp::ResistPushback, delayReduce, this); + delayReduce += unitCaster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; if (delayReduce >= 100) return; @@ -7073,21 +7208,23 @@ 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); - WorldPackets::Spells::SpellDelayed spellDelayed; - spellDelayed.Caster = m_caster->GetGUID(); + spellDelayed.Caster = unitCaster->GetGUID(); spellDelayed.ActualDelay = delaytime; - m_caster->SendMessageToSet(spellDelayed.Write(), true); + unitCaster->SendMessageToSet(spellDelayed.Write(), 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 (isDelayableNoMore()) // Spells may only be delayed twice + if (m_spellState != SPELL_STATE_CASTING) + return; + + if (IsDelayableNoMore()) // Spells may only be delayed twice return; //check pushback reduce @@ -7095,9 +7232,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, SpellModOp::ResistPushback, delayReduce, this); - delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; + if (Player* player = unitCaster->GetSpellModOwner()) + player->ApplySpellMod(m_spellInfo, SpellModOp::ResistPushback, delayReduce, this); + delayReduce += unitCaster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; if (delayReduce >= 100) return; @@ -7111,15 +7250,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); @@ -7136,7 +7273,7 @@ bool Spell::HasPowerTypeCost(Powers power) const 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); @@ -7341,7 +7478,7 @@ bool Spell::IsProcDisabled() const bool Spell::IsChannelActive() const { - return m_caster->GetChannelSpellId() != 0; + return m_caster->IsUnit() && m_caster->ToUnit()->GetChannelSpellId() != 0; } bool Spell::IsAutoActionResetSpell() const @@ -7544,29 +7681,30 @@ void Spell::DoEffectOnLaunchTarget(TargetInfo& targetInfo, float multiplier, Spe 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, effect->EffectIndex, SPELL_EFFECT_HANDLE_LAUNCH_TARGET); - if (m_damage > 0) + if (m_originalCaster && m_damage > 0) { if (effect->IsTargetingArea() || effect->IsAreaAuraEffect() || effect->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 int64 targetAmount = GetUnitTargetCountForEffect(SpellEffIndex(effect->EffectIndex)); @@ -7588,9 +7726,14 @@ void Spell::DoEffectOnLaunchTarget(TargetInfo& targetInfo, float multiplier, Spe targetInfo.Healing += m_healing; float critChance = m_spellValue->CriticalChance; - if (!critChance) - critChance = m_caster->SpellCritChanceDone(this, nullptr, m_spellSchoolMask, m_attackType); - targetInfo.IsCrit = roll_chance_f(unit->SpellCritChanceTaken(m_caster, this, nullptr, m_spellSchoolMask, critChance, m_attackType)); + if (m_originalCaster) + { + if (!critChance) + critChance = m_originalCaster->SpellCritChanceDone(this, nullptr, m_spellSchoolMask, m_attackType); + critChance = unit->SpellCritChanceTaken(m_originalCaster, this, nullptr, m_spellSchoolMask, critChance, m_attackType); + } + + targetInfo.IsCrit = roll_chance_f(critChance); } SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& skillId, int32& reqSkillValue, int32& skillValue) @@ -7598,6 +7741,10 @@ SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& sk if (!lockId) // possible case for GO and maybe for items. return SPELL_CAST_OK; + Unit const* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + // Get LockInfo LockEntry const* lockInfo = sLockStore.LookupEntry(lockId); @@ -7637,10 +7784,10 @@ SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& sk // castitem check: rogue using skeleton keys. the skill values should not be added in this case. skillValue = 0; - if (!m_CastItem && m_caster->GetTypeId() == TYPEID_PLAYER) - skillValue = m_caster->ToPlayer()->GetSkillValue(skillId); + if (!m_CastItem && unitCaster->GetTypeId() == TYPEID_PLAYER) + skillValue = unitCaster->ToPlayer()->GetSkillValue(skillId); else if (lockInfo->Index[j] == LOCKTYPE_LOCKPICKING) - skillValue = m_caster->getLevel() * 5; + skillValue = unitCaster->getLevel() * 5; // skill bonus provided by casting spell (mostly item spells) // add the effect base points modifier from the spell cast (cheat lock / skeleton key etc.) @@ -7964,10 +8111,14 @@ bool Spell::CanExecuteTriggersOnHit(uint32 effMask, SpellInfo const* triggeredBy 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->IsAffectingSpell(m_spellInfo)) @@ -7979,7 +8130,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, aurEff->GetSpellInfo(), aurEff->GetEffIndex(), &auraBaseAmount); + int32 chance = unitCaster->CalculateSpellDamage(nullptr, aurEff->GetSpellInfo(), aurEff->GetEffIndex(), &auraBaseAmount); chance *= aurEff->GetBase()->GetStackAmount(); // build trigger and add to the list @@ -7995,23 +8146,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(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 || !m_spellInfo->StartRecoveryCategory) + 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 || !m_spellInfo->StartRecoveryCategory) return; if (m_caster->GetTypeId() == TYPEID_PLAYER) @@ -8035,58 +8193,55 @@ void Spell::TriggerGlobalCooldown() // Apply haste rating if (gcd > MIN_GCD && ((m_spellInfo->StartRecoveryCategory == 133 && !isMeleeOrRangedSpell))) { - gcd = int32(float(gcd) * m_caster->m_unitData->ModSpellHaste); + gcd = int32(float(gcd) * m_caster->ToUnit()->m_unitData->ModSpellHaste); RoundToInterval(gcd, MIN_GCD, MAX_GCD); } - if (gcd > MIN_GCD && m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_GLOBAL_COOLDOWN_BY_HASTE_REGEN, m_spellInfo)) + if (gcd > MIN_GCD && m_caster->ToUnit()->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_GLOBAL_COOLDOWN_BY_HASTE_REGEN, m_spellInfo)) { - gcd = int32(float(gcd) * m_caster->m_unitData->ModHasteRegen); + gcd = int32(float(gcd) * m_caster->ToUnit()->m_unitData->ModHasteRegen); RoundToInterval(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, SpellTargetObjectTypes objectType) : - _caster(caster), _referer(referer), _spellInfo(spellInfo), _targetSelectionType(selectionType), _condList(condList), _objectType(objectType) +WorldObjectSpellTargetCheck::WorldObjectSpellTargetCheck(WorldObject* caster, WorldObject* referer, SpellInfo const* spellInfo, + SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType) : _caster(caster), _referer(referer), _spellInfo(spellInfo), + _targetSelectionType(selectionType), _condSrcInfo(nullptr), _condList(condList), _objectType(objectType) { if (condList) - _condSrcInfo = new ConditionSourceInfo(nullptr, caster); - else - _condSrcInfo = nullptr; + _condSrcInfo = std::make_unique(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()) { @@ -8097,6 +8252,7 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) return false; } + Unit* refUnit = _referer->ToUnit(); if (unitTarget) { // do only faction checks here @@ -8105,7 +8261,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: @@ -8115,23 +8271,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; /* fallthrough */ 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; case TARGET_CHECK_SUMMONED: @@ -8141,7 +8303,7 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) return false; break; case TARGET_CHECK_THREAT: - if (_referer->GetThreatManager().GetThreat(unitTarget, true) <= 0.0f) + if (!_referer->IsUnit() || _referer->ToUnit()->GetThreatManager().GetThreat(unitTarget, true) <= 0.0f) return false; break; case TARGET_CHECK_TAP: @@ -8185,8 +8347,8 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) return sConditionMgr->IsObjectMeetToConditions(*_condSrcInfo, *_condList); } -WorldObjectSpellNearbyTargetCheck::WorldObjectSpellNearbyTargetCheck(float range, Unit* caster, SpellInfo const* spellInfo, - SpellTargetCheckTypes selectionType, ConditionContainer* condList, SpellTargetObjectTypes objectType) +WorldObjectSpellNearbyTargetCheck::WorldObjectSpellNearbyTargetCheck(float range, WorldObject* caster, SpellInfo const* spellInfo, + SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType) : WorldObjectSpellTargetCheck(caster, caster, spellInfo, selectionType, condList, objectType), _range(range), _position(caster) { } bool WorldObjectSpellNearbyTargetCheck::operator()(WorldObject* target) @@ -8200,11 +8362,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, SpellTargetObjectTypes objectType) +WorldObjectSpellAreaTargetCheck::WorldObjectSpellAreaTargetCheck(float range, Position const* position, WorldObject* caster, + WorldObject* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType) : WorldObjectSpellTargetCheck(caster, referer, spellInfo, selectionType, condList, objectType), _range(range), _position(position) { } -bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) +bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) const { if (target->ToGameObject()) { @@ -8223,11 +8385,11 @@ bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) return WorldObjectSpellTargetCheck::operator ()(target); } -WorldObjectSpellConeTargetCheck::WorldObjectSpellConeTargetCheck(float coneAngle, float lineWidth, float range, Unit* caster, - SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList, SpellTargetObjectTypes objectType) +WorldObjectSpellConeTargetCheck::WorldObjectSpellConeTargetCheck(float coneAngle, float lineWidth, float range, WorldObject* caster, + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType) : WorldObjectSpellAreaTargetCheck(range, caster, caster, caster, spellInfo, selectionType, condList, objectType), _coneAngle(coneAngle), _lineWidth(lineWidth) { } -bool WorldObjectSpellConeTargetCheck::operator()(WorldObject* target) +bool WorldObjectSpellConeTargetCheck::operator()(WorldObject* target) const { if (_spellInfo->HasAttribute(SPELL_ATTR0_CU_CONE_BACK)) { @@ -8241,7 +8403,7 @@ bool WorldObjectSpellConeTargetCheck::operator()(WorldObject* target) } else { - if (!_caster->IsWithinBoundaryRadius(target->ToUnit())) + if (!_caster->IsUnit() || !_caster->ToUnit()->IsWithinBoundaryRadius(target->ToUnit())) // ConeAngle > 0 -> select targets in front // ConeAngle < 0 -> select targets in back if (_caster->HasInArc(_coneAngle, target) != G3D::fuzzyGe(_coneAngle, 0.f)) @@ -8250,10 +8412,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, SpellTargetObjectTypes objectType) +WorldObjectSpellTrajTargetCheck::WorldObjectSpellTrajTargetCheck(float range, Position const* position, WorldObject* caster, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType) : WorldObjectSpellTargetCheck(caster, caster, spellInfo, selectionType, condList, objectType), _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)) @@ -8265,11 +8427,11 @@ bool WorldObjectSpellTrajTargetCheck::operator()(WorldObject* target) return WorldObjectSpellTargetCheck::operator ()(target); } -WorldObjectSpellLineTargetCheck::WorldObjectSpellLineTargetCheck(Position const* srcPosition, Position const* dstPosition, float lineWidth, float range, Unit* caster, - SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList, SpellTargetObjectTypes objectType) +WorldObjectSpellLineTargetCheck::WorldObjectSpellLineTargetCheck(Position const* srcPosition, Position const* dstPosition, float lineWidth, float range, WorldObject* caster, + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType) : WorldObjectSpellAreaTargetCheck(range, caster, caster, caster, spellInfo, selectionType, condList, objectType), _srcPosition(srcPosition), _dstPosition(dstPosition), _lineWidth(lineWidth) { } -bool WorldObjectSpellLineTargetCheck::operator()(WorldObject* target) +bool WorldObjectSpellLineTargetCheck::operator()(WorldObject* target) const { float angle = _caster->GetOrientation(); if (*_srcPosition != *_dstPosition) -- cgit v1.2.3