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) noexcept : 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 const& groupRef : group->GetMembers())
64 if (groupRef.GetSource()->IsInMap(player))
65 groupRef.GetSource()->FailQuest(_escortQuest->GetQuestId());
66 }
67 else
68 player->FailQuest(_escortQuest->GetQuestId());
69 }
70}
71
73{
75
78
79 // add a small delay before going to first waypoint, normal in near all cases
80 _pauseTimer = 2s;
81
84
85 Reset();
86}
87
92
94{
96 me->CombatStop(true);
98 me->SetTappedBy(nullptr);
99
101
103 {
106 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::EnterEvadeMode: left combat and is now returning to last point ({})", me->GetGUID().ToString());
107 }
108 else
109 {
112 me->SetImmuneToNPC(true);
113 Reset();
114 }
115}
116
118{
119 // no action allowed if there is no escort
121 return;
122
123 if (type == POINT_MOTION_TYPE)
124 {
125 if (_pauseTimer == 0s)
126 _pauseTimer = 2s;
127
128 // continue waypoint movement
129 if (id == POINT_LAST_POINT)
130 {
131 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::MovementInform: returned to before combat position ({})", me->GetGUID().ToString());
132 me->SetWalk(false);
134 }
135 else if (id == POINT_HOME)
136 {
137 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::MovementInform: returned to home location and restarting waypoint path ({})", me->GetGUID().ToString());
138 _started = false;
139 }
140 }
141 else if (type == WAYPOINT_MOTION_TYPE)
142 {
143 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());
145
146 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::MovementInform: waypoint node {} reached ({})", waypoint.Id, me->GetGUID().ToString());
147
148 // last point
149 if (id == _path.Nodes.size() - 1)
150 {
151 _started = false;
152 _ended = true;
153 _pauseTimer = 1s;
154 }
155 }
156}
157
159{
160 // Waypoint Updating
162 {
163 if (_pauseTimer.count() <= diff)
164 {
166 {
167 _pauseTimer = 0s;
168
169 if (_ended)
170 {
171 _ended = false;
173
174 if (_despawnAtEnd)
175 {
176 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::UpdateAI: reached end of waypoints, despawning at end ({})", me->GetGUID().ToString());
177 if (_returnToStart)
178 {
179 Position respawnPosition = me->GetRespawnPosition();
180 me->GetMotionMaster()->MovePoint(POINT_HOME, respawnPosition);
181 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::UpdateAI: returning to spawn location: {} ({})", respawnPosition, me->GetGUID().ToString());
182 }
183 else if (_instantRespawn)
184 me->Respawn(true);
185 else
187 }
188 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::UpdateAI: reached end of waypoints ({})", me->GetGUID().ToString());
190 return;
191 }
192
193 if (!_started)
194 {
195 _started = true;
196 me->GetMotionMaster()->MovePath(_path, false);
197 }
198 else if (_resume)
199 {
200 _resume = false;
202 movementGenerator->Resume(0);
203 }
204 }
205 }
206 else
207 _pauseTimer -= Milliseconds(diff);
208 }
209
210 // Check if player or any member of his group is within range
212 {
213 if (_playerCheckTimer <= diff)
214 {
216 {
217 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::UpdateAI: failed because player/group was to far away or not found ({})", me->GetGUID().ToString());
218
219 bool isEscort = false;
221 isEscort = (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (creatureData->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC));
222
223 if (_instantRespawn)
224 {
225 if (!isEscort)
226 me->DespawnOrUnsummon(0s, 1s);
227 else
229 }
230 else
232
233 return;
234 }
235
236 _playerCheckTimer = 1000;
237 }
238 else
239 _playerCheckTimer -= diff;
240 }
241
242 UpdateEscortAI(diff);
243}
244
246{
247 UpdateVictim();
248}
249
250void EscortAI::AddWaypoint(uint32 id, float x, float y, float z, bool run)
251{
252 AddWaypoint(id, x, y, z, 0.0f, {}, run);
253}
254
255void EscortAI::AddWaypoint(uint32 id, float x, float y, float z, float orientation/* = 0*/, Optional<Milliseconds> waitTime/* = {}*/, bool run /*= false*/)
256{
259
260 _path.Nodes.emplace_back(id, x, y, z, orientation, waitTime, run ? WaypointMoveType::Run : WaypointMoveType::Walk);
261}
262
264{
265 _path.Nodes.clear();
266}
267
269{
270 WaypointPath const* path = sWaypointMgr->GetPath(pathId);
271 if (!path)
272 {
273 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::LoadPath: (script: {}) path {} is invalid ({})", me->GetScriptName(), pathId, me->GetGUID().ToString());
274 return;
275 }
276 _path = *path;
277}
278
280void EscortAI::Start(bool isActiveAttacker /* = true*/, ObjectGuid playerGUID /* = 0 */, Quest const* quest /* = nullptr */, bool instantRespawn /* = false */, bool canLoopPath /* = false */)
281{
282 if (_path.Nodes.empty())
283 {
284 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::Start: (script: {}) path is empty ({})", me->GetScriptName(), me->GetGUID().ToString());
285 return;
286 }
287
288 // Queue respawn from the point it starts
289 if (CreatureData const* cdata = me->GetCreatureData())
290 {
291 if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (cdata->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC))
293 }
294
295 if (me->IsEngaged())
296 {
297 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::Start: (script: {}) attempts to Start while in combat ({})", me->GetScriptName(), me->GetGUID().ToString());
298 return;
299 }
300
302 {
303 TC_LOG_ERROR("scripts.ai.escortai", "EscortAI::Start: (script: {}) attempts to Start while already escorting ({})", me->GetScriptName(), me->GetGUID().ToString());
304 return;
305 }
306
307 if (_path.Nodes.empty())
308 {
309 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());
310 return;
311 }
312
313 // set variables
314 _activeAttacker = isActiveAttacker;
315 _playerGUID = playerGUID;
316 _escortQuest = quest;
317 _instantRespawn = instantRespawn;
318 _returnToStart = canLoopPath;
319
321 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());
322
325
326 // disable npcflags
329 if (me->IsImmuneToNPC())
330 {
332 me->SetImmuneToNPC(false);
333 }
334
335 TC_LOG_DEBUG("scripts.ai.escortai", "EscortAI::Start: (script: {}) started with {} waypoints. ActiveAttacker = {}, Player = {} ({})",
337
338 _started = false;
340}
341
343{
345 return;
346
347 if (on)
348 {
351 movementGenerator->Pause(0);
352 }
353 else
354 {
356 _resume = true;
357 }
358}
359
364
365// see followerAI
367{
368 if (!who || !who->GetVictim())
369 return false;
370
372 return false;
373
374 // experimental (unknown) flag not present
376 return false;
377
378 // not a player
380 return false;
381
382 if (!who->isInAccessiblePlaceFor(me))
383 return false;
384
385 if (!CanAIAttack(who))
386 return false;
387
388 // we cannot attack in evade mode
389 if (me->IsInEvadeMode())
390 return false;
391
392 // or if enemy is in evade mode
393 if (who->GetTypeId() == TYPEID_UNIT && who->ToCreature()->IsInEvadeMode())
394 return false;
395
396 if (!me->IsValidAssistTarget(who->GetVictim()))
397 return false;
398
399 // too far away and no free sight
401 {
402 me->EngageWithTarget(who);
403 return true;
404 }
405
406 return false;
407}
408
410{
411 if (Player* player = GetPlayerForEscort())
412 {
413 if (Group* group = player->GetGroup())
414 {
415 for (GroupReference const& groupRef : group->GetMembers())
416 if (me->IsWithinDistInMap(groupRef.GetSource(), GetMaxPlayerDistance()))
417 return true;
418 }
419 else if (me->IsWithinDistInMap(player, GetMaxPlayerDistance()))
420 return true;
421 }
422
423 return false;
424}
uint32_t uint32
Definition Define.h:154
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition Duration.h:24
#define ASSERT
Definition Errors.h:80
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:181
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
@ MOTION_PRIORITY_NORMAL
@ MOTION_SLOT_DEFAULT
@ WAYPOINT_MOTION_TYPE
@ POINT_MOTION_TYPE
@ TYPEID_UNIT
Definition ObjectGuid.h:43
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:60
@ SPAWN_TYPE_CREATURE
Definition SpawnData.h:36
EvadeReason
@ REACT_PASSIVE
@ UNIT_NPC_FLAG_NONE
@ UNIT_NPC_FLAG_2_NONE
#define sWaypointMgr
static float waypoint[6][3]
Definition boss_alar.cpp:54
ObjectGuid const & GetGUID() const
Definition BaseEntity.h:163
TypeID GetTypeId() const
Definition BaseEntity.h:166
virtual void MoveInLineOfSight(Unit *)
bool UpdateVictim()
Creature *const me
Definition CreatureAI.h:63
void EngagementOver()
bool IsTapListNotClearedOnEvade() const
Definition Creature.h:307
void Respawn(bool force=false)
CreatureDifficulty const * GetCreatureDifficulty() const
Definition Creature.h:268
void GetHomePosition(float &x, float &y, float &z, float &ori) const
Definition Creature.h:388
bool HasReactState(ReactStates state) const
Definition Creature.h:176
bool IsEngaged() const override
void SetTappedBy(Unit const *unit, bool withGroup=true)
bool IsImmuneToNPC() const
Definition Unit.h:1048
void DespawnOrUnsummon(Milliseconds timeToDespawn=0s, Seconds forceRespawnTime=0s)
CreatureData const * GetCreatureData() const
Definition Creature.h:267
ObjectGuid::LowType GetSpawnId() const
Definition Creature.h:110
Position GetRespawnPosition(float *dist=nullptr) const
CreatureTemplate const * GetCreatureTemplate() const
Definition Creature.h:266
uint32 GetRespawnDelay() const
Definition Creature.h:359
void SetImmuneToNPC(bool apply) override
Definition Creature.h:187
std::string GetScriptName() const
void SaveRespawnTime(uint32 forceDelay=0)
bool IsInEvadeMode() const
Definition Creature.h:217
Definition Group.h:205
void Respawn(RespawnInfo *info, CharacterDatabaseTransaction dbTrans=nullptr)
Definition Map.cpp:2060
void MovePoint(uint32 id, Position const &pos, bool generatePath=true, Optional< float > finalOrient={}, Optional< float > speed={}, MovementWalkRunSpeedSelectionMode speedSelectionMode=MovementWalkRunSpeedSelectionMode::Default, Optional< float > closeEnoughDistance={}, Optional< MovementFadeObject > fadeObject={}, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
MovementGenerator * GetCurrentMovementGenerator() const
void MoveTargetedHome()
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< MovementFadeObject > fadeObject={}, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
bool IsEmpty() const
Definition ObjectGuid.h:362
std::string ToString() const
Creature * ToCreature()
Definition Object.h:121
uint32 GetQuestId() const
Definition QuestDef.h:637
virtual void Reset()
Definition UnitAI.h:64
virtual bool CanAIAttack(Unit const *) const
Definition UnitAI.h:58
Definition Unit.h:635
void CombatStop(bool includingCast=false, bool mutualPvP=true, bool(*unitFilter)(Unit const *otherUnit)=nullptr)
Definition Unit.cpp:6012
void ReplaceAllNpcFlags2(NPCFlags2 flags)
Definition Unit.h:1005
void RestoreFaction()
Definition Unit.cpp:12048
MotionMaster * GetMotionMaster()
Definition Unit.h:1723
bool isInAccessiblePlaceFor(Creature const *c) const
Definition Unit.cpp:3310
Unit * EnsureVictim() const
Definition Unit.h:728
uint32 GetFaction() const override
Definition Unit.h:871
bool SetWalk(bool enable)
Definition Unit.cpp:13343
void EngageWithTarget(Unit *who)
Definition Unit.cpp:8494
Unit * GetVictim() const
Definition Unit.h:726
void RemoveAllAuras()
Definition Unit.cpp:4382
void ReplaceAllNpcFlags(NPCFlags flags)
Definition Unit.h:999
Map * GetMap() const
Definition Object.h:411
bool IsWithinLOSInMap(WorldObject const *obj, LineOfSightChecks checks=LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags ignoreFlags=VMAP::ModelIgnoreFlags::Nothing) const
Definition Object.cpp:535
Player * GetCharmerOrOwnerPlayerOrPlayerItself() const
Definition Object.cpp:1621
bool IsWithinDistInMap(WorldObject const *obj, float dist2compare, bool is3D=true, bool incOwnRadius=true, bool incTargetRadius=true) const
Definition Object.cpp:501
bool IsValidAssistTarget(WorldObject const *target, SpellInfo const *bySpell=nullptr) const
Definition Object.cpp:2482
#define sWorld
Definition World.h:916
@ CONFIG_RESPAWN_DYNAMIC_ESCORTNPC
Definition World.h:194
static constexpr ObjectData 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)
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
EscortAI(Creature *creature) noexcept
void SetEscortPaused(bool on)
WaypointPath _path
void UpdateAI(uint32 diff) override
bool _hasImmuneToNPCFlags
uint32 _playerCheckTimer
bool AssistPlayerInCombatAgainst(Unit *who)
bool _instantRespawn
bool _returnToStart
void SetCombatMovement(bool allowMovement)
bool IsCombatMovementAllowed() const
std::vector< WaypointNode > Nodes