feat(Core/AI): port OnSpellStart/OnSpellCast/OnSpellFailed/OnChannelF… (#25026)

Co-authored-by: offl <11556157+offl@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Andrew
2026-03-07 18:02:58 -03:00
committed by GitHub
parent b632aa9dd6
commit 33a438a585
8 changed files with 55 additions and 27 deletions

View File

@@ -29,8 +29,6 @@ class Unit;
class Creature;
class Player;
class SpellInfo;
enum SpellFinishReason : uint8;
typedef std::vector<AreaBoundary const*> CreatureBoundary;
#define TIME_INTERVAL_LOOK 5000
@@ -147,8 +145,17 @@ public:
// Called when spell hits a target
virtual void SpellHitTarget(Unit* /*target*/, SpellInfo const* /*spell*/) {}
// Called when a spell either finishes, interrupts or cancels a spell cast
virtual void OnSpellCastFinished(SpellInfo const* /*spell*/, SpellFinishReason /*reason*/) {}
// Called when the creature begins casting a spell (has cast time or is channeled)
virtual void OnSpellStart(SpellInfo const* /*spell*/) {}
// Called when the creature successfully executes a spell cast
virtual void OnSpellCast(SpellInfo const* /*spell*/) {}
// Called when a spell cast is interrupted or cancelled
virtual void OnSpellFailed(SpellInfo const* /*spell*/) {}
// Called when a channeled spell finishes its full duration
virtual void OnChannelFinished(SpellInfo const* /*spell*/) {}
// Called when the creature is target of hostile action: swing, hostile spell landed, fear/etc)
virtual void AttackedBy(Unit* /*attacker*/) {}

View File

@@ -756,9 +756,26 @@ void BossAI::UpdateAI(uint32 diff)
DoMeleeAttackIfReady();
}
void BossAI::OnSpellCastFinished(SpellInfo const* spellInfo, SpellFinishReason reason)
void BossAI::OnSpellCast(SpellInfo const* spellInfo)
{
ScriptedAI::OnSpellCast(spellInfo);
_CheckHealthAfterCast();
}
void BossAI::OnChannelFinished(SpellInfo const* spellInfo)
{
ScriptedAI::OnChannelFinished(spellInfo);
_CheckHealthAfterCast();
}
void BossAI::OnSpellFailed(SpellInfo const* spellInfo)
{
ScriptedAI::OnSpellFailed(spellInfo);
_CheckHealthAfterCast();
}
void BossAI::_CheckHealthAfterCast()
{
ScriptedAI::OnSpellCastFinished(spellInfo, reason);
// Check if any health check events are pending (i.e. waiting for the boss to stop casting.
if (_nextHealthCheck.IsPending() && me->IsInCombat())
{

View File

@@ -490,7 +490,9 @@ public:
bool CanRespawn() override;
void OnSpellCastFinished(SpellInfo const* spell, SpellFinishReason reason) override;
void OnSpellCast(SpellInfo const* spell) override;
void OnChannelFinished(SpellInfo const* spell) override;
void OnSpellFailed(SpellInfo const* spell) override;
void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask) override;
void JustSummoned(Creature* summon) override;
void SummonedCreatureDespawn(Creature* summon) override;
@@ -534,6 +536,7 @@ protected:
SummonList summons;
private:
void _CheckHealthAfterCast();
uint32 const _bossId;
std::list<HealthCheckEventData> _healthCheckEvents;
HealthCheckEventData _nextHealthCheck;

View File

@@ -1250,10 +1250,10 @@ bool SmartAI::IsMainSpellPrevented(SpellInfo const* spellInfo) const
return false;
}
void SmartAI::OnSpellCastFinished(SpellInfo const* spell, SpellFinishReason reason)
void SmartAI::OnSpellFailed(SpellInfo const* spell)
{
CreatureAI::OnSpellCastFinished(spell, reason);
if (reason == SPELL_FINISHED_CANCELED && _mainSpellId == spell->Id)
CreatureAI::OnSpellFailed(spell);
if (_mainSpellId == spell->Id)
if (_currentRangeMode && IsMainSpellPrevented(spell))
SetCurrentRangeMode(false);
}

