TrinityCore
ScriptedEscortAI.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 "ScriptedEscortAI.h"
19#include "Creature.h"
20#include "Group.h"
21#include "Log.h"
22#include "Map.h"
23#include "MotionMaster.h"
24#include "MovementGenerator.h"
25#include "ObjectAccessor.h"
26#include "Player.h"
27#include "ScriptSystem.h"
28#include "WaypointManager.h"
29#include "World.h"
30
32{
33 POINT_LAST_POINT = 0xFFFFFF,
34 POINT_HOME = 0xFFFFFE
35};
36
37EscortAI::EscortAI(Creature* creature) : ScriptedAI(creature), _pauseTimer(2500ms), _playerCheckTimer(1000), _escortState(STATE_ESCORT_NONE), _maxPlayerDistance(DEFAULT_MAX_PLAYER_DISTANCE),
38 _escortQuest(nullptr), _activeAttacker(true), _instantRespawn(false), _returnToStart(false), _despawnAtEnd(true), _despawnAtFar(true),
39 _hasImmuneToNPCFlags(false), _started(false), _ended(false), _resume(false)
40{
41}
42
44{
45 if (!who)
46 return;
47
49 return;
50
52}
53
54void EscortAI::JustDied(Unit* /*killer*/)
55{
57 return;
58
59 if (Player* player = GetPlayerForEscort())
60 {
61 if (Group* group = player->GetGroup())
62 {
63 for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next())
64 if (Player* member = groupRef->GetSource())
65 if (member->IsInMap(player))
66 member->FailQuest(_escortQuest->GetQuestId());
67 }
68 else
69 player->FailQuest(_escortQuest->GetQuestId());
70 }
71}
72
74{
76
79
80 // add a small delay before going to first waypoint, normal in near all cases
81 _pauseTimer = 2s;
82
85
86 Reset();
87}
88
90{
92}
93
95{
97 me->CombatStop(true);
99 me->SetTappedBy(nullptr);
100
102
104 {
107 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::EnterEvadeMode: left combat and is now returning to last point ({})", me->GetGUID().ToString());
108 }
109 else
110 {
113 me->SetImmuneToNPC(true);
114 Reset();
115 }
116}
117
119{
120 // no action allowed if there is no escort
122 return;
123
124 if (type == POINT_MOTION_TYPE)
125 {
126 if (_pauseTimer == 0s)
127 _pauseTimer = 2s;
128
129 // continue waypoint movement
130 if (id == POINT_LAST_POINT)
131 {
132 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::MovementInform: returned to before combat position ({})", me->GetGUID().ToString());
133 me->SetWalk(false);
135 }
136 else if (id == POINT_HOME)
137 {
138 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::MovementInform: returned to home location and restarting waypoint path ({})", me->GetGUID().ToString());
139 _started = false;
140 }
141 }
142 else if (type == WAYPOINT_MOTION_TYPE)
143 {
144 ASSERT(id < _path.Nodes.size(), "EscortAI::MovementInform: referenced movement id (%u) points to non-existing node in loaded path (%s)", id, me->GetGUID().ToString().c_str());
146
147 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::MovementInform: waypoint node {} reached ({})", waypoint.Id, me->GetGUID().ToString());
148
149 // last point
150 if (id == _path.Nodes.size() - 1)
151 {
152 _started = false;
153 _ended = true;
154 _pauseTimer = 1s;
155 }
156 }
157}
158
160{
161 // Waypoint Updating
163 {
164 if (_pauseTimer.count() <= diff)
165 {
167 {
168 _pauseTimer = 0s;
169
170 if (_ended)
171 {
172 _ended = false;
174
175 if (_despawnAtEnd)
176 {
177 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::UpdateAI: reached end of waypoints, despawning at end ({})", me->GetGUID().ToString());
178 if (_returnToStart)
179 {
180 Position respawnPosition;
181 float orientation = 0.f;
182 me->GetRespawnPosition(respawnPosition.m_positionX, respawnPosition.m_positionY, respawnPosition.m_positionZ, &orientation);
183 respawnPosition.SetOrientation(orientation);
184 me->GetMotionMaster()->MovePoint(POINT_HOME, respawnPosition);
185 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::UpdateAI: returning to spawn location: {} ({})", respawnPosition.ToString(), me->GetGUID().ToString());
186 }
187 else if (_instantRespawn)
188 me->Respawn(true);
189 else
191 }
192 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::UpdateAI: reached end of waypoints ({})", me->GetGUID().ToString());
194 return;
195 }
196
197 if (!_started)
198 {
199 _started = true;
200 me->GetMotionMaster()->MovePath(_path, false);
201 }
202 else if (_resume)
203 {
204 _resume = false;
206 movementGenerator->Resume(0);
207 }
208 }
209 }
210 else
211 _pauseTimer -= Milliseconds(diff);
212 }
213
214 // Check if player or any member of his group is within range
216 {
217 if (_playerCheckTimer <= diff)
218 {
220 {
221 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::UpdateAI: failed because player/group was to far away or not found ({})", me->GetGUID().ToString());
222
223 bool isEscort = false;
225 isEscort = (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (creatureData->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC));
226
227 if (_instantRespawn)
228 {
229 if (!isEscort)
230 me->DespawnOrUnsummon(0s, 1s);
231 else
233 }
234 else
236
237 return;
238 }
239
240 _playerCheckTimer = 1000;
241 }
242 else
243 _playerCheckTimer -= diff;
244 }
245
246 UpdateEscortAI(diff);
247}
248
250{
251 UpdateVictim();
252}
253
254void EscortAI::AddWaypoint(uint32 id, float x, float y, float z, bool run)
255{
256 AddWaypoint(id, x, y, z, 0.0f, 0s, run);
257}
258
259void EscortAI::AddWaypoint(uint32 id, float x, float y, float z, float orientation/* = 0*/, Milliseconds waitTime/* = 0s*/, bool run /*= false*/)
260{
263
264 WaypointNode waypoint(id, x, y, z, orientation, waitTime.count());
266 _path.Nodes.push_back(std::move(waypoint));
267}
268
270{
271 _path.Nodes.clear();
272}
273
275{
276 WaypointPath const* path = sWaypointMgr->GetPath(pathId);
277 if (!path)
278 {
279 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::LoadPath: (script: {}) path {} is invalid ({})", me->GetScriptName(), pathId, me->GetGUID().ToString());
280 return;
281 }
282 _path = *path;
283}
284
286void EscortAI::Start(bool isActiveAttacker /* = true*/, ObjectGuid playerGUID /* = 0 */, Quest const* quest /* = nullptr */, bool instantRespawn /* = false */, bool canLoopPath /* = false */)
287{
288 if (_path.Nodes.empty())
289 {
290 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::Start: (script: {}) path is empty ({})", me->GetScriptName(), me->GetGUID().ToString());
291 return;
292 }
293
294 // Queue respawn from the point it starts
295 if (CreatureData const* cdata = me->GetCreatureData())
296 {
297 if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (cdata->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC))
299 }
300
301 if (me->IsEngaged())
302 {
303 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::Start: (script: {}) attempts to Start while in combat ({})", me->GetScriptName(), me->GetGUID().ToString());
304 return;
305 }
306
308 {
309 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::Start: (script: {}) attempts to Start while already escorting ({})", me->GetScriptName(), me->GetGUID().ToString());
310 return;
311 }
312
313 if (_path.Nodes.empty())
314 {
315 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::Start: (script: {}) is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn ({})", me->GetScriptName(), me->GetGUID().ToString());
316 return;
317 }
318
319 // set variables
320 _activeAttacker = isActiveAttacker;
321 _playerGUID = playerGUID;
322 _escortQuest = quest;
323 _instantRespawn = instantRespawn;
324 _returnToStart = canLoopPath;
325
327 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::Start: (script: {}) is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn ({})", me->GetScriptName(), me->GetGUID().ToString());
328
331
332 // disable npcflags
335 if (me->IsImmuneToNPC())
336 {
338 me->SetImmuneToNPC(false);
339 }
340
341 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::Start: (script: {}) started with {} waypoints. ActiveAttacker = {}, Player = {} ({})",
343
344 _started = false;
346}
347
349{
351 return;
352
353 if (on)
354 {
357 movementGenerator->Pause(0);
358 }
359 else
360 {
362 _resume = true;
363 }
364}
365
367{
369}
370
371// see followerAI
373{
374 if (!who || !who->GetVictim())
375 return false;
376
378 return false;
379
380 // experimental (unknown) flag not present
382 return false;
383
384 // not a player
386 return false;
387
388 if (!who->isInAccessiblePlaceFor(me))
389 return false;
390
391 if (!CanAIAttack(who))
392 return false;
393
394 // we cannot attack in evade mode
395 if (me->IsInEvadeMode())
396 return false;
397
398 // or if enemy is in evade mode
399 if (who->GetTypeId() == TYPEID_UNIT && who->ToCreature()->IsInEvadeMode())
400 return false;
401
402 if (!me->IsValidAssistTarget(who->GetVictim()))
403 return false;
404
405 // too far away and no free sight
407 {
408 me->EngageWithTarget(who);
409 return true;
410 }
411
412 return false;
413}
414
416{
417 if (Player* player = GetPlayerForEscort())
418 {
419 if (Group* group = player->GetGroup())
420 {
421 for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next())
422 if (Player* member = groupRef->GetSource())
424 return true;
425 }
426 else if (me->IsWithinDistInMap(player, GetMaxPlayerDistance()))
427 return true;
428 }
429
430 return false;
431}
uint32_t uint32
Definition: Define.h:142
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition: Duration.h:29
#define ASSERT
Definition: Errors.h:68
#define TC_LOG_DEBUG(filterType__,...)
Definition: Log.h:156
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
@ MOTION_PRIORITY_NORMAL
@ MOTION_SLOT_DEFAULT
@ WAYPOINT_MOTION_TYPE
@ POINT_MOTION_TYPE
@ TYPEID_UNIT
Definition: ObjectGuid.h:40
@ POINT_HOME
@ POINT_LAST_POINT
@ STATE_ESCORT_PAUSED
@ STATE_ESCORT_ESCORTING
@ STATE_ESCORT_NONE
@ STATE_ESCORT_RETURNING
#define DEFAULT_MAX_PLAYER_DISTANCE
@ CREATURE_TYPE_FLAG_CAN_ASSIST
@ SPAWNGROUP_FLAG_ESCORTQUESTNPC
Definition: SpawnData.h:58
@ SPAWN_TYPE_CREATURE
Definition: SpawnData.h:34
EvadeReason
Definition: UnitAICommon.h:30
@ REACT_PASSIVE
Definition: UnitDefines.h:506
@ UNIT_NPC_FLAG_NONE
Definition: UnitDefines.h:296
@ UNIT_NPC_FLAG_2_NONE
Definition: UnitDefines.h:336
#define sWaypointMgr
static float waypoint[6][3]
Definition: boss_alar.cpp:54
virtual void MoveInLineOfSight(Unit *)
Definition: CreatureAI.cpp:122
bool UpdateVictim()
Definition: CreatureAI.cpp:245
Creature *const me
Definition: CreatureAI.h:61
void EngagementOver()
Definition: CreatureAI.cpp:287
bool IsTapListNotClearedOnEvade() const
Definition: Creature.h:285
void Respawn(bool force=false)
Definition: Creature.cpp:2303
void GetRespawnPosition(float &x, float &y, float &z, float *ori=nullptr, float *dist=nullptr) const
Definition: Creature.cpp:2882
CreatureDifficulty const * GetCreatureDifficulty() const
Definition: Creature.h:252
void GetHomePosition(float &x, float &y, float &z, float &ori) const
Definition: Creature.h:373
bool HasReactState(ReactStates state) const
Definition: Creature.h:162
bool IsEngaged() const override
Definition: Creature.cpp:3601
void SetTappedBy(Unit const *unit, bool withGroup=true)
Definition: Creature.cpp:1339
bool IsImmuneToNPC() const
Definition: Unit.h:1033
void DespawnOrUnsummon(Milliseconds timeToDespawn=0s, Seconds forceRespawnTime=0s)
Definition: Creature.cpp:2415
CreatureData const * GetCreatureData() const
Definition: Creature.h:251
ObjectGuid::LowType GetSpawnId() const
Definition: Creature.h:98
CreatureTemplate const * GetCreatureTemplate() const
Definition: Creature.h:250
uint32 GetRespawnDelay() const
Definition: Creature.h:337
void SetImmuneToNPC(bool apply) override
Definition: Creature.h:173
std::string GetScriptName() const
Definition: Creature.cpp:3151
void SaveRespawnTime(uint32 forceDelay=0)
Definition: Creature.cpp:2666
bool IsInEvadeMode() const
Definition: Creature.h:203
GroupReference * next()
Definition: Group.h:197
void Respawn(RespawnInfo *info, CharacterDatabaseTransaction dbTrans=nullptr)
Definition: Map.cpp:2058
void MovePath(uint32 pathId, bool repeatable, Optional< Milliseconds > duration={}, Optional< float > speed={}, MovementWalkRunSpeedSelectionMode speedSelectionMode=MovementWalkRunSpeedSelectionMode::Default, Optional< std::pair< Milliseconds, Milliseconds > > waitTimeRangeAtPathEnd={}, Optional< float > wanderDistanceAtPathEnds={}, Optional< bool > followPathBackwardsFromEndToStart={}, bool generatePath=true)
void MovePoint(uint32 id, Position const &pos, bool generatePath=true, Optional< float > finalOrient={}, Optional< float > speed={}, MovementWalkRunSpeedSelectionMode speedSelectionMode=MovementWalkRunSpeedSelectionMode::Default, Optional< float > closeEnoughDistance={})
MovementGenerator * GetCurrentMovementGenerator() const
void MoveTargetedHome()
bool IsEmpty() const
Definition: ObjectGuid.h:319
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
uint32 GetQuestId() const
Definition: QuestDef.h:587
virtual void Reset()
Definition: UnitAI.h:63
virtual bool CanAIAttack(Unit const *) const
Definition: UnitAI.h:57
Definition: Unit.h:627
void CombatStop(bool includingCast=false, bool mutualPvP=true, bool(*unitFilter)(Unit const *otherUnit)=nullptr)
Definition: Unit.cpp:5827
void ReplaceAllNpcFlags2(NPCFlags2 flags)
Definition: Unit.h:990
void RestoreFaction()
Definition: Unit.cpp:11417
MotionMaster * GetMotionMaster()
Definition: Unit.h:1652
bool isInAccessiblePlaceFor(Creature const *c) const
Definition: Unit.cpp:3165
Unit * EnsureVictim() const
Definition: Unit.h:717
uint32 GetFaction() const override
Definition: Unit.h:858
bool SetWalk(bool enable)
Definition: Unit.cpp:12707
void EngageWithTarget(Unit *who)
Definition: Unit.cpp:8077
Unit * GetVictim() const
Definition: Unit.h:715
void RemoveAllAuras()
Definition: Unit.cpp:4242
void ReplaceAllNpcFlags(NPCFlags flags)
Definition: Unit.h:984
Map * GetMap() const
Definition: Object.h:624
bool IsWithinLOSInMap(WorldObject const *obj, LineOfSightChecks checks=LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags ignoreFlags=VMAP::ModelIgnoreFlags::Nothing) const
Definition: Object.cpp:1181
Player * GetCharmerOrOwnerPlayerOrPlayerItself() const
Definition: Object.cpp:2252
bool IsWithinDistInMap(WorldObject const *obj, float dist2compare, bool is3D=true, bool incOwnRadius=true, bool incTargetRadius=true) const
Definition: Object.cpp:1147
bool IsValidAssistTarget(WorldObject const *target, SpellInfo const *bySpell=nullptr) const
Definition: Object.cpp:3144
#define sWorld
Definition: World.h:931
@ CONFIG_RESPAWN_DYNAMIC_ESCORTNPC
Definition: World.h:195
ObjectData const creatureData[]
TC_GAME_API Player * GetPlayer(Map const *, ObjectGuid const &guid)
void NormalizeMapCoord(float &c)
Definition: GridDefines.h:223
void InitializeAI() override
float GetMaxPlayerDistance() const
void Start(bool isActiveAttacker=true, ObjectGuid playerGUID=ObjectGuid::Empty, Quest const *quest=nullptr, bool instantRespawn=false, bool canLoopPath=false)
void ReturnToLastPoint()
void AddWaypoint(uint32 id, float x, float y, float z, bool run)
EscortAI(Creature *creature)
uint32 _escortState
void EnterEvadeMode(EvadeReason why) override
void MoveInLineOfSight(Unit *who) override
ObjectGuid _playerGUID
bool HasEscortState(uint32 escortState)
Milliseconds _pauseTimer
virtual void UpdateEscortAI(uint32 diff)
void AddEscortState(uint32 escortState)
Quest const * _escortQuest
void RemoveEscortState(uint32 escortState)
void MovementInform(uint32, uint32) override
void LoadPath(uint32 pathId)
Player * GetPlayerForEscort()
void JustDied(Unit *) override
bool IsPlayerOrGroupInRange()
bool _activeAttacker
void SetEscortPaused(bool on)
bool _despawnAtEnd
WaypointPath _path
void UpdateAI(uint32 diff) override
bool _despawnAtFar
bool _hasImmuneToNPCFlags
uint32 _playerCheckTimer
bool AssistPlayerInCombatAgainst(Unit *who)
bool _instantRespawn
bool _returnToStart
constexpr void SetOrientation(float orientation)
Definition: Position.h:71
float m_positionZ
Definition: Position.h:55
std::string ToString() const
Definition: Position.cpp:128
float m_positionX
Definition: Position.h:53
float m_positionY
Definition: Position.h:54
void SetCombatMovement(bool allowMovement)
bool IsCombatMovementAllowed() const
std::vector< WaypointNode > Nodes