TrinityCore
Loading...
Searching...
No Matches
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 {
65 if (Player* member = groupRef->GetSource())
66 if (member->IsInMap(player))
67 member->FailQuest(_escortQuest->GetQuestId());
68 }
69 }
70 else
71 player->FailQuest(_escortQuest->GetQuestId());
72 }
73}
74
76{
78
81
82 // add a small delay before going to first waypoint, normal in near all cases
83 _pauseTimer = 2s;
84
87
88 Reset();
89}
90
95
97{
99 me->CombatStop(true);
100 me->SetLootRecipient(nullptr);
101
103
105 {
108 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::EnterEvadeMode: left combat and is now returning to last point ({})", me->GetGUID().ToString());
109 }
110 else
111 {
114 me->SetImmuneToNPC(true);
115 Reset();
116 }
117}
118
120{
121 // no action allowed if there is no escort
123 return;
124
125 if (type == POINT_MOTION_TYPE)
126 {
127 if (_pauseTimer == 0s)
128 _pauseTimer = 2s;
129
130 // continue waypoint movement
131 if (id == POINT_LAST_POINT)
132 {
133 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::MovementInform: returned to before combat position ({})", me->GetGUID().ToString());
134 me->SetWalk(false);
136 }
137 else if (id == POINT_HOME)
138 {
139 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::MovementInform: returned to home location and restarting waypoint path ({})", me->GetGUID().ToString());
140 _started = false;
141 }
142 }
143 else if (type == WAYPOINT_MOTION_TYPE)
144 {
145 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 WaypointNode waypoint = _path.nodes[id];
147
148 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::MovementInform: waypoint node {} reached ({})", waypoint.id, me->GetGUID().ToString());
149
150 // last point
151 if (id == _path.nodes.size() - 1)
152 {
153 _started = false;
154 _ended = true;
155 _pauseTimer = 1s;
156 }
157 }
158}
159
161{
162 // Waypoint Updating
164 {
165 if (_pauseTimer.count() <= diff)
166 {
168 {
169 _pauseTimer = 0s;
170
171 if (_ended)
172 {
173 _ended = false;
175
176 if (_despawnAtEnd)
177 {
178 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::UpdateAI: reached end of waypoints, despawning at end ({})", me->GetGUID().ToString());
179 if (_instantRespawn)
180 me->Respawn(true);
181 else
183 }
184 else if (_returnToStart)
185 {
186 Position respawnPosition;
187 float orientation = 0.f;
188 me->GetRespawnPosition(respawnPosition.m_positionX, respawnPosition.m_positionY, respawnPosition.m_positionZ, &orientation);
189 respawnPosition.SetOrientation(orientation);
190 me->GetMotionMaster()->MovePoint(POINT_HOME, respawnPosition);
191 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::UpdateAI: returning to spawn location: {} ({})", respawnPosition.ToString(), me->GetGUID().ToString());
192 }
193 else
195 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::UpdateAI: reached end of waypoints ({})", me->GetGUID().ToString());
196 return;
197 }
198
199 if (!_started)
200 {
201 _started = true;
202 me->GetMotionMaster()->MovePath(_path, false);
203 }
204 else if (_resume)
205 {
206 _resume = false;
208 movementGenerator->Resume(0);
209 }
210 }
211 }
212 else
213 _pauseTimer -= Milliseconds(diff);
214 }
215
216 // Check if player or any member of his group is within range
218 {
219 if (_playerCheckTimer <= diff)
220 {
222 {
223 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::UpdateAI: failed because player/group was to far away or not found ({})", me->GetGUID().ToString());
224
225 bool isEscort = false;
227 isEscort = (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (creatureData->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC));
228
229 if (_instantRespawn)
230 {
231 if (!isEscort)
232 me->DespawnOrUnsummon(0s, 1s);
233 else
235 }
236 else
238
239 return;
240 }
241
242 _playerCheckTimer = 1000;
243 }
244 else
245 _playerCheckTimer -= diff;
246 }
247
248 UpdateEscortAI(diff);
249}
250
252{
253 if (!UpdateVictim())
254 return;
255
257}
258
259void EscortAI::AddWaypoint(uint32 id, float x, float y, float z, bool run)
260{
261 AddWaypoint(id, x, y, z, 0.0f, 0s, run);
262}
263
264void EscortAI::AddWaypoint(uint32 id, float x, float y, float z, float orientation/* = 0*/, Milliseconds waitTime/* = 0s*/, bool run /*= false*/)
265{
268
269 WaypointNode waypoint;
270 waypoint.id = id;
271 waypoint.x = x;
272 waypoint.y = y;
273 waypoint.z = z;
274 waypoint.orientation = orientation;
276 waypoint.delay = waitTime.count();
277 waypoint.eventId = 0;
278 waypoint.eventChance = 100;
279 _path.nodes.push_back(std::move(waypoint));
280}
281
283{
284 _path.nodes.clear();
285}
286
288{
289 WaypointPath const* path = sWaypointMgr->GetPath(pathId);
290 if (!path)
291 {
292 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::LoadPath: (script: {}) path {} is invalid ({})", me->GetScriptName(), pathId, me->GetGUID().ToString());
293 return;
294 }
295 _path = *path;
296}
297
299void EscortAI::Start(bool isActiveAttacker /* = true*/, ObjectGuid playerGUID /* = 0 */, Quest const* quest /* = nullptr */, bool instantRespawn /* = false */, bool canLoopPath /* = false */)
300{
301 if (_path.nodes.empty())
302 {
303 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::Start: (script: {}) path is empty ({})", me->GetScriptName(), me->GetGUID().ToString());
304 return;
305 }
306
307 // Queue respawn from the point it starts
308 if (CreatureData const* cdata = me->GetCreatureData())
309 {
310 if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (cdata->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC))
312 }
313
314 if (me->IsEngaged())
315 {
316 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::Start: (script: {}) attempts to Start while in combat ({})", me->GetScriptName(), me->GetGUID().ToString());
317 return;
318 }
319
321 {
322 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::Start: (script: {}) attempts to Start while already escorting ({})", me->GetScriptName(), me->GetGUID().ToString());
323 return;
324 }
325
326 if (_path.nodes.empty())
327 {
328 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::Start: (script: {}) starts with 0 waypoints (possible missing entry in script_waypoint. Quest: {}) ({})", me->GetScriptName(), quest ? quest->GetQuestId() : 0, me->GetGUID());
329 return;
330 }
331
332 // set variables
333 _activeAttacker = isActiveAttacker;
334 _playerGUID = playerGUID;
335 _escortQuest = quest;
336 _instantRespawn = instantRespawn;
337 _returnToStart = canLoopPath;
338
340 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());
341
344
345 // disable npcflags
347 if (me->IsImmuneToNPC())
348 {
350 me->SetImmuneToNPC(false);
351 }
352
353 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::Start: (script: {}) started with {} waypoints. ActiveAttacker = {}, Player = {} ({})",
355
356 _started = false;
358}
359
361{
363 return;
364
365 if (on)
366 {
369 movementGenerator->Pause(0);
370 }
371 else
372 {
374 _resume = true;
375 }
376}
377
382
383// see followerAI
385{
386 if (!who || !who->GetVictim())
387 return false;
388
390 return false;
391
392 // experimental (unknown) flag not present
394 return false;
395
396 // not a player
398 return false;
399
400 if (!who->isInAccessiblePlaceFor(me))
401 return false;
402
403 if (!CanAIAttack(who))
404 return false;
405
406 // we cannot attack in evade mode
407 if (me->IsInEvadeMode())
408 return false;
409
410 // or if enemy is in evade mode
411 if (who->GetTypeId() == TYPEID_UNIT && who->ToCreature()->IsInEvadeMode())
412 return false;
413
414 if (!me->IsValidAssistTarget(who->GetVictim()))
415 return false;
416
417 // too far away and no free sight
419 {
420 me->EngageWithTarget(who);
421 return true;
422 }
423
424 return false;
425}
426
428{
429 if (Player* player = GetPlayerForEscort())
430 {
431 if (Group* group = player->GetGroup())
432 {
433 for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next())
434 {
435 if (Player* member = groupRef->GetSource())
437 return true;
438 }
439 }
440 else if (me->IsWithinDistInMap(player, GetMaxPlayerDistance()))
441 return true;
442 }
443
444 return false;
445}
uint32_t uint32
Definition Define.h:133
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition Duration.h:24
#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:38
@ 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:53
@ SPAWN_TYPE_CREATURE
Definition SpawnData.h:31
@ REACT_PASSIVE
@ UNIT_NPC_FLAG_NONE
@ WAYPOINT_MOVE_TYPE_RUN
@ WAYPOINT_MOVE_TYPE_WALK
#define sWaypointMgr
virtual void MoveInLineOfSight(Unit *)
bool UpdateVictim()
Creature *const me
Definition CreatureAI.h:82
void EngagementOver()
void Respawn(bool force=false)
void GetRespawnPosition(float &x, float &y, float &z, float *ori=nullptr, float *dist=nullptr) const
void SetLootRecipient(Unit *unit, bool withGroup=true)
void GetHomePosition(float &x, float &y, float &z, float &ori) const
Definition Creature.h:295
bool HasReactState(ReactStates state) const
Definition Creature.h:121
bool IsEngaged() const override
bool IsImmuneToNPC() const
Definition Unit.h:1140
void DespawnOrUnsummon(Milliseconds timeToDespawn=0s, Seconds forceRespawnTime=0s)
CreatureData const * GetCreatureData() const
Definition Creature.h:187
ObjectGuid::LowType GetSpawnId() const
Definition Creature.h:83
CreatureTemplate const * GetCreatureTemplate() const
Definition Creature.h:186
uint32 GetRespawnDelay() const
Definition Creature.h:261
void SetImmuneToNPC(bool apply) override
Definition Creature.h:132
std::string GetScriptName() const
void SaveRespawnTime(uint32 forceDelay=0)
bool IsInEvadeMode() const
Definition Creature.h:146
Definition Group.h:165
void Respawn(RespawnInfo *info, CharacterDatabaseTransaction dbTrans=nullptr)
Definition Map.cpp:3105
void MovePoint(uint32 id, Position const &pos, bool generatePath=true, Optional< float > finalOrient={})
void MovePath(uint32 pathId, bool repeatable)
MovementGenerator * GetCurrentMovementGenerator() const
void MoveTargetedHome()
bool IsEmpty() const
Definition ObjectGuid.h:172
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
uint32 GetQuestId() const
Definition QuestDef.h:229
void DoMeleeAttackIfReady()
Definition UnitAI.cpp:54
virtual void Reset()
Definition UnitAI.h:145
virtual bool CanAIAttack(Unit const *) const
Definition UnitAI.h:139
Definition Unit.h:769
void RestoreFaction()
Definition Unit.cpp:11776
MotionMaster * GetMotionMaster()
Definition Unit.h:1667
bool isInAccessiblePlaceFor(Creature const *c) const
Definition Unit.cpp:3154
Unit * EnsureVictim() const
Definition Unit.h:861
uint32 GetFaction() const override
Definition Unit.h:973
bool SetWalk(bool enable)
Definition Unit.cpp:13268
void EngageWithTarget(Unit *who)
Definition Unit.cpp:8292
Unit * GetVictim() const
Definition Unit.h:859
void RemoveAllAuras()
Definition Unit.cpp:4157
void CombatStop(bool includingCast=false, bool mutualPvP=true)
Definition Unit.cpp:5691
void ReplaceAllNpcFlags(NPCFlags flags)
Definition Unit.h:1099
Map * GetMap() const
Definition Object.h:449
bool IsWithinLOSInMap(WorldObject const *obj, LineOfSightChecks checks=LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags ignoreFlags=VMAP::ModelIgnoreFlags::Nothing) const
Definition Object.cpp:1226
Player * GetCharmerOrOwnerPlayerOrPlayerItself() const
Definition Object.cpp:2203
bool IsWithinDistInMap(WorldObject const *obj, float dist2compare, bool is3D=true, bool incOwnRadius=true, bool incTargetRadius=true) const
Definition Object.cpp:1192
bool IsValidAssistTarget(WorldObject const *target, SpellInfo const *bySpell=nullptr) const
Definition Object.cpp:3000
#define sWorld
Definition World.h:900
@ CONFIG_RESPAWN_DYNAMIC_ESCORTNPC
Definition World.h:178
ObjectData const creatureData[]
TC_GAME_API Player * GetPlayer(Map const *, ObjectGuid const &guid)
void NormalizeMapCoord(float &c)
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 MoveInLineOfSight(Unit *who) override
ObjectGuid _playerGUID
bool HasEscortState(uint32 escortState)
void EnterEvadeMode(EvadeReason=EVADE_REASON_OTHER) override
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)
WaypointPath _path
void UpdateAI(uint32 diff) override
bool _hasImmuneToNPCFlags
uint32 _playerCheckTimer
bool AssistPlayerInCombatAgainst(Unit *who)
bool _instantRespawn
bool _returnToStart
float m_positionZ
Definition Position.h:58
std::string ToString() const
Definition Position.cpp:149
float m_positionX
Definition Position.h:56
float m_positionY
Definition Position.h:57
void SetOrientation(float orientation)
Definition Position.h:74
void SetCombatMovement(bool allowMovement)
bool IsCombatMovementAllowed() const
Optional< float > orientation
std::vector< WaypointNode > nodes