View File

@@ -216,7 +216,7 @@ public:
bool IsMainSpellPrevented(SpellInfo const* spellInfo) const;
void OnSpellCastFinished(SpellInfo const* spell, SpellFinishReason reason) override;
void OnSpellFailed(SpellInfo const* spell) override;
private:
bool mIsCharmed;

View File

@@ -4209,8 +4209,6 @@ void Unit::InterruptSpell(CurrentSpellTypes spellType, bool withDelayed, bool wi
spell->SetReferencedFromCurrent(false);
}
if (IsCreature() && IsAIEnabled)
ToCreature()->AI()->OnSpellCastFinished(spell->GetSpellInfo(), SPELL_FINISHED_CANCELED);
}
}

View File

@@ -3629,6 +3629,12 @@ SpellCastResult Spell::prepare(SpellCastTargets const* targets, AuraEffect const
m_caster->SetCurrentCastedSpell(this);
SendSpellStart();
// Call CreatureAI hook OnSpellStart for spells with cast time or channeled spells
if (m_casttime > 0 || m_spellInfo->IsChanneled())
if (Creature* caster = m_caster->ToCreature())
if (caster->IsAIEnabled)
caster->AI()->OnSpellStart(GetSpellInfo());
// set target for proper facing
if ((m_casttime || m_spellInfo->IsChanneled()) && !HasTriggeredCastFlag(TRIGGERED_IGNORE_SET_FACING))
{
@@ -3727,6 +3733,11 @@ void Spell::cancel(bool bySelf)
sScriptMgr->OnSpellCastCancel(this, m_caster, m_spellInfo, bySelf);
// Call CreatureAI hook OnSpellFailed only for true interrupts/cancels, not prepare-time failures
if (Creature* creatureCaster = m_caster->ToCreature())
if (creatureCaster->IsAIEnabled)
creatureCaster->AI()->OnSpellFailed(m_spellInfo);
finish(false);
}
@@ -4052,11 +4063,10 @@ void Spell::_cast(bool skipCheck)
SetExecutedCurrently(false);
// Call CreatureAI hook OnSpellCastFinished
if (m_originalCaster)
if (Creature* caster = m_originalCaster->ToCreature())
if (caster->IsAIEnabled)
caster->AI()->OnSpellCastFinished(GetSpellInfo(), SPELL_FINISHED_SUCCESSFUL_CAST);
// Call CreatureAI hook on successful cast
if (Creature* caster = m_caster->ToCreature())
if (caster->IsAIEnabled)
caster->AI()->OnSpellCast(GetSpellInfo());
}
void Spell::handle_immediate()
@@ -4416,7 +4426,7 @@ void Spell::update(uint32 difftime)
// We call the hook here instead of in Spell::finish because we only want to call it for completed channeling. Everything else is handled by interrupts
if (Creature* creatureCaster = m_caster->ToCreature())
if (creatureCaster->IsAIEnabled)
creatureCaster->AI()->OnSpellCastFinished(m_spellInfo, SPELL_FINISHED_CHANNELING_COMPLETE);
creatureCaster->AI()->OnChannelFinished(m_spellInfo);
}
// Xinef: Dont update channeled target list on last tick, allow auras to update duration properly
// Xinef: Added this strange check because of diffrent update routines for players / creatures
@@ -4478,8 +4488,8 @@ void Spell::finish(bool ok)
// Xinef: Reset cooldown event in case of fail cast
if (m_spellInfo->IsCooldownStartedOnEvent())
m_caster->ToPlayer()->SendCooldownEvent(m_spellInfo, 0, 0, false);
}
return;
}

View File

@@ -93,13 +93,6 @@ enum SpellRangeFlag
SPELL_RANGE_RANGED = 2, //hunter range and ranged weapon
};
enum SpellFinishReason : uint8
{
SPELL_FINISHED_SUCCESSFUL_CAST = 0, // spell has sucessfully launched
SPELL_FINISHED_CANCELED = 1, // spell has been canceled (interrupts)
SPELL_FINISHED_CHANNELING_COMPLETE = 2 // spell channeling has been finished
};
struct SpellDestination
{
SpellDestination();