Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
ScriptedFollowerAI.cpp
Go to the documentation of this file.
1/*
2* This file is part of Project SkyFire https://www.projectskyfire.org.
3* See LICENSE.md file for Copyright information
4*/
5
6/* ScriptData
7SDName: FollowerAI
8SD%Complete: 50
9SDComment: This AI is under development
10SDCategory: Npc
11EndScriptData */
12
13#include "Group.h"
14#include "Player.h"
15#include "ScriptedCreature.h"
16#include "ScriptedFollowerAI.h"
17
18const float MAX_PLAYER_DISTANCE = 100.0f;
19
21{
23};
24
31
33{
34 if (!who)
35 return;
36
37 if (me->Attack(who, true))
38 {
39 me->AddThreat(who, 0.0f);
40 me->SetInCombatWith(who);
41 who->SetInCombatWith(me);
42
43 if (me->HasUnitState(UNIT_STATE_FOLLOW))
44 me->ClearUnitState(UNIT_STATE_FOLLOW);
45
47 me->GetMotionMaster()->MoveChase(who);
48 }
49}
50
51//This part provides assistance to a player that are attacked by who, even if out of normal aggro range
52//It will cause me to attack who that are attacking _any_ player (which has been confirmed may happen also on offi)
53//The flag (type_flag) is unconfirmed, but used here for further research and is a good candidate.
55{
56 if (!who || !who->GetVictim())
57 return false;
58
59 //experimental (unknown) flag not present
60 if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS))
61 return false;
62
63 //not a player
65 return false;
66
67 //never attack friendly
68 if (me->IsFriendlyTo(who))
69 return false;
70
71 //too far away and no free sight?
72 if (me->IsWithinDistInMap(who, MAX_PLAYER_DISTANCE) && me->IsWithinLOSInMap(who))
73 {
74 //already fighting someone?
75 if (!me->GetVictim())
76 {
77 AttackStart(who);
78 return true;
79 }
80 else
81 {
82 who->SetInCombatWith(me);
83 me->AddThreat(who, 0.0f);
84 return true;
85 }
86 }
87
88 return false;
89}
90
92{
93 if (!me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack() && who->isInAccessiblePlaceFor(me))
94 {
96 return;
97
98 if (!me->CanFly() && me->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE)
99 return;
100
101 if (me->IsHostileTo(who))
102 {
103 float fAttackRadius = me->GetAttackDistance(who);
104 if (me->IsWithinDistInMap(who, fAttackRadius) && me->IsWithinLOSInMap(who))
105 {
106 if (!me->GetVictim())
107 {
109 AttackStart(who);
110 }
111 else if (me->GetMap()->IsInstance())
112 {
113 who->SetInCombatWith(me);
114 me->AddThreat(who, 0.0f);
115 }
116 }
117 }
118 }
119}
120
121void FollowerAI::JustDied(Unit* /*killer*/)
122{
124 return;
125
127 if (Player* player = GetLeaderForFollower())
128 {
129 if (Group* group = player->GetGroup())
130 {
131 for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
132 {
133 if (Player* member = groupRef->GetSource())
134 {
135 if (member->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
136 member->FailQuest(m_pQuestForFollow->GetQuestId());
137 }
138 }
139 }
140 else
141 {
142 if (player->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
143 player->FailQuest(m_pQuestForFollow->GetQuestId());
144 }
145 }
146}
147
149{
151
153 SetCombatMovement(true);
154
155 if (me->getFaction() != me->GetCreatureTemplate()->faction_A)
156 me->setFaction(me->GetCreatureTemplate()->faction_A);
157
158 Reset();
159}
160
162{
163 me->RemoveAllAuras();
164 me->DeleteThreatList();
165 me->CombatStop(true);
166 me->SetLootRecipient(NULL);
167
169 {
170 SF_LOG_DEBUG("scripts", "FollowerAI left combat, returning to CombatStartPosition.");
171
172 if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
173 {
174 float fPosX, fPosY, fPosZ;
175 me->GetPosition(fPosX, fPosY, fPosZ);
176 me->GetMotionMaster()->MovePoint(POINT_COMBAT_START, fPosX, fPosY, fPosZ);
177 }
178 }
179 else
180 {
181 if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
182 me->GetMotionMaster()->MoveTargetedHome();
183 }
184
185 Reset();
186}
187
189{
190 if (HasFollowState(STATE_FOLLOW_INPROGRESS) && !me->GetVictim())
191 {
192 if (m_uiUpdateFollowTimer <= uiDiff)
193 {
195 {
196 SF_LOG_DEBUG("scripts", "FollowerAI is set completed, despawns.");
197 me->DespawnOrUnsummon();
198 return;
199 }
200
201 bool bIsMaxRangeExceeded = true;
202
203 if (Player* player = GetLeaderForFollower())
204 {
206 {
207 SF_LOG_DEBUG("scripts", "FollowerAI is returning to leader.");
208
210 me->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
211 return;
212 }
213
214 if (Group* group = player->GetGroup())
215 {
216 for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
217 {
218 Player* member = groupRef->GetSource();
219
220 if (member && me->IsWithinDistInMap(member, MAX_PLAYER_DISTANCE))
221 {
222 bIsMaxRangeExceeded = false;
223 break;
224 }
225 }
226 }
227 else
228 {
229 if (me->IsWithinDistInMap(player, MAX_PLAYER_DISTANCE))
230 bIsMaxRangeExceeded = false;
231 }
232 }
233
234 if (bIsMaxRangeExceeded)
235 {
236 SF_LOG_DEBUG("scripts", "FollowerAI failed because player/group was to far away or not found");
237 me->DespawnOrUnsummon();
238 return;
239 }
240
242 }
243 else
244 m_uiUpdateFollowTimer -= uiDiff;
245 }
246
247 UpdateFollowerAI(uiDiff);
248}
249
251{
252 if (!UpdateVictim())
253 return;
254
256}
257
258void FollowerAI::MovementInform(uint32 motionType, uint32 pointId)
259{
261 return;
262
263 if (pointId == POINT_COMBAT_START)
264 {
266 {
269 }
270 else
271 me->DespawnOrUnsummon();
272 }
273}
274
275void FollowerAI::StartFollow(Player* player, uint32 factionForFollower, const Quest* quest)
276{
277 if (me->GetVictim())
278 {
279 SF_LOG_DEBUG("scripts", "FollowerAI attempt to StartFollow while in combat.");
280 return;
281 }
282
284 {
285 SF_LOG_ERROR("scripts", "FollowerAI attempt to StartFollow while already following.");
286 return;
287 }
288
289 //set variables
290 m_uiLeaderGUID = player->GetGUID();
291
292 if (factionForFollower)
293 me->setFaction(factionForFollower);
294
295 m_pQuestForFollow = quest;
296
297 if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
298 {
299 me->GetMotionMaster()->Clear();
300 me->GetMotionMaster()->MoveIdle();
301 SF_LOG_DEBUG("scripts", "FollowerAI start with WAYPOINT_MOTION_TYPE, set to MoveIdle.");
302 }
303
305
307
308 me->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
309
310 SF_LOG_DEBUG("scripts", "FollowerAI start follow %s (GUID " UI64FMTD ")", player->GetName().c_str(), m_uiLeaderGUID);
311}
312
314{
316 {
317 if (player->IsAlive())
318 return player;
319 else
320 {
321 if (Group* group = player->GetGroup())
322 {
323 for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
324 {
325 Player* member = groupRef->GetSource();
326
327 if (member && member->IsAlive() && me->IsWithinDistInMap(member, MAX_PLAYER_DISTANCE))
328 {
329 SF_LOG_DEBUG("scripts", "FollowerAI GetLeader changed and returned new leader.");
330 m_uiLeaderGUID = member->GetGUID();
331 return member;
332 }
333 }
334 }
335 }
336 }
337
338 SF_LOG_DEBUG("scripts", "FollowerAI GetLeader can not find suitable leader.");
339 return NULL;
340}
341
342void FollowerAI::SetFollowComplete(bool bWithEndEvent)
343{
344 if (me->HasUnitState(UNIT_STATE_FOLLOW))
345 {
346 me->ClearUnitState(UNIT_STATE_FOLLOW);
347
348 me->StopMoving();
349 me->GetMotionMaster()->Clear();
350 me->GetMotionMaster()->MoveIdle();
351 }
352
353 if (bWithEndEvent)
355 else
356 {
359 }
360
362}
363
365{
367 return;
368
369 if (paused)
370 {
372
373 if (me->HasUnitState(UNIT_STATE_FOLLOW))
374 {
375 me->ClearUnitState(UNIT_STATE_FOLLOW);
376
377 me->StopMoving();
378 me->GetMotionMaster()->Clear();
379 me->GetMotionMaster()->MoveIdle();
380 }
381 }
382 else
383 {
385
386 if (Player* leader = GetLeaderForFollower())
387 me->GetMotionMaster()->MoveFollow(leader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
388 }
389}
#define CREATURE_Z_ATTACK_RANGE
Definition Creature.h:422
#define UI64FMTD
Definition Define.h:64
std::uint32_t uint32
Definition Define.h:77
#define SF_LOG_DEBUG(filterType__,...)
Definition Log.h:134
#define SF_LOG_ERROR(filterType__,...)
Definition Log.h:143
@ CHASE_MOTION_TYPE
@ WAYPOINT_MOTION_TYPE
@ POINT_MOTION_TYPE
#define PET_FOLLOW_ANGLE
Definition PetDefines.h:58
#define PET_FOLLOW_DIST
Definition PetDefines.h:57
@ QUEST_STATUS_INCOMPLETE
Definition QuestDef.h:89
@ POINT_COMBAT_START
const float MAX_PLAYER_DISTANCE
@ STATE_FOLLOW_COMPLETE
@ STATE_FOLLOW_POSTEVENT
@ STATE_FOLLOW_NONE
@ STATE_FOLLOW_INPROGRESS
@ STATE_FOLLOW_PAUSED
@ STATE_FOLLOW_RETURNING
@ CREATURE_TYPEFLAGS_AID_PLAYERS
@ SPELL_AURA_MOD_STEALTH
@ UNIT_NPC_FLAG_NONE
Definition Unit.h:686
@ UNIT_STATE_FOLLOW
Definition Unit.h:521
@ UNIT_STATE_STUNNED
Definition Unit.h:515
@ UNIT_FIELD_NPC_FLAGS
bool UpdateVictim()
void SetFollowPaused(bool bPaused)
uint32 m_uiUpdateFollowTimer
void MovementInform(uint32 motionType, uint32 pointId) OVERRIDE
void JustDied(Unit *) OVERRIDE
void JustRespawned() OVERRIDE
const Quest * m_pQuestForFollow
void MoveInLineOfSight(Unit *) OVERRIDE
virtual void UpdateFollowerAI(uint32)
void EnterEvadeMode() OVERRIDE
void UpdateAI(uint32) OVERRIDE
== Triggered Actions Requested ==================
void RemoveFollowState(uint32 uiFollowState)
void AttackStart(Unit *) OVERRIDE
Player * GetLeaderForFollower()
void SetFollowComplete(bool bWithEndEvent=false)
bool AssistPlayerInCombat(Unit *who)
FollowerAI(Creature *creature)
void StartFollow(Player *player, uint32 factionForFollower=0, const Quest *quest=NULL)
bool HasFollowState(uint32 uiFollowState) const
void AddFollowState(uint32 uiFollowState)
Definition Group.h:147
GroupReference * next()
static Player * GetPlayer(WorldObject const &, uint64 guid)
uint64 GetGUID() const
Definition Object.h:119
void DoMeleeAttackIfReady()
Definition UnitAI.cpp:28
Definition Unit.h:1367
bool isTargetableForAttack(bool checkFakeDeath=true) const
Definition Unit.cpp:3795
Player * GetCharmerOrOwnerPlayerOrPlayerItself() const
Definition Unit.cpp:2355
bool IsAlive() const
Definition Unit.h:2032
bool isInAccessiblePlaceFor(Creature const *c) const
Definition Unit.cpp:1473
void RemoveAurasByType(AuraType auraType, uint64 casterGUID=0, Aura *except=NULL, bool negative=true, bool positive=true)
Unit * GetVictim() const
Definition Unit.h:1468
void SetInCombatWith(Unit *enemy)
std::string const & GetName() const
Definition Object.h:664
void Reset() OVERRIDE
void SetCombatMovement(bool allowMovement)
Creature * me
bool IsCombatMovementAllowed() const
ScriptedAI(Creature *creature)