feat(Core/CreatureAI): improve npc position during the combat (#3369)

+ tangent equation to find correct angle and distance when moving

+ implemented proper backward

* Improved performance + random angle margin

* chore: add tollerance calculation in instance

* improved LOS checks with movements

* implemented collisions using raycast (imported by TC)

+ improved collision detection for CanReachPositionAndGetCoords

+ improved collision check

+ set correct flags for the backward movement

+ first implementation of slope angle (to improve)

Co-authored-by: Yehonal <yehonal.azeroth@gmail.com>
This commit is contained in:
Stefano Borzì
2021-01-04 20:23:08 +01:00
committed by GitHub
parent 039e143d48
commit b2761626fe
22 changed files with 1411 additions and 766 deletions
+80 -2
View File
@@ -586,6 +586,36 @@ void Creature::Update(uint32 diff)
RemoveCharmAuras();
}
// Circling the target
if (diff >= m_moveCircleMovementTime)
{
AI()->MoveCircleChecks();
m_moveCircleMovementTime = urand(MOVE_CIRCLE_CHECK_INTERVAL * 2, MOVE_CIRCLE_CHECK_INTERVAL * 3);
}
else
{
m_moveCircleMovementTime -= diff;
}
// If we are closer than 50% of the combat reach we are going to reposition the victim
if (Unit *victim = GetVictim();
victim && GetDistance(victim->GetPosition()) < CalculatePct(GetCombatReach() + victim->GetCombatReach(), 50)) {
if (diff >= m_moveBackwardsMovementTime)
{
AI()->MoveBackwardsChecks();
m_moveBackwardsMovementTime = urand(MOVE_BACKWARDS_CHECK_INTERVAL/2, MOVE_BACKWARDS_CHECK_INTERVAL * 2);
}
else
{
m_moveBackwardsMovementTime -= diff;
}
}
else
{
m_moveBackwardsMovementTime = MOVE_BACKWARDS_CHECK_INTERVAL;
}
if (!IsInEvadeMode() && IsAIEnabled)
{
// do not allow the AI to be changed during update
@@ -620,7 +650,7 @@ void Creature::Update(uint32 diff)
{
AI()->EnterEvadeMode();
}
}
}
break;
}
@@ -655,6 +685,20 @@ void Creature::Update(uint32 diff)
}
}
bool Creature::IsFreeToMove()
{
uint32 moveFlags = m_movementInfo.GetMovementFlags();
// Do not reposition ourself when we are not allowed to move
if ((IsMovementPreventedByCasting() || isMoving() || !CanFreeMove()) &&
(GetMotionMaster()->GetCurrentMovementGeneratorType() != CHASE_MOTION_TYPE ||
moveFlags & MOVEMENTFLAG_SPLINE_ENABLED))
{
return false;
}
return true;
}
void Creature::Regenerate(Powers power)
{
uint32 curValue = GetPower(power);
@@ -1522,8 +1566,10 @@ bool Creature::CanStartAttack(Unit const* who) const
// This set of checks is should be done only for creatures
if ((HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC) && who->GetTypeId() != TYPEID_PLAYER) || // flag is valid only for non player characters
(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) && who->GetTypeId() == TYPEID_PLAYER)) // immune to PC and target is a player, return false
(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) && who->GetTypeId() == TYPEID_PLAYER)) // immune to PC and target is a player, return false
{
return false;
}
if (Unit* owner = who->GetOwner())
if (owner->GetTypeId() == TYPEID_PLAYER && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC)) // immune to PC and target has player owner
@@ -2833,6 +2879,16 @@ void Creature::FocusTarget(Spell const* focusSpell, WorldObject const* target)
SetInFront(target);
}
bool Creature::HasSpellFocus(Spell const* focusSpell) const
{
if (isDead()) // dead creatures cannot focus
{
return false;
}
return focusSpell ? (focusSpell == _spellFocusInfo.Spell) : (_spellFocusInfo.Spell || _spellFocusInfo.Delay);
}
void Creature::ReleaseFocus(Spell const* focusSpell)
{
// focused to something else
@@ -2890,3 +2946,25 @@ float Creature::GetAttackDistance(Unit const* player) const
return (retDistance * aggroRate);
}
bool Creature::IsMovementPreventedByCasting() const
{
Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL];
// first check if currently a movement allowed channel is active and we're not casting
if (!!spell && spell->getState() != SPELL_STATE_FINISHED && spell->IsChannelActive() && spell->GetSpellInfo()->IsMoveAllowedChannel())
{
return false;
}
if (HasSpellFocus())
{
return true;
}
if (HasUnitState(UNIT_STATE_CASTING))
{
return true;
}
return false;
}
+18 -2
View File
@@ -385,7 +385,7 @@ typedef std::list<VendorItemCount> VendorItemCounts;
struct TrainerSpell
{
TrainerSpell()
TrainerSpell()
{
for (unsigned int & i : learnedSpell)
i = 0;
@@ -510,6 +510,15 @@ public:
bool SetWaterWalking(bool enable, bool packetOnly = false) override;
bool SetFeatherFall(bool enable, bool packetOnly = false) override;
bool SetHover(bool enable, bool packetOnly = false) override;
bool HasSpellFocus(Spell const* focusSpell = nullptr) const;
struct
{
::Spell const* Spell = nullptr;
uint32 Delay = 0; // ms until the creature's target should snap back (0 = no snapback scheduled)
uint64 Target; // the creature's "real" target while casting
float Orientation = 0.0f; // the creature's "real" orientation while casting
} _spellFocusInfo;
[[nodiscard]] uint32 GetShieldBlockValue() const override
{
@@ -714,11 +723,18 @@ public:
void SetTarget(uint64 guid) override;
void FocusTarget(Spell const* focusSpell, WorldObject const* target);
void ReleaseFocus(Spell const* focusSpell);
bool IsMovementPreventedByCasting() const;
// Part of Evade mechanics
[[nodiscard]] time_t GetLastDamagedTime() const { return _lastDamagedTime; }
void SetLastDamagedTime(time_t val) { _lastDamagedTime = val; }
bool IsFreeToMove();
static constexpr uint32 MOVE_CIRCLE_CHECK_INTERVAL = 1500;
static constexpr uint32 MOVE_BACKWARDS_CHECK_INTERVAL = 2000;
uint32 m_moveCircleMovementTime = MOVE_CIRCLE_CHECK_INTERVAL;
uint32 m_moveBackwardsMovementTime = MOVE_BACKWARDS_CHECK_INTERVAL;
protected:
bool CreateFromProto(uint32 guidlow, uint32 Entry, uint32 vehId, const CreatureData* data = nullptr);
bool InitEntry(uint32 entry, const CreatureData* data = nullptr);
@@ -787,7 +803,7 @@ private:
bool m_cannotReachTarget;
uint32 m_cannotReachTimer;
Spell const* _focusSpell; ///> Locks the target during spell cast for proper facing
};