TrinityCore
Loading...
Searching...
No Matches
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
34
38
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 std::tie(itr, std::ignore) = map->CreatureGroupHolder.emplace(leaderSpawnId, group);
74 }
75
76 itr->second->AddMember(creature);
77}
78
80{
81 TC_LOG_DEBUG("entities.unit", "Deleting member pointer to GUID: {} from group {}", 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 %u 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].GetUInt32();
117 ObjectGuid::LowType memberSpawnId = fields[1].GetUInt32();
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
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
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
263{
264 for (auto const& pair : _members)
265 {
266 if (pair.first != _leader && pair.first->IsAlive())
267 {
268 if (dismiss)
269 pair.first->GetMotionMaster()->Remove(FORMATION_MOTION_TYPE, MOTION_SLOT_DEFAULT);
270 else
271 pair.first->GetMotionMaster()->MoveIdle();
272 TC_LOG_DEBUG("entities.unit", "CreatureGroup::FormationReset: Set {} movement for member {}", dismiss ? "default" : "idle", pair.first->GetGUID().ToString());
273 }
274 }
275
276 _formed = !dismiss;
277}
278
280{
281 if (!_leader)
282 return;
283
284 for (auto const& pair : _members)
285 {
286 Creature* member = pair.first;
287 if (member == _leader || !member->IsAlive() || member->IsEngaged() || !(pair.second->GroupAI & FLAG_IDLE_IN_FORMATION))
288 continue;
289
290 float angle = pair.second->FollowAngle + float(M_PI); // for some reason, someone thought it was a great idea to invert relativ angles...
291 float dist = pair.second->FollowDist;
292
294 member->GetMotionMaster()->MoveFormation(_leader, dist, angle, pair.second->LeaderWaypointIDs[0], pair.second->LeaderWaypointIDs[1]);
295 }
296}
297
299{
300 for (std::unordered_map<Creature*, FormationInfo*>::value_type const& pair : _members)
301 {
302 if (pair.first != _leader && pair.first->IsAlive())
303 {
304 if (pair.first->IsEngaged() || pair.first->IsReturningHome())
305 return false;
306 }
307 }
308
309 return true;
310}
#define M_PI
Definition Common.h:72
@ 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.
uint8_t uint8
Definition Define.h:135
uint32_t uint32
Definition Define.h:133
#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
@ MOTION_SLOT_DEFAULT
@ FORMATION_MOTION_TYPE
#define sObjectMgr
Definition ObjectMgr.h:1721
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition Timer.h:57
uint32 getMSTime()
Definition Timer.h:33
@ UNIT_STATE_FOLLOW_FORMATION
Definition Unit.h:239
CreatureGroup(ObjectGuid::LowType leaderSpawnId)
void FormationReset(bool dismiss)
ObjectGuid::LowType GetLeaderSpawnId() const
void RemoveMember(Creature *member)
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
ObjectGuid::LowType GetSpawnId() const
Definition Creature.h:83
void SetFormation(CreatureGroup *formation)
Definition Creature.h:314
Class used to access individual fields of database query result.
Definition Field.h:92
uint16 GetUInt16() const
Definition Field.cpp:45
float GetFloat() const
Definition Field.cpp:93
uint32 GetUInt32() const
Definition Field.cpp:61
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:281
std::unordered_map< ObjectGuid::LowType, CreatureGroup * > CreatureGroupHolder
Definition Map.h:461
uint32 GetId() const
Definition Map.cpp:4216
CreatureBySpawnIdContainer & GetCreatureBySpawnIdStore()
Definition Map.h:492
void MoveFormation(Unit *leader, float range, float angle, uint32 point1, uint32 point2)
std::string ToString() const
uint32 LowType
Definition ObjectGuid.h:142
static ObjectGuid GetGUID(Object const *o)
Definition Object.h:78
Definition Unit.h:769
MotionMaster * GetMotionMaster()
Definition Unit.h:1667
bool IsAlive() const
Definition Unit.h:1234
void EngageWithTarget(Unit *who)
Definition Unit.cpp:8292
bool HasUnitState(const uint32 f) const
Definition Unit.h:876
Map * GetMap() const
Definition Object.h:449
bool IsValidAttackTarget(WorldObject const *target, SpellInfo const *bySpell=nullptr) const
Definition Object.cpp:2856
uint32 GetInstanceId() const
Definition Object.h:365
auto MapEqualRange(M &map, typename M::key_type const &key)
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition MapUtils.h:29
ObjectGuid::LowType LeaderSpawnId
uint32 LeaderWaypointIDs[2]