fix(Core/SAI): idle casters (#23005)
This commit is contained in:
@@ -231,6 +231,9 @@ public:
|
|||||||
// Called when an aura is removed or expires.
|
// Called when an aura is removed or expires.
|
||||||
virtual void OnAuraRemove(AuraApplication* /*aurApp*/, AuraRemoveMode /*mode*/) { }
|
virtual void OnAuraRemove(AuraApplication* /*aurApp*/, AuraRemoveMode /*mode*/) { }
|
||||||
|
|
||||||
|
virtual void DistancingStarted() {}
|
||||||
|
virtual void DistancingEnded() {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void MoveInLineOfSight(Unit* /*who*/);
|
virtual void MoveInLineOfSight(Unit* /*who*/);
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c)
|
|||||||
mEvadeDisabled = false;
|
mEvadeDisabled = false;
|
||||||
|
|
||||||
mCanAutoAttack = true;
|
mCanAutoAttack = true;
|
||||||
mCanCombatMove = true;
|
|
||||||
|
|
||||||
mForcedPaused = false;
|
mForcedPaused = false;
|
||||||
|
|
||||||
@@ -80,6 +79,9 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c)
|
|||||||
m_ConditionsTimer = 0;
|
m_ConditionsTimer = 0;
|
||||||
if (me->GetVehicleKit())
|
if (me->GetVehicleKit())
|
||||||
conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry());
|
conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry());
|
||||||
|
|
||||||
|
_currentRangeMode = false;
|
||||||
|
_attackDistance = 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SmartAI::IsAIControlled() const
|
bool SmartAI::IsAIControlled() const
|
||||||
@@ -846,7 +848,7 @@ void SmartAI::AttackStart(Unit* who)
|
|||||||
|
|
||||||
if (who && me->Attack(who, me->IsWithinMeleeRange(who)))
|
if (who && me->Attack(who, me->IsWithinMeleeRange(who)))
|
||||||
{
|
{
|
||||||
if (mCanCombatMove)
|
if (!me->HasUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT))
|
||||||
{
|
{
|
||||||
SetRun(mRun);
|
SetRun(mRun);
|
||||||
MovementGeneratorType type = me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE);
|
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);
|
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;
|
return;
|
||||||
|
|
||||||
mCanCombatMove = on;
|
if (stopOrStartMovement && me->GetVictim()) // Only change current movement while in combat
|
||||||
|
|
||||||
if (!IsAIControlled())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!HasEscortState(SMART_ESCORT_ESCORTING))
|
|
||||||
{
|
{
|
||||||
if (on && me->GetVictim())
|
if (!me->IsCrowdControlled())
|
||||||
{
|
{
|
||||||
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE)
|
if (on)
|
||||||
{
|
me->GetMotionMaster()->MoveChase(me->GetVictim());
|
||||||
SetRun(mRun);
|
else if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
|
||||||
me->GetMotionMaster()->MoveChase(me->GetVictim(), chaseRange);
|
me->StopMoving();
|
||||||
me->CastStop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
me->StopMoving();
|
|
||||||
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
|
|
||||||
me->GetMotionMaster()->Clear(false);
|
|
||||||
me->GetMotionMaster()->MoveIdle();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
void SmartAI::SetFollow(Unit* target, float dist, float angle, uint32 credit, uint32 end, uint32 creditType, bool aliveState)
|
||||||
{
|
{
|
||||||
if (!target)
|
if (!target)
|
||||||
@@ -1130,32 +1144,6 @@ void SmartAI::StopFollow(bool complete)
|
|||||||
GetScript()->ProcessEventsFor(SMART_EVENT_FOLLOW_COMPLETED, player);
|
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)
|
void SmartAI::SetScript9(SmartScriptHolder& e, uint32 entry, WorldObject* invoker)
|
||||||
{
|
{
|
||||||
if (invoker)
|
if (invoker)
|
||||||
@@ -1183,6 +1171,12 @@ void SmartAI::PathEndReached(uint32 /*pathId*/)
|
|||||||
me->LoadPath(0);
|
me->LoadPath(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SmartAI::DistancingEnded()
|
||||||
|
{
|
||||||
|
SetCurrentRangeMode(true, _pendingDistancing);
|
||||||
|
_pendingDistancing = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
void SmartGameObjectAI::SummonedCreatureDies(Creature* summon, Unit* /*killer*/)
|
void SmartGameObjectAI::SummonedCreatureDies(Creature* summon, Unit* /*killer*/)
|
||||||
{
|
{
|
||||||
GetScript()->ProcessEventsFor(SMART_EVENT_SUMMONED_UNIT_DIES, summon);
|
GetScript()->ProcessEventsFor(SMART_EVENT_SUMMONED_UNIT_DIES, summon);
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ enum SmartEscortVars
|
|||||||
SMART_MAX_AID_DIST = SMART_ESCORT_MAX_PLAYER_DIST / 2,
|
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
|
class SmartAI : public CreatureAI
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -63,11 +65,11 @@ public:
|
|||||||
bool IsEscorted() override { return (mEscortState & SMART_ESCORT_ESCORTING); }
|
bool IsEscorted() override { return (mEscortState & SMART_ESCORT_ESCORTING); }
|
||||||
void RemoveEscortState(uint32 uiEscortState) { mEscortState &= ~uiEscortState; }
|
void RemoveEscortState(uint32 uiEscortState) { mEscortState &= ~uiEscortState; }
|
||||||
void SetAutoAttack(bool on) { mCanAutoAttack = on; }
|
void SetAutoAttack(bool on) { mCanAutoAttack = on; }
|
||||||
void SetCombatMove(bool on, float chaseRange = 0.0f);
|
void SetCombatMovement(bool on, bool stopOrStartMovement);
|
||||||
bool CanCombatMove() { return mCanCombatMove; }
|
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 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);
|
||||||
void MoveAway(float distance);
|
|
||||||
|
|
||||||
void SetScript9(SmartScriptHolder& e, uint32 entry, WorldObject* invoker);
|
void SetScript9(SmartScriptHolder& e, uint32 entry, WorldObject* invoker);
|
||||||
SmartScript* GetScript() { return &mScript; }
|
SmartScript* GetScript() { return &mScript; }
|
||||||
@@ -212,8 +214,7 @@ public:
|
|||||||
// Xinef
|
// Xinef
|
||||||
void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; }
|
void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; }
|
||||||
|
|
||||||
void SetChaseOnInterrupt(bool apply) { _chaseOnInterrupt = apply; }
|
void DistancingEnded() override;
|
||||||
[[nodiscard]] bool CanChaseOnInterrupt() const { return _chaseOnInterrupt; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool mIsCharmed;
|
bool mIsCharmed;
|
||||||
@@ -242,7 +243,6 @@ private:
|
|||||||
bool mRun;
|
bool mRun;
|
||||||
bool mEvadeDisabled;
|
bool mEvadeDisabled;
|
||||||
bool mCanAutoAttack;
|
bool mCanAutoAttack;
|
||||||
bool mCanCombatMove;
|
|
||||||
bool mForcedPaused;
|
bool mForcedPaused;
|
||||||
uint32 mInvincibilityHpLevel;
|
uint32 mInvincibilityHpLevel;
|
||||||
|
|
||||||
@@ -263,6 +263,10 @@ private:
|
|||||||
|
|
||||||
bool _chaseOnInterrupt;
|
bool _chaseOnInterrupt;
|
||||||
std::unordered_map<uint32, uint32> aiDataSet;
|
std::unordered_map<uint32, uint32> aiDataSet;
|
||||||
|
|
||||||
|
bool _currentRangeMode;
|
||||||
|
float _attackDistance;
|
||||||
|
float _pendingDistancing;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SmartGameObjectAI : public GameObjectAI
|
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 spellMinRange = me->GetSpellMinRangeForTarget(target->ToUnit(), spellInfo);
|
||||||
float meleeRange = me->GetMeleeRange(target->ToUnit());
|
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 isWithinMeleeRange = distanceToTarget <= meleeRange;
|
||||||
bool isRangedAttack = spellMaxRange > NOMINAL_MELEE_RANGE;
|
bool isRangedAttack = spellMaxRange > NOMINAL_MELEE_RANGE;
|
||||||
bool isTargetRooted = target->ToUnit()->HasUnitState(UNIT_STATE_ROOT);
|
bool isTargetRooted = target->ToUnit()->HasUnitState(UNIT_STATE_ROOT);
|
||||||
@@ -703,7 +703,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
float minDistance = std::max(meleeRange, spellMinRange) - distanceToTarget + NOMINAL_MELEE_RANGE;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -715,7 +715,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
|||||||
if (me->IsRooted()) // Rooted inhabit type, never move/reposition
|
if (me->IsRooted()) // Rooted inhabit type, never move/reposition
|
||||||
continue;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
else if (distanceToTarget < spellMinRange || !(isWithinLOSInMap || isSpellIgnoreLOS))
|
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
|
if (me->IsRooted()) // Rooted inhabit type, never move/reposition
|
||||||
continue;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -743,18 +745,13 @@ 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)
|
||||||
{
|
{
|
||||||
CAST_AI(SmartAI, me->AI())->SetChaseOnInterrupt(true);
|
// 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 (!me->isMoving()) // Don't try to reposition while we are moving
|
// 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));
|
||||||
// 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.
|
else // move into melee on any other fail
|
||||||
if (result == SPELL_FAILED_OUT_OF_RANGE)
|
// if spell fail for any other reason, we chase to melee range, or stay where we are if spellcast was successful.
|
||||||
// if we are just out of range, we only chase until we are back in spell range.
|
CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(false, 0.f);
|
||||||
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 (spellCastFailed)
|
if (spellCastFailed)
|
||||||
@@ -989,7 +986,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
bool move = e.action.combatMove.move;
|
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 = {}",
|
LOG_DEBUG("sql.sql", "SmartScript::ProcessAction:: SMART_ACTION_ALLOW_COMBAT_MOVEMENT: Creature {} bool on = {}",
|
||||||
me->GetGUID().ToString(), e.action.combatMove.move);
|
me->GetGUID().ToString(), e.action.combatMove.move);
|
||||||
break;
|
break;
|
||||||
@@ -2061,7 +2058,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
|||||||
for (WorldObject* target : targets)
|
for (WorldObject* target : targets)
|
||||||
if (Creature* creature = target->ToCreature())
|
if (Creature* creature = target->ToCreature())
|
||||||
if (IsSmart(creature) && creature->GetVictim())
|
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);
|
creature->GetMotionMaster()->MoveChase(creature->GetVictim(), attackDistance, attackAngle);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -2730,26 +2727,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
|||||||
me->InterruptNonMeleeSpells(false);
|
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))
|
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;
|
CustomSpellValues values;
|
||||||
if (e.action.castCustom.bp1)
|
if (e.action.castCustom.bp1)
|
||||||
values.AddSpellMod(SPELLVALUE_BASE_POINT0, 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);
|
values.AddSpellMod(SPELLVALUE_BASE_POINT1, e.action.castCustom.bp2);
|
||||||
if (e.action.castCustom.bp3)
|
if (e.action.castCustom.bp3)
|
||||||
values.AddSpellMod(SPELLVALUE_BASE_POINT2, 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_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,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SmartFollowType
|
enum SmartFollowType
|
||||||
|
|||||||
@@ -1283,6 +1283,11 @@ SpellCastResult Unit::CastCustomSpell(uint32 spellId, CustomSpellValues const& v
|
|||||||
return SPELL_FAILED_SPELL_UNAVAILABLE;
|
return SPELL_FAILED_SPELL_UNAVAILABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return CastCustomSpell(spellInfo, value, victim, triggerFlags, castItem, triggeredByAura, originalCaster);
|
||||||
|
}
|
||||||
|
|
||||||
|
SpellCastResult Unit::CastCustomSpell(SpellInfo const* spellInfo, CustomSpellValues const& value, Unit* victim, TriggerCastFlags triggerFlags, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster)
|
||||||
|
{
|
||||||
SpellCastTargets targets;
|
SpellCastTargets targets;
|
||||||
targets.SetUnitTarget(victim);
|
targets.SetUnitTarget(victim);
|
||||||
|
|
||||||
@@ -4130,15 +4135,6 @@ void Unit::InterruptSpell(CurrentSpellTypes spellType, bool withDelayed, bool wi
|
|||||||
spell->SetReferencedFromCurrent(false);
|
spell->SetReferencedFromCurrent(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAI creatures only
|
|
||||||
// Start chasing victim if they are spell casters (at least one SMC spell) if interrupted/silenced.
|
|
||||||
if (IsCreature())
|
|
||||||
{
|
|
||||||
if (SmartAI* ai = dynamic_cast<SmartAI*>(ToCreature()->AI()))
|
|
||||||
if (ai->CanChaseOnInterrupt())
|
|
||||||
ai->SetCombatMove(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsCreature() && IsAIEnabled)
|
if (IsCreature() && IsAIEnabled)
|
||||||
ToCreature()->AI()->OnSpellCastFinished(spell->GetSpellInfo(), SPELL_FINISHED_CANCELED);
|
ToCreature()->AI()->OnSpellCastFinished(spell->GetSpellInfo(), SPELL_FINISHED_CANCELED);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -736,6 +736,8 @@ public:
|
|||||||
[[nodiscard]] uint16 GetExtraUnitMovementFlags() const { return m_movementInfo.flags2; }
|
[[nodiscard]] uint16 GetExtraUnitMovementFlags() const { return m_movementInfo.flags2; }
|
||||||
void SetExtraUnitMovementFlags(uint16 f) { m_movementInfo.flags2 = f; }
|
void SetExtraUnitMovementFlags(uint16 f) { m_movementInfo.flags2 = f; }
|
||||||
|
|
||||||
|
inline bool IsCrowdControlled() const { return HasFlag(UNIT_FIELD_FLAGS, (UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING | UNIT_FLAG_STUNNED)); }
|
||||||
|
|
||||||
/*********************************************************/
|
/*********************************************************/
|
||||||
/*** UNIT TYPES, CLASSES, RACES... ***/
|
/*** UNIT TYPES, CLASSES, RACES... ***/
|
||||||
/*********************************************************/
|
/*********************************************************/
|
||||||
@@ -1595,6 +1597,7 @@ public:
|
|||||||
SpellCastResult CastCustomSpell(uint32 spellId, SpellValueMod mod, int32 value, Unit* victim, bool triggered, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
|
SpellCastResult CastCustomSpell(uint32 spellId, SpellValueMod mod, int32 value, Unit* victim, bool triggered, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
|
||||||
SpellCastResult CastCustomSpell(uint32 spellId, SpellValueMod mod, int32 value, Unit* victim = nullptr, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
|
SpellCastResult CastCustomSpell(uint32 spellId, SpellValueMod mod, int32 value, Unit* victim = nullptr, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
|
||||||
SpellCastResult CastCustomSpell(uint32 spellId, CustomSpellValues const& value, Unit* victim = nullptr, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
|
SpellCastResult CastCustomSpell(uint32 spellId, CustomSpellValues const& value, Unit* victim = nullptr, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
|
||||||
|
SpellCastResult CastCustomSpell(SpellInfo const* spellInfo, CustomSpellValues const& value, Unit* victim = nullptr, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
|
||||||
|
|
||||||
/*********************************************************/
|
/*********************************************************/
|
||||||
/*** METHODS RELATED TO GAMEOBJECT & DYNOBEJCTS ***/
|
/*** METHODS RELATED TO GAMEOBJECT & DYNOBEJCTS ***/
|
||||||
|
|||||||
@@ -197,6 +197,8 @@ enum UnitState
|
|||||||
UNIT_STATE_IGNORE_PATHFINDING = 0x10000000, // do not use pathfinding in any MovementGenerator
|
UNIT_STATE_IGNORE_PATHFINDING = 0x10000000, // do not use pathfinding in any MovementGenerator
|
||||||
UNIT_STATE_NO_ENVIRONMENT_UPD = 0x20000000,
|
UNIT_STATE_NO_ENVIRONMENT_UPD = 0x20000000,
|
||||||
|
|
||||||
|
UNIT_STATE_NO_COMBAT_MOVEMENT, // serverside only - should never be changed outside of core and hence shouldnt have a defined static value and be at the end
|
||||||
|
|
||||||
UNIT_STATE_ALL_STATE_SUPPORTED = UNIT_STATE_DIED | UNIT_STATE_MELEE_ATTACKING | UNIT_STATE_STUNNED | UNIT_STATE_ROAMING | UNIT_STATE_CHASE
|
UNIT_STATE_ALL_STATE_SUPPORTED = UNIT_STATE_DIED | UNIT_STATE_MELEE_ATTACKING | UNIT_STATE_STUNNED | UNIT_STATE_ROAMING | UNIT_STATE_CHASE
|
||||||
| UNIT_STATE_FLEEING | UNIT_STATE_IN_FLIGHT | UNIT_STATE_FOLLOW | UNIT_STATE_ROOT | UNIT_STATE_CONFUSED
|
| UNIT_STATE_FLEEING | UNIT_STATE_IN_FLIGHT | UNIT_STATE_FOLLOW | UNIT_STATE_ROOT | UNIT_STATE_CONFUSED
|
||||||
| UNIT_STATE_DISTRACTED | UNIT_STATE_ISOLATED | UNIT_STATE_ATTACK_PLAYER | UNIT_STATE_CASTING
|
| UNIT_STATE_DISTRACTED | UNIT_STATE_ISOLATED | UNIT_STATE_ATTACK_PLAYER | UNIT_STATE_CASTING
|
||||||
|
|||||||
@@ -310,12 +310,29 @@ void MotionMaster::MoveConfused()
|
|||||||
/**
|
/**
|
||||||
* @brief Force the unit to chase this target. Doesn't work with UNIT_FLAG_DISABLE_MOVE
|
* @brief Force the unit to chase this target. Doesn't work with UNIT_FLAG_DISABLE_MOVE
|
||||||
*/
|
*/
|
||||||
void MotionMaster::MoveChase(Unit* target, std::optional<ChaseRange> dist, std::optional<ChaseAngle> angle)
|
void MotionMaster::MoveChase(Unit* target, std::optional<ChaseRange> dist, std::optional<ChaseAngle> angle)
|
||||||
{
|
{
|
||||||
// ignore movement request if target not exist
|
// ignore movement request if target not exist
|
||||||
if (!target || target == _owner || _owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
|
if (!target || target == _owner || _owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
|
||||||
|
{
|
||||||
|
if (_owner->IsPlayer())
|
||||||
|
{
|
||||||
|
ChaseMovementGenerator<Player>* gen = (ChaseMovementGenerator<Player>*)top();
|
||||||
|
gen->SetOffsetAndAngle(dist, angle);
|
||||||
|
gen->SetNewTarget(target);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ChaseMovementGenerator<Creature>* gen = (ChaseMovementGenerator<Creature>*)top();
|
||||||
|
gen->SetOffsetAndAngle(dist, angle);
|
||||||
|
gen->SetNewTarget(target);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//_owner->ClearUnitState(UNIT_STATE_FOLLOW);
|
//_owner->ClearUnitState(UNIT_STATE_FOLLOW);
|
||||||
if (_owner->IsPlayer())
|
if (_owner->IsPlayer())
|
||||||
{
|
{
|
||||||
@@ -331,6 +348,24 @@ void MotionMaster::MoveChase(Unit* target, std::optional<ChaseRange> dist, std:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MotionMaster::DistanceYourself(float dist)
|
||||||
|
{
|
||||||
|
if (GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
|
||||||
|
{
|
||||||
|
if (_owner->IsPlayer())
|
||||||
|
{
|
||||||
|
ChaseMovementGenerator<Player>* gen = (ChaseMovementGenerator<Player>*)top();
|
||||||
|
gen->DistanceYourself((Player*)_owner, dist);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ChaseMovementGenerator<Creature>* gen = (ChaseMovementGenerator<Creature>*)top();
|
||||||
|
gen->DistanceYourself((Creature*)_owner, dist);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MotionMaster::MoveBackwards(Unit* target, float dist)
|
void MotionMaster::MoveBackwards(Unit* target, float dist)
|
||||||
{
|
{
|
||||||
if (!target)
|
if (!target)
|
||||||
|
|||||||
@@ -247,6 +247,8 @@ public:
|
|||||||
void ReinitializeMovement();
|
void ReinitializeMovement();
|
||||||
|
|
||||||
bool GetDestination(float& x, float& y, float& z);
|
bool GetDestination(float& x, float& y, float& z);
|
||||||
|
|
||||||
|
void DistanceYourself(float range);
|
||||||
private:
|
private:
|
||||||
void Mutate(MovementGenerator* m, MovementSlot slot); // use Move* functions instead
|
void Mutate(MovementGenerator* m, MovementSlot slot); // use Move* functions instead
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,81 @@ bool ChaseMovementGenerator<T>::PositionOkay(T* owner, Unit* target, Optional<fl
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
void ChaseMovementGenerator<T>::SetOffsetAndAngle(std::optional<ChaseRange> dist, std::optional<ChaseAngle> angle)
|
||||||
|
{
|
||||||
|
_range = dist;
|
||||||
|
_angle = angle;
|
||||||
|
_lastTargetPosition.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
void ChaseMovementGenerator<T>::SetNewTarget(Unit* target)
|
||||||
|
{
|
||||||
|
i_target.link(target, this);
|
||||||
|
_lastTargetPosition.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
void ChaseMovementGenerator<T>::DistanceYourself(T* owner, float distance)
|
||||||
|
{
|
||||||
|
// make a new path if we have to...
|
||||||
|
if (!i_path)
|
||||||
|
i_path = std::make_unique<PathGenerator>(owner);
|
||||||
|
|
||||||
|
float x, y, z;
|
||||||
|
i_target->GetNearPoint(owner, x, y, z, owner->GetBoundaryRadius(), distance, i_target->GetAngle(owner));
|
||||||
|
if (DispatchSplineToPosition(owner, x, y, z, false, false, 0.f, false, false))
|
||||||
|
{
|
||||||
|
m_currentMode = CHASE_MODE_DISTANCING;
|
||||||
|
if constexpr (!std::is_same_v<T, Player>)
|
||||||
|
{
|
||||||
|
owner->AI()->DistancingStarted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
bool ChaseMovementGenerator<T>::DispatchSplineToPosition(T* owner, float x, float y, float z, bool walk, bool cutPath, float maxTarget, bool forceDest, bool target)
|
||||||
|
{
|
||||||
|
Creature* cOwner = owner->ToCreature();
|
||||||
|
|
||||||
|
if (owner->IsHovering())
|
||||||
|
owner->UpdateAllowedPositionZ(x, y, z);
|
||||||
|
|
||||||
|
bool success = i_path->CalculatePath(x, y, z, forceDest);
|
||||||
|
if (!success || i_path->GetPathType() & PATHFIND_NOPATH)
|
||||||
|
{
|
||||||
|
if (cOwner)
|
||||||
|
{
|
||||||
|
cOwner->SetCannotReachTarget(i_target.getTarget()->GetGUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
owner->StopMoving();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cutPath)
|
||||||
|
i_path->ShortenPathUntilDist(G3D::Vector3(x, y, z), maxTarget);
|
||||||
|
|
||||||
|
if (cOwner)
|
||||||
|
{
|
||||||
|
cOwner->SetCannotReachTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
owner->AddUnitState(UNIT_STATE_CHASE_MOVE);
|
||||||
|
i_recalculateTravel = true;
|
||||||
|
|
||||||
|
Movement::MoveSplineInit init(owner);
|
||||||
|
init.MovebyPath(i_path->GetPath());
|
||||||
|
if (target)
|
||||||
|
init.SetFacing(i_target.getTarget());
|
||||||
|
init.SetWalk(walk);
|
||||||
|
init.Launch();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
||||||
{
|
{
|
||||||
@@ -71,6 +146,13 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
|||||||
if (!owner || !owner->IsAlive())
|
if (!owner || !owner->IsAlive())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (owner->HasUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT)) // script paused combat movement
|
||||||
|
{
|
||||||
|
owner->StopMoving();
|
||||||
|
_lastTargetPosition.reset();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Creature* cOwner = owner->ToCreature();
|
Creature* cOwner = owner->ToCreature();
|
||||||
bool isStoppedBecauseOfCasting = cOwner && cOwner->IsMovementPreventedByCasting();
|
bool isStoppedBecauseOfCasting = cOwner && cOwner->IsMovementPreventedByCasting();
|
||||||
|
|
||||||
@@ -243,53 +325,23 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
|||||||
shortenPath = false;
|
shortenPath = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (owner->IsHovering())
|
|
||||||
owner->UpdateAllowedPositionZ(x, y, z);
|
|
||||||
|
|
||||||
bool success = i_path->CalculatePath(x, y, z, forceDest);
|
|
||||||
if (!success || i_path->GetPathType() & PATHFIND_NOPATH)
|
|
||||||
{
|
|
||||||
if (cOwner)
|
|
||||||
{
|
|
||||||
cOwner->SetCannotReachTarget(target->GetGUID());
|
|
||||||
}
|
|
||||||
|
|
||||||
owner->StopMoving();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shortenPath)
|
|
||||||
i_path->ShortenPathUntilDist(G3D::Vector3(x, y, z), maxTarget);
|
|
||||||
|
|
||||||
if (cOwner)
|
|
||||||
{
|
|
||||||
cOwner->SetCannotReachTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool walk = false;
|
bool walk = false;
|
||||||
if (cOwner && !cOwner->IsPet())
|
if (cOwner && !cOwner->IsPet())
|
||||||
{
|
{
|
||||||
switch (cOwner->GetMovementTemplate().GetChase())
|
switch (cOwner->GetMovementTemplate().GetChase())
|
||||||
{
|
{
|
||||||
case CreatureChaseMovementType::CanWalk:
|
case CreatureChaseMovementType::CanWalk:
|
||||||
walk = owner->IsWalking();
|
walk = owner->IsWalking();
|
||||||
break;
|
break;
|
||||||
case CreatureChaseMovementType::AlwaysWalk:
|
case CreatureChaseMovementType::AlwaysWalk:
|
||||||
walk = true;
|
walk = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
owner->AddUnitState(UNIT_STATE_CHASE_MOVE);
|
DispatchSplineToPosition(owner, x, y, z, walk, shortenPath, maxTarget, forceDest, true);
|
||||||
i_recalculateTravel = true;
|
|
||||||
|
|
||||||
Movement::MoveSplineInit init(owner);
|
|
||||||
init.MovebyPath(i_path->GetPath());
|
|
||||||
init.SetFacing(target);
|
|
||||||
init.SetWalk(walk);
|
|
||||||
init.Launch();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,9 +391,24 @@ void ChaseMovementGenerator<T>::MovementInform(T* owner)
|
|||||||
if (!owner->IsCreature())
|
if (!owner->IsCreature())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
|
switch (m_currentMode)
|
||||||
if (CreatureAI* AI = owner->ToCreature()->AI())
|
{
|
||||||
AI->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUID().GetCounter());
|
default:
|
||||||
|
{
|
||||||
|
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
|
||||||
|
if (CreatureAI* AI = owner->ToCreature()->AI())
|
||||||
|
AI->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUID().GetCounter());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CHASE_MODE_DISTANCING:
|
||||||
|
{
|
||||||
|
if (CreatureAI* AI = owner->ToCreature()->AI())
|
||||||
|
AI->DistancingEnded();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_currentMode = CHASE_MODE_NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------//
|
//-----------------------------------------------//
|
||||||
@@ -604,6 +671,13 @@ template bool ChaseMovementGenerator<Player>::DoUpdate(Player*, uint32);
|
|||||||
template bool ChaseMovementGenerator<Creature>::DoUpdate(Creature*, uint32);
|
template bool ChaseMovementGenerator<Creature>::DoUpdate(Creature*, uint32);
|
||||||
template void ChaseMovementGenerator<Unit>::MovementInform(Unit*);
|
template void ChaseMovementGenerator<Unit>::MovementInform(Unit*);
|
||||||
|
|
||||||
|
template void ChaseMovementGenerator<Creature>::SetOffsetAndAngle(std::optional<ChaseRange>, std::optional<ChaseAngle>);
|
||||||
|
template void ChaseMovementGenerator<Creature>::SetNewTarget(Unit*);
|
||||||
|
template void ChaseMovementGenerator<Creature>::DistanceYourself(Creature*, float);
|
||||||
|
template void ChaseMovementGenerator<Player>::SetOffsetAndAngle(std::optional<ChaseRange>, std::optional<ChaseAngle>);
|
||||||
|
template void ChaseMovementGenerator<Player>::SetNewTarget(Unit*);
|
||||||
|
template void ChaseMovementGenerator<Player>::DistanceYourself(Player*, float);
|
||||||
|
|
||||||
template void FollowMovementGenerator<Player>::DoInitialize(Player*);
|
template void FollowMovementGenerator<Player>::DoInitialize(Player*);
|
||||||
template void FollowMovementGenerator<Creature>::DoInitialize(Creature*);
|
template void FollowMovementGenerator<Creature>::DoInitialize(Creature*);
|
||||||
template void FollowMovementGenerator<Player>::DoFinalize(Player*);
|
template void FollowMovementGenerator<Player>::DoFinalize(Player*);
|
||||||
|
|||||||
@@ -34,12 +34,20 @@ protected:
|
|||||||
FollowerReference i_target;
|
FollowerReference i_target;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ChaseMovementMode
|
||||||
|
{
|
||||||
|
CHASE_MODE_NORMAL, // chasing target
|
||||||
|
CHASE_MODE_BACKPEDAL, // collision movement
|
||||||
|
CHASE_MODE_DISTANCING, // running away from melee
|
||||||
|
CHASE_MODE_FANNING, // mob collision movement
|
||||||
|
};
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
class ChaseMovementGenerator : public MovementGeneratorMedium<T, ChaseMovementGenerator<T>>, public TargetedMovementGeneratorBase
|
class ChaseMovementGenerator : public MovementGeneratorMedium<T, ChaseMovementGenerator<T>>, public TargetedMovementGeneratorBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ChaseMovementGenerator(Unit* target, Optional<ChaseRange> range = {}, Optional<ChaseAngle> angle = {})
|
ChaseMovementGenerator(Unit* target, Optional<ChaseRange> range = {}, Optional<ChaseAngle> angle = {})
|
||||||
: TargetedMovementGeneratorBase(target), i_leashExtensionTimer(5000), i_path(nullptr), i_recheckDistance(0), i_recalculateTravel(true), _range(range), _angle(angle) {}
|
: TargetedMovementGeneratorBase(target), i_leashExtensionTimer(5000), i_path(nullptr), i_recheckDistance(0), i_recalculateTravel(true), _range(range), _angle(angle), m_currentMode(CHASE_MODE_NORMAL) {}
|
||||||
~ChaseMovementGenerator() { }
|
~ChaseMovementGenerator() { }
|
||||||
|
|
||||||
MovementGeneratorType GetMovementGeneratorType() { return CHASE_MOTION_TYPE; }
|
MovementGeneratorType GetMovementGeneratorType() { return CHASE_MOTION_TYPE; }
|
||||||
@@ -58,6 +66,11 @@ public:
|
|||||||
bool EnableWalking() const { return false; }
|
bool EnableWalking() const { return false; }
|
||||||
bool HasLostTarget(Unit* unit) const { return unit->GetVictim() != this->GetTarget(); }
|
bool HasLostTarget(Unit* unit) const { return unit->GetVictim() != this->GetTarget(); }
|
||||||
|
|
||||||
|
void SetOffsetAndAngle(std::optional<ChaseRange> dist, std::optional<ChaseAngle> angle);
|
||||||
|
void SetNewTarget(Unit* target);
|
||||||
|
|
||||||
|
void DistanceYourself(T* owner, float distance);
|
||||||
|
bool DispatchSplineToPosition(T* owner, float x, float y, float z, bool walk, bool cutPath, float maxTarget, bool forceDest, bool target = false);
|
||||||
private:
|
private:
|
||||||
TimeTrackerSmall i_leashExtensionTimer;
|
TimeTrackerSmall i_leashExtensionTimer;
|
||||||
std::unique_ptr<PathGenerator> i_path;
|
std::unique_ptr<PathGenerator> i_path;
|
||||||
@@ -65,10 +78,12 @@ private:
|
|||||||
bool i_recalculateTravel;
|
bool i_recalculateTravel;
|
||||||
|
|
||||||
Optional<Position> _lastTargetPosition;
|
Optional<Position> _lastTargetPosition;
|
||||||
Optional<ChaseRange> const _range;
|
Optional<ChaseRange> _range;
|
||||||
Optional<ChaseAngle> const _angle;
|
Optional<ChaseAngle> _angle;
|
||||||
bool _movingTowards = true;
|
bool _movingTowards = true;
|
||||||
bool _mutualChase = true;
|
bool _mutualChase = true;
|
||||||
|
|
||||||
|
ChaseMovementMode m_currentMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
|
|||||||
Reference in New Issue
Block a user