Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
ScriptedEscortAI.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: Npc_EscortAI
8SD%Complete: 100
9SDComment:
10SDCategory: Npc
11EndScriptData */
12
13#include "Group.h"
14#include "Player.h"
15#include "ScriptedCreature.h"
16#include "ScriptedEscortAI.h"
17
19{
20 POINT_LAST_POINT = 0xFFFFFF,
21 POINT_HOME = 0xFFFFFE
22};
23
40
42{
43 if (!who)
44 return;
45
46 if (me->Attack(who, true))
47 {
48 if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE)
49 me->GetMotionMaster()->MovementExpired();
50
52 me->GetMotionMaster()->MoveChase(who);
53 }
54}
55
56//see followerAI
58{
59 if (!who || !who->GetVictim())
60 return false;
61
62 //experimental (unknown) flag not present
63 if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS))
64 return false;
65
66 //not a player
68 return false;
69
70 //never attack friendly
71 if (me->IsFriendlyTo(who))
72 return false;
73
74 //too far away and no free sight?
75 if (me->IsWithinDistInMap(who, GetMaxPlayerDistance()) && me->IsWithinLOSInMap(who))
76 {
77 //already fighting someone?
78 if (!me->GetVictim())
79 {
80 AttackStart(who);
81 return true;
82 }
83 else
84 {
85 who->SetInCombatWith(me);
86 me->AddThreat(who, 0.0f);
87 return true;
88 }
89 }
90
91 return false;
92}
93
95{
96 if (!me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack() && who->isInAccessiblePlaceFor(me))
97 {
99 return;
100
101 if (!me->CanFly() && me->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE)
102 return;
103
104 if (me->IsHostileTo(who))
105 {
106 float fAttackRadius = me->GetAttackDistance(who);
107 if (me->IsWithinDistInMap(who, fAttackRadius) && me->IsWithinLOSInMap(who))
108 {
109 if (!me->GetVictim())
110 {
112 AttackStart(who);
113 }
114 else if (me->GetMap()->IsInstance())
115 {
116 who->SetInCombatWith(me);
117 me->AddThreat(who, 0.0f);
118 }
119 }
120 }
121 }
122}
123
125{
127 return;
128
129 if (Player* player = GetPlayerForEscort())
130 {
131 if (Group* group = player->GetGroup())
132 {
133 for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
134 if (Player* member = groupRef->GetSource())
135 if (member->GetQuestStatus(m_pQuestForEscort->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
136 member->FailQuest(m_pQuestForEscort->GetQuestId());
137 }
138 else
139 {
140 if (player->GetQuestStatus(m_pQuestForEscort->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
141 player->FailQuest(m_pQuestForEscort->GetQuestId());
142 }
143 }
144}
145
147{
149
151 SetCombatMovement(true);
152
153 //add a small delay before going to first waypoint, normal in near all cases
154 m_uiWPWaitTimer = 2500;
155
156 if (me->getFaction() != me->GetCreatureTemplate()->faction_A)
157 me->RestoreFaction();
158
159 Reset();
160}
161
163{
164 float x, y, z, o;
165 me->GetHomePosition(x, y, z, o);
166 me->GetMotionMaster()->MovePoint(POINT_LAST_POINT, x, y, z);
167}
168
170{
171 me->RemoveAllAuras();
172 me->DeleteThreatList();
173 me->CombatStop(true);
174 me->SetLootRecipient(NULL);
175
177 {
180 SF_LOG_DEBUG("scripts", "EscortAI has left combat and is now returning to last point");
181 }
182 else
183 {
184 me->GetMotionMaster()->MoveTargetedHome();
187 Reset();
188 }
189}
190
192{
193 if (Player* player = GetPlayerForEscort())
194 {
195 if (Group* group = player->GetGroup())
196 {
197 for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
198 if (Player* member = groupRef->GetSource())
199 if (me->IsWithinDistInMap(member, GetMaxPlayerDistance()))
200 return true;
201 }
202 else if (me->IsWithinDistInMap(player, GetMaxPlayerDistance()))
203 return true;
204 }
205
206 return false;
207}
208
210{
211 //Waypoint Updating
213 {
214 if (m_uiWPWaitTimer <= diff)
215 {
216 //End of the line
217 if (CurrentWP == WaypointList.end())
218 {
219 if (DespawnAtEnd)
220 {
221 SF_LOG_DEBUG("scripts", "EscortAI reached end of waypoints");
222
224 {
225 float fRetX, fRetY, fRetZ;
226 me->GetRespawnPosition(fRetX, fRetY, fRetZ);
227
228 me->GetMotionMaster()->MovePoint(POINT_HOME, fRetX, fRetY, fRetZ);
229
230 m_uiWPWaitTimer = 0;
231
232 SF_LOG_DEBUG("scripts", "EscortAI are returning home to spawn location: %u, %f, %f, %f", POINT_HOME, fRetX, fRetY, fRetZ);
233 return;
234 }
235
237 {
238 me->setDeathState(DeathState::JUST_DIED);
239 me->Respawn();
240 }
241 else
242 me->DespawnOrUnsummon();
243
244 return;
245 }
246 else
247 {
248 SF_LOG_DEBUG("scripts", "EscortAI reached end of waypoints with Despawn off");
249
250 return;
251 }
252 }
253
255 {
256 me->GetMotionMaster()->MovePoint(CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z);
257 SF_LOG_DEBUG("scripts", "EscortAI start waypoint %u (%f, %f, %f).", CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z);
258
260
261 m_uiWPWaitTimer = 0;
262 }
263 }
264 else
265 m_uiWPWaitTimer -= diff;
266 }
267
268 //Check if player or any member of his group is within range
270 {
271 if (m_uiPlayerCheckTimer <= diff)
272 {
274 {
275 SF_LOG_DEBUG("scripts", "EscortAI failed because player/group was to far away or not found");
276
278 {
279 me->setDeathState(DeathState::JUST_DIED);
280 me->Respawn();
281 }
282 else
283 me->DespawnOrUnsummon();
284
285 return;
286 }
287
289 }
290 else
291 m_uiPlayerCheckTimer -= diff;
292 }
293
294 UpdateEscortAI(diff);
295}
296
298{
299 if (!UpdateVictim())
300 return;
301
303}
304
306{
308 return;
309
310 //Combat start position reached, continue waypoint movement
311 if (pointId == POINT_LAST_POINT)
312 {
313 SF_LOG_DEBUG("scripts", "EscortAI has returned to original position before combat");
314
315 me->SetWalk(!m_bIsRunning);
317
318 if (!m_uiWPWaitTimer)
319 m_uiWPWaitTimer = 1;
320 }
321 else if (pointId == POINT_HOME)
322 {
323 SF_LOG_DEBUG("scripts", "EscortAI has returned to original home location and will continue from beginning of waypoint list.");
324
325 CurrentWP = WaypointList.begin();
326 m_uiWPWaitTimer = 1;
327 }
328 else
329 {
330 //Make sure that we are still on the right waypoint
331 if (CurrentWP->id != pointId)
332 {
333 SF_LOG_ERROR("misc", "TSCR ERROR: EscortAI reached waypoint out of order %u, expected %u, creature entry %u", pointId, CurrentWP->id, me->GetEntry());
334 return;
335 }
336
337 SF_LOG_DEBUG("scripts", "EscortAI Waypoint %u reached", CurrentWP->id);
338
339 //Call WP function
341
342 m_uiWPWaitTimer = CurrentWP->WaitTimeMs + 1;
343
344 ++CurrentWP;
345 }
346}
347
348/*
349void npc_escortAI::OnPossess(bool apply)
350{
351 // We got possessed in the middle of being escorted, store the point
352 // where we left off to come back to when possess is removed
353 if (HasEscortState(STATE_ESCORT_ESCORTING))
354 {
355 if (apply)
356 me->GetPosition(LastPos.x, LastPos.y, LastPos.z);
357 else
358 {
359 Returning = true;
360 me->GetMotionMaster()->MovementExpired();
361 me->GetMotionMaster()->MovePoint(WP_LAST_POINT, LastPos.x, LastPos.y, LastPos.z);
362 }
363 }
364}
365*/
366
367void npc_escortAI::AddWaypoint(uint32 id, float x, float y, float z, uint32 waitTime)
368{
369 Escort_Waypoint t(id, x, y, z, waitTime);
370
371 WaypointList.push_back(t);
372
373 // i think SD2 no longer uses this function
374 ScriptWP = true;
375 /*PointMovement wp;
376 wp.m_uiCreatureEntry = me->GetEntry();
377 wp.m_uiPointId = id;
378 wp.m_fX = x;
379 wp.m_fY = y;
380 wp.m_fZ = z;
381 wp.m_uiWaitTime = WaitTimeMs;
382 PointMovementMap[wp.m_uiCreatureEntry].push_back(wp);*/
383}
384
386{
387 ScriptPointVector const& movePoints = sScriptSystemMgr->GetPointMoveList(me->GetEntry());
388 if (movePoints.empty())
389 return;
390
391 ScriptPointVector::const_iterator itrEnd = movePoints.end();
392 for (ScriptPointVector::const_iterator itr = movePoints.begin(); itr != itrEnd; ++itr)
393 {
394 Escort_Waypoint point(itr->uiPointId, itr->fX, itr->fY, itr->fZ, itr->uiWaitTime);
395 WaypointList.push_back(point);
396 }
397}
398
400{
401 if (on)
402 {
403 if (!m_bIsRunning)
404 me->SetWalk(false);
405 else
406 SF_LOG_DEBUG("scripts", "EscortAI attempt to set run mode, but is already running.");
407 }
408 else
409 {
410 if (m_bIsRunning)
411 me->SetWalk(true);
412 else
413 SF_LOG_DEBUG("scripts", "EscortAI attempt to set walk mode, but is already walking.");
414 }
415
416 m_bIsRunning = on;
417}
418
420void npc_escortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false */, uint64 playerGUID /* = 0 */, Quest const* quest /* = NULL */, bool instantRespawn /* = false */, bool canLoopPath /* = false */, bool resetWaypoints /* = true */)
421{
422 if (me->GetVictim())
423 {
424 SF_LOG_ERROR("misc", "TSCR ERROR: EscortAI (script: %s, creature entry: %u) attempts to Start while in combat", me->GetScriptName().c_str(), me->GetEntry());
425 return;
426 }
427
429 {
430 SF_LOG_ERROR("scripts", "EscortAI (script: %s, creature entry: %u) attempts to Start while already escorting", me->GetScriptName().c_str(), me->GetEntry());
431 return;
432 }
433
434 if (!ScriptWP && resetWaypoints) // sd2 never adds wp in script, but tc does
435 {
436 if (!WaypointList.empty())
437 WaypointList.clear();
439 }
440
441 if (WaypointList.empty())
442 {
443 SF_LOG_ERROR("scripts", "EscortAI (script: %s, creature entry: %u) starts with 0 waypoints (possible missing entry in script_waypoint. Quest: %u).",
444 me->GetScriptName().c_str(), me->GetEntry(), quest ? quest->GetQuestId() : 0);
445 return;
446 }
447
448 //set variables
449 m_bIsActiveAttacker = isActiveAttacker;
450 m_bIsRunning = run;
451
452 m_uiPlayerGUID = playerGUID;
453 m_pQuestForEscort = quest;
454
455 m_bCanInstantRespawn = instantRespawn;
456 m_bCanReturnToStart = canLoopPath;
457
459 SF_LOG_DEBUG("scripts", "EscortAI is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn.");
460
461 if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
462 {
463 me->GetMotionMaster()->MovementExpired();
464 me->GetMotionMaster()->MoveIdle();
465 SF_LOG_DEBUG("scripts", "EscortAI start with WAYPOINT_MOTION_TYPE, changed to MoveIdle.");
466 }
467
468 //disable npcflags
471 {
472 HasImmuneToNPCFlags = true;
474 }
475
476 SF_LOG_DEBUG("scripts", "EscortAI started with " UI64FMTD " waypoints. ActiveAttacker = %d, Run = %d, PlayerGUID = " UI64FMTD "", uint64(WaypointList.size()), m_bIsActiveAttacker, m_bIsRunning, m_uiPlayerGUID);
477
478 CurrentWP = WaypointList.begin();
479
480 //Set initial speed
481 if (m_bIsRunning)
482 me->SetWalk(false);
483 else
484 me->SetWalk(true);
485
487}
488
499
500bool npc_escortAI::SetNextWaypoint(uint32 pointId, float x, float y, float z, float orientation)
501{
502 me->UpdatePosition(x, y, z, orientation);
503 return SetNextWaypoint(pointId, false, true);
504}
505
506bool npc_escortAI::SetNextWaypoint(uint32 pointId, bool setPosition, bool resetWaypointsOnFail)
507{
508 if (!WaypointList.empty())
509 WaypointList.clear();
510
512
513 if (WaypointList.empty())
514 return false;
515
516 size_t const size = WaypointList.size();
517 Escort_Waypoint waypoint(0, 0, 0, 0, 0);
518 do
519 {
520 waypoint = WaypointList.front();
521 WaypointList.pop_front();
522 if (waypoint.id == pointId)
523 {
524 if (setPosition)
525 me->UpdatePosition(waypoint.x, waypoint.y, waypoint.z, me->GetOrientation());
526
527 CurrentWP = WaypointList.begin();
528 return true;
529 }
530 } while (!WaypointList.empty());
531
532 // we failed.
533 // we reset the waypoints in the start; if we pulled any, reset it again
534 if (resetWaypointsOnFail && size != WaypointList.size())
535 {
536 if (!WaypointList.empty())
537 WaypointList.clear();
538
540 }
541
542 return false;
543}
544
545bool npc_escortAI::GetWaypointPosition(uint32 pointId, float& x, float& y, float& z)
546{
547 ScriptPointVector const& waypoints = sScriptSystemMgr->GetPointMoveList(me->GetEntry());
548 if (waypoints.empty())
549 return false;
550
551 for (ScriptPointVector::const_iterator itr = waypoints.begin(); itr != waypoints.end(); ++itr)
552 {
553 if (itr->uiPointId == pointId)
554 {
555 x = itr->fX;
556 y = itr->fY;
557 z = itr->fZ;
558 return true;
559 }
560 }
561
562 return false;
563}
#define CREATURE_Z_ATTACK_RANGE
Definition Creature.h:422
#define UI64FMTD
Definition Define.h:64
std::uint32_t uint32
Definition Define.h:77
std::uint64_t uint64
Definition Define.h:76
#define SF_LOG_DEBUG(filterType__,...)
Definition Log.h:134
#define SF_LOG_ERROR(filterType__,...)
Definition Log.h:143
@ WAYPOINT_MOTION_TYPE
@ POINT_MOTION_TYPE
@ QUEST_STATUS_INCOMPLETE
Definition QuestDef.h:89
#define sScriptSystemMgr
std::vector< ScriptPointMove > ScriptPointVector
@ POINT_HOME
@ POINT_LAST_POINT
#define DEFAULT_MAX_PLAYER_DISTANCE
@ STATE_ESCORT_PAUSED
@ STATE_ESCORT_ESCORTING
@ STATE_ESCORT_NONE
@ STATE_ESCORT_RETURNING
@ CREATURE_TYPEFLAGS_AID_PLAYERS
@ SPELL_AURA_MOD_STEALTH
@ UNIT_NPC_FLAG_NONE
Definition Unit.h:686
@ JUST_DIED
Definition Unit.h:504
@ UNIT_STATE_STUNNED
Definition Unit.h:515
@ UNIT_FLAG_IMMUNE_TO_NPC
Definition Unit.h:634
@ UNIT_FIELD_NPC_FLAGS
@ UNIT_FIELD_FLAGS
static float waypoint[6][3]
Definition boss_alar.cpp:36
bool UpdateVictim()
Definition Group.h:147
GroupReference * next()
uint32 GetQuestId() const
Definition QuestDef.h:270
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 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)
void Reset() OVERRIDE
void SetCombatMovement(bool allowMovement)
Creature * me
bool IsCombatMovementAllowed() const
ScriptedAI(Creature *creature)
void EnterEvadeMode() OVERRIDE
void FillPointMovementListForCreature()
uint32 m_uiPlayerCheckTimer
std::list< Escort_Waypoint > WaypointList
std::list< Escort_Waypoint >::iterator CurrentWP
void AddEscortState(uint32 escortState)
virtual void UpdateEscortAI(uint32 const diff)
void JustDied(Unit *) OVERRIDE
void UpdateAI(uint32 diff) OVERRIDE
== Triggered Actions Requested ==================
virtual void WaypointReached(uint32 pointId)=0
bool IsPlayerOrGroupInRange()
Player * GetPlayerForEscort()
void SetRun(bool on=true)
bool HasEscortState(uint32 escortState) const
npc_escortAI(Creature *creature)
void RemoveEscortState(uint32 escortState)
float GetMaxPlayerDistance() const
void SetEscortPaused(bool on)
void Start(bool isActiveAttacker=true, bool run=false, uint64 playerGUID=0, Quest const *quest=NULL, bool instantRespawn=false, bool canLoopPath=false, bool resetWaypoints=true)
void MovementInform(uint32, uint32) OVERRIDE
bool AssistPlayerInCombat(Unit *who)
bool SetNextWaypoint(uint32 pointId, float x, float y, float z, float orientation)
void MoveInLineOfSight(Unit *who) OVERRIDE
void JustRespawned() OVERRIDE
virtual void WaypointStart(uint32)
void AddWaypoint(uint32 id, float x, float y, float z, uint32 waitTime=0)
Quest const * m_pQuestForEscort
bool GetWaypointPosition(uint32 pointId, float &x, float &y, float &z)
void AttackStart(Unit *who) OVERRIDE