TrinityCore
Loading...
Searching...
No Matches
ScriptedFollowerAI.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 "ScriptedFollowerAI.h"
19#include "Creature.h"
20#include "Group.h"
21#include "Log.h"
22#include "MotionMaster.h"
23#include "ObjectAccessor.h"
24#include "Player.h"
25#include "World.h"
26
27float constexpr MAX_PLAYER_DISTANCE = 100.0f;
28
30{
31 POINT_COMBAT_START = 0xFFFFFF
32};
33
34FollowerAI::FollowerAI(Creature* creature) noexcept : ScriptedAI(creature), _updateFollowTimer(2500), _followState(STATE_FOLLOW_NONE), _questForFollow(0) { }
35
43
44void FollowerAI::JustDied(Unit* /*killer*/)
45{
47 return;
48
50 if (Player* player = GetLeaderForFollower())
51 {
52 if (Group* group = player->GetGroup())
53 {
54 for (GroupReference const& groupRef : group->GetMembers())
55 if (groupRef.GetSource()->IsInMap(player))
56 groupRef.GetSource()->FailQuest(_questForFollow);
57 }
58 else
59 player->FailQuest(_questForFollow);
60 }
61}
62
64{
66 return;
67
68 if (Player* player = GetLeaderForFollower())
69 {
71 return;
73 }
74 else
76}
77
83
85{
87 {
88 if (_updateFollowTimer <= uiDiff)
89 {
91 {
92 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::UpdateAI: is set completed, despawns. ({})", me->GetGUID().ToString());
94 return;
95 }
96
97 bool maxRangeExceeded = true;
98 bool questAbandoned = (_questForFollow != 0);
99 if (Player* player = GetLeaderForFollower())
100 {
101 if (Group* group = player->GetGroup())
102 {
103 for (GroupReference const& groupRef : group->GetMembers())
104 {
105 Player* member = groupRef.GetSource();
106 if (maxRangeExceeded && me->IsWithinDistInMap(member, MAX_PLAYER_DISTANCE))
107 maxRangeExceeded = false;
108 if (questAbandoned)
109 {
111 if ((status == QUEST_STATUS_COMPLETE) || (status == QUEST_STATUS_INCOMPLETE))
112 questAbandoned = false;
113 }
114 }
115 }
116 else
117 {
119 maxRangeExceeded = false;
120 if (questAbandoned)
121 {
122 QuestStatus status = player->GetQuestStatus(_questForFollow);
123 if ((status == QUEST_STATUS_COMPLETE) || (status == QUEST_STATUS_INCOMPLETE))
124 questAbandoned = false;
125 }
126 }
127 }
128
129 if (maxRangeExceeded || questAbandoned)
130 {
131 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::UpdateAI: failed because player/group was to far away or not found ({})", me->GetGUID().ToString());
133 return;
134 }
135
136 _updateFollowTimer = 1000;
137 }
138 else
139 _updateFollowTimer -= uiDiff;
140 }
141
142 UpdateFollowerAI(uiDiff);
143}
144
146{
147 UpdateVictim();
148}
149
150void FollowerAI::StartFollow(Player* player, uint32 factionForFollower, uint32 quest)
151{
152 if (CreatureData const* cdata = me->GetCreatureData())
153 {
154 if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (cdata->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC))
156 }
157
158 if (me->IsEngaged())
159 {
160 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::StartFollow: attempt to StartFollow while in combat. ({})", me->GetGUID().ToString());
161 return;
162 }
163
165 {
166 TC_LOG_ERROR("scripts.ai.followerai", "FollowerAI::StartFollow: attempt to StartFollow while already following. ({})", me->GetGUID().ToString());
167 return;
168 }
169
170 // set variables
171 _leaderGUID = player->GetGUID();
172
173 if (factionForFollower)
174 me->SetFaction(factionForFollower);
175
176 _questForFollow = quest;
177
179 me->PauseMovement();
180
183
185
187
188 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::StartFollow: start follow {} - {} ({})", player->GetName(), _leaderGUID.ToString(), me->GetGUID().ToString());
189}
190
211
227
229{
231 {
232 if (player->IsAlive())
233 return player;
234 else
235 {
236 if (Group* group = player->GetGroup())
237 {
238 for (GroupReference const& groupRef : group->GetMembers())
239 {
240 Player* member = groupRef.GetSource();
241 if (me->IsWithinDistInMap(member, MAX_PLAYER_DISTANCE) && member->IsAlive())
242 {
243 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::GetLeaderForFollower: GetLeader changed and returned new leader. ({})", me->GetGUID().ToString());
244 _leaderGUID = member->GetGUID();
245 return member;
246 }
247 }
248 }
249 }
250 }
251
252 TC_LOG_DEBUG("scripts.ai.followerai", "FollowerAI::GetLeaderForFollower: GetLeader can not find suitable leader. ({})", me->GetGUID().ToString());
253 return nullptr;
254}
255
256// This part provides assistance to a player that are attacked by who, even if out of normal aggro range
257// It will cause me to attack who that are attacking _any_ player (which has been confirmed may happen also on offi)
258// The flag (type_flag) is unconfirmed, but used here for further research and is a good candidate.
260{
261 if (!who || !who->GetVictim())
262 return false;
263
264 // experimental (unknown) flag not present
266 return false;
267
268 if (!who->isInAccessiblePlaceFor(me))
269 return false;
270
271 if (!CanAIAttack(who))
272 return false;
273
274 // we cannot attack in evade mode
275 if (me->IsInEvadeMode())
276 return false;
277
278 // or if enemy is in evade mode
279 if (who->GetTypeId() == TYPEID_UNIT && who->ToCreature()->IsInEvadeMode())
280 return false;
281
282 // never attack friendly
283 if (me->IsFriendlyTo(who))
284 return false;
285
286 // too far away and no free sight
288 return false;
289
290 return true;
291}
uint32_t uint32
Definition Define.h:154
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:181
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
@ MOTION_PRIORITY_NORMAL
@ FOLLOW_MOTION_TYPE
@ TYPEID_UNIT
Definition ObjectGuid.h:43
#define PET_FOLLOW_ANGLE
Definition PetDefines.h:99
#define PET_FOLLOW_DIST
Definition PetDefines.h:98
QuestStatus
Definition QuestDef.h:146
@ QUEST_STATUS_INCOMPLETE
Definition QuestDef.h:150
@ QUEST_STATUS_COMPLETE
Definition QuestDef.h:148
@ POINT_COMBAT_START
float constexpr MAX_PLAYER_DISTANCE
@ STATE_FOLLOW_COMPLETE
@ STATE_FOLLOW_POSTEVENT
@ STATE_FOLLOW_NONE
@ STATE_FOLLOW_INPROGRESS
@ STATE_FOLLOW_PAUSED
@ CREATURE_TYPE_FLAG_CAN_ASSIST
@ SPAWNGROUP_FLAG_ESCORTQUESTNPC
Definition SpawnData.h:60
@ REACT_PASSIVE
@ UNIT_NPC_FLAG_NONE
@ UNIT_NPC_FLAG_2_NONE
@ UNIT_STATE_FOLLOW
Definition Unit.h:270
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
CreatureDifficulty const * GetCreatureDifficulty() const
Definition Creature.h:268
bool HasReactState(ReactStates state) const
Definition Creature.h:176
bool IsEngaged() const override
void DespawnOrUnsummon(Milliseconds timeToDespawn=0s, Seconds forceRespawnTime=0s)
CreatureData const * GetCreatureData() const
Definition Creature.h:267
uint32 GetRespawnDelay() const
Definition Creature.h:359
void SaveRespawnTime(uint32 forceDelay=0)
bool IsInEvadeMode() const
Definition Creature.h:217
FollowerAI(Creature *creature) noexcept
void StartFollow(Player *player, uint32 factionForFollower=0, uint32 quest=0)
bool ShouldAssistPlayerInCombatAgainst(Unit *who) const
void JustReachedHome() override
virtual void UpdateFollowerAI(uint32)
void JustDied(Unit *) override
void MoveInLineOfSight(Unit *) override
void AddFollowState(uint32 followState)
Player * GetLeaderForFollower()
void SetFollowComplete(bool withEndEvent=false)
ObjectGuid _leaderGUID
void RemoveFollowState(uint32 followState)
void UpdateAI(uint32) override
uint32 _updateFollowTimer
bool HasFollowState(uint32 uiFollowState) const
void OwnerAttackedBy(Unit *other) override
void SetFollowPaused(bool paused)
Definition Group.h:205
void MoveFollow(Unit *target, float dist, Optional< ChaseAngle > angle={}, Optional< Milliseconds > duration={}, bool ignoreTargetWalk=false, MovementSlot slot=MOTION_SLOT_ACTIVE, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
void Remove(MovementGenerator *movement, MovementSlot slot=MOTION_SLOT_ACTIVE)
std::string ToString() const
Creature * ToCreature()
Definition Object.h:121
QuestStatus GetQuestStatus(uint32 quest_id) const
Definition Player.cpp:15962
virtual bool CanAIAttack(Unit const *) const
Definition UnitAI.h:58
Definition Unit.h:635
void ReplaceAllNpcFlags2(NPCFlags2 flags)
Definition Unit.h:1005
void SetFaction(uint32 faction) override
Definition Unit.h:872
MotionMaster * GetMotionMaster()
Definition Unit.h:1723
void PauseMovement(uint32 timer=0, uint8 slot=0, bool forced=true)
Definition Unit.cpp:10695
bool IsAlive() const
Definition Unit.h:1185
bool isInAccessiblePlaceFor(Creature const *c) const
Definition Unit.cpp:3310
void EngageWithTarget(Unit *who)
Definition Unit.cpp:8494
Unit * GetVictim() const
Definition Unit.h:726
bool HasUnitState(const uint32 f) const
Definition Unit.h:743
void ReplaceAllNpcFlags(NPCFlags flags)
Definition Unit.h:999
std::string const & GetName() const
Definition Object.h:342
bool IsWithinLOSInMap(WorldObject const *obj, LineOfSightChecks checks=LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags ignoreFlags=VMAP::ModelIgnoreFlags::Nothing) const
Definition Object.cpp:535
bool IsWithinDistInMap(WorldObject const *obj, float dist2compare, bool is3D=true, bool incOwnRadius=true, bool incTargetRadius=true) const
Definition Object.cpp:501
bool IsFriendlyTo(WorldObject const *target) const
Definition Object.cpp:2186
#define sWorld
Definition World.h:916
@ CONFIG_RESPAWN_DYNAMIC_ESCORTNPC
Definition World.h:194
TC_GAME_API Player * GetPlayer(Map const *, ObjectGuid const &guid)