fix(Core/SAI): SMARTCAST_COMBAT_MOVE prevents movement on successful cast (#23913)

This commit is contained in:
blinkysc
2025-12-02 09:44:23 -06:00
committed by GitHub
parent be37b5e395
commit bb7753d363
5 changed files with 61 additions and 18 deletions

View File

@@ -858,7 +858,7 @@ void SmartAI::AttackStart(Unit* who)
return; return;
} }
if (who && me->Attack(who, me->IsWithinMeleeRange(who))) if (who && me->Attack(who, me->IsWithinMeleeRange(who) || _currentRangeMode))
{ {
if (!me->HasUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT)) if (!me->HasUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT))
{ {
@@ -870,7 +870,7 @@ void SmartAI::AttackStart(Unit* who)
me->GetMotionMaster()->Clear(false); me->GetMotionMaster()->Clear(false);
} }
me->GetMotionMaster()->MoveChase(who); me->GetMotionMaster()->MoveChase(who, _attackDistance);
} }
} }
} }
@@ -941,6 +941,35 @@ void SmartAI::PassengerBoarded(Unit* who, int8 seatId, bool apply)
void SmartAI::InitializeAI() void SmartAI::InitializeAI()
{ {
GetScript()->OnInitialize(me); GetScript()->OnInitialize(me);
for (SmartScriptHolder const& event : GetScript()->GetEvents())
{
if (event.GetActionType() != SMART_ACTION_CAST)
continue;
if (!(event.action.cast.castFlags & SMARTCAST_MAIN_SPELL))
continue;
SetMainSpell(event.action.cast.spell);
break;
}
// Fallback: use first SMARTCAST_COMBAT_MOVE if no MAIN_SPELL found
if (!_currentRangeMode)
{
for (SmartScriptHolder const& event : GetScript()->GetEvents())
{
if (event.GetActionType() != SMART_ACTION_CAST)
continue;
if (!(event.action.cast.castFlags & SMARTCAST_COMBAT_MOVE))
continue;
SetMainSpell(event.action.cast.spell);
break;
}
}
if (!me->isDead()) if (!me->isDead())
{ {
mJustReset = true; mJustReset = true;
@@ -1083,6 +1112,20 @@ void SmartAI::SetCurrentRangeMode(bool on, float range)
me->GetMotionMaster()->MoveChase(victim, _attackDistance); me->GetMotionMaster()->MoveChase(victim, _attackDistance);
} }
void SmartAI::SetMainSpell(uint32 spellId)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo)
return;
float maxRange = spellInfo->GetMaxRange(false);
if (maxRange <= NOMINAL_MELEE_RANGE)
return;
_attackDistance = std::max(maxRange - NOMINAL_MELEE_RANGE, 0.0f);
_currentRangeMode = true;
}
void SmartAI::DistanceYourself(float range) void SmartAI::DistanceYourself(float range)
{ {
Unit* victim = me->GetVictim(); Unit* victim = me->GetVictim();

View File

@@ -67,6 +67,7 @@ public:
void SetAutoAttack(bool on) { mCanAutoAttack = on; } void SetAutoAttack(bool on) { mCanAutoAttack = on; }
void SetCombatMovement(bool on, bool stopOrStartMovement); void SetCombatMovement(bool on, bool stopOrStartMovement);
void SetCurrentRangeMode(bool on, float range = 0.f); void SetCurrentRangeMode(bool on, float range = 0.f);
void SetMainSpell(uint32 spellId);
void DistanceYourself(float range); void DistanceYourself(float range);
void SetFollow(Unit* target, float dist = 0.0f, float angle = 0.0f, uint32 credit = 0, uint32 end = 0, uint32 creditType = 0, bool aliveState = true); void SetFollow(Unit* target, float dist = 0.0f, float angle = 0.0f, uint32 credit = 0, uint32 end = 0, uint32 creditType = 0, bool aliveState = true);
void StopFollow(bool complete); void StopFollow(bool complete);

View File

@@ -707,7 +707,6 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
continue; continue;
} }
// Let us not try to cast spell if we know it is going to fail anyway. Stick to chasing and continue.
if (distanceToTarget > spellMaxRange && isWithinLOSInMap) if (distanceToTarget > spellMaxRange && isWithinLOSInMap)
{ {
failedSpellCast = true; failedSpellCast = true;
@@ -745,12 +744,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE) if (e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE)
{ {
// If cast flag SMARTCAST_COMBAT_MOVE is set combat movement will not be allowed unless target is outside spell range, out of mana, or LOS. if (result == SPELL_FAILED_OUT_OF_RANGE)
if (result == SPELL_FAILED_OUT_OF_RANGE || result == SPELL_CAST_OK)
// if we are just out of range, we only chase until we are back in spell range.
CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f)); CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f));
else // move into melee on any other fail else if (result != SPELL_CAST_OK)
// if spell fail for any other reason, we chase to melee range, or stay where we are if spellcast was successful.
CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(false, 0.f); CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(false, 0.f);
} }

