TrinityCore
Loading...
Searching...
No Matches
ScriptedFollowerAI.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "ScriptedFollowerAI.h"
19#include "Creature.h"
20#include "Group.h"
21#include "Log.h"
22#include "Map.h"
23#include "MotionMaster.h"
24#include "ObjectAccessor.h"
25#include "Player.h"
26#include "World.h"
27
28float constexpr MAX_PLAYER_DISTANCE = 100.0f;
29
31{
32 POINT_COMBAT_START = 0xFFFFFF
33};
34
35FollowerAI::FollowerAI(Creature* creature) : ScriptedAI(creature), _updateFollowTimer(2500), _followState(STATE_FOLLOW_NONE), _questForFollow(0) { }
36
44
45void FollowerAI::JustDied(Unit* /*killer*/)
46{
48 return;
49
51 if (Player* player = GetLeaderForFollower())
52 {
53 if (Group* group = player->GetGroup())
54 {
55 for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next())
56 if (Player* member = groupRef->GetSource())
57 if (member->IsInMap(player))
58 member->FailQuest(_questForFollow);
59 }
60 else
61 player->FailQuest(_questForFollow);
62 }
63}
64
66{
68 return;
69
70 if (Player* player = GetLeaderForFollower())
71 {
73 return;
75 }
76 else
78}
79
85
87{
89 {
90 if (_updateFollowTimer <= uiDiff)
91 {
93 {
94 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::UpdateAI: is set completed, despawns. ({})", me->GetGUID().ToString());
96 return;
97 }
98
99 bool maxRangeExceeded = true;
100 bool questAbandoned = (_questForFollow != 0);
101 if (Player* player = GetLeaderForFollower())
102 {
103 if (Group* group = player->GetGroup())
104 {
105 for (GroupReference* groupRef = group->GetFirstMember(); groupRef && (maxRangeExceeded || questAbandoned); groupRef = groupRef->next())
106 {
107 Player* member = groupRef->GetSource();
108 if (!member)
109 continue;
110 if (maxRangeExceeded && me->IsWithinDistInMap(member, MAX_PLAYER_DISTANCE))
111 maxRangeExceeded = false;
112 if (questAbandoned)
113 {
115 if ((status == QUEST_STATUS_COMPLETE) || (status == QUEST_STATUS_INCOMPLETE))
116 questAbandoned = false;
117 }
118 }
119 }
120 else
121 {
123 maxRangeExceeded = false;
124 if (questAbandoned)
125 {
126 QuestStatus status = player->GetQuestStatus(_questForFollow);
127 if ((status == QUEST_STATUS_COMPLETE) || (status == QUEST_STATUS_INCOMPLETE))
128 questAbandoned = false;
129 }
130 }
131 }
132
133 if (maxRangeExceeded || questAbandoned)
134 {
135 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::UpdateAI: failed because player/group was to far away or not found ({})", me->GetGUID().ToString());
137 return;
138 }
139
140 _updateFollowTimer = 1000;
141 }
142 else
143 _updateFollowTimer -= uiDiff;
144 }
145
146 UpdateFollowerAI(uiDiff);
147}
148
150{
151 if (!UpdateVictim())
152 return;
153
155}
156
157void FollowerAI::StartFollow(Player* player, uint32 factionForFollower, uint32 quest)
158{
159 if (CreatureData const* cdata = me->GetCreatureData())
160 {
161 if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (cdata->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC))
163 }
164
165 if (me->IsEngaged())
166 {
167 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::StartFollow: attempt to StartFollow while in combat. ({})", me->GetGUID().ToString());
168 return;
169 }
170
172 {
173 TC_LOG_ERROR("scripts.ai.followerai", "FollowerAI::StartFollow: attempt to StartFollow while already following. ({})", me->GetGUID().ToString());
174 return;
175 }
176
177 // set variables
178 _leaderGUID = player->GetGUID();
179
180 if (factionForFollower)
181 me->SetFaction(factionForFollower);
182
183 _questForFollow = quest;
184
186 me->PauseMovement();
187
189
191
193
194 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::StartFollow: start follow {} - {} ({})", player->GetName(), _leaderGUID.ToString(), me->GetGUID().ToString());
195}
196
217
233
235{
237 {
238 if (player->IsAlive())
239 return player;
240 else
241 {
242 if (Group* group = player->GetGroup())
243 {
244 for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next())
245 {
246 Player* member = groupRef->GetSource();
247 if (member && me->IsWithinDistInMap(member, MAX_PLAYER_DISTANCE) && member->IsAlive())
248 {
249 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::GetLeaderForFollower: GetLeader changed and returned new leader. ({})", me->GetGUID().ToString());
250 _leaderGUID = member->GetGUID();
251 return member;
252 }
253 }
254 }
255 }
256 }
257
258 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::GetLeaderForFollower: GetLeader can not find suitable leader. ({})", me->GetGUID().ToString());
259 return nullptr;
260}
261
262// This part provides assistance to a player that are attacked by who, even if out of normal aggro range
263// It will cause me to attack who that are attacking _any_ player (which has been confirmed may happen also on offi)
264// The flag (type_flag) is unconfirmed, but used here for further research and is a good candidate.
266{
267 if (!who || !who->GetVictim())
268 return false;
269
270 // experimental (unknown) flag not present
272 return false;
273
274 if (!who->isInAccessiblePlaceFor(me))
275 return false;
276
277 if (!CanAIAttack(who))
278 return false;
279
280 // we cannot attack in evade mode
281 if (me->IsInEvadeMode())
282 return false;
283
284 // or if enemy is in evade mode
285 if (who->GetTypeId() == TYPEID_UNIT && who->ToCreature()->IsInEvadeMode())
286 return false;
287
288 // never attack friendly
289 if (me->IsFriendlyTo(who))
290 return false;
291
292 // too far away and no free sight
294 return false;
295
296 return true;
297}
uint32_t uint32
Definition Define.h:133
#define TC_LOG_DEBUG(filterType__,...)
Definition Log.h:156
#define TC_LOG_ERROR(filterType__,...)
Definition Log.h:165
@ MOTION_PRIORITY_NORMAL
@ FOLLOW_MOTION_TYPE
@ TYPEID_UNIT
Definition ObjectGuid.h:38
#define PET_FOLLOW_ANGLE
Definition PetDefines.h:86
#define PET_FOLLOW_DIST
Definition PetDefines.h:85
QuestStatus
Definition QuestDef.h:103
@ QUEST_STATUS_INCOMPLETE
Definition QuestDef.h:107
@ QUEST_STATUS_COMPLETE
Definition QuestDef.h:105
@ POINT_COMBAT_START
float constexpr MAX_PLAYER_DISTANCE
@ STATE_FOLLOW_COMPLETE
@ STATE_FOLLOW_POSTEVENT
@ STATE_FOLLOW_NONE
@ STATE_FOLLOW_INPROGRESS
@ STATE_FOLLOW_PAUSED
@ CREATURE_TYPE_FLAG_CAN_ASSIST
@ SPAWNGROUP_FLAG_ESCORTQUESTNPC
Definition SpawnData.h:53
@ REACT_PASSIVE
@ UNIT_NPC_FLAG_NONE
@ UNIT_STATE_FOLLOW
Definition Unit.h:229
virtual void MoveInLineOfSight(Unit *)
bool UpdateVictim()
Creature *const me
Definition CreatureAI.h:82
bool HasReactState(ReactStates state) const
Definition Creature.h:121
bool IsEngaged() const override
void DespawnOrUnsummon(Milliseconds timeToDespawn=0s, Seconds forceRespawnTime=0s)
CreatureData const * GetCreatureData() const
Definition Creature.h:187
CreatureTemplate const * GetCreatureTemplate() const
Definition Creature.h:186
uint32 GetRespawnDelay() const
Definition Creature.h:261
void SaveRespawnTime(uint32 forceDelay=0)
bool IsInEvadeMode() const
Definition Creature.h:146
void StartFollow(Player *player, uint32 factionForFollower=0, uint32 quest=0)
bool ShouldAssistPlayerInCombatAgainst(Unit *who) const
void JustReachedHome() override
virtual void UpdateFollowerAI(uint32)
void JustDied(Unit *) override
void MoveInLineOfSight(Unit *) override
void AddFollowState(uint32 followState)
Player * GetLeaderForFollower()
void SetFollowComplete(bool withEndEvent=false)
ObjectGuid _leaderGUID
FollowerAI(Creature *creature)
void RemoveFollowState(uint32 followState)
void UpdateAI(uint32) override
uint32 _updateFollowTimer
bool HasFollowState(uint32 uiFollowState) const
void OwnerAttackedBy(Unit *other) override
void SetFollowPaused(bool paused)
GroupReference * next()
Definition Group.h:165
void MoveFollow(Unit *target, float dist, ChaseAngle angle, MovementSlot slot=MOTION_SLOT_ACTIVE)
void Remove(MovementGenerator *movement, MovementSlot slot=MOTION_SLOT_ACTIVE)
std::string ToString() const
static Creature * ToCreature(Object *o)
Definition Object.h:186
TypeID GetTypeId() const
Definition Object.h:93
static ObjectGuid GetGUID(Object const *o)
Definition Object.h:78
QuestStatus GetQuestStatus(uint32 quest_id) const
Definition Player.cpp:15642
void DoMeleeAttackIfReady()
Definition UnitAI.cpp:54
virtual bool CanAIAttack(Unit const *) const
Definition UnitAI.h:139
Definition Unit.h:769
void SetFaction(uint32 faction) override
Definition Unit.h:974
MotionMaster * GetMotionMaster()
Definition Unit.h:1667
void PauseMovement(uint32 timer=0, uint8 slot=0, bool forced=true)
Definition Unit.cpp:10327
bool IsAlive() const
Definition Unit.h:1234
bool isInAccessiblePlaceFor(Creature const *c) const
Definition Unit.cpp:3154
void EngageWithTarget(Unit *who)
Definition Unit.cpp:8292
Unit * GetVictim() const
Definition Unit.h:859
bool HasUnitState(const uint32 f) const
Definition Unit.h:876
void ReplaceAllNpcFlags(NPCFlags flags)
Definition Unit.h:1099
std::string const & GetName() const
Definition Object.h:382
bool IsWithinLOSInMap(WorldObject const *obj, LineOfSightChecks checks=LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags ignoreFlags=VMAP::ModelIgnoreFlags::Nothing) const
Definition Object.cpp:1226
bool IsWithinDistInMap(WorldObject const *obj, float dist2compare, bool is3D=true, bool incOwnRadius=true, bool incTargetRadius=true) const
Definition Object.cpp:1192
bool IsFriendlyTo(WorldObject const *target) const
Definition Object.cpp:2801
#define sWorld
Definition World.h:900
@ CONFIG_RESPAWN_DYNAMIC_ESCORTNPC
Definition World.h:178
TC_GAME_API Player * GetPlayer(Map const *, ObjectGuid const &guid)