TrinityCore
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 "MotionMaster.h"
23#include "ObjectAccessor.h"
24#include "Player.h"
25#include "World.h"
26
27float constexpr MAX_PLAYER_DISTANCE = 100.0f;
28
30{
31 POINT_COMBAT_START = 0xFFFFFF
32};
33
34FollowerAI::FollowerAI(Creature* creature) : ScriptedAI(creature), _updateFollowTimer(2500), _followState(STATE_FOLLOW_NONE), _questForFollow(0) { }
35
37{
39 return;
40
42}
43
44void FollowerAI::JustDied(Unit* /*killer*/)
45{
47 return;
48
50 if (Player* player = GetLeaderForFollower())
51 {
52 if (Group* group = player->GetGroup())
53 {
54 for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next())
55 if (Player* member = groupRef->GetSource())
56 if (member->IsInMap(player))
57 member->FailQuest(_questForFollow);
58 }
59 else
60 player->FailQuest(_questForFollow);
61 }
62}
63
65{
67 return;
68
69 if (Player* player = GetLeaderForFollower())
70 {
72 return;
74 }
75 else
77}
78
80{
82 me->EngageWithTarget(other);
83}
84
86{
88 {
89 if (_updateFollowTimer <= uiDiff)
90 {
92 {
93 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::UpdateAI: is set completed, despawns. ({})", me->GetGUID().ToString());
95 return;
96 }
97
98 bool maxRangeExceeded = true;
99 bool questAbandoned = (_questForFollow != 0);
100 if (Player* player = GetLeaderForFollower())
101 {
102 if (Group* group = player->GetGroup())
103 {
104 for (GroupReference* groupRef = group->GetFirstMember(); groupRef && (maxRangeExceeded || questAbandoned); groupRef = groupRef->next())
105 {
106 Player* member = groupRef->GetSource();
107 if (!member)
108 continue;
109 if (maxRangeExceeded && me->IsWithinDistInMap(member, MAX_PLAYER_DISTANCE))
110 maxRangeExceeded = false;
111 if (questAbandoned)
112 {
114 if ((status == QUEST_STATUS_COMPLETE) || (status == QUEST_STATUS_INCOMPLETE))
115 questAbandoned = false;
116 }
117 }
118 }
119 else
120 {
122 maxRangeExceeded = false;
123 if (questAbandoned)
124 {
125 QuestStatus status = player->GetQuestStatus(_questForFollow);
126 if ((status == QUEST_STATUS_COMPLETE) || (status == QUEST_STATUS_INCOMPLETE))
127 questAbandoned = false;
128 }
129 }
130 }
131
132 if (maxRangeExceeded || questAbandoned)
133 {
134 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::UpdateAI: failed because player/group was to far away or not found ({})", me->GetGUID().ToString());
136 return;
137 }
138
139 _updateFollowTimer = 1000;
140 }
141 else
142 _updateFollowTimer -= uiDiff;
143 }
144
145 UpdateFollowerAI(uiDiff);
146}
147
149{
150 UpdateVictim();
151}
152
153void FollowerAI::StartFollow(Player* player, uint32 factionForFollower, uint32 quest)
154{
155 if (CreatureData const* cdata = me->GetCreatureData())
156 {
157 if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (cdata->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC))
159 }
160
161 if (me->IsEngaged())
162 {
163 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::StartFollow: attempt to StartFollow while in combat. ({})", me->GetGUID().ToString());
164 return;
165 }
166
168 {
169 TC_LOG_ERROR("scripts.ai.followerai", "FollowerAI::StartFollow: attempt to StartFollow while already following. ({})", me->GetGUID().ToString());
170 return;
171 }
172
173 // set variables
174 _leaderGUID = player->GetGUID();
175
176 if (factionForFollower)
177 me->SetFaction(factionForFollower);
178
179 _questForFollow = quest;
180
182 me->PauseMovement();
183
186
188
190
191 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::StartFollow: start follow {} - {} ({})", player->GetName(), _leaderGUID.ToString(), me->GetGUID().ToString());
192}
193
195{
197 return;
198
199 if (paused)
200 {
202
205 }
206 else
207 {
209
210 if (Player* leader = GetLeaderForFollower())
212 }
213}
214
215void FollowerAI::SetFollowComplete(bool withEndEvent)
216{
219
220 if (withEndEvent)
222 else
223 {
226 }
227
229}
230
232{
234 {
235 if (player->IsAlive())
236 return player;
237 else
238 {
239 if (Group* group = player->GetGroup())
240 {
241 for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next())
242 {
243 Player* member = groupRef->GetSource();
244 if (member && me->IsWithinDistInMap(member, MAX_PLAYER_DISTANCE) && member->IsAlive())
245 {
246 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::GetLeaderForFollower: GetLeader changed and returned new leader. ({})", me->GetGUID().ToString());
247 _leaderGUID = member->GetGUID();
248 return member;
249 }
250 }
251 }
252 }
253 }
254
255 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::GetLeaderForFollower: GetLeader can not find suitable leader. ({})", me->GetGUID().ToString());
256 return nullptr;
257}
258
259// This part provides assistance to a player that are attacked by who, even if out of normal aggro range
260// It will cause me to attack who that are attacking _any_ player (which has been confirmed may happen also on offi)
261// The flag (type_flag) is unconfirmed, but used here for further research and is a good candidate.
263{
264 if (!who || !who->GetVictim())
265 return false;
266
267 // experimental (unknown) flag not present
269 return false;
270
271 if (!who->isInAccessiblePlaceFor(me))
272 return false;
273
274 if (!CanAIAttack(who))
275 return false;
276
277 // we cannot attack in evade mode
278 if (me->IsInEvadeMode())
279 return false;
280
281 // or if enemy is in evade mode
282 if (who->GetTypeId() == TYPEID_UNIT && who->ToCreature()->IsInEvadeMode())
283 return false;
284
285 // never attack friendly
286 if (me->IsFriendlyTo(who))
287 return false;
288
289 // too far away and no free sight
291 return false;
292
293 return true;
294}
uint32_t uint32
Definition: Define.h:142
#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:40
#define PET_FOLLOW_ANGLE
Definition: PetDefines.h:98
#define PET_FOLLOW_DIST
Definition: PetDefines.h:97
QuestStatus
Definition: QuestDef.h:141
@ QUEST_STATUS_INCOMPLETE
Definition: QuestDef.h:145
@ QUEST_STATUS_COMPLETE
Definition: QuestDef.h:143
@ 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:58
@ REACT_PASSIVE
Definition: UnitDefines.h:506
@ UNIT_NPC_FLAG_NONE
Definition: UnitDefines.h:296
@ UNIT_NPC_FLAG_2_NONE
Definition: UnitDefines.h:336
@ UNIT_STATE_FOLLOW
Definition: Unit.h:264
virtual void MoveInLineOfSight(Unit *)
Definition: CreatureAI.cpp:122
bool UpdateVictim()
Definition: CreatureAI.cpp:245
Creature *const me
Definition: CreatureAI.h:61
CreatureDifficulty const * GetCreatureDifficulty() const
Definition: Creature.h:252
bool HasReactState(ReactStates state) const
Definition: Creature.h:162
bool IsEngaged() const override
Definition: Creature.cpp:3601
void DespawnOrUnsummon(Milliseconds timeToDespawn=0s, Seconds forceRespawnTime=0s)
Definition: Creature.cpp:2415
CreatureData const * GetCreatureData() const
Definition: Creature.h:251
uint32 GetRespawnDelay() const
Definition: Creature.h:337
void SaveRespawnTime(uint32 forceDelay=0)
Definition: Creature.cpp:2666
bool IsInEvadeMode() const
Definition: Creature.h:203
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)
uint32 _questForFollow
GroupReference * next()
Definition: Group.h:197
void MoveFollow(Unit *target, float dist, ChaseAngle angle, Optional< Milliseconds > duration={}, MovementSlot slot=MOTION_SLOT_ACTIVE)
void Remove(MovementGenerator *movement, MovementSlot slot=MOTION_SLOT_ACTIVE)
std::string ToString() const
Definition: ObjectGuid.cpp:554
static Creature * ToCreature(Object *o)
Definition: Object.h:219
TypeID GetTypeId() const
Definition: Object.h:173
static ObjectGuid GetGUID(Object const *o)
Definition: Object.h:159
QuestStatus GetQuestStatus(uint32 quest_id) const
Definition: Player.cpp:16050
virtual bool CanAIAttack(Unit const *) const
Definition: UnitAI.h:57
Definition: Unit.h:627
void ReplaceAllNpcFlags2(NPCFlags2 flags)
Definition: Unit.h:990
void SetFaction(uint32 faction) override
Definition: Unit.h:859
MotionMaster * GetMotionMaster()
Definition: Unit.h:1652
void PauseMovement(uint32 timer=0, uint8 slot=0, bool forced=true)
Definition: Unit.cpp:10064
bool IsAlive() const
Definition: Unit.h:1164
bool isInAccessiblePlaceFor(Creature const *c) const
Definition: Unit.cpp:3165
void EngageWithTarget(Unit *who)
Definition: Unit.cpp:8077
Unit * GetVictim() const
Definition: Unit.h:715
bool HasUnitState(const uint32 f) const
Definition: Unit.h:732
void ReplaceAllNpcFlags(NPCFlags flags)
Definition: Unit.h:984
std::string const & GetName() const
Definition: Object.h:555
bool IsWithinLOSInMap(WorldObject const *obj, LineOfSightChecks checks=LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags ignoreFlags=VMAP::ModelIgnoreFlags::Nothing) const
Definition: Object.cpp:1181
bool IsWithinDistInMap(WorldObject const *obj, float dist2compare, bool is3D=true, bool incOwnRadius=true, bool incTargetRadius=true) const
Definition: Object.cpp:1147
bool IsFriendlyTo(WorldObject const *target) const
Definition: Object.cpp:2865
#define sWorld
Definition: World.h:931
@ CONFIG_RESPAWN_DYNAMIC_ESCORTNPC
Definition: World.h:195
TC_GAME_API Player * GetPlayer(Map const *, ObjectGuid const &guid)