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, {}, run);
257}
258
259void EscortAI::AddWaypoint(uint32 id, float x, float y, float z, float orientation/* = 0*/, Optional<Milliseconds> waitTime/* = {}*/, bool run /*= false*/)
260{
263
264 WaypointNode& waypoint = _path.Nodes.emplace_back(id, x, y, z, orientation, waitTime);
266}
267
269{
270 _path.Nodes.clear();
271}
272
274{
275 WaypointPath const* path = sWaypointMgr->GetPath(pathId);
276 if (!path)
277 {
278 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::LoadPath: (script: {}) path {} is invalid ({})", me->GetScriptName(), pathId, me->GetGUID().ToString());
279 return;
280 }
281 _path = *path;
282}
283
285void EscortAI::Start(bool isActiveAttacker /* = true*/, ObjectGuid playerGUID /* = 0 */, Quest const* quest /* = nullptr */, bool instantRespawn /* = false */, bool canLoopPath /* = false */)
286{
287 if (_path.Nodes.empty())
288 {
289 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::Start: (script: {}) path is empty ({})", me->GetScriptName(), me->GetGUID().ToString());
290 return;
291 }
292
293 // Queue respawn from the point it starts
294 if (CreatureData const* cdata = me->GetCreatureData())
295 {
296 if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (cdata->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC))
298 }
299
300 if (me->IsEngaged())
301 {
302 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::Start: (script: {}) attempts to Start while in combat ({})", me->GetScriptName(), me->GetGUID().ToString());
303 return;
304 }
305
307 {
308 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::Start: (script: {}) attempts to Start while already escorting ({})", me->GetScriptName(), me->GetGUID().ToString());
309 return;
310 }
311
312 if (_path.Nodes.empty())
313 {
314 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());
315 return;
316 }
317
318 // set variables
319 _activeAttacker = isActiveAttacker;
320 _playerGUID = playerGUID;
321 _escortQuest = quest;
322 _instantRespawn = instantRespawn;
323 _returnToStart = canLoopPath;
324
326 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());
327
330
331 // disable npcflags
334 if (me->IsImmuneToNPC())
335 {
337 me->SetImmuneToNPC(false);
338 }
339
340 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::Start: (script: {}) started with {} waypoints. ActiveAttacker = {}, Player = {} ({})",
342
343 _started = false;
345}
346
348{
350 return;
351
352 if (on)
353 {
356 movementGenerator->Pause(0);
357 }
358 else
359 {
361 _resume = true;
362 }
363}
364
366{
368}
369
370// see followerAI
372{
373 if (!who || !who->GetVictim())
374 return false;
375
377 return false;
378
379 // experimental (unknown) flag not present
381 return false;
382
383 // not a player
385 return false;
386
387 if (!who->isInAccessiblePlaceFor(me))
388 return false;
389
390 if (!CanAIAttack(who))
391 return false;
392
393 // we cannot attack in evade mode
394 if (me->IsInEvadeMode())
395 return false;
396
397 // or if enemy is in evade mode
398 if (who->GetTypeId() == TYPEID_UNIT && who->ToCreature()->IsInEvadeMode())
399 return false;
400
401 if (!me->IsValidAssistTarget(who->GetVictim()))
402 return false;
403
404 // too far away and no free sight
406 {
407 me->EngageWithTarget(who);
408 return true;
409 }
410
411 return false;
412}
413
415{
416 if (Player* player = GetPlayerForEscort())
417 {
418 if (Group* group = player->GetGroup())
419 {
420 for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next())
421 if (Player* member = groupRef->GetSource())
423 return true;
424 }
425 else if (me->IsWithinDistInMap(player, GetMaxPlayerDistance()))
426 return true;
427 }
428
429 return false;
430}
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__, message__,...)
Definition: Log.h:179
#define TC_LOG_ERROR(filterType__, message__,...)
Definition: Log.h:188
@ MOTION_PRIORITY_NORMAL
@ MOTION_SLOT_DEFAULT
@ WAYPOINT_MOTION_TYPE
@ POINT_MOTION_TYPE
@ TYPEID_UNIT
Definition: ObjectGuid.h:41
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
@ 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:511
@ 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:288
void Respawn(bool force=false)
Definition: Creature.cpp:2288
void GetRespawnPosition(float &x, float &y, float &z, float *ori=nullptr, float *dist=nullptr) const
Definition: Creature.cpp:2872
CreatureDifficulty const * GetCreatureDifficulty() const
Definition: Creature.h:254
void GetHomePosition(float &x, float &y, float &z, float &ori) const
Definition: Creature.h:369
bool HasReactState(ReactStates state) const
Definition: Creature.h:164
bool IsEngaged() const override
Definition: Creature.cpp:3600
void SetTappedBy(Unit const *unit, bool withGroup=true)
Definition: Creature.cpp:1347
bool IsImmuneToNPC() const
Definition: Unit.h:1039
void DespawnOrUnsummon(Milliseconds timeToDespawn=0s, Seconds forceRespawnTime=0s)
Definition: Creature.cpp:2400
CreatureData const * GetCreatureData() const
Definition: Creature.h:253
ObjectGuid::LowType GetSpawnId() const
Definition: Creature.h:98
CreatureTemplate const * GetCreatureTemplate() const
Definition: Creature.h:252
uint32 GetRespawnDelay() const
Definition: Creature.h:340
void SetImmuneToNPC(bool apply) override
Definition: Creature.h:175
std::string GetScriptName() const
Definition: Creature.cpp:3141
void SaveRespawnTime(uint32 forceDelay=0)
Definition: Creature.cpp:2651
bool IsInEvadeMode() const
Definition: Creature.h:205
Definition: Group.h:205
void Respawn(RespawnInfo *info, CharacterDatabaseTransaction dbTrans=nullptr)
Definition: Map.cpp:2060
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={}, Optional< bool > exactSplinePath={}, bool generatePath=true, Optional< Scripting::v2::ActionResultSetter< MovementStopReason > > &&scriptResult={})
void MovePoint(uint32 id, Position const &pos, bool generatePath=true, Optional< float > finalOrient={}, Optional< float > speed={}, MovementWalkRunSpeedSelectionMode speedSelectionMode=MovementWalkRunSpeedSelectionMode::Default, Optional< float > closeEnoughDistance={}, Optional< Scripting::v2::ActionResultSetter< MovementStopReason > > &&scriptResult={})
MovementGenerator * GetCurrentMovementGenerator() const
void MoveTargetedHome()
bool IsEmpty() const
Definition: ObjectGuid.h:321
std::string ToString() const
Definition: ObjectGuid.cpp:777
static Creature * ToCreature(Object *o)
Definition: Object.h:220
TypeID GetTypeId() const
Definition: Object.h:174
static ObjectGuid GetGUID(Object const *o)
Definition: Object.h:160
uint32 GetQuestId() const
Definition: QuestDef.h:615
Derived * next()
Definition: Reference.h:84
virtual void Reset()
Definition: UnitAI.h:63
virtual bool CanAIAttack(Unit const *) const
Definition: UnitAI.h:57
Definition: Unit.h:628
void CombatStop(bool includingCast=false, bool mutualPvP=true, bool(*unitFilter)(Unit const *otherUnit)=nullptr)
Definition: Unit.cpp:5890
void ReplaceAllNpcFlags2(NPCFlags2 flags)
Definition: Unit.h:996
void RestoreFaction()
Definition: Unit.cpp:11573
MotionMaster * GetMotionMaster()
Definition: Unit.h:1663
bool isInAccessiblePlaceFor(Creature const *c) const
Definition: Unit.cpp:3206
Unit * EnsureVictim() const
Definition: Unit.h:721
uint32 GetFaction() const override
Definition: Unit.h:864
bool SetWalk(bool enable)
Definition: Unit.cpp:12856
void EngageWithTarget(Unit *who)
Definition: Unit.cpp:8232
Unit * GetVictim() const
Definition: Unit.h:719
void RemoveAllAuras()
Definition: Unit.cpp:4286
void ReplaceAllNpcFlags(NPCFlags flags)
Definition: Unit.h:990
Map * GetMap() const
Definition: Object.h:625
bool IsWithinLOSInMap(WorldObject const *obj, LineOfSightChecks checks=LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags ignoreFlags=VMAP::ModelIgnoreFlags::Nothing) const
Definition: Object.cpp:1195
Player * GetCharmerOrOwnerPlayerOrPlayerItself() const
Definition: Object.cpp:2290
bool IsWithinDistInMap(WorldObject const *obj, float dist2compare, bool is3D=true, bool incOwnRadius=true, bool incTargetRadius=true) const
Definition: Object.cpp:1161
bool IsValidAssistTarget(WorldObject const *target, SpellInfo const *bySpell=nullptr) const
Definition: Object.cpp:3201
#define sWorld
Definition: World.h:929
@ CONFIG_RESPAWN_DYNAMIC_ESCORTNPC
Definition: World.h:194
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:72
float m_positionZ
Definition: Position.h:56
std::string ToString() const
Definition: Position.cpp:199
float m_positionX
Definition: Position.h:54
float m_positionY
Definition: Position.h:55
void SetCombatMovement(bool allowMovement)
bool IsCombatMovementAllowed() const
std::vector< WaypointNode > Nodes