Skip to content

Commit

Permalink
Core/Spells: Prevent creatures from being able to cast all their spel…
Browse files Browse the repository at this point in the history
…ls while moving

Closes TrinityCore#26137

(cherry picked from commit f80f931)
  • Loading branch information
Shauren committed May 18, 2024
1 parent 7a3eafb commit bdb5c72
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 46 deletions.
17 changes: 3 additions & 14 deletions src/server/game/Entities/Creature/Creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3246,21 +3246,10 @@ void Creature::DoNotReacquireSpellFocusTarget()

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])
{
if (spell->getState() != SPELL_STATE_FINISHED && spell->IsChannelActive())
if (spell->GetSpellInfo()->IsMoveAllowedChannel())
return false;
}

if (HasSpellFocus())
return true;

if (HasUnitState(UNIT_STATE_CASTING))
return true;
if (!Unit::IsMovementPreventedByCasting() && !HasSpellFocus())
return false;

return false;
return true;
}

void Creature::StartPickPocketRefillTimer()
Expand Down
5 changes: 5 additions & 0 deletions src/server/game/Entities/Unit/Unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3123,6 +3123,11 @@ bool Unit::IsMovementPreventedByCasting() const
if (!HasUnitState(UNIT_STATE_CASTING))
return false;

if (Spell* spell = m_currentSpells[CURRENT_GENERIC_SPELL])
if (spell->getState() == SPELL_STATE_FINISHED ||
!(spell->m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT))
return false;

// channeled spells during channel stage (after the initial cast timer) allow movement with a specific spell attribute
if (Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL])
if (spell->getState() != SPELL_STATE_FINISHED && spell->IsChannelActive())
Expand Down
15 changes: 15 additions & 0 deletions src/server/game/Movement/MovementDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@ enum MovementGeneratorType : uint8
MAX_MOTION_TYPE // SKIP
};

constexpr bool CanStopMovementForSpellCasting(MovementGeneratorType type)
{
// MovementGenerators that don't check Unit::IsMovementPreventedByCasting
switch (type)
{
case HOME_MOTION_TYPE:
case FLIGHT_MOTION_TYPE:
case EFFECT_MOTION_TYPE: // knockbacks, jumps, falling, land/takeoff transitions
return false;
default:
break;
}
return true;
}

enum MovementGeneratorMode : uint8
{
MOTION_MODE_DEFAULT = 0,
Expand Down
71 changes: 43 additions & 28 deletions src/server/game/Spells/Spell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "Item.h"
#include "Log.h"
#include "LootMgr.h"
#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "Opcodes.h"
Expand Down Expand Up @@ -3148,19 +3149,9 @@ SpellCastResult Spell::prepare(SpellCastTargets const& targets, AuraEffect const
else
m_casttime = m_spellInfo->CalcCastTime(this);

// don't allow channeled spells / spells with cast time to be cast while moving
// exception are only channeled spells that have no casttime and SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING
// (even if they are interrupted on moving, spells with almost immediate effect get to have their effect processed before movement interrupter kicks in)
if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && !(m_caster->ToPlayer()->IsCharmed() && m_caster->ToPlayer()->GetCharmerGUID().IsCreature()) && m_caster->ToPlayer()->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT))
{
// 1. Has casttime, 2. Or doesn't have flag to allow movement during channel
if (m_casttime || !m_spellInfo->IsMoveAllowedChannel())
{
SendCastResult(SPELL_FAILED_MOVING);
finish(false);
return SPELL_FAILED_MOVING;
}
}
SpellCastResult movementResult = SPELL_CAST_OK;
if (m_caster->IsUnit() && m_caster->ToUnit()->isMoving())
movementResult = CheckMovement();

// Creatures focus their target when possible
if (m_casttime && m_caster->IsCreature() && !m_spellInfo->IsNextMeleeSwingSpell() && !IsAutoRepeat() && !m_caster->ToUnit()->HasUnitFlag(UNIT_FLAG_POSSESSED))
Expand All @@ -3173,6 +3164,24 @@ SpellCastResult Spell::prepare(SpellCastTargets const& targets, AuraEffect const
m_caster->ToCreature()->SetSpellFocus(this, nullptr);
}

