Core/Spells: Prevent creatures from being able to cast all their spells while moving

Closes #26137
This commit is contained in:
Shauren
2024-05-18 11:50:00 +02:00
parent 2ad7c7829b
commit f80f931e2b
3 changed files with 38 additions and 28 deletions

View File

@@ -49,6 +49,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,

View File

@@ -43,6 +43,7 @@
#include "Log.h"
#include "Loot.h"
#include "LootMgr.h"
#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "PathGenerator.h"
@@ -3528,16 +3529,9 @@ SpellCastResult Spell::prepare(SpellCastTargets const& targets, AuraEffect const
m_casttime = CallScriptCalcCastTimeHandlers(m_spellInfo->CalcCastTime(this));
SpellCastResult movementResult = SPELL_CAST_OK;
if (m_caster->IsUnit() && m_caster->ToUnit()->isMoving())
{
result = CheckMovement();
if (result != SPELL_CAST_OK)
{
SendCastResult(result);
finish(result);
return result;
}
}
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))
@@ -3550,6 +3544,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();
}
}
CallScriptOnPrecastHandler();
// set timer base at cast time
@@ -4278,16 +4290,9 @@ void Spell::update(uint32 difftime)
return;
}
// check if the player caster has moved before the spell finished
// with the exception of spells affected with SPELL_AURA_CAST_WHILE_WALKING effect
// 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)
{
// 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->ToUnit()->GetCharmerGUID().IsCreature())
cancel();
}
cancel();
switch (m_spellState)
{
@@ -7238,12 +7243,6 @@ SpellCastResult Spell::CheckMovement() const
if (IsTriggered())
return SPELL_CAST_OK;
// Creatures (not controlled) give priority to spell casting over movement.
// We assume that the casting is always valid and the current movement
// is stopped by Unit:IsmovementPreventedByCasting to prevent casting interruption.
if (m_caster->IsCreature() && !m_caster->ToCreature()->IsControlledByPlayer())
return SPELL_CAST_OK;
if (Unit* unitCaster = m_caster->ToUnit())
{
if (!unitCaster->CanCastSpellWhileMoving(m_spellInfo))

View File

@@ -3917,10 +3917,6 @@ void Spell::EffectKnockBack()
if (unitTarget->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
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;