refactor(Core/Movement): follower management of references (#25060)

Co-authored-by: Shauren <shauren.trinity@gmail.com>
This commit is contained in:
sogladev
2026-03-12 10:59:16 +01:00
committed by GitHub
parent a8a96eb8f8
commit 40f55bb0f3
9 changed files with 28 additions and 135 deletions

View File

@@ -16,6 +16,7 @@
*/
#include "Unit.h"
#include "AbstractFollower.h"
#include "AreaDefines.h"
#include "ArenaSpectator.h"
#include "Battlefield.h"
@@ -44,7 +45,6 @@
#include "MoveSpline.h"
#include "MoveSplineInit.h"
#include "MovementGenerator.h"
#include "AbstractFollower.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "OutdoorPvP.h"
@@ -5530,12 +5530,8 @@ void Unit::RemoveAreaAurasDueToLeaveWorld()
void Unit::RemoveAllFollowers()
{
while (auto* ref = m_FollowingRefMgr.getFirst())
{
auto* source = ref->GetSource();
ref->delink();
source->SetTarget(nullptr);
}
while (!m_followingMe.empty())
(*m_followingMe.begin())->SetTarget(nullptr);
}
void Unit::RemoveAllAuras()

View File

@@ -20,8 +20,6 @@
#include "EnumFlag.h"
#include "EventProcessor.h"
#include "FollowerRefMgr.h"
#include "FollowerReference.h"
#include "HostileRefMgr.h"
#include "ItemTemplate.h"
#include "MotionMaster.h"
@@ -74,6 +72,7 @@ class MotionTransport;
class Vehicle;
class TransportBase;
class SpellCastTargets;
class AbstractFollower;
typedef std::list<Unit*> UnitList;
typedef std::list< std::pair<Aura*, uint8> > DispelChargesList;
@@ -1881,9 +1880,9 @@ public:
[[nodiscard]] bool IsInDisallowedMountForm() const;
// Followers
void AddFollower(FollowerReference* ref) { m_FollowingRefMgr.insertFirst(ref); }
void FollowerAdded(AbstractFollower* f) { m_followingMe.insert(f); }
void FollowerRemoved(AbstractFollower* f) { m_followingMe.erase(f); }
[[nodiscard]] virtual float GetFollowAngle() const { return static_cast<float>(M_PI / 2); }
void RemoveFollower(FollowerReference* /*ref*/ ) { /* nothing to do yet */ }
void RemoveAllFollowers();
// Pets, guardians, minions...
@@ -2229,7 +2228,7 @@ private:
// Manage all Units that are threatened by us
HostileRefMgr m_HostileRefMgr;
FollowerRefMgr m_FollowingRefMgr;
std::unordered_set<AbstractFollower*> m_followingMe;
Unit* m_comboTarget;
int8 m_comboPoints;

View File

@@ -20,9 +20,13 @@
void AbstractFollower::SetTarget(Unit* unit)
{
if (unit == GetTarget())
if (unit == _target)
return;
i_target.link(unit, this);
_target = i_target.getTarget();
if (_target)
_target->FollowerRemoved(this);
_target = unit;
if (_target)
_target->FollowerAdded(this);
}

View File

@@ -18,8 +18,6 @@
#ifndef ACORE_ABSTRACTFOLLOWER_H
#define ACORE_ABSTRACTFOLLOWER_H
#include "FollowerReference.h"
class Unit;
class AbstractFollower
@@ -29,15 +27,10 @@ public:
virtual ~AbstractFollower() { SetTarget(nullptr); }
void SetTarget(Unit* unit);
Unit* GetTarget() const { return _target; }
bool IsTargetValid() const { return i_target.isValid(); }
virtual void stopFollowing() { }
[[nodiscard]] Unit* GetTarget() const { return _target; }
private:
Unit* _target = nullptr;
protected:
FollowerReference i_target;
};
#endif

View File

@@ -1,29 +0,0 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _FOLLOWERREFMANAGER
#define _FOLLOWERREFMANAGER
#include "RefMgr.h"
class Unit;
class AbstractFollower;
class FollowerRefMgr : public RefMgr<Unit, AbstractFollower>
{
};
#endif

View File

@@ -1,35 +0,0 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "FollowerReference.h"
#include "AbstractFollower.h"
#include "Unit.h"
void FollowerReference::targetObjectBuildLink()
{
getTarget()->AddFollower(this);
}
void FollowerReference::targetObjectDestroyLink()
{
getTarget()->RemoveFollower(this);
}
void FollowerReference::sourceObjectDestroyLink()
{
GetSource()->stopFollowing();
}

View File

@@ -1,33 +0,0 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _FOLLOWERREFERENCE_H
#define _FOLLOWERREFERENCE_H
#include "Reference.h"
class AbstractFollower;
class Unit;
class FollowerReference : public Reference<Unit, AbstractFollower>
{
protected:
void targetObjectBuildLink() override;
void targetObjectDestroyLink() override;
void sourceObjectDestroyLink() override;
};
#endif

View File