if (movementResult != SPELL_CAST_OK)
{
if (m_caster->ToUnit()->IsControlledByPlayer() || !CanStopMovementForSpellCasting(m_caster->ToUnit()->GetMotionMaster()->GetCurrentMovementGeneratorType()))
{
SendCastResult(movementResult);
finish(movementResult);
return movementResult;
}
else
{
// Creatures (not controlled) give priority to spell casting over movement.
// We assume that the casting is always valid and the current movement
// is stopped immediately (because spells are updated before movement, so next Unit::Update would cancel the spell before stopping movement)
// and future attempts are stopped by by Unit::IsMovementPreventedByCasting in movement generators to prevent casting interruption.
m_caster->ToUnit()->StopMoving();
}
}

// set timer base at cast time
ReSetTimer();

Expand Down Expand Up @@ -3805,21 +3814,9 @@ void Spell::update(uint32 difftime)
return;
}

// check if the player caster has moved before the spell finished
if (m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0 &&
m_caster->ToPlayer()->isMoving() && m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT &&
(!m_spellInfo->HasEffect(SPELL_EFFECT_STUCK) || !m_caster->ToPlayer()->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR)))
{
// don't cancel for melee, autorepeat, triggered and instant spells
if (!m_spellInfo->IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered() && !(IsChannelActive() && m_spellInfo->IsMoveAllowedChannel()))
{
// 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->ToPlayer()->GetCharmerGUID().IsCreature())
cancel();
}
}
// check if the unit caster has moved before the spell finished
if (m_timer != 0 && m_caster->IsUnit() && m_caster->ToUnit()->isMoving() && CheckMovement() != SPELL_CAST_OK)
cancel();

switch (m_spellState)
{
Expand Down Expand Up @@ -6396,6 +6393,24 @@ int32 Spell::CalculateDamage(SpellEffectInfo const& spellEffectInfo) const
return m_caster->CalculateSpellDamage(spellEffectInfo, m_spellValue->EffectBasePoints + spellEffectInfo.EffectIndex);
}

SpellCastResult Spell::CheckMovement() const
{
if (IsTriggered())
return SPELL_CAST_OK;

if (getState() == SPELL_STATE_PREPARING)
{
if (m_casttime > 0)
if (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT)
return SPELL_FAILED_MOVING;
}
else if (getState() == SPELL_STATE_CASTING)
if (!m_spellInfo->IsMoveAllowedChannel())
return SPELL_FAILED_MOVING;

return SPELL_CAST_OK;
}

bool Spell::CanAutoCast(Unit* target)
{
if (!target)
Expand Down
1 change: 1 addition & 0 deletions src/server/game/Spells/Spell.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ class TC_GAME_API Spell
SpellCastResult CheckPower() const;
SpellCastResult CheckRuneCost(uint32 runeCostID) const;
SpellCastResult CheckCasterAuras(uint32* param1) const;
SpellCastResult CheckMovement() const;

bool CheckSpellCancelsAuraEffect(AuraType auraType, uint32* param1) const;
bool CheckSpellCancelsCharm(uint32* param1) const;
Expand Down
4 changes: 0 additions & 4 deletions src/server/game/Spells/SpellEffects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4309,10 +4309,6 @@ void Spell::EffectKnockBack()
if (unitTarget->HasUnitState(UNIT_STATE_ROOT))
return;

// Instantly interrupt non melee spells being cast
if (unitTarget->IsNonMeleeSpellCast(true))
unitTarget->InterruptNonMeleeSpells(true);

float ratio = 0.1f;
float speedxy = float(effectInfo->MiscValue) * ratio;
float speedz = float(damage) * ratio;
Expand Down

0 comments on commit bdb5c72

Please sign in to comment.