diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index cd2308cd92..3c17a69e43 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -3077,7 +3077,7 @@ void Creature::SetTarget(ObjectGuid guid) void Creature::SetSpellFocus(Spell const* focusSpell, WorldObject const* target) { // Pointer validation - if (!focusSpell || !target) + if (!focusSpell) return; // Creature already has a spell focus @@ -3092,13 +3092,13 @@ void Creature::SetSpellFocus(Spell const* focusSpell, WorldObject const* target) if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED)) return; - // some spells shouldn't track targets + // Ignore casts with the according trigger flag if (focusSpell->IsFocusDisabled()) return; SpellInfo const* spellInfo = focusSpell->GetSpellInfo(); - // don't use spell focus for vehicle spells + // Don't use spell focus for vehicle spells if (spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE)) return; @@ -3116,43 +3116,28 @@ void Creature::SetSpellFocus(Spell const* focusSpell, WorldObject const* target) _spellFocusInfo.FocusSpell = focusSpell; - // Focusing behaves different based on the given spell attributes. Tracking channel spells only face the target's direction and stay there. Normal spell casts fixate on their target instead - bool const noTurnDuringCast = spellInfo->HasAttribute(SPELL_ATTR5_AI_DOESNT_FACE_TARGET); - bool const turnDisabled = HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_DISABLE_TURN); + // Block target based focusing when the attributes or unit flags say so. + bool dontFaceTarget = spellInfo->HasAttribute(SPELL_ATTR5_AI_DOESNT_FACE_TARGET)|| spellInfo->HasAttribute(SPELL_ATTR1_TRACK_TARGET_IN_CHANNEL) || HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_DISABLE_TURN); - ObjectGuid newTarget = [&]() - { - // Spells that do not allow turning or channeled spells that will track their channel target do not set a unit field target - if (turnDisabled || noTurnDuringCast || spellInfo->HasAttribute(SPELL_ATTR1_TRACK_TARGET_IN_CHANNEL) || target == this) - return ObjectGuid::Empty; - - return target->GetGUID(); - }(); + ObjectGuid newTarget = (dontFaceTarget || target == this || target == nullptr) ? ObjectGuid::Empty : target->GetGUID(); if (GetTarget() != newTarget) { + // Sniffs imply that UNIT_FIELD_TARGET via spell focusing is being sent out in an extra packet so we do so as well. SetGuidValue(UNIT_FIELD_TARGET, newTarget); - - // Sniffs indicate that UNIT_FIELD_TARGET is being updated outside of the regular update object interval so we squeeze the packet out as well - std::vector playersNearby; - GetPlayerListInGrid(playersNearby, GetVisibilityRange()); - for (Player* player : playersNearby) - { - // only update players that are known to the client (have already been created) - if (player->HaveAtClient(this)) - SendUpdateToPlayer(player); - } + SendUpdateToSet(); + ClearUpdateMask(true); } // Spells that do not face via UNIT_FIELD_TARGET and can turn, will face their target one last time - if (newTarget.IsEmpty() && !turnDisabled && target != this) + if (!newTarget.IsEmpty() && target != this) { SetFacingTo(GetAngle(target)); - // We to manually update the orientation as the spline does so on the next update tick while we need a updated position now for further spell target selection + // Update the orentation right away. Splines take one tick to update which might become a problem in scripts. SetOrientationTowards(target); } - if (noTurnDuringCast || (!turnDisabled && newTarget.IsEmpty())) + if (newTarget.IsEmpty()) AddUnitState(UNIT_STATE_FOCUSING); } @@ -3219,21 +3204,38 @@ void Creature::ReacquireSpellFocusTarget() bool Creature::IsMovementPreventedByCasting() const { - // first check if currently a movement allowed channel is active and we're not casting - if (Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL]) + // Creature is not casting. This state check is needed for scripted casting states which are usually considered hacks but for now we keep it. + if (!HasUnitState(UNIT_STATE_CASTING)) + return false; + + Spell* spellToCheck = nullptr; + bool needsMovementCheck = [&]() { - if (spell->getState() != SPELL_STATE_FINISHED && spell->IsChannelActive()) - if (spell->CheckMovement() != SPELL_CAST_OK) - return false; - } + // Check for active channeled spells + if (Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL]) + { + if (spell->getState() != SPELL_STATE_FINISHED && spell->IsChannelActive()) + { + spellToCheck = spell; + return true; + } + } - if (HasSpellFocus()) - return true; + // Check for ongoing spell casts + if (Spell* spell = m_currentSpells[CURRENT_GENERIC_SPELL]) + { + spellToCheck = spell; + return true; + } - if (HasUnitState(UNIT_STATE_CASTING)) - return true; + return false; + }(); - return false; + + if (!spellToCheck || !needsMovementCheck) + return false; + + return (spellToCheck->CheckMovement() != SPELL_CAST_OK); } void Creature::StartPickPocketRefillTimer() diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index a6d2f52028..7991c1bf3e 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -3354,7 +3354,7 @@ SpellCastResult Spell::prepare(SpellCastTargets const& targets, AuraEffect const else m_casttime = m_spellInfo->CalcCastTime(m_caster->IsUnit() ? m_caster->ToUnit()->getLevel() : 0, this); - if (m_caster->IsUnit() && m_caster->ToUnit()->isMoving()) + if (m_caster->IsPlayer() && m_caster->ToUnit()->isMoving()) { result = CheckMovement(); if (result != SPELL_CAST_OK) @@ -4044,7 +4044,7 @@ void Spell::update(uint32 difftime) // check if the player caster has moved before the spell finished // with the exception of spells affected with SPELL_AURA_CAST_WHILE_WALKING effect - if (m_timer != 0 && m_caster->IsUnit() && m_caster->ToUnit()->isMoving() && CheckMovement() != SPELL_CAST_OK) + if (m_timer != 0 && m_caster->IsPlayer() && m_caster->ToUnit()->isMoving() && CheckMovement() != SPELL_CAST_OK) { // 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 @@ -5012,6 +5012,8 @@ void Spell::SendChannelStart(uint32 duration) packet.InterruptImmunities->Immunities = mechanicImmunityMask; // CastImmunities } + m_timer = duration; + if (!m_targets.HasDst()) { uint32 channelAuraMask = 0; @@ -5064,12 +5066,11 @@ void Spell::SendChannelStart(uint32 duration) } m_caster->SendMessageToSet(packet.Write(), true); - m_timer = duration; + m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL, m_spellInfo->Id); + if (Creature* creatureCaster = m_caster->ToCreature()) if (!creatureCaster->HasSpellFocus(this)) - creatureCaster->SetSpellFocus(this, ObjectAccessor::GetWorldObject(*creatureCaster, unitCaster->GetChannelObjectGuid())); - - m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL, m_spellInfo->Id); + creatureCaster->SetSpellFocus(this, nullptr); } void Spell::SendResurrectRequest(Player* target) @@ -6792,17 +6793,15 @@ SpellCastResult Spell::CheckMovement() const { if (!unitCaster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo)) { - if (m_casttime) + if (getState() == SPELL_STATE_PREPARING) { - if (m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement)) - return SPELL_FAILED_MOVING; + if (m_casttime > 0) + if (m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement)) + return SPELL_FAILED_MOVING; } - else - { - // only fail channeled casts if they are instant but cannot be channeled while moving - if (m_spellInfo->IsChanneled() && !m_spellInfo->IsMoveAllowedChannel()) + else if (getState() == SPELL_STATE_CASTING) + if (!m_spellInfo->IsMoveAllowedChannel()) return SPELL_FAILED_MOVING; - } } } @@ -7772,8 +7771,7 @@ bool Spell::IsIgnoringCooldowns() const bool Spell::IsFocusDisabled() const { - return ((_triggeredCastFlags & TRIGGERED_IGNORE_SET_FACING) != 0 - || (m_spellInfo->IsChanneled() && !m_spellInfo->HasAttribute(SPELL_ATTR1_TRACK_TARGET_IN_CHANNEL))); + return (_triggeredCastFlags & TRIGGERED_IGNORE_SET_FACING) != 0; } bool Spell::IsProcDisabled() const