fix(Core/Unit): restore UNIT_STATE_CASTING for all channels and gate actions on ALLOW_ACTION_DURING_CHANNEL (#26914)

Co-authored-by: sogladev <[email protected]>
Co-authored-by: Gultask <[email protected]>
This commit is contained in:
Rocco Silipo
2026-08-05 11:13:40 +02:00
committed by GitHub
co-authored by sogladev Gultask
parent af97edcbd4
commit 58f0fca353
7 changed files with 42 additions and 19 deletions
+2 -2
View File
@@ -91,7 +91,7 @@ void CombatAI::UpdateAI(uint32 diff)
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
if (me->IsActionPreventedByCasting())
return;
if (uint32 spellId = events.ExecuteEvent())
@@ -161,7 +161,7 @@ void CasterAI::UpdateAI(uint32 diff)
return;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
if (me->IsActionPreventedByCasting())
return;
if (uint32 spellId = events.ExecuteEvent())
+5 -5
View File
@@ -40,7 +40,7 @@ void UnitAI::AttackStartCaster(Unit* victim, float dist)
void UnitAI::DoMeleeAttackIfReady()
{
if (me->HasUnitState(UNIT_STATE_CASTING))
if (me->IsActionPreventedByCasting())
return;
Unit* victim = me->GetVictim();
@@ -75,7 +75,7 @@ void UnitAI::DoMeleeAttackIfReady()
bool UnitAI::DoSpellAttackIfReady(uint32 spell)
{
if (me->HasUnitState(UNIT_STATE_CASTING) || !me->isAttackReady())
if (me->IsActionPreventedByCasting() || !me->isAttackReady())
return true;
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell))
@@ -93,7 +93,7 @@ bool UnitAI::DoSpellAttackIfReady(uint32 spell)
void UnitAI::DoSpellAttackToRandomTargetIfReady(uint32 spell, uint32 threatTablePosition /*= 0*/, float dist /*= 0.f*/, bool playerOnly /*= true*/)
{
if (me->HasUnitState(UNIT_STATE_CASTING) || !me->isAttackReady())
if (me->IsActionPreventedByCasting() || !me->isAttackReady())
return;
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell))
@@ -269,7 +269,7 @@ SpellCastResult UnitAI::DoCast(Unit* victim, uint32 spellId, bool triggered)
if (!victim)
return SPELL_FAILED_BAD_TARGETS;
if (me->HasUnitState(UNIT_STATE_CASTING) && !triggered)
if (me->IsActionPreventedByCasting() && !triggered)
return SPELL_FAILED_SPELL_IN_PROGRESS;
return me->CastSpell(victim, spellId, triggered);
@@ -285,7 +285,7 @@ SpellCastResult UnitAI::DoCastVictim(uint32 spellId, bool triggered)
SpellCastResult UnitAI::DoCastAOE(uint32 spellId, bool triggered)
{
if (!triggered && me->HasUnitState(UNIT_STATE_CASTING))
if (!triggered && me->IsActionPreventedByCasting())
return SPELL_FAILED_SPELL_IN_PROGRESS;
return me->CastSpell((Unit*)nullptr, spellId, triggered);
@@ -624,7 +624,7 @@ BossAI::BossAI(Creature* creature, uint32 bossId) : ScriptedAI(creature),
// Clear it in the script if you need it to update while the creature is casting.
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
return !me->IsActionPreventedByCasting();
});
}
@@ -749,7 +749,7 @@ void BossAI::UpdateAI(uint32 diff)
events.Update(diff);
scheduler.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
if (me->IsActionPreventedByCasting())
{
return;
}
@@ -757,7 +757,7 @@ void BossAI::UpdateAI(uint32 diff)
while (uint32 const eventId = events.ExecuteEvent())
{
ExecuteEvent(eventId);
if (me->HasUnitState(UNIT_STATE_CASTING))
if (me->IsActionPreventedByCasting())
{
return;
}
@@ -794,7 +794,7 @@ void BossAI::_CheckHealthAfterCast()
// This must be delayed because creature might still have unit state casting at this point, which might break scripts.
scheduler.Schedule(1s, [this](TaskContext context)
{
if (me->HasUnitState(UNIT_STATE_CASTING))
if (me->IsActionPreventedByCasting())
context.Repeat();
else
ProcessHealthCheck();
@@ -810,7 +810,7 @@ void BossAI::DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damage
{
if (me->HealthBelowPctDamaged(_nextHealthCheck._healthPct, damage))
{
if (!_nextHealthCheck._allowedWhileCasting && me->HasUnitState(UNIT_STATE_CASTING))
if (!_nextHealthCheck._allowedWhileCasting && me->IsActionPreventedByCasting())
{
_nextHealthCheck.UpdateStatus(HEALTH_CHECK_PENDING);
return;
@@ -920,7 +920,7 @@ void WorldBossAI::UpdateAI(uint32 diff)
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
if (me->IsActionPreventedByCasting())
return;
while (uint32 eventId = events.ExecuteEvent())
@@ -5100,7 +5100,7 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
{
if (!(e.action.cast.castFlags & SMARTCAST_INTERRUPT_PREVIOUS))
{
if (me && me->HasUnitState(UNIT_STATE_CASTING))
if (me && me->IsActionPreventedByCasting())
{
RaisePriority(e);
return;
@@ -5109,7 +5109,7 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
}
// Delay flee for assist event if casting
if (e.GetActionType() == SMART_ACTION_FLEE_FOR_ASSIST && me && me->HasUnitState(UNIT_STATE_CASTING))
if (e.GetActionType() == SMART_ACTION_FLEE_FOR_ASSIST && me && me->IsActionPreventedByCasting())
{
e.timer = 1200;
return;
+24 -3
View File
@@ -2739,7 +2739,9 @@ void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType /*= BASE_A
return;
}
if (HasUnitState(UNIT_STATE_CANNOT_AUTOATTACK) && !extra && !ignoreCasting)
// UNIT_STATE_CASTING is checked through IsActionPreventedByCasting() so that channels
// flagged as "allow actions during channel" do not block the auto attack
if ((HasUnitState(UNIT_STATE_LOST_CONTROL) || IsActionPreventedByCasting()) && !extra && !ignoreCasting)
{
return;
}
@@ -4193,8 +4195,7 @@ void Unit::SetCurrentCastedSpell(Spell* pSpell)
m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id != 75)
InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
if (!pSpell->GetSpellInfo()->IsActionAllowedChannel())
AddUnitState(UNIT_STATE_CASTING);
AddUnitState(UNIT_STATE_CASTING);
break;
}
@@ -4371,6 +4372,26 @@ bool Unit::IsMovementPreventedByCasting() const
return true;
}
bool Unit::IsActionPreventedByCasting() const
{
// can always act when not casting
if (!HasUnitState(UNIT_STATE_CASTING))
return false;
// a regular cast in progress always prevents other actions, even alongside a permissive channel
if (Spell* spell = m_currentSpells[CURRENT_GENERIC_SPELL])
if (spell->getState() != SPELL_STATE_FINISHED)
return true;
// channeled spells during channel stage (after the initial cast timer) allow actions with a specific spell attribute
if (Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL])
if (spell->getState() != SPELL_STATE_FINISHED && spell->IsChannelActive() && spell->GetSpellInfo()->IsActionAllowedChannel())
return false;
// prohibit actions for all other spell casts
return true;
}
bool Unit::isInFrontInMap(Unit const* target, float distance, float arc) const
{
return IsWithinDistInMap(target, distance) && HasInArc(arc, target);
+1
View File
@@ -1721,6 +1721,7 @@ public:
[[nodiscard]] float GetHoverHeight() const { return IsHovering() ? GetFloatValue(UNIT_FIELD_HOVERHEIGHT) : 0.0f; }
[[nodiscard]] virtual bool IsMovementPreventedByCasting() const;
[[nodiscard]] bool IsActionPreventedByCasting() const;
[[nodiscard]] virtual bool CanEnterWater() const = 0;
[[nodiscard]] virtual bool CanSwim() const;
+2 -1
View File
@@ -3672,7 +3672,8 @@ SpellCastResult Spell::prepare(SpellCastTargets const* targets, AuraEffect const
caster->AI()->OnSpellStart(GetSpellInfo());
// set target for proper facing
if ((m_casttime || m_spellInfo->IsChanneled()) && !HasTriggeredCastFlag(TRIGGERED_IGNORE_SET_FACING))
// channels that allow actions must not lock target and facing, the creature keeps fighting while channeling
if ((m_casttime || m_spellInfo->IsChanneled()) && !m_spellInfo->IsActionAllowedChannel() && !HasTriggeredCastFlag(TRIGGERED_IGNORE_SET_FACING))
{
if (m_caster->IsCreature() && !m_caster->ToCreature()->IsInEvadeMode() &&
((m_targets.GetObjectTarget() && m_caster != m_targets.GetObjectTarget()) || m_spellInfo->IsPositive()))