@@ -85,7 +85,7 @@ void ChaseMovementGenerator<T>::DistanceYourself(T* owner, float distance)
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));
GetTarget()->GetNearPoint(owner, x, y, z, owner->GetBoundaryRadius(), distance, GetTarget()->GetAngle(owner));
if (DispatchSplineToPosition(owner, x, y, z, false, false, 0.f, false, false))
{
m_currentMode = CHASE_MODE_DISTANCING;
@@ -110,7 +110,7 @@ bool ChaseMovementGenerator<T>::DispatchSplineToPosition(T* owner, float x, floa
// For pets, treat incomplete paths as failures to avoid clipping through geometry
// Players and Player-controlled units have more erratic movement, skip failure
if (cOwner && (cOwner->IsPet() || cOwner->IsControlledByPlayer()) && !i_target.getTarget()->IsCharmedOwnedByPlayerOrPlayer())
if (cOwner && (cOwner->IsPet() || cOwner->IsControlledByPlayer()) && !GetTarget()->IsCharmedOwnedByPlayerOrPlayer())
if (pathType & PATHFIND_INCOMPLETE)
pathFailed = true;
@@ -118,7 +118,7 @@ bool ChaseMovementGenerator<T>::DispatchSplineToPosition(T* owner, float x, floa
{
if (cOwner)
{
cOwner->SetCannotReachTarget(i_target.getTarget()->GetGUID());
cOwner->SetCannotReachTarget(GetTarget()->GetGUID());
if (cOwner->IsPet() || cOwner->IsControlledByPlayer())
cOwner->AttackStop();
@@ -142,7 +142,7 @@ bool ChaseMovementGenerator<T>::DispatchSplineToPosition(T* owner, float x, floa
Movement::MoveSplineInit init(owner);
init.MovebyPath(i_path->GetPath());
if (target)
init.SetFacing(i_target.getTarget());
init.SetFacing(GetTarget());
init.SetWalk(walk);
init.Launch();
@@ -152,7 +152,7 @@ bool ChaseMovementGenerator<T>::DispatchSplineToPosition(T* owner, float x, floa
template<class T>
bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
{
if (!i_target.isValid() || !i_target->IsInWorld() || !owner->IsInMap(i_target.getTarget()))
if (!GetTarget() || !GetTarget()->IsInWorld() || !owner->IsInMap(GetTarget()))
return false;
if (!owner || !owner->IsAlive())
@@ -194,11 +194,11 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
bool forceDest =
//(cOwner && (cOwner->isWorldBoss() || cOwner->IsDungeonBoss())) || // force for all bosses, even not in instances
(i_target->IsPlayer() && i_target->ToPlayer()->IsGameMaster()) || // for .npc follow
(GetTarget()->IsPlayer() && GetTarget()->ToPlayer()->IsGameMaster()) || // for .npc follow
(owner->CanFly())
; // closes "bool forceDest", that way it is more appropriate, so we can comment out crap whenever we need to
Unit* target = i_target.getTarget();
Unit* target = GetTarget();
bool mutualChase = IsMutualChase(owner, target);
bool const mutualTarget = target->GetVictim() == owner;
@@ -414,7 +414,7 @@ void ChaseMovementGenerator<T>::MovementInform(T* owner)
{
// 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());
AI->MovementInform(CHASE_MOTION_TYPE, GetTarget()->GetGUID().GetCounter());
break;
}
case CHASE_MODE_DISTANCING:
@@ -552,14 +552,14 @@ bool FollowMovementGenerator<T>::PositionOkay(Unit* target, bool isPlayerPet, bo
template<class T>
bool FollowMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
{
if (!i_target.isValid() || !i_target->IsInWorld() || !owner->IsInMap(i_target.getTarget()))
if (!GetTarget() || !GetTarget()->IsInWorld() || !owner->IsInMap(GetTarget()))
return false;
if (!owner || !owner->IsAlive())
return false;
Creature* cOwner = owner->ToCreature();
Unit* target = i_target.getTarget();
Unit* target = GetTarget();
// the owner might be unable to move (rooted or casting), or we have lost the target, pause movement
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || (cOwner && owner->ToCreature()->IsMovementPreventedByCasting()))
@@ -580,7 +580,7 @@ bool FollowMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
bool forceDest =
(followingMaster) || // allow pets following their master to cheat while generating paths
(i_target->IsPlayer() && i_target->ToPlayer()->IsGameMaster()) // for .npc follow
(GetTarget()->IsPlayer() && GetTarget()->ToPlayer()->IsGameMaster()) // for .npc follow
; // closes "bool forceDest", that way it is more appropriate, so we can comment out crap whenever we need to
bool targetIsMoving = false;
@@ -725,7 +725,7 @@ void FollowMovementGenerator<T>::MovementInform(T* owner)
// 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(FOLLOW_MOTION_TYPE, i_target.getTarget()->GetGUID().GetCounter());
AI->MovementInform(FOLLOW_MOTION_TYPE, GetTarget()->GetGUID().GetCounter());
}
//-----------------------------------------------//

View File

@@ -25,8 +25,6 @@
#include "Timer.h"
#include "Unit.h"
using TargetedMovementGeneratorBase = AbstractFollower;
enum ChaseMovementMode
{
CHASE_MODE_NORMAL, // chasing target