mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-24 22:26:22 +00:00
fix(Core/SAI): idle casters (#23005)
This commit is contained in:
@@ -49,7 +49,6 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c)
|
||||
mEvadeDisabled = false;
|
||||
|
||||
mCanAutoAttack = true;
|
||||
mCanCombatMove = true;
|
||||
|
||||
mForcedPaused = false;
|
||||
|
||||
@@ -80,6 +79,9 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c)
|
||||
m_ConditionsTimer = 0;
|
||||
if (me->GetVehicleKit())
|
||||
conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry());
|
||||
|
||||
_currentRangeMode = false;
|
||||
_attackDistance = 0.f;
|
||||
}
|
||||
|
||||
bool SmartAI::IsAIControlled() const
|
||||
@@ -846,7 +848,7 @@ void SmartAI::AttackStart(Unit* who)
|
||||
|
||||
if (who && me->Attack(who, me->IsWithinMeleeRange(who)))
|
||||
{
|
||||
if (mCanCombatMove)
|
||||
if (!me->HasUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT))
|
||||
{
|
||||
SetRun(mRun);
|
||||
MovementGeneratorType type = me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE);
|
||||
@@ -1047,37 +1049,49 @@ void SmartAI::sQuestReward(Player* player, Quest const* quest, uint32 opt)
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_REWARD_QUEST, player, quest->GetQuestId(), opt);
|
||||
}
|
||||
|
||||
void SmartAI::SetCombatMove(bool on, float chaseRange)
|
||||
void SmartAI::SetCombatMovement(bool on, bool stopOrStartMovement)
|
||||
{
|
||||
if (mCanCombatMove == on)
|
||||
if (on)
|
||||
me->ClearUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT);
|
||||
else
|
||||
me->AddUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT);
|
||||
|
||||
if (!IsAIControlled() || HasEscortState(SMART_ESCORT_ESCORTING))
|
||||
return;
|
||||
|
||||
mCanCombatMove = on;
|
||||
|
||||
if (!IsAIControlled())
|
||||
return;
|
||||
|
||||
if (!HasEscortState(SMART_ESCORT_ESCORTING))
|
||||
if (stopOrStartMovement && me->GetVictim()) // Only change current movement while in combat
|
||||
{
|
||||
if (on && me->GetVictim())
|
||||
if (!me->IsCrowdControlled())
|
||||
{
|
||||
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE)
|
||||
{
|
||||
SetRun(mRun);
|
||||
me->GetMotionMaster()->MoveChase(me->GetVictim(), chaseRange);
|
||||
me->CastStop();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
me->StopMoving();
|
||||
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
|
||||
me->GetMotionMaster()->Clear(false);
|
||||
me->GetMotionMaster()->MoveIdle();
|
||||
if (on)
|
||||
me->GetMotionMaster()->MoveChase(me->GetVictim());
|
||||
else if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
|
||||
me->StopMoving();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SmartAI::SetCurrentRangeMode(bool on, float range)
|
||||
{
|
||||
_currentRangeMode = on;
|
||||
_attackDistance = range;
|
||||
|
||||
if (Unit* victim = me->GetVictim())
|
||||
me->GetMotionMaster()->MoveChase(victim, _attackDistance);
|
||||
}
|
||||
|
||||
void SmartAI::DistanceYourself(float range)
|
||||
{
|
||||
Unit* victim = me->GetVictim();
|
||||
if (!victim || !victim->IsWithinMeleeRange(me))
|
||||
return;
|
||||
|
||||
float combatReach = me->GetMeleeRange(victim);
|
||||
float distance = DISTANCING_CONSTANT + std::max(combatReach * 1.5f, combatReach + range);
|
||||
me->GetMotionMaster()->DistanceYourself(distance);
|
||||
_pendingDistancing = distance;
|
||||
}
|
||||
|
||||
void SmartAI::SetFollow(Unit* target, float dist, float angle, uint32 credit, uint32 end, uint32 creditType, bool aliveState)
|
||||
{
|
||||
if (!target)
|
||||
@@ -1130,32 +1144,6 @@ void SmartAI::StopFollow(bool complete)
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_FOLLOW_COMPLETED, player);
|
||||
}
|
||||
|
||||
void SmartAI::MoveAway(float distance)
|
||||
{
|
||||
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE)
|
||||
return;
|
||||
|
||||
mCanCombatMove = false;
|
||||
|
||||
if (!IsAIControlled())
|
||||
return;
|
||||
|
||||
if (!HasEscortState(SMART_ESCORT_ESCORTING))
|
||||
{
|
||||
if (me->GetVictim())
|
||||
{
|
||||
me->StopMoving();
|
||||
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
|
||||
me->GetMotionMaster()->Clear(false);
|
||||
|
||||
float x, y, z;
|
||||
me->GetClosePoint(x, y, z, me->GetObjectSize(), distance, M_PI);
|
||||
if (me->GetVictim()->IsWithinLOS(x, y, z))
|
||||
me->GetMotionMaster()->MovePoint(SMART_RANDOM_POINT, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SmartAI::SetScript9(SmartScriptHolder& e, uint32 entry, WorldObject* invoker)
|
||||
{
|
||||
if (invoker)
|
||||
@@ -1183,6 +1171,12 @@ void SmartAI::PathEndReached(uint32 /*pathId*/)
|
||||
me->LoadPath(0);
|
||||
}
|
||||
|
||||
void SmartAI::DistancingEnded()
|
||||
{
|
||||
SetCurrentRangeMode(true, _pendingDistancing);
|
||||
_pendingDistancing = 0.f;
|
||||
}
|
||||
|
||||
void SmartGameObjectAI::SummonedCreatureDies(Creature* summon, Unit* /*killer*/)
|
||||
{
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_SUMMONED_UNIT_DIES, summon);
|
||||
|
||||
@@ -40,6 +40,8 @@ enum SmartEscortVars
|
||||
SMART_MAX_AID_DIST = SMART_ESCORT_MAX_PLAYER_DIST / 2,
|
||||
};
|
||||
|
||||
#define DISTANCING_CONSTANT 1.f // buffer for better functionality of distancing
|
||||
|
||||
class SmartAI : public CreatureAI
|
||||
{
|
||||
public:
|
||||
@@ -63,11 +65,11 @@ public:
|
||||
bool IsEscorted() override { return (mEscortState & SMART_ESCORT_ESCORTING); }
|
||||
void RemoveEscortState(uint32 uiEscortState) { mEscortState &= ~uiEscortState; }
|
||||
void SetAutoAttack(bool on) { mCanAutoAttack = on; }
|
||||
void SetCombatMove(bool on, float chaseRange = 0.0f);
|
||||
bool CanCombatMove() { return mCanCombatMove; }
|
||||
void SetCombatMovement(bool on, bool stopOrStartMovement);
|
||||
void SetCurrentRangeMode(bool on, float range = 0.f);
|
||||
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 StopFollow(bool complete);
|
||||
void MoveAway(float distance);
|
||||
|
||||
void SetScript9(SmartScriptHolder& e, uint32 entry, WorldObject* invoker);
|
||||
SmartScript* GetScript() { return &mScript; }
|
||||
@@ -212,8 +214,7 @@ public:
|
||||
// Xinef
|
||||
void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; }
|
||||
|
||||
void SetChaseOnInterrupt(bool apply) { _chaseOnInterrupt = apply; }
|
||||
[[nodiscard]] bool CanChaseOnInterrupt() const { return _chaseOnInterrupt; }
|
||||
void DistancingEnded() override;
|
||||
|
||||
private:
|
||||
bool mIsCharmed;
|
||||
@@ -242,7 +243,6 @@ private:
|
||||
bool mRun;
|
||||
bool mEvadeDisabled;
|
||||
bool mCanAutoAttack;
|
||||
bool mCanCombatMove;
|
||||
bool mForcedPaused;
|
||||
uint32 mInvincibilityHpLevel;
|
||||
|
||||
@@ -263,6 +263,10 @@ private:
|
||||
|
||||
bool _chaseOnInterrupt;
|
||||
std::unordered_map<uint32, uint32> aiDataSet;
|
||||
|
||||
bool _currentRangeMode;
|
||||
float _attackDistance;
|
||||
float _pendingDistancing;
|
||||
};
|
||||
|
||||
class SmartGameObjectAI : public GameObjectAI
|
||||
|
||||
@@ -686,7 +686,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
float spellMinRange = me->GetSpellMinRangeForTarget(target->ToUnit(), spellInfo);
|
||||
float meleeRange = me->GetMeleeRange(target->ToUnit());
|
||||
|
||||
bool isWithinLOSInMap = me->IsWithinLOSInMap(target->ToUnit());
|
||||
bool isWithinLOSInMap = me->IsWithinLOSInMap(target->ToUnit(), VMAP::ModelIgnoreFlags::M2);
|
||||
bool isWithinMeleeRange = distanceToTarget <= meleeRange;
|
||||
bool isRangedAttack = spellMaxRange > NOMINAL_MELEE_RANGE;
|
||||
bool isTargetRooted = target->ToUnit()->HasUnitState(UNIT_STATE_ROOT);
|
||||
@@ -703,7 +703,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
continue;
|
||||
|
||||
float minDistance = std::max(meleeRange, spellMinRange) - distanceToTarget + NOMINAL_MELEE_RANGE;
|
||||
CAST_AI(SmartAI, me->AI())->MoveAway(std::min(minDistance, spellMaxRange));
|
||||
CAST_AI(SmartAI, me->AI())->DistanceYourself(std::min(minDistance, spellMaxRange));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -715,7 +715,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
if (me->IsRooted()) // Rooted inhabit type, never move/reposition
|
||||
continue;
|
||||
|
||||
CAST_AI(SmartAI, me->AI())->SetCombatMove(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f));
|
||||
CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f));
|
||||
continue;
|
||||
}
|
||||
else if (distanceToTarget < spellMinRange || !(isWithinLOSInMap || isSpellIgnoreLOS))
|
||||
@@ -725,7 +725,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
if (me->IsRooted()) // Rooted inhabit type, never move/reposition
|
||||
continue;
|
||||
|
||||
CAST_AI(SmartAI, me->AI())->SetCombatMove(true);
|
||||
CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(true, 0.f);
|
||||
if (e.action.cast.castFlags & SMARTCAST_ENABLE_COMBAT_MOVE_ON_LOS)
|
||||
CAST_AI(SmartAI, me->AI())->SetCombatMovement(true, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -743,18 +745,13 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
|
||||
if (e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE)
|
||||
{
|
||||
CAST_AI(SmartAI, me->AI())->SetChaseOnInterrupt(true);
|
||||
|
||||
if (!me->isMoving()) // Don't try to reposition while we are moving
|
||||
{
|
||||
// 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 we are just out of range, we only chase until we are back in spell range.
|
||||
CAST_AI(SmartAI, me->AI())->SetCombatMove(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f));
|
||||
else
|
||||
// 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())->SetCombatMove(spellCastFailed);
|
||||
}
|
||||
// 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 || 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));
|
||||
else // move into melee on any other fail
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (spellCastFailed)
|
||||
@@ -989,7 +986,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
break;
|
||||
|
||||
bool move = e.action.combatMove.move;
|
||||
CAST_AI(SmartAI, me->AI())->SetCombatMove(move);
|
||||
CAST_AI(SmartAI, me->AI())->SetCombatMovement(move, true);
|
||||
LOG_DEBUG("sql.sql", "SmartScript::ProcessAction:: SMART_ACTION_ALLOW_COMBAT_MOVEMENT: Creature {} bool on = {}",
|
||||
me->GetGUID().ToString(), e.action.combatMove.move);
|
||||
break;
|
||||
@@ -2061,7 +2058,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
for (WorldObject* target : targets)
|
||||
if (Creature* creature = target->ToCreature())
|
||||
if (IsSmart(creature) && creature->GetVictim())
|
||||
if (CAST_AI(SmartAI, creature->AI())->CanCombatMove())
|
||||
if (!creature->HasUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT))
|
||||
creature->GetMotionMaster()->MoveChase(creature->GetVictim(), attackDistance, attackAngle);
|
||||
|
||||
break;
|
||||
@@ -2730,26 +2727,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
me->InterruptNonMeleeSpells(false);
|
||||
}
|
||||
|
||||
if (e.action.castCustom.flags & 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.
|
||||
|
||||
bool _allowMove = false;
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(e.action.castCustom.spell); // AssertSpellInfo?
|
||||
int32 mana = me->GetPower(POWER_MANA);
|
||||
|
||||
if (me->GetDistance(target->ToUnit()) > spellInfo->GetMaxRange(true) ||
|
||||
me->GetDistance(target->ToUnit()) < spellInfo->GetMinRange(true) ||
|
||||
!me->IsWithinLOSInMap(target->ToUnit()) ||
|
||||
mana < spellInfo->CalcPowerCost(me, spellInfo->GetSchoolMask()))
|
||||
_allowMove = true;
|
||||
|
||||
CAST_AI(SmartAI, me->AI())->SetCombatMove(_allowMove);
|
||||
}
|
||||
|
||||
if (!(e.action.castCustom.flags & SMARTCAST_AURA_NOT_PRESENT) || !target->ToUnit()->HasAura(e.action.castCustom.spell))
|
||||
{
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(e.action.castCustom.spell);
|
||||
CustomSpellValues values;
|
||||
if (e.action.castCustom.bp1)
|
||||
values.AddSpellMod(SPELLVALUE_BASE_POINT0, e.action.castCustom.bp1);
|
||||
@@ -2757,7 +2737,19 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
values.AddSpellMod(SPELLVALUE_BASE_POINT1, e.action.castCustom.bp2);
|
||||
if (e.action.castCustom.bp3)
|
||||
values.AddSpellMod(SPELLVALUE_BASE_POINT2, e.action.castCustom.bp3);
|
||||
me->CastCustomSpell(e.action.castCustom.spell, values, target->ToUnit(), (e.action.castCustom.flags & SMARTCAST_TRIGGERED) ? TRIGGERED_FULL_MASK : TRIGGERED_NONE);
|
||||
SpellCastResult result = me->CastCustomSpell(spellInfo, values, target->ToUnit(), (e.action.castCustom.flags & SMARTCAST_TRIGGERED) ? TRIGGERED_FULL_MASK : TRIGGERED_NONE);
|
||||
|
||||
float spellMaxRange = me->GetSpellMaxRangeForTarget(target->ToUnit(), spellInfo);
|
||||
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 || 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));
|
||||
else // move into melee on any other fail
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1957,7 +1957,8 @@ enum SmartCastFlags
|
||||
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_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,
|
||||
};
|
||||
|
||||
enum SmartFollowType
|
||||
|
||||
Reference in New Issue
Block a user