View File

@@ -207,6 +207,8 @@ public:
void AddCreatureSummon(ObjectGuid const& guid); void AddCreatureSummon(ObjectGuid const& guid);
void RemoveCreatureSummon(ObjectGuid const& guid); void RemoveCreatureSummon(ObjectGuid const& guid);
SmartAIEventList const& GetEvents() const { return mEvents; }
private: private:
void IncPhase(uint32 p); void IncPhase(uint32 p);
void DecPhase(uint32 p); void DecPhase(uint32 p);

View File

@@ -1932,16 +1932,17 @@ enum SmartEventFlags
enum SmartCastFlags enum SmartCastFlags
{ {
SMARTCAST_INTERRUPT_PREVIOUS = 0x001, // Interrupt any spell casting SMARTCAST_INTERRUPT_PREVIOUS = 0x001, // Interrupt any spell casting
SMARTCAST_TRIGGERED = 0x002, // Triggered (this makes spell cost zero mana and have no cast time) SMARTCAST_TRIGGERED = 0x002, // Triggered (this makes spell cost zero mana and have no cast time)
//CAST_FORCE_CAST = 0x004, // Forces cast even if creature is out of mana or out of range //CAST_FORCE_CAST = 0x004, // Forces cast even if creature is out of mana or out of range
//CAST_NO_MELEE_IF_OOM = 0x008, // Prevents creature from entering melee if out of mana or out of range //CAST_NO_MELEE_IF_OOM = 0x008, // Prevents creature from entering melee if out of mana or out of range
//CAST_FORCE_TARGET_SELF = 0x010, // Forces the target to cast this spell on itself //CAST_FORCE_TARGET_SELF = 0x010, // Forces the target to cast this spell on itself
SMARTCAST_AURA_NOT_PRESENT = 0x020, // Only casts the spell if the target does not have an aura from the spell SMARTCAST_AURA_NOT_PRESENT = 0x020, // Only casts the spell if the target does not have an aura from the spell
SMARTCAST_COMBAT_MOVE = 0x040, // Prevents combat movement if cast successful. Allows movement on range, OOM, LOS SMARTCAST_COMBAT_MOVE = 0x040, // Prevents combat movement if cast successful. Allows movement on range, OOM, LOS
SMARTCAST_THREATLIST_NOT_SINGLE = 0x080, // Only cast if the source's threatlist is higher than one. This includes pets (see Skeram's True Fulfillment) SMARTCAST_THREATLIST_NOT_SINGLE = 0x080, // Only cast if the source's threatlist is higher than one. This includes pets (see Skeram's True Fulfillment)
SMARTCAST_TARGET_POWER_MANA = 0x100, // Only cast if the target has power type mana (e.g. Mana Drain) SMARTCAST_TARGET_POWER_MANA = 0x100, // Only cast if the target has power type mana (e.g. Mana Drain)
SMARTCAST_ENABLE_COMBAT_MOVE_ON_LOS = 0x200, SMARTCAST_ENABLE_COMBAT_MOVE_ON_LOS = 0x200, // Allows combat movement when not in line of sight
SMARTCAST_MAIN_SPELL = 0x400, // Sets this spell's max range as the creature's chase distance on spawn
}; };
enum SmartFollowType enum SmartFollowType