TrinityCore
CreatureGroups.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 "CreatureGroups.h"
19#include "Containers.h"
20#include "Creature.h"
21#include "CreatureAI.h"
22#include "DatabaseEnv.h"
23#include "Log.h"
24#include "Map.h"
25#include "MotionMaster.h"
26#include "MovementGenerator.h"
27#include "ObjectMgr.h"
28
29#define MAX_DESYNC 5.0f
30
32{
33}
34
36{
37}
38
40{
42 return &instance;
43}
44
46{
47 Map* map = creature->GetMap();
48
49 auto itr = map->CreatureGroupHolder.find(leaderSpawnId);
50 if (itr != map->CreatureGroupHolder.end())
51 {
52 //Add member to an existing group
53 TC_LOG_DEBUG("entities.unit", "Group found: {}, inserting creature {}, Group InstanceID {}", leaderSpawnId, creature->GetGUID().ToString(), creature->GetInstanceId());
54
55 // With dynamic spawn the creature may have just respawned
56 // we need to find previous instance of creature and delete it from the formation, as it'll be invalidated
58 for (auto const& pair : bounds)
59 {
60 Creature* other = pair.second;
61 if (other == creature)
62 continue;
63
64 if (itr->second->HasMember(other))
65 itr->second->RemoveMember(other);
66 }
67 }
68 else
69 {
70 //Create new group
71 TC_LOG_DEBUG("entities.unit", "Group not found: {}. Creating new group.", leaderSpawnId);
72 CreatureGroup* group = new CreatureGroup(leaderSpawnId);
73 itr = map->CreatureGroupHolder.emplace(leaderSpawnId, group).first;
74 }
75
76 itr->second->AddMember(creature);
77}
78
80{
81 TC_LOG_DEBUG("entities.unit", "Deleting member pointer to GUID: {} from group " UI64FMTD, group->GetLeaderSpawnId(), member->GetSpawnId());
82 group->RemoveMember(member);
83
84 if (group->IsEmpty())
85 {
86 Map* map = member->GetMap();
87
88 TC_LOG_DEBUG("entities.unit", "Deleting group with InstanceID {}", member->GetInstanceId());
89 auto itr = map->CreatureGroupHolder.find(group->GetLeaderSpawnId());
90 ASSERT(itr != map->CreatureGroupHolder.end(), "Not registered group " UI64FMTD " in map %u", group->GetLeaderSpawnId(), map->GetId());
91 map->CreatureGroupHolder.erase(itr);
92 delete group;
93 }
94}
95
97{
98 uint32 oldMSTime = getMSTime();
99
100 //Get group data
101 QueryResult result = WorldDatabase.Query("SELECT leaderGUID, memberGUID, dist, angle, groupAI, point_1, point_2 FROM creature_formations ORDER BY leaderGUID");
102 if (!result)
103 {
104 TC_LOG_INFO("server.loading", ">> Loaded 0 creatures in formations. DB table `creature_formations` is empty!");
105 return;
106 }
107
108 uint32 count = 0;
109 std::unordered_set<ObjectGuid::LowType> leaderSpawnIds;
110 do
111 {
112 Field* fields = result->Fetch();
113
114 //Load group member data
115 FormationInfo member;
116 member.LeaderSpawnId = fields[0].GetUInt64();
117 ObjectGuid::LowType memberSpawnId = fields[1].GetUInt64();
118 member.FollowDist = 0.f;
119 member.FollowAngle = 0.f;
120
121 //If creature is group leader we may skip loading of dist/angle
122 if (member.LeaderSpawnId != memberSpawnId)
123 {
124 member.FollowDist = fields[2].GetFloat();
125 member.FollowAngle = fields[3].GetFloat() * float(M_PI) / 180.0f;
126 }
127
128 member.GroupAI = fields[4].GetUInt32();
129 for (uint8 i = 0; i < 2; ++i)
130 member.LeaderWaypointIDs[i] = fields[5 + i].GetUInt16();
131
132 // check data correctness
133 {
134 if (!sObjectMgr->GetCreatureData(member.LeaderSpawnId))
135 {
136 TC_LOG_ERROR("sql.sql", "creature_formations table leader guid {} incorrect (not exist)", member.LeaderSpawnId);
137 continue;
138 }
139
140 if (!sObjectMgr->GetCreatureData(memberSpawnId))
141 {
142 TC_LOG_ERROR("sql.sql", "creature_formations table member guid {} incorrect (not exist)", memberSpawnId);
143 continue;
144 }
145
146 leaderSpawnIds.insert(member.LeaderSpawnId);
147 }
148
149 _creatureGroupMap.emplace(memberSpawnId, std::move(member));
150 ++count;
151 } while (result->NextRow());
152
153 for (ObjectGuid::LowType leaderSpawnId : leaderSpawnIds)
154 {
155 if (!_creatureGroupMap.count(leaderSpawnId))
156 {
157 TC_LOG_ERROR("sql.sql", "creature_formation contains leader spawn {} which is not included on its formation, removing", leaderSpawnId);
158 for (auto itr = _creatureGroupMap.begin(); itr != _creatureGroupMap.end();)
159 {
160 if (itr->second.LeaderSpawnId == leaderSpawnId)
161 {
162 itr = _creatureGroupMap.erase(itr);
163 continue;
164 }
165
166 ++itr;
167 }
168 }
169 }
170
171 TC_LOG_INFO("server.loading", ">> Loaded {} creatures in formations in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
172}
173
175{
177}
178
179void FormationMgr::AddFormationMember(ObjectGuid::LowType spawnId, float followAng, float followDist, ObjectGuid::LowType leaderSpawnId, uint32 groupAI)
180{
181 FormationInfo member;
182 member.LeaderSpawnId = leaderSpawnId;
183 member.FollowDist = followDist;
184 member.FollowAngle = followAng;
185 member.GroupAI = groupAI;
186 for (uint8 i = 0; i < 2; ++i)
187 member.LeaderWaypointIDs[i] = 0;
188
189 _creatureGroupMap.emplace(spawnId, std::move(member));
190}
191
192CreatureGroup::CreatureGroup(ObjectGuid::LowType leaderSpawnId) : _leader(nullptr), _members(), _leaderSpawnId(leaderSpawnId), _formed(false), _engaging(false)
193{
194}
195
197{
198}
199
201{
202 TC_LOG_DEBUG("entities.unit", "CreatureGroup::AddMember: Adding unit {}.", member->GetGUID().ToString());
203
204 //Check if it is a leader
205 if (member->GetSpawnId() == _leaderSpawnId)
206 {
207 TC_LOG_DEBUG("entities.unit", "Unit {} is formation leader. Adding group.", member->GetGUID().ToString());
208 _leader = member;
209 }
210
211 // formation must be registered at this point
212 FormationInfo* formationInfo = ASSERT_NOTNULL(sFormationMgr->GetFormationInfo(member->GetSpawnId()));
213 _members.emplace(member, formationInfo);
214 member->SetFormation(this);
215}
216
218{
219 if (_leader == member)
220 _leader = nullptr;
221
222 _members.erase(member);
223 member->SetFormation(nullptr);
224}
225
227{
228 // used to prevent recursive calls
229 if (_engaging)
230 return;
231
232 uint8 groupAI = ASSERT_NOTNULL(sFormationMgr->GetFormationInfo(member->GetSpawnId()))->GroupAI;
233 if (!groupAI)
234 return;
235
236 if (member == _leader)
237 {
238 if (!(groupAI & FLAG_MEMBERS_ASSIST_LEADER))
239 return;
240 }
241 else if (!(groupAI & FLAG_LEADER_ASSISTS_MEMBER))
242 return;
243
244 _engaging = true;
245
246 for (auto const& pair : _members)
247 {
248 Creature* other = pair.first;
249 if (other == member)
250 continue;
251
252 if (!other->IsAlive())
253 continue;
254
255 if (((other != _leader && (groupAI & FLAG_MEMBERS_ASSIST_LEADER)) || (other == _leader && (groupAI & FLAG_LEADER_ASSISTS_MEMBER))) && other->IsValidAttackTarget(target))
256 other->EngageWithTarget(target);
257 }
258
259 _engaging = false;
260}
261
262void CreatureGroup::FormationReset(bool /*dismiss*/)
263{
264 for (auto const& pair : _members)
265 {
266 if (pair.first != _leader && pair.first->IsAlive())
267 {
268 pair.first->GetMotionMaster()->MoveIdle();
269 // TC_LOG_DEBUG("entities.unit", "CreatureGroup::FormationReset: Set {} movement for member {}", dismiss ? "default" : "idle", pair.first->GetGUID().ToString());
270 }
271 }
272
273 // _formed = !dismiss;
274}
275
277{
278 if (!_leader)
279 return;
280
281 for (auto const& pair : _members)
282 {
283 Creature* member = pair.first;
284 if (member == _leader || !member->IsAlive() || member->IsEngaged() || !(pair.second->GroupAI & FLAG_IDLE_IN_FORMATION))
285 continue;
286
287 float angle = pair.second->FollowAngle + float(M_PI); // for some reason, someone thought it was a great idea to invert relativ angles...
288 float dist = pair.second->FollowDist;
289
291 member->GetMotionMaster()->MoveFormation(_leader, dist, angle, pair.second->LeaderWaypointIDs[0], pair.second->LeaderWaypointIDs[1]);
292 }
293}
294
296{
297 for (std::unordered_map<Creature*, FormationInfo*>::value_type const& pair : _members)
298 {
299 if (pair.first != _leader && pair.first->IsAlive())
300 {
301 if (pair.first->IsEngaged() || pair.first->IsReturningHome())
302 return false;
303 }
304 }
305
306 return true;
307}
#define M_PI
Definition: Common.h:115
@ FLAG_LEADER_ASSISTS_MEMBER
@ FLAG_IDLE_IN_FORMATION
@ FLAG_MEMBERS_ASSIST_LEADER
#define sFormationMgr
std::shared_ptr< ResultSet > QueryResult
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
Definition: DatabaseEnv.cpp:20
#define UI64FMTD
Definition: Define.h:126
uint8_t uint8
Definition: Define.h:144
uint32_t uint32
Definition: Define.h:142
#define ASSERT_NOTNULL(pointer)
Definition: Errors.h:84
#define ASSERT
Definition: Errors.h:68
#define TC_LOG_DEBUG(filterType__,...)
Definition: Log.h:156
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition: Log.h:159
#define sObjectMgr
Definition: ObjectMgr.h:1946
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition: Timer.h:57
uint32 getMSTime()
Definition: Timer.h:33
@ UNIT_STATE_FOLLOW_FORMATION
Definition: Unit.h:274
CreatureGroup(ObjectGuid::LowType leaderSpawnId)
void FormationReset(bool dismiss)
ObjectGuid::LowType GetLeaderSpawnId() const
void RemoveMember(Creature *member)
void LeaderStartedMoving()
void MemberEngagingTarget(Creature *member, Unit *target)
bool CanLeaderStartMoving() const
bool IsEmpty() const
std::unordered_map< Creature *, FormationInfo * > _members
ObjectGuid::LowType _leaderSpawnId
Creature * _leader
void AddMember(Creature *member)
bool IsEngaged() const override
Definition: Creature.cpp:3601
ObjectGuid::LowType GetSpawnId() const
Definition: Creature.h:98
void SetFormation(CreatureGroup *formation)
Definition: Creature.h:392
Class used to access individual fields of database query result.
Definition: Field.h:90
uint64 GetUInt64() const
Definition: Field.cpp:78
uint16 GetUInt16() const
Definition: Field.cpp:46
float GetFloat() const
Definition: Field.cpp:94
uint32 GetUInt32() const
Definition: Field.cpp:62
void AddCreatureToGroup(ObjectGuid::LowType leaderSpawnId, Creature *creature)
void RemoveCreatureFromGroup(CreatureGroup *group, Creature *creature)
static FormationMgr * instance()
FormationInfo * GetFormationInfo(ObjectGuid::LowType spawnId)
void AddFormationMember(ObjectGuid::LowType spawnId, float followAng, float followDist, ObjectGuid::LowType leaderSpawnId, uint32 groupAI)
std::unordered_map< ObjectGuid::LowType, FormationInfo > _creatureGroupMap
void LoadCreatureFormations()
Definition: Map.h:189
std::unordered_map< ObjectGuid::LowType, CreatureGroup * > CreatureGroupHolder
Definition: Map.h:388
uint32 GetId() const
Definition: Map.cpp:3228
CreatureBySpawnIdContainer & GetCreatureBySpawnIdStore()
Definition: Map.h:425
void MoveFormation(Unit *leader, float range, float angle, uint32 point1, uint32 point2)
std::string ToString() const
Definition: ObjectGuid.cpp:554
uint64 LowType
Definition: ObjectGuid.h:278
static ObjectGuid GetGUID(Object const *o)
Definition: Object.h:159
Definition: Unit.h:627
MotionMaster * GetMotionMaster()
Definition: Unit.h:1652
bool IsAlive() const
Definition: Unit.h:1164
void EngageWithTarget(Unit *who)
Definition: Unit.cpp:8077
bool HasUnitState(const uint32 f) const
Definition: Unit.h:732
Map * GetMap() const
Definition: Object.h:624
bool IsValidAttackTarget(WorldObject const *target, SpellInfo const *bySpell=nullptr) const
Definition: Object.cpp:2991
uint32 GetInstanceId() const
Definition: Object.h:521
auto MapEqualRange(M &map, typename M::key_type const &key)
Definition: IteratorPair.h:60
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition: MapUtils.h:29
ObjectGuid::LowType LeaderSpawnId
uint32 LeaderWaypointIDs[2]