TrinityCore
Map.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 "Map.h"
19#include "Battleground.h"
20#include "BattlegroundMgr.h"
21#include "BattlegroundScript.h"
22#include "CellImpl.h"
23#include "CharacterPackets.h"
24#include "Conversation.h"
25#include "DB2Stores.h"
26#include "DatabaseEnv.h"
27#include "DynamicTree.h"
28#include "GameObjectModel.h"
29#include "GameTime.h"
30#include "GridNotifiers.h"
31#include "GridNotifiersImpl.h"
32#include "GridStates.h"
33#include "Group.h"
34#include "InstanceLockMgr.h"
35#include "InstancePackets.h"
36#include "InstanceScenario.h"
37#include "InstanceScript.h"
38#include "Log.h"
39#include "MapManager.h"
40#include "MapUtils.h"
41#include "Metric.h"
42#include "MiscPackets.h"
43#include "MotionMaster.h"
44#include "ObjectAccessor.h"
45#include "ObjectGridLoader.h"
46#include "ObjectMgr.h"
47#include "Pet.h"
48#include "PhasingHandler.h"
49#include "PoolMgr.h"
50#include "ScriptMgr.h"
51#include "SpellAuras.h"
52#include "TerrainMgr.h"
53#include "Transport.h"
54#include "VMapFactory.h"
55#include "VMapManager2.h"
56#include "Vehicle.h"
57#include "Vignette.h"
58#include "VignettePackets.h"
59#include "Weather.h"
60#include "WeatherMgr.h"
61#include "World.h"
62#include "WorldSession.h"
63#include "WorldStateMgr.h"
64#include "WorldStatePackets.h"
65#include <boost/heap/fibonacci_heap.hpp>
66#include <sstream>
67
68#define DEFAULT_GRID_EXPIRY 300
69#define MAX_GRID_LOAD_TIME 50
70#define MAX_CREATURE_ATTACK_RADIUS (45.0f * sWorld->getRate(RATE_CREATURE_AGGRO))
71
73
74ZoneDynamicInfo::ZoneDynamicInfo() : MusicId(0), DefaultWeather(nullptr), WeatherId(WEATHER_STATE_FINE),
75 Intensity(0.0f) { }
76
78
80struct RespawnListContainer : boost::heap::fibonacci_heap<RespawnInfoWithHandle*, boost::heap::compare<CompareRespawnInfo>>
81{
82};
83
85{
86 explicit RespawnInfoWithHandle(RespawnInfo const& other) : RespawnInfo(other) { }
87
88 RespawnListContainer::handle_type handle;
89};
90
92{
93 // Delete all waiting spawns, else there will be a memory leak
94 // This doesn't delete from database.
96
97 while (!i_worldObjects.empty())
98 {
99 WorldObject* obj = *i_worldObjects.begin();
101 //ASSERT(obj->GetTypeId() == TYPEID_CORPSE);
102 obj->RemoveFromWorld();
103 obj->ResetMap();
104 }
105
106 if (!m_scriptSchedule.empty())
107 sMapMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size());
108
109 m_terrain->UnloadMMapInstance(GetId(), GetInstanceId());
110}
111
113{
114 for (uint32 cellX = 0; cellX < TOTAL_NUMBER_OF_CELLS_PER_MAP; cellX++)
115 for (uint32 cellY = 0; cellY < TOTAL_NUMBER_OF_CELLS_PER_MAP; cellY++)
117}
118
120{
125}
126
128{
133}
134
135Map::Map(uint32 id, time_t expiry, uint32 InstanceId, Difficulty SpawnMode) :
136_creatureToMoveLock(false), _gameObjectsToMoveLock(false), _dynamicObjectsToMoveLock(false), _areaTriggersToMoveLock(false),
137i_mapEntry(sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), i_InstanceId(InstanceId),
138m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE),
139m_VisibilityNotifyPeriod(DEFAULT_VISIBILITY_NOTIFY_PERIOD),
140m_activeNonPlayersIter(m_activeNonPlayers.end()), _transportsUpdateIter(_transports.end()),
141i_gridExpiry(expiry), m_terrain(sTerrainMgr.LoadTerrain(id)), m_forceEnabledNavMeshFilterFlags(0), m_forceDisabledNavMeshFilterFlags(0),
142i_scriptLock(false), _respawnTimes(std::make_unique<RespawnListContainer>()), _respawnCheckTimer(0), _vignetteUpdateTimer(5200, 5200)
143{
144 for (uint32 x = 0; x < MAX_NUMBER_OF_GRIDS; ++x)
145 {
146 for (uint32 y = 0; y < MAX_NUMBER_OF_GRIDS; ++y)
147 {
148 //z code
149 setNGrid(nullptr, x, y);
150 }
151 }
152
153 _zonePlayerCountMap.clear();
154
155 //lets initialize visibility distance for map
157
159
160 GetGuidSequenceGenerator(HighGuid::Transport).Set(sObjectMgr->GetGenerator<HighGuid::Transport>().GetNextAfterMaxUsed());
161
162 _poolData = sPoolMgr->InitPoolsForMap(this);
163
164 sTransportMgr->CreateTransportsForMap(this);
165
166 m_terrain->LoadMMapInstance(GetId(), GetInstanceId());
167
168 _worldStateValues = sWorldStateMgr->GetInitialWorldStatesForMap(this);
169}
170
172{
173 //init visibility for continents
176}
177
178// Template specialization of utility methods
179template<class T>
180void Map::AddToGrid(T* obj, Cell const& cell)
181{
182 NGridType* grid = getNGrid(cell.GridX(), cell.GridY());
183 if constexpr (WorldTypeMapContainer::TypeExists<T> && GridTypeMapContainer::TypeExists<T>)
184 {
185 NGridType::GridType& cellType = grid->GetGridType(cell.CellX(), cell.CellY());
186 if (obj->IsStoredInWorldObjectGridContainer())
187 cellType.AddWorldObject<T>(obj);
188 else
189 cellType.AddGridObject<T>(obj);
190 }
191 else if constexpr (WorldTypeMapContainer::TypeExists<T>)
192 grid->GetGridType(cell.CellX(), cell.CellY()).AddWorldObject<T>(obj);
193 else if constexpr (GridTypeMapContainer::TypeExists<T>)
194 grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject<T>(obj);
195
196 if constexpr (std::is_base_of_v<MapObject, T>)
197 obj->SetCurrentCell(cell);
198}
199
200template<>
201void Map::AddToGrid(Corpse* obj, Cell const& cell)
202{
203 NGridType* grid = getNGrid(cell.GridX(), cell.GridY());
204 // Corpses are a special object type - they can be added to grid via a call to AddToMap
205 // or loaded through ObjectGridLoader.
206 // Both corpses loaded from database and these freshly generated by Player::CreateCoprse are added to _corpsesByCell
207 // ObjectGridLoader loads all corpses from _corpsesByCell even if they were already added to grid before it was loaded
208 // so we need to explicitly check it here (Map::AddToGrid is only called from Player::BuildPlayerRepop, not from ObjectGridLoader)
209 // to avoid failing an assertion in GridObject::AddToGrid
210 if (grid->isGridObjectDataLoaded())
211 {
212 NGridType::GridType& cellType = grid->GetGridType(cell.CellX(), cell.CellY());
214 cellType.AddWorldObject(obj);
215 else
216 cellType.AddGridObject(obj);
217 }
218}
219
220template<class T>
221void Map::SwitchGridContainers(T* /*obj*/, bool /*on*/) { }
222
223template<>
225{
228 if (!p.IsCoordValid())
229 {
230 TC_LOG_ERROR("maps", "Map::SwitchGridContainers: Object {} has invalid coordinates X:{} Y:{} grid cell [{}:{}]", obj->GetGUID().ToString(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
231 return;
232 }
233
234 Cell cell(p);
236 return;
237
238 if (sLog->ShouldLog("maps", LOG_LEVEL_DEBUG))
239 {
240 // Extract bitfield values
241 uint32 const grid_x = cell.data.Part.grid_x;
242 uint32 const grid_y = cell.data.Part.grid_y;
243
244 TC_LOG_DEBUG("maps", "Switch object {} from grid[{}, {}] {}", obj->GetGUID().ToString(), grid_x, grid_y, on);
245 }
246
247 NGridType *ngrid = getNGrid(cell.GridX(), cell.GridY());
248 ASSERT(ngrid != nullptr);
249
250 GridType &grid = ngrid->GetGridType(cell.CellX(), cell.CellY());
251
252 obj->RemoveFromGrid(); //This step is not really necessary but we want to do ASSERT in remove/add
253
254 if (on)
255 {
256 grid.AddWorldObject(obj);
257 AddWorldObject(obj);
258 }
259 else
260 {
261 grid.AddGridObject(obj);
263 }
264
265 obj->m_isTempWorldObject = on;
266}
267
268template<class T>
270{
271 // Note: In case resurrectable corpse and pet its removed from global lists in own destructor
272 delete obj;
273}
274
275template<>
277{
279 RemoveUpdateObject(player);
280 delete player;
281}
282
283//Create NGrid so the object can be added to it
284//But object data is not loaded here
286{
287 if (!getNGrid(p.x_coord, p.y_coord))
288 {
289 TC_LOG_DEBUG("maps", "Creating grid[{}, {}] for map {} instance {}", p.x_coord, p.y_coord, GetId(), i_InstanceId);
290
292 setNGrid(ngrid, p.x_coord, p.y_coord);
293
294 // build a linkage between this map and NGridType
295 buildNGridLinkage(ngrid);
296
298
299 //z coord
300 int gx = (MAX_NUMBER_OF_GRIDS - 1) - p.x_coord;
301 int gy = (MAX_NUMBER_OF_GRIDS - 1) - p.y_coord;
302
303 m_terrain->LoadMapAndVMap(gx, gy);
304 }
305}
306
307//Load NGrid and make it active
309{
310 EnsureGridLoaded(cell);
311 NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
312 ASSERT(grid != nullptr);
313
314 if (object->IsPlayer())
315 GetMultiPersonalPhaseTracker().LoadGrid(object->GetPhaseShift(), *grid, this, cell);
316
317 // refresh grid state & timer
318 if (grid->GetGridState() != GRID_STATE_ACTIVE)
319 {
320 TC_LOG_DEBUG("maps", "Active object {} triggers loading of grid [{}, {}] on map {}", object->GetGUID().ToString(), cell.GridX(), cell.GridY(), GetId());
321 ResetGridExpiry(*grid, 0.1f);
323 }
324}
325
326//Create NGrid and load the object data in it
328{
329 EnsureGridCreated(GridCoord(cell.GridX(), cell.GridY()));
330 NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
331
332 ASSERT(grid != nullptr);
333 if (!isGridObjectDataLoaded(cell.GridX(), cell.GridY()))
334 {
335 TC_LOG_DEBUG("maps", "Loading grid[{}, {}] for map {} instance {}", cell.GridX(), cell.GridY(), GetId(), i_InstanceId);
336
337 setGridObjectDataLoaded(true, cell.GridX(), cell.GridY());
338
339 LoadGridObjects(grid, cell);
340
341 Balance();
342 return true;
343 }
344
345 return false;
346}
347
348void Map::LoadGridObjects(NGridType* grid, Cell const& cell)
349{
350 ObjectGridLoader loader(*grid, this, cell);
351 loader.LoadN();
352}
353
355{
356 // First make sure this grid is loaded
357 float gX = ((float(x) - 0.5f - CENTER_GRID_ID) * SIZE_OF_GRIDS) + (CENTER_GRID_OFFSET * 2);
358 float gY = ((float(y) - 0.5f - CENTER_GRID_ID) * SIZE_OF_GRIDS) + (CENTER_GRID_OFFSET * 2);
359 Cell cell = Cell(gX, gY);
360 EnsureGridLoaded(cell);
361
362 // Mark as don't unload
363 NGridType* grid = getNGrid(x, y);
364 grid->setUnloadExplicitLock(true);
365}
366
368{
369 // If grid is loaded, clear unload lock
370 if (IsGridLoaded(GridCoord(x, y)))
371 {
372 NGridType* grid = getNGrid(x, y);
373 grid->setUnloadExplicitLock(false);
374 }
375}
376
377void Map::LoadGrid(float x, float y)
378{
379 EnsureGridLoaded(Cell(x, y));
380}
381
382void Map::LoadGridForActiveObject(float x, float y, WorldObject const* object)
383{
385}
386
387bool Map::AddPlayerToMap(Player* player, bool initPlayer /*= true*/)
388{
389 CellCoord cellCoord = Trinity::ComputeCellCoord(player->GetPositionX(), player->GetPositionY());
390 if (!cellCoord.IsCoordValid())
391 {
392 TC_LOG_ERROR("maps", "Map::Add: Player {} has invalid coordinates X:{} Y:{} grid cell [{}:{}]", player->GetGUID().ToString(), player->GetPositionX(), player->GetPositionY(), cellCoord.x_coord, cellCoord.y_coord);
393 return false;
394 }
395
396 Cell cell(cellCoord);
398 AddToGrid(player, cell);
399
400 // Check if we are adding to correct map
401 ASSERT (player->GetMap() == this);
402 player->SetMap(this);
403 player->AddToWorld();
404
405 if (initPlayer)
406 SendInitSelf(player);
407
408 SendInitTransports(player);
409
410 if (initPlayer)
411 player->m_clientGUIDs.clear();
412
413 player->UpdateObjectVisibility(false);
415
416 if (Instanceable())
418
419 if (player->IsAlive())
420 ConvertCorpseToBones(player->GetGUID());
421
422 sScriptMgr->OnPlayerEnterMap(this, player);
423 return true;
424}
425
427{
428 Cell cell(player->GetPositionX(), player->GetPositionY());
429 GetMultiPersonalPhaseTracker().OnOwnerPhaseChanged(player, getNGrid(cell.GridX(), cell.GridY()), this, cell);
430}
431
433{
434 if (int32 const* value = Trinity::Containers::MapGetValuePtr(_worldStateValues, worldStateId))
435 return *value;
436
437 return 0;
438}
439
440void Map::SetWorldStateValue(int32 worldStateId, int32 value, bool hidden)
441{
442 auto [itr, inserted] = _worldStateValues.try_emplace(worldStateId, 0);
443 int32 oldValue = itr->second;
444 if (oldValue == value && !inserted)
445 return;
446
447 itr->second = value;
448
449 WorldStateTemplate const* worldStateTemplate = sWorldStateMgr->GetWorldStateTemplate(worldStateId);
450 if (worldStateTemplate)
451 sScriptMgr->OnWorldStateValueChange(worldStateTemplate, oldValue, value, this);
452
453 // Broadcast update to all players on the map
455 updateWorldState.VariableID = worldStateId;
456 updateWorldState.Value = value;
457 updateWorldState.Hidden = hidden;
458 updateWorldState.Write();
459
460 for (MapReference const& mapReference : m_mapRefManager)
461 {
462 if (worldStateTemplate && !worldStateTemplate->AreaIds.empty())
463 {
464 bool isInAllowedArea = std::any_of(worldStateTemplate->AreaIds.begin(), worldStateTemplate->AreaIds.end(),
465 [playerAreaId = mapReference.GetSource()->GetAreaId()](uint32 requiredAreaId) { return DB2Manager::IsInArea(playerAreaId, requiredAreaId); });
466 if (!isInAllowedArea)
467 continue;
468 }
469
470 mapReference.GetSource()->SendDirectMessage(updateWorldState.GetRawPacket());
471 }
472}
473
475{
476 _infiniteAOIVignettes.push_back(vignette);
477
479 vignette->FillPacket(vignetteUpdate.Added);
480 vignetteUpdate.Write();
481
482 for (MapReference const& ref : m_mapRefManager)
483 if (Vignettes::CanSee(ref.GetSource(), *vignette))
484 ref.GetSource()->SendDirectMessage(vignetteUpdate.GetRawPacket());
485}
486
488{
489 if (!std::erase(_infiniteAOIVignettes, vignette))
490 return;
491
493 vignetteUpdate.Removed.push_back(vignette->Guid);
494 vignetteUpdate.Write();
495
496 if (vignette->Data->GetFlags().HasFlag(VignetteFlags::ZoneInfiniteAOI))
497 {
498 for (MapReference const& ref : m_mapRefManager)
499 if (ref.GetSource()->GetZoneId() == vignette->ZoneID)
500 ref.GetSource()->SendDirectMessage(vignetteUpdate.GetRawPacket());
501 }
502 else
503 SendToPlayers(vignetteUpdate.GetRawPacket());
504}
505
506template<class T>
507void Map::InitializeObject(T* /*obj*/) { }
508
509template<>
511{
513}
514
515template<>
517{
519}
520
521template<class T>
522bool Map::AddToMap(T* obj)
523{
525 if (obj->IsInWorld())
526 {
527 ASSERT(obj->IsInGrid());
528 obj->UpdateObjectVisibility(true);
529 return true;
530 }
531
532 CellCoord cellCoord = Trinity::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY());
533 //It will create many problems (including crashes) if an object is not added to grid after creation
534 //The correct way to fix it is to make AddToMap return false and delete the object if it is not added to grid
535 //But now AddToMap is used in too many places, I will just see how many ASSERT failures it will cause
536 ASSERT(cellCoord.IsCoordValid());
537 if (!cellCoord.IsCoordValid())
538 {
539 TC_LOG_ERROR("maps", "Map::Add: Object {} has invalid coordinates X:{} Y:{} grid cell [{}:{}]", obj->GetGUID().ToString(), obj->GetPositionX(), obj->GetPositionY(), cellCoord.x_coord, cellCoord.y_coord);
540 return false; //Should delete object
541 }
542
543 if (IsAlwaysActive())
544 obj->setActive(true);
545
546 Cell cell(cellCoord);
547 if (obj->isActiveObject())
549 else
550 EnsureGridCreated(GridCoord(cell.GridX(), cell.GridY()));
551 AddToGrid(obj, cell);
552 TC_LOG_DEBUG("maps", "Object {} enters grid[{}, {}]", obj->GetGUID().ToString(), cell.GridX(), cell.GridY());
553
554 //Must already be set before AddToMap. Usually during obj->Create.
555 //obj->SetMap(this);
556 obj->AddToWorld();
557
558 InitializeObject(obj);
559
560 if (obj->isActiveObject())
561 AddToActive(obj);
562
563 //something, such as vehicle, needs to be update immediately
564 //also, trigger needs to cast spell, if not update, cannot see visual
565 obj->SetIsNewObject(true);
566 obj->UpdateObjectVisibilityOnCreate();
567 obj->SetIsNewObject(false);
568 return true;
569}
570
571template<>
573{
574 //TODO: Needs clean up. An object should not be added to map twice.
575 if (obj->IsInWorld())
576 return true;
577
579 if (!cellCoord.IsCoordValid())
580 {
581 TC_LOG_ERROR("maps", "Map::Add: Object {} has invalid coordinates X:{} Y:{} grid cell [{}:{}]", obj->GetGUID().ToString(), obj->GetPositionX(), obj->GetPositionY(), cellCoord.x_coord, cellCoord.y_coord);
582 return false; //Should delete object
583 }
584
585 _transports.insert(obj);
586
587 if (obj->GetExpectedMapId() == GetId())
588 {
589 obj->AddToWorld();
590
591 // Broadcast creation to players
592 for (Map::PlayerList::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
593 {
594 if (itr->GetSource()->GetTransport() != obj && itr->GetSource()->InSamePhase(obj))
595 {
596 UpdateData data(GetId());
597 obj->BuildCreateUpdateBlockForPlayer(&data, itr->GetSource());
598 itr->GetSource()->m_visibleTransports.insert(obj->GetGUID());
599 WorldPacket packet;
600 data.BuildPacket(&packet);
601 itr->GetSource()->SendDirectMessage(&packet);
602 }
603 }
604 }
605
606 return true;
607}
608
609bool Map::IsGridLoaded(GridCoord const& p) const
610{
612}
613
615{
616 // Check for valid position
617 if (!obj->IsPositionValid())
618 return;
619
620 // Update mobs/objects in ALL visible cells around object!
622
623 for (uint32 x = area.low_bound.x_coord; x <= area.high_bound.x_coord; ++x)
624 {
625 for (uint32 y = area.low_bound.y_coord; y <= area.high_bound.y_coord; ++y)
626 {
627 // marked cells are those that have been visited
628 // don't visit the same cell twice
629 uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x;
630 if (isCellMarked(cell_id))
631 continue;
632
633 markCell(cell_id);
634 CellCoord pair(x, y);
635 Cell cell(pair);
636 cell.SetNoCreate();
637 Visit(cell, gridVisitor);
638 Visit(cell, worldVisitor);
639 }
640 }
641}
642
644{
645 // Nothing to do if no change
646 if (oldZone == newZone)
647 return;
648
649 if (oldZone != MAP_INVALID_ZONE)
650 {
651 uint32& oldZoneCount = _zonePlayerCountMap[oldZone];
652 ASSERT(oldZoneCount, "A player left zone %u (went to %u) - but there were no players in the zone!", oldZone, newZone);
653 --oldZoneCount;
654 }
655 ++_zonePlayerCountMap[newZone];
656}
657
658void Map::Update(uint32 t_diff)
659{
660 _dynamicTree.update(t_diff);
663 {
664 Player* player = m_mapRefIter->GetSource();
665 if (player && player->IsInWorld())
666 {
667 //player->Update(t_diff);
668 WorldSession* session = player->GetSession();
669 MapSessionFilter updater(session);
670 session->Update(t_diff, updater);
671 }
672 }
673
675 if (_respawnCheckTimer <= t_diff)
676 {
680 }
681 else
682 _respawnCheckTimer -= t_diff;
683
686
687 Trinity::ObjectUpdater updater(t_diff);
688 // for creature
690 // for pets
692
693 // the player iterator is stored in the map object
694 // to make sure calls to Map::Remove don't invalidate it
696 {
697 Player* player = m_mapRefIter->GetSource();
698
699 if (!player || !player->IsInWorld())
700 continue;
701
702 // update players at tick
703 player->Update(t_diff);
704
705 VisitNearbyCellsOf(player, grid_object_update, world_object_update);
706
707 // If player is using far sight or mind vision, visit that object too
708 if (WorldObject* viewPoint = player->GetViewpoint())
709 VisitNearbyCellsOf(viewPoint, grid_object_update, world_object_update);
710
711 // Handle updates for creatures in combat with player and are more than 60 yards away
712 if (player->IsInCombat())
713 {
714 std::vector<Unit*> toVisit;
715 for (auto const& pair : player->GetCombatManager().GetPvECombatRefs())
716 if (Creature* unit = pair.second->GetOther(player)->ToCreature())
717 if (unit->GetMapId() == player->GetMapId() && !unit->IsWithinDistInMap(player, GetVisibilityRange(), false))
718 toVisit.push_back(unit);
719 for (Unit* unit : toVisit)
720 VisitNearbyCellsOf(unit, grid_object_update, world_object_update);
721 }
722
723 { // Update any creatures that own auras the player has applications of
724 std::unordered_set<Unit*> toVisit;
725 for (std::pair<uint32, AuraApplication*> pair : player->GetAppliedAuras())
726 {
727 if (Unit* caster = pair.second->GetBase()->GetCaster())
728 if (caster->GetTypeId() != TYPEID_PLAYER && !caster->IsWithinDistInMap(player, GetVisibilityRange(), false))
729 toVisit.insert(caster);
730 }
731 for (Unit* unit : toVisit)
732 VisitNearbyCellsOf(unit, grid_object_update, world_object_update);
733 }
734
735 { // Update player's summons
736 std::vector<Unit*> toVisit;
737
738 // Totems
739 for (ObjectGuid const& summonGuid : player->m_SummonSlot)
740 if (!summonGuid.IsEmpty())
741 if (Creature* unit = GetCreature(summonGuid))
742 if (unit->GetMapId() == player->GetMapId() && !unit->IsWithinDistInMap(player, GetVisibilityRange(), false))
743 toVisit.push_back(unit);
744
745 for (Unit* unit : toVisit)
746 VisitNearbyCellsOf(unit, grid_object_update, world_object_update);
747 }
748 }
749
750 // non-player active objects, increasing iterator in the loop in case of object removal
752 {
755
756 if (!obj || !obj->IsInWorld())
757 continue;
758
759 VisitNearbyCellsOf(obj, grid_object_update, world_object_update);
760 }
761
763 {
766 obj->Update(t_diff);
767 }
768
769 if (_vignetteUpdateTimer.Update(t_diff))
770 {
772 {
773 if (vignette->NeedUpdate)
774 {
776 vignette->FillPacket(vignetteUpdate.Updated);
777 vignetteUpdate.Write();
778 for (MapReference const& ref : m_mapRefManager)
779 if (Vignettes::CanSee(ref.GetSource(), *vignette))
780 ref.GetSource()->SendDirectMessage(vignetteUpdate.GetRawPacket());
781
782 vignette->NeedUpdate = false;
783 }
784 }
785 }
786
788
790 if (!m_scriptSchedule.empty())
791 {
792 i_scriptLock = true;
794 i_scriptLock = false;
795 }
796
799 {
800 for (auto&& zoneInfo : _zoneDynamicInfo)
801 if (zoneInfo.second.DefaultWeather && !zoneInfo.second.DefaultWeather->Update(_weatherUpdateTimer.GetInterval()))
802 zoneInfo.second.DefaultWeather.reset();
803
805 }
806
807 // update phase shift objects
808 GetMultiPersonalPhaseTracker().Update(this, t_diff);
809
813
814 if (!m_mapRefManager.isEmpty() || !m_activeNonPlayers.empty())
816
817 sScriptMgr->OnMapUpdate(this, t_diff);
818
819 TC_METRIC_VALUE("map_creatures", uint64(GetObjectsStore().Size<Creature>()),
820 TC_METRIC_TAG("map_id", std::to_string(GetId())),
821 TC_METRIC_TAG("map_instanceid", std::to_string(GetInstanceId())));
822
823 TC_METRIC_VALUE("map_gameobjects", uint64(GetObjectsStore().Size<GameObject>()),
824 TC_METRIC_TAG("map_id", std::to_string(GetId())),
825 TC_METRIC_TAG("map_instanceid", std::to_string(GetInstanceId())));
826}
827
829{
830 template<class T>inline void resetNotify(GridRefManager<T> &m)
831 {
832 for (typename GridRefManager<T>::iterator iter=m.begin(); iter != m.end(); ++iter)
833 iter->GetSource()->ResetAllNotifies();
834 }
835 template<class T> void Visit(GridRefManager<T> &) { }
836 void Visit(CreatureMapType &m) { resetNotify<Creature>(m);}
837 void Visit(PlayerMapType &m) { resetNotify<Player>(m);}
838};
839
841{
843 {
844 NGridType *grid = i->GetSource();
845
846 if (grid->GetGridState() != GRID_STATE_ACTIVE)
847 continue;
848
850 if (!grid->getGridInfoRef()->getRelocationTimer().TPassed())
851 continue;
852
853 uint32 gx = grid->getX(), gy = grid->getY();
854
856 CellCoord cell_max(cell_min.x_coord + MAX_NUMBER_OF_CELLS, cell_min.y_coord+MAX_NUMBER_OF_CELLS);
857
858 for (uint32 x = cell_min.x_coord; x < cell_max.x_coord; ++x)
859 {
860 for (uint32 y = cell_min.y_coord; y < cell_max.y_coord; ++y)
861 {
862 uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x;
863 if (!isCellMarked(cell_id))
864 continue;
865
866 CellCoord pair(x, y);
867 Cell cell(pair);
868 cell.SetNoCreate();
869
870 Trinity::DelayedUnitRelocation cell_relocation(cell, pair, *this, MAX_VISIBILITY_DISTANCE);
873 Visit(cell, grid_object_relocation);
874 Visit(cell, world_object_relocation);
875 }
876 }
877 }
878
879 ResetNotifier reset;
883 {
884 NGridType *grid = i->GetSource();
885
886 if (grid->GetGridState() != GRID_STATE_ACTIVE)
887 continue;
888
889 if (!grid->getGridInfoRef()->getRelocationTimer().TPassed())
890 continue;
891
893
894 uint32 gx = grid->getX(), gy = grid->getY();
895
897 CellCoord cell_max(cell_min.x_coord + MAX_NUMBER_OF_CELLS, cell_min.y_coord+MAX_NUMBER_OF_CELLS);
898
899 for (uint32 x = cell_min.x_coord; x < cell_max.x_coord; ++x)
900 {
901 for (uint32 y = cell_min.y_coord; y < cell_max.y_coord; ++y)
902 {
903 uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x;
904 if (!isCellMarked(cell_id))
905 continue;
906
907 CellCoord pair(x, y);
908 Cell cell(pair);
909 cell.SetNoCreate();
910 Visit(cell, grid_notifier);
911 Visit(cell, world_notifier);
912 }
913 }
914 }
915}
916
917void Map::RemovePlayerFromMap(Player* player, bool remove)
918{
919 // Before leaving map, update zone/area for stats
920 player->UpdateZone(MAP_INVALID_ZONE, 0);
921 sScriptMgr->OnPlayerLeaveMap(this, player);
922
924
925 player->CombatStop();
926
927 bool const inWorld = player->IsInWorld();
928 player->RemoveFromWorld();
929 SendRemoveTransports(player);
930
931 if (!inWorld) // if was in world, RemoveFromWorld() called DestroyForNearbyPlayers()
933
934 if (player->IsInGrid())
935 player->RemoveFromGrid();
936 else
937 ASSERT(remove); //maybe deleted in logoutplayer when player is not in a map
938
939 if (remove)
940 DeleteFromWorld(player);
941}
942
943template<class T>
944void Map::RemoveFromMap(T *obj, bool remove)
945{
946 bool const inWorld = obj->IsInWorld() && obj->GetTypeId() >= TYPEID_UNIT && obj->GetTypeId() <= TYPEID_GAMEOBJECT;
947 obj->RemoveFromWorld();
948 if (obj->isActiveObject())
949 RemoveFromActive(obj);
950
952
953 if (!inWorld) // if was in world, RemoveFromWorld() called DestroyForNearbyPlayers()
954 obj->UpdateObjectVisibilityOnDestroy();
955
956 obj->RemoveFromGrid();
957
958 obj->ResetMap();
959
960 if (remove)
961 DeleteFromWorld(obj);
962}
963
964template<>
965void Map::RemoveFromMap(Transport* obj, bool remove)
966{
967 if (obj->IsInWorld())
968 {
969 obj->RemoveFromWorld();
970
971 UpdateData data(GetId());
972 if (obj->IsDestroyedObject())
973 obj->BuildDestroyUpdateBlock(&data);
974 else
975 obj->BuildOutOfRangeUpdateBlock(&data);
976
977 WorldPacket packet;
978 data.BuildPacket(&packet);
979 for (Map::PlayerList::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
980 {
981 if (itr->GetSource()->GetTransport() != obj && itr->GetSource()->m_visibleTransports.count(obj->GetGUID()))
982 {
983 itr->GetSource()->SendDirectMessage(&packet);
984 itr->GetSource()->m_visibleTransports.erase(obj->GetGUID());
985 }
986 }
987 }
988
990 {
991 TransportsContainer::iterator itr = _transports.find(obj);
992 if (itr == _transports.end())
993 return;
994 if (itr == _transportsUpdateIter)
996 _transports.erase(itr);
997 }
998 else
999 _transports.erase(obj);
1000
1001 obj->ResetMap();
1002
1003 if (remove)
1004 DeleteFromWorld(obj);
1005}
1006
1007template <typename T>
1008/*static*/ bool Map::CheckGridIntegrity(T* object, bool moved, char const* objType)
1009{
1010 Cell const& cur_cell = object->GetCurrentCell();
1011 Cell xy_cell(object->GetPositionX(), object->GetPositionY());
1012 if (xy_cell != cur_cell)
1013 {
1014 TC_LOG_DEBUG("maps", "{} {} X: {} Y: {} ({}) is in grid[{}, {}]cell[{}, {}] instead of grid[{}, {}]cell[{}, {}]",
1015 objType, object->GetGUID().ToString(),
1016 object->GetPositionX(), object->GetPositionY(), (moved ? "final" : "original"),
1017 cur_cell.GridX(), cur_cell.GridY(), cur_cell.CellX(), cur_cell.CellY(),
1018 xy_cell.GridX(), xy_cell.GridY(), xy_cell.CellX(), xy_cell.CellY());
1019 return true; // not crash at error, just output error in debug mode
1020 }
1021
1022 return true;
1023}
1024
1025void Map::PlayerRelocation(Player* player, float x, float y, float z, float orientation)
1026{
1027 ASSERT(player);
1028
1029 Cell old_cell(player->GetPositionX(), player->GetPositionY());
1030 Cell new_cell(x, y);
1031
1032 player->Relocate(x, y, z, orientation);
1033 if (player->IsVehicle())
1034 player->GetVehicleKit()->RelocatePassengers();
1035
1036 if (old_cell.DiffGrid(new_cell) || old_cell.DiffCell(new_cell))
1037 {
1038 TC_LOG_DEBUG("maps", "Player {} relocation grid[{}, {}]cell[{}, {}]->grid[{}, {}]cell[{}, {}]", player->GetName(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
1039
1040 player->RemoveFromGrid();
1041
1042 if (old_cell.DiffGrid(new_cell))
1043 EnsureGridLoadedForActiveObject(new_cell, player);
1044
1045 AddToGrid(player, new_cell);
1046 }
1047
1048 player->UpdatePositionData();
1049 player->UpdateObjectVisibility(false);
1050}
1051
1052void Map::CreatureRelocation(Creature* creature, float x, float y, float z, float ang, bool respawnRelocationOnFail)
1053{
1054 ASSERT(CheckGridIntegrity(creature, false, "Creature"));
1055
1056 Cell new_cell(x, y);
1057
1058 if (!respawnRelocationOnFail && !getNGrid(new_cell.GridX(), new_cell.GridY()))
1059 return;
1060
1061 Cell old_cell = creature->GetCurrentCell();
1062 // delay creature move for grid/cell to grid/cell moves
1063 if (old_cell.DiffCell(new_cell) || old_cell.DiffGrid(new_cell))
1064 {
1065#ifdef TRINITY_DEBUG
1066 TC_LOG_DEBUG("maps", "Creature {} added to moving list from grid[{}, {}]cell[{}, {}] to grid[{}, {}]cell[{}, {}].", creature->GetGUID().ToString(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
1067#endif
1068 AddCreatureToMoveList(creature, x, y, z, ang);
1069 // in diffcell/diffgrid case notifiers called at finishing move creature in Map::MoveAllCreaturesInMoveList
1070 }
1071 else
1072 {
1073 creature->Relocate(x, y, z, ang);
1074 if (creature->IsVehicle())
1075 creature->GetVehicleKit()->RelocatePassengers();
1076 creature->UpdateObjectVisibility(false);
1077 creature->UpdatePositionData();
1079 }
1080
1081 ASSERT(CheckGridIntegrity(creature, true, "Creature"));
1082}
1083
1084void Map::GameObjectRelocation(GameObject* go, float x, float y, float z, float orientation, bool respawnRelocationOnFail)
1085{
1086 ASSERT(CheckGridIntegrity(go, false, "GameObject"));
1087 Cell new_cell(x, y);
1088
1089 if (!respawnRelocationOnFail && !getNGrid(new_cell.GridX(), new_cell.GridY()))
1090 return;
1091
1092 Cell old_cell = go->GetCurrentCell();
1093
1094 // delay creature move for grid/cell to grid/cell moves
1095 if (old_cell.DiffCell(new_cell) || old_cell.DiffGrid(new_cell))
1096 {
1097#ifdef TRINITY_DEBUG
1098 TC_LOG_DEBUG("maps", "GameObject {} added to moving list from grid[{}, {}]cell[{}, {}] to grid[{}, {}]cell[{}, {}].", go->GetGUID().ToString(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
1099#endif
1100 AddGameObjectToMoveList(go, x, y, z, orientation);
1101 // in diffcell/diffgrid case notifiers called at finishing move go in Map::MoveAllGameObjectsInMoveList
1102 }
1103 else
1104 {
1105 go->Relocate(x, y, z, orientation);
1106 go->AfterRelocation();
1108 }
1109
1110 ASSERT(CheckGridIntegrity(go, true, "GameObject"));
1111}
1112
1113void Map::DynamicObjectRelocation(DynamicObject* dynObj, float x, float y, float z, float orientation)
1114{
1115 ASSERT(CheckGridIntegrity(dynObj, false, "DynamicObject"));
1116 Cell new_cell(x, y);
1117
1118 if (!getNGrid(new_cell.GridX(), new_cell.GridY()))
1119 return;
1120
1121 Cell old_cell = dynObj->GetCurrentCell();
1122
1123 // delay creature move for grid/cell to grid/cell moves
1124 if (old_cell.DiffCell(new_cell) || old_cell.DiffGrid(new_cell))
1125 {
1126#ifdef TRINITY_DEBUG
1127 TC_LOG_DEBUG("maps", "GameObject {} added to moving list from grid[{}, {}]cell[{}, {}] to grid[{}, {}]cell[{}, {}].", dynObj->GetGUID().ToString(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
1128#endif
1129 AddDynamicObjectToMoveList(dynObj, x, y, z, orientation);
1130 // in diffcell/diffgrid case notifiers called at finishing move dynObj in Map::MoveAllGameObjectsInMoveList
1131 }
1132 else
1133 {
1134 dynObj->Relocate(x, y, z, orientation);
1135 dynObj->UpdatePositionData();
1136 dynObj->UpdateObjectVisibility(false);
1138 }
1139
1140 ASSERT(CheckGridIntegrity(dynObj, true, "DynamicObject"));
1141}
1142
1143void Map::AreaTriggerRelocation(AreaTrigger* at, float x, float y, float z, float orientation)
1144{
1145 ASSERT(CheckGridIntegrity(at, false, "AreaTrigger"));
1146 Cell new_cell(x, y);
1147
1148 if (!getNGrid(new_cell.GridX(), new_cell.GridY()))
1149 return;
1150
1151 Cell old_cell = at->GetCurrentCell();
1152
1153 // delay areatrigger move for grid/cell to grid/cell moves
1154 if (old_cell.DiffCell(new_cell) || old_cell.DiffGrid(new_cell))
1155 {
1156#ifdef TRINITY_DEBUG
1157 TC_LOG_DEBUG("maps", "AreaTrigger ({}) added to moving list from grid[{}, {}]cell[{}, {}] to grid[{}, {}]cell[{}, {}].", at->GetGUID().ToString(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
1158#endif
1159 AddAreaTriggerToMoveList(at, x, y, z, orientation);
1160 // in diffcell/diffgrid case notifiers called at finishing move at in Map::MoveAllAreaTriggersInMoveList
1161 }
1162 else
1163 {
1164 at->Relocate(x, y, z, orientation);
1165 at->UpdateShape();
1166 at->UpdateObjectVisibility(false);
1168 }
1169
1170 ASSERT(CheckGridIntegrity(at, true, "AreaTrigger"));
1171}
1172
1173void Map::AddCreatureToMoveList(Creature* c, float x, float y, float z, float ang)
1174{
1175 if (_creatureToMoveLock) //can this happen?
1176 return;
1177
1179 _creaturesToMove.push_back(c);
1180 c->SetNewCellPosition(x, y, z, ang);
1181}
1182
1184{
1185 if (_creatureToMoveLock) //can this happen?
1186 return;
1187
1190}
1191
1192void Map::AddGameObjectToMoveList(GameObject* go, float x, float y, float z, float ang)
1193{
1194 if (_gameObjectsToMoveLock) //can this happen?
1195 return;
1196
1198 _gameObjectsToMove.push_back(go);
1199 go->SetNewCellPosition(x, y, z, ang);
1200}
1201
1203{
1204 if (_gameObjectsToMoveLock) //can this happen?
1205 return;
1206
1209}
1210
1211void Map::AddDynamicObjectToMoveList(DynamicObject* dynObj, float x, float y, float z, float ang)
1212{
1213 if (_dynamicObjectsToMoveLock) //can this happen?
1214 return;
1215
1217 _dynamicObjectsToMove.push_back(dynObj);
1218 dynObj->SetNewCellPosition(x, y, z, ang);
1219}
1220
1222{
1223 if (_dynamicObjectsToMoveLock) //can this happen?
1224 return;
1225
1228}
1229
1230void Map::AddAreaTriggerToMoveList(AreaTrigger* at, float x, float y, float z, float ang)
1231{
1232 if (_areaTriggersToMoveLock) //can this happen?
1233 return;
1234
1236 _areaTriggersToMove.push_back(at);
1237 at->SetNewCellPosition(x, y, z, ang);
1238}
1239
1241{
1242 if (_areaTriggersToMoveLock) //can this happen?
1243 return;
1244
1247}
1248
1250{
1251 _creatureToMoveLock = true;
1252 for (std::vector<Creature*>::iterator itr = _creaturesToMove.begin(); itr != _creaturesToMove.end(); ++itr)
1253 {
1254 Creature* c = *itr;
1255 if (c->FindMap() != this) //pet is teleported to another map
1256 continue;
1257
1259 {
1261 continue;
1262 }
1263
1265 if (!c->IsInWorld())
1266 continue;
1267
1268 // do move or do move to respawn or remove creature if previous all fail
1270 {
1271 // update pos
1272 c->Relocate(c->_newPosition);
1273 if (c->IsVehicle())
1275 //CreatureRelocationNotify(c, new_cell, new_cell.cellCoord());
1276 c->UpdatePositionData();
1277 c->UpdateObjectVisibility(false);
1278 }
1279 else
1280 {
1281 // if creature can't be move in new cell/grid (not loaded) move it to repawn cell/grid
1282 // creature coordinates will be updated and notifiers send
1283 if (!CreatureRespawnRelocation(c, false))
1284 {
1285 // ... or unload (if respawn grid also not loaded)
1286#ifdef TRINITY_DEBUG
1287 TC_LOG_DEBUG("maps", "Creature {} cannot be move to unloaded respawn grid.", c->GetGUID().ToString());
1288#endif
1289 //AddObjectToRemoveList(Pet*) should only be called in Pet::Remove
1290 //This may happen when a player just logs in and a pet moves to a nearby unloaded cell
1291 //To avoid this, we can load nearby cells when player log in
1292 //But this check is always needed to ensure safety
1294 //need to check why pet is frequently relocated to an unloaded cell
1295 if (c->IsPet())
1296 ((Pet*)c)->Remove(PET_SAVE_NOT_IN_SLOT, true);
1297 else
1299 }
1300 }
1301 }
1302 _creaturesToMove.clear();
1303 _creatureToMoveLock = false;
1304}
1305
1307{
1309 for (std::vector<GameObject*>::iterator itr = _gameObjectsToMove.begin(); itr != _gameObjectsToMove.end(); ++itr)
1310 {
1311 GameObject* go = *itr;
1312 if (go->FindMap() != this) //transport is teleported to another map
1313 continue;
1314
1316 {
1318 continue;
1319 }
1320
1322 if (!go->IsInWorld())
1323 continue;
1324
1325 // do move or do move to respawn or remove creature if previous all fail
1327 {
1328 // update pos
1329 go->Relocate(go->_newPosition);
1330 go->AfterRelocation();
1331 }
1332 else
1333 {
1334 // if GameObject can't be move in new cell/grid (not loaded) move it to repawn cell/grid
1335 // GameObject coordinates will be updated and notifiers send
1336 if (!GameObjectRespawnRelocation(go, false))
1337 {
1338 // ... or unload (if respawn grid also not loaded)
1339#ifdef TRINITY_DEBUG
1340 TC_LOG_DEBUG("maps", "GameObject {} cannot be move to unloaded respawn grid.", go->GetGUID().ToString());
1341#endif
1343 }
1344 }
1345 }
1346 _gameObjectsToMove.clear();
1347 _gameObjectsToMoveLock = false;
1348}
1349
1351{
1353 for (std::vector<DynamicObject*>::iterator itr = _dynamicObjectsToMove.begin(); itr != _dynamicObjectsToMove.end(); ++itr)
1354 {
1355 DynamicObject* dynObj = *itr;
1356 if (dynObj->FindMap() != this) //transport is teleported to another map
1357 continue;
1358
1360 {
1362 continue;
1363 }
1364
1366 if (!dynObj->IsInWorld())
1367 continue;
1368
1369 // do move or do move to respawn or remove creature if previous all fail
1371 {
1372 // update pos
1373 dynObj->Relocate(dynObj->_newPosition);
1374 dynObj->UpdatePositionData();
1375 dynObj->UpdateObjectVisibility(false);
1376 }
1377 else
1378 {
1379#ifdef TRINITY_DEBUG
1380 TC_LOG_DEBUG("maps", "DynamicObject {} cannot be moved to unloaded grid.", dynObj->GetGUID().ToString());
1381#endif
1382 }
1383 }
1384
1385 _dynamicObjectsToMove.clear();
1387}
1388
1390{
1392 for (std::vector<AreaTrigger*>::iterator itr = _areaTriggersToMove.begin(); itr != _areaTriggersToMove.end(); ++itr)
1393 {
1394 AreaTrigger* at = *itr;
1395 if (at->FindMap() != this) //transport is teleported to another map
1396 continue;
1397
1399 {
1401 continue;
1402 }
1403
1405 if (!at->IsInWorld())
1406 continue;
1407
1408 // do move or do move to respawn or remove creature if previous all fail
1410 {
1411 // update pos
1412 at->Relocate(at->_newPosition);
1413 at->UpdateShape();
1414 at->UpdateObjectVisibility(false);
1415 }
1416 else
1417 {
1418#ifdef TRINITY_DEBUG
1419 TC_LOG_DEBUG("maps", "AreaTrigger {} cannot be moved to unloaded grid.", at->GetGUID().ToString());
1420#endif
1421 }
1422 }
1423
1424 _areaTriggersToMove.clear();
1426}
1427
1428template <typename T>
1429bool Map::MapObjectCellRelocation(T* object, Cell new_cell, [[maybe_unused]] char const* objType)
1430{
1431 Cell const& old_cell = object->GetCurrentCell();
1432 if (!old_cell.DiffGrid(new_cell)) // in same grid
1433 {
1434 // if in same cell then none do
1435 if (old_cell.DiffCell(new_cell))
1436 {
1437#ifdef TRINITY_DEBUG
1438 TC_LOG_DEBUG("maps", "{} {} moved in grid[{}, {}] from cell[{}, {}] to cell[{}, {}].", objType, object->GetGUID().ToString(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.CellX(), new_cell.CellY());
1439#endif
1440
1441 object->RemoveFromGrid();
1442 AddToGrid(object, new_cell);
1443 }
1444 else
1445 {
1446#ifdef TRINITY_DEBUG
1447 TC_LOG_DEBUG("maps", "{} {} moved in same grid[{}, {}]cell[{}, {}].", objType, object->GetGUID().ToString(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY());
1448#endif
1449 }
1450
1451 return true;
1452 }
1453
1454 // in diff. grids but active creature
1455 if (object->isActiveObject())
1456 {
1457 EnsureGridLoadedForActiveObject(new_cell, object);
1458
1459#ifdef TRINITY_DEBUG
1460 TC_LOG_DEBUG("maps", "Active {} {} moved from grid[{}, {}]cell[{}, {}] to grid[{}, {}]cell[{}, {}].", objType, object->GetGUID().ToString(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
1461#endif
1462
1463 object->RemoveFromGrid();
1464 AddToGrid(object, new_cell);
1465
1466 return true;
1467 }
1468
1469 if (Creature* c = object->ToCreature())
1470 if (c->GetCharmerOrOwnerGUID().IsPlayer())
1471 EnsureGridLoaded(new_cell);
1472
1473 // in diff. loaded grid normal object
1474 if (IsGridLoaded(GridCoord(new_cell.GridX(), new_cell.GridY())))
1475 {
1476#ifdef TRINITY_DEBUG
1477 TC_LOG_DEBUG("maps", "{} {} moved from grid[{}, {}]cell[{}, {}] to grid[{}, {}]cell[{}, {}].", objType, object->GetGUID().ToString(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
1478#endif
1479
1480 object->RemoveFromGrid();
1481 EnsureGridCreated(GridCoord(new_cell.GridX(), new_cell.GridY()));
1482 AddToGrid(object, new_cell);
1483
1484 return true;
1485 }
1486
1487 // fail to move: normal object attempt move to unloaded grid
1488#ifdef TRINITY_DEBUG
1489 TC_LOG_DEBUG("maps", "{} {} attempted to move from grid[{}, {}]cell[{}, {}] to unloaded grid[{}, {}]cell[{}, {}].", objType, object->GetGUID().ToString(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
1490#endif
1491 return false;
1492}
1493
1495{
1496 return MapObjectCellRelocation(c, new_cell, "Creature");
1497}
1498
1500{
1501 return MapObjectCellRelocation(go, new_cell, "GameObject");
1502}
1503
1505{
1506 return MapObjectCellRelocation(go, new_cell, "DynamicObject");
1507}
1508
1510{
1511 return MapObjectCellRelocation(at, new_cell, "AreaTrigger");
1512}
1513
1514bool Map::CreatureRespawnRelocation(Creature* c, bool diffGridOnly)
1515{
1516 float resp_x, resp_y, resp_z, resp_o;
1517 c->GetRespawnPosition(resp_x, resp_y, resp_z, &resp_o);
1518 Cell resp_cell(resp_x, resp_y);
1519
1520 //creature will be unloaded with grid
1521 if (diffGridOnly && !c->GetCurrentCell().DiffGrid(resp_cell))
1522 return true;
1523
1524 c->CombatStop();
1525 c->GetMotionMaster()->Clear();
1526
1527#ifdef TRINITY_DEBUG
1528 TC_LOG_DEBUG("maps", "Creature {} moved from grid[{}, {}]cell[{}, {}] to respawn grid[{}, {}]cell[{}, {}].", c->GetGUID().ToString(), c->GetCurrentCell().GridX(), c->GetCurrentCell().GridY(), c->GetCurrentCell().CellX(), c->GetCurrentCell().CellY(), resp_cell.GridX(), resp_cell.GridY(), resp_cell.CellX(), resp_cell.CellY());
1529#endif
1530
1531 // teleport it to respawn point (like normal respawn if player see)
1532 if (CreatureCellRelocation(c, resp_cell))
1533 {
1534 c->Relocate(resp_x, resp_y, resp_z, resp_o);
1535 c->GetMotionMaster()->Initialize(); // prevent possible problems with default move generators
1536 //CreatureRelocationNotify(c, resp_cell, resp_cell.GetCellCoord());
1537 c->UpdatePositionData();
1538 c->UpdateObjectVisibility(false);
1539 return true;
1540 }
1541
1542 return false;
1543}
1544
1546{
1547 float resp_x, resp_y, resp_z, resp_o;
1548 go->GetRespawnPosition(resp_x, resp_y, resp_z, &resp_o);
1549 Cell resp_cell(resp_x, resp_y);
1550
1551 //GameObject will be unloaded with grid
1552 if (diffGridOnly && !go->GetCurrentCell().DiffGrid(resp_cell))
1553 return true;
1554
1555#ifdef TRINITY_DEBUG
1556 TC_LOG_DEBUG("maps", "GameObject {} moved from grid[{}, {}]cell[{}, {}] to respawn grid[{}, {}]cell[{}, {}].", go->GetGUID().ToString(), go->GetCurrentCell().GridX(), go->GetCurrentCell().GridY(), go->GetCurrentCell().CellX(), go->GetCurrentCell().CellY(), resp_cell.GridX(), resp_cell.GridY(), resp_cell.CellX(), resp_cell.CellY());
1557#endif
1558
1559 // teleport it to respawn point (like normal respawn if player see)
1560 if (GameObjectCellRelocation(go, resp_cell))
1561 {
1562 go->Relocate(resp_x, resp_y, resp_z, resp_o);
1563 go->UpdatePositionData();
1564 go->UpdateObjectVisibility(false);
1565 return true;
1566 }
1567
1568 return false;
1569}
1570
1571bool Map::UnloadGrid(NGridType& ngrid, bool unloadAll)
1572{
1573 const uint32 x = ngrid.getX();
1574 const uint32 y = ngrid.getY();
1575
1576 {
1577 if (!unloadAll)
1578 {
1579 //pets, possessed creatures (must be active), transport passengers
1581 return false;
1582
1583 if (ActiveObjectsNearGrid(ngrid))
1584 return false;
1585 }
1586
1587 TC_LOG_DEBUG("maps", "Unloading grid[{}, {}] for map {}", x, y, GetId());
1588
1589 if (!unloadAll)
1590 {
1591 // Finish creature moves, remove and delete all creatures with delayed remove before moving to respawn grids
1592 // Must know real mob position before move
1596
1597 // move creatures to respawn grids if this is diff.grid or to remove list
1598 ObjectGridEvacuator worker;
1600 ngrid.VisitAllGrids(visitor);
1601
1602 // Finish creature moves, remove and delete all creatures with delayed remove before unload
1606 }
1607
1608 {
1609 ObjectGridCleaner worker;
1611 ngrid.VisitAllGrids(visitor);
1612 }
1613
1615
1616 // After removing all objects from the map, purge empty tracked phases
1618
1619 {
1620 ObjectGridUnloader worker;
1622 ngrid.VisitAllGrids(visitor);
1623 }
1624
1625 ASSERT(i_objectsToRemove.empty());
1626
1627 delete &ngrid;
1628 setNGrid(nullptr, x, y);
1629 }
1630 int gx = (MAX_NUMBER_OF_GRIDS - 1) - x;
1631 int gy = (MAX_NUMBER_OF_GRIDS - 1) - y;
1632
1633 m_terrain->UnloadMap(gx, gy);
1634
1635 TC_LOG_DEBUG("maps", "Unloading grid[{}, {}] for map {} finished", x, y, GetId());
1636 return true;
1637}
1638
1640{
1641 if (HavePlayers())
1642 {
1643 for (MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
1644 {
1645 Player* player = itr->GetSource();
1646 if (!player->IsBeingTeleportedFar())
1647 {
1648 // this is happening for bg
1649 TC_LOG_ERROR("maps", "Map::UnloadAll: player {} is still in map {} during unload, this should not happen!", player->GetName(), GetId());
1650 player->TeleportTo(player->m_homebind);
1651 }
1652 }
1653 }
1654}
1655
1657{
1658 // clear all delayed moves, useless anyway do this moves before map unload.
1659 _creaturesToMove.clear();
1660 _gameObjectsToMove.clear();
1661
1663 {
1664 NGridType &grid(*i->GetSource());
1665 ++i;
1666 UnloadGrid(grid, true); // deletes the grid and removes it from the GridRefManager
1667 }
1668
1669 for (TransportsContainer::iterator itr = _transports.begin(); itr != _transports.end();)
1670 {
1671 Transport* transport = *itr;
1672 ++itr;
1673
1674 RemoveFromMap<Transport>(transport, true);
1675 }
1676
1677 for (auto& cellCorpsePair : _corpsesByCell)
1678 {
1679 for (Corpse* corpse : cellCorpsePair.second)
1680 {
1681 corpse->RemoveFromWorld();
1682 corpse->ResetMap();
1683 delete corpse;
1684 }
1685 }
1686
1687 _corpsesByCell.clear();
1688 _corpsesByPlayer.clear();
1689 _corpseBones.clear();
1690}
1691
1692void Map::GetFullTerrainStatusForPosition(PhaseShift const& phaseShift, float x, float y, float z, PositionFullTerrainStatus& data,
1693 Optional<map_liquidHeaderTypeFlags> reqLiquidType, float collisionHeight)
1694{
1695 m_terrain->GetFullTerrainStatusForPosition(phaseShift, GetId(), x, y, z, data, reqLiquidType, collisionHeight, &_dynamicTree);
1696}
1697
1698ZLiquidStatus Map::GetLiquidStatus(PhaseShift const& phaseShift, float x, float y, float z, Optional<map_liquidHeaderTypeFlags> ReqLiquidType, LiquidData* data,
1699 float collisionHeight)
1700{
1701 return m_terrain->GetLiquidStatus(phaseShift, GetId(), x, y, z, ReqLiquidType, data, collisionHeight);
1702}
1703
1704uint32 Map::GetAreaId(PhaseShift const& phaseShift, float x, float y, float z)
1705{
1706 return m_terrain->GetAreaId(phaseShift, GetId(), x, y, z, &_dynamicTree);
1707}
1708
1709uint32 Map::GetZoneId(PhaseShift const& phaseShift, float x, float y, float z)
1710{
1711 return m_terrain->GetZoneId(phaseShift, GetId(), x, y, z, &_dynamicTree);
1712}
1713
1714void Map::GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, float x, float y, float z)
1715{
1716 return m_terrain->GetZoneAndAreaId(phaseShift, GetId(), zoneid, areaid, x, y, z, &_dynamicTree);
1717}
1718
1719float Map::GetMinHeight(PhaseShift const& phaseShift, float x, float y)
1720{
1721 return m_terrain->GetMinHeight(phaseShift, GetId(), x, y);
1722}
1723
1724float Map::GetGridHeight(PhaseShift const& phaseShift, float x, float y)
1725{
1726 return m_terrain->GetGridHeight(phaseShift, GetId(), x, y);
1727}
1728
1729float Map::GetStaticHeight(PhaseShift const& phaseShift, float x, float y, float z, bool checkVMap, float maxSearchDist)
1730{
1731 return m_terrain->GetStaticHeight(phaseShift, GetId(), x, y, z, checkVMap, maxSearchDist);
1732}
1733
1734float Map::GetWaterLevel(PhaseShift const& phaseShift, float x, float y)
1735{
1736 return m_terrain->GetWaterLevel(phaseShift, GetId(), x, y);
1737}
1738
1739bool Map::IsInWater(PhaseShift const& phaseShift, float x, float y, float z, LiquidData* data)
1740{
1741 return m_terrain->IsInWater(phaseShift, GetId(), x, y, z, data);
1742}
1743
1744bool Map::IsUnderWater(PhaseShift const& phaseShift, float x, float y, float z)
1745{
1746 return m_terrain->IsUnderWater(phaseShift, GetId(), x, y, z);
1747}
1748
1749float Map::GetWaterOrGroundLevel(PhaseShift const& phaseShift, float x, float y, float z, float* ground, bool swim, float collisionHeight)
1750{
1751 return m_terrain->GetWaterOrGroundLevel(phaseShift, GetId(), x, y, z, ground, swim, collisionHeight, &_dynamicTree);
1752}
1753
1754bool Map::isInLineOfSight(PhaseShift const& phaseShift, float x1, float y1, float z1, float x2, float y2, float z2, LineOfSightChecks checks, VMAP::ModelIgnoreFlags ignoreFlags) const
1755{
1756 if ((checks & LINEOFSIGHT_CHECK_VMAP)
1757 && !VMAP::VMapFactory::createOrGetVMapManager()->isInLineOfSight(PhasingHandler::GetTerrainMapId(phaseShift, GetId(), m_terrain.get(), x1, y1), x1, y1, z1, x2, y2, z2, ignoreFlags))
1758 return false;
1759 if (sWorld->getBoolConfig(CONFIG_CHECK_GOBJECT_LOS) && (checks & LINEOFSIGHT_CHECK_GOBJECT)
1760 && !_dynamicTree.isInLineOfSight({ x1, y1, z1 }, { x2, y2, z2 }, phaseShift))
1761 return false;
1762 return true;
1763}
1764
1765bool Map::getObjectHitPos(PhaseShift const& phaseShift, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist)
1766{
1767 G3D::Vector3 startPos(x1, y1, z1);
1768 G3D::Vector3 dstPos(x2, y2, z2);
1769
1770 G3D::Vector3 resultPos;
1771 bool result = _dynamicTree.getObjectHitPos(startPos, dstPos, resultPos, modifyDist, phaseShift);
1772
1773 rx = resultPos.x;
1774 ry = resultPos.y;
1775 rz = resultPos.z;
1776 return result;
1777}
1778
1780{
1781 MapEntry const* entry = sMapStore.LookupEntry(mapid);
1782 if (!entry)
1784
1785 if (!entry->IsDungeon())
1786 return TRANSFER_ABORT_NONE;
1787
1788 Difficulty targetDifficulty = player->GetDifficultyID(entry);
1789 // Get the highest available difficulty if current setting is higher than the instance allows
1790 MapDifficultyEntry const* mapDiff = sDB2Manager.GetDownscaledMapDifficultyData(mapid, targetDifficulty);
1791 if (!mapDiff)
1793
1794 //Bypass checks for GMs
1795 if (player->IsGameMaster())
1796 return TRANSFER_ABORT_NONE;
1797
1798 //Other requirements
1799 {
1801 if (!player->Satisfy(sObjectMgr->GetAccessRequirement(mapid, targetDifficulty), mapid, &params, true))
1802 return params;
1803 }
1804
1805 Group* group = player->GetGroup();
1806 if (entry->IsRaid() && entry->Expansion() >= sWorld->getIntConfig(CONFIG_EXPANSION)) // can only enter in a raid group but raids from old expansion don't need a group
1807 if ((!group || !group->isRaidGroup()) && !sWorld->getBoolConfig(CONFIG_INSTANCE_IGNORE_RAID))
1809
1810 if (entry->Instanceable())
1811 {
1812 //Get instance where player's group is bound & its map
1813 uint32 instanceIdToCheck = sMapMgr->FindInstanceIdForPlayer(mapid, player);
1814 if (Map* boundMap = sMapMgr->FindMap(mapid, instanceIdToCheck))
1815 if (TransferAbortParams denyReason = boundMap->CannotEnter(player))
1816 return denyReason;
1817
1818 // players are only allowed to enter 10 instances per hour
1819 if (!entry->GetFlags2().HasFlag(MapFlags2::IgnoreInstanceFarmLimit) && entry->IsDungeon() && !player->CheckInstanceCount(instanceIdToCheck) && !player->isDead())
1821 }
1822
1823 return TRANSFER_ABORT_NONE;
1824}
1825
1826char const* Map::GetMapName() const
1827{
1828 return i_mapEntry->MapName[sWorld->GetDefaultDbcLocale()];
1829}
1830
1832{
1833 TC_LOG_DEBUG("maps", "Creating player data for himself {}", player->GetGUID().ToString());
1834
1835 UpdateData data(player->GetMapId());
1836
1837 // attach to player data current transport data
1838 if (Transport* transport = dynamic_cast<Transport*>(player->GetTransport()))
1839 {
1840 transport->BuildCreateUpdateBlockForPlayer(&data, player);
1841 player->m_visibleTransports.insert(transport->GetGUID());
1842 }
1843
1844 // build data for self presence in world at own client (one time for map)
1845 player->BuildCreateUpdateBlockForPlayer(&data, player);
1846
1847 // build other passengers at transport also (they always visible and marked as visible and will not send at visibility update at add to map
1848 if (Transport* transport = dynamic_cast<Transport*>(player->GetTransport()))
1849 for (WorldObject* passenger : transport->GetPassengers())
1850 if (player != passenger && player->HaveAtClient(passenger))
1851 passenger->BuildCreateUpdateBlockForPlayer(&data, player);
1852
1853 WorldPacket packet;
1854 data.BuildPacket(&packet);
1855 player->SendDirectMessage(&packet);
1856}
1857
1859{
1860 // Hack to send out transports
1861 UpdateData transData(GetId());
1862 for (Transport* transport : _transports)
1863 {
1864 if (transport->IsInWorld() && transport != player->GetTransport() && player->InSamePhase(transport))
1865 {
1866 transport->BuildCreateUpdateBlockForPlayer(&transData, player);
1867 player->m_visibleTransports.insert(transport->GetGUID());
1868 }
1869 }
1870
1871 if (!transData.HasData())
1872 return;
1873
1874 WorldPacket packet;
1875 transData.BuildPacket(&packet);
1876 player->SendDirectMessage(&packet);
1877}
1878
1880{
1881 // Hack to send out transports
1882 UpdateData transData(player->GetMapId());
1883 for (Transport* transport : _transports)
1884 {
1885 if (player->m_visibleTransports.count(transport->GetGUID()) && transport != player->GetTransport())
1886 {
1887 transport->BuildOutOfRangeUpdateBlock(&transData);
1888 player->m_visibleTransports.erase(transport->GetGUID());
1889 }
1890 }
1891
1892 if (!transData.HasData())
1893 return;
1894
1895 WorldPacket packet;
1896 transData.BuildPacket(&packet);
1897 player->SendDirectMessage(&packet);
1898}
1899
1901{
1902 // Hack to send out transports
1903 UpdateData transData(player->GetMapId());
1904 for (Transport* transport : _transports)
1905 {
1906 if (!transport->IsInWorld())
1907 continue;
1908
1909 auto transportItr = player->m_visibleTransports.find(transport->GetGUID());
1910 if (player->InSamePhase(transport))
1911 {
1912 if (transportItr == player->m_visibleTransports.end())
1913 {
1914 transport->BuildCreateUpdateBlockForPlayer(&transData, player);
1915 player->m_visibleTransports.insert(transport->GetGUID());
1916 }
1917 }
1918 else if (transportItr != player->m_visibleTransports.end())
1919 {
1920 transport->BuildOutOfRangeUpdateBlock(&transData);
1921 player->m_visibleTransports.erase(transportItr);
1922 }
1923 }
1924
1925 if (!transData.HasData())
1926 return;
1927
1928 WorldPacket packet;
1929 transData.BuildPacket(&packet);
1930 player->GetSession()->SendPacket(&packet);
1931}
1932
1933inline void Map::setNGrid(NGridType *grid, uint32 x, uint32 y)
1934{
1936 {
1937 TC_LOG_ERROR("maps", "map::setNGrid() Invalid grid coordinates found: {}, {}!", x, y);
1938 ABORT();
1939 }
1940 i_grids[x][y] = grid;
1941}
1942
1944{
1945 UpdateDataMapType update_players;
1946
1947 while (!_updateObjects.empty())
1948 {
1949 Object* obj = *_updateObjects.begin();
1950 ASSERT(obj->IsInWorld());
1951 _updateObjects.erase(_updateObjects.begin());
1952 obj->BuildUpdate(update_players);
1953 }
1954
1955 WorldPacket packet; // here we allocate a std::vector with a size of 0x10000
1956 for (UpdateDataMapType::iterator iter = update_players.begin(); iter != update_players.end(); ++iter)
1957 {
1958 iter->second.BuildPacket(&packet);
1959 iter->first->SendDirectMessage(&packet);
1960 packet.clear(); // clean the string
1961 }
1962}
1963
1964// CheckRespawn MUST do one of the following:
1965// -) return true
1966// -) set info->respawnTime to zero, which indicates the respawn time should be deleted (and will never be processed again without outside intervention)
1967// -) set info->respawnTime to a new respawn time, which must be strictly GREATER than the current time (GameTime::GetGameTime())
1969{
1970 SpawnData const* data = sObjectMgr->GetSpawnData(info->type, info->spawnId);
1971 ASSERT(data, "Invalid respawn info with type %u, spawnID " UI64FMTD " in respawn queue.", info->type, info->spawnId);
1972
1973 // First, check if this creature's spawn group is inactive
1975 {
1976 info->respawnTime = 0;
1977 return false;
1978 }
1979
1980 // Next, check if there's already an instance of this object that would block the respawn
1981 bool alreadyExists = false;
1982 switch (info->type)
1983 {
1985 {
1986 // escort check for creatures only (if the world config boolean is set)
1987 bool const isEscort = (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && data->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC);
1988
1989 auto range = _creatureBySpawnIdStore.equal_range(info->spawnId);
1990 for (auto it = range.first; it != range.second; ++it)
1991 {
1992 Creature* creature = it->second;
1993 if (!creature->IsAlive())
1994 continue;
1995 // escort NPCs are allowed to respawn as long as all other instances are already escorting
1996 if (isEscort && creature->IsEscorted())
1997 continue;
1998 alreadyExists = true;
1999 break;
2000 }
2001 break;
2002 }
2004 // gameobject check is simpler - they cannot be dead or escorting
2006 alreadyExists = true;
2007 break;
2008 default:
2009 ABORT_MSG("Invalid spawn type %u with spawnId " UI64FMTD " on map %u", uint32(info->type), info->spawnId, GetId());
2010 return true;
2011 }
2012 if (alreadyExists)
2013 {
2014 info->respawnTime = 0;
2015 return false;
2016 }
2017
2018 // next, check linked respawn time
2019 ObjectGuid thisGUID = info->type == SPAWN_TYPE_GAMEOBJECT
2020 ? ObjectGuid::Create<HighGuid::GameObject>(GetId(), info->entry, info->spawnId)
2021 : ObjectGuid::Create<HighGuid::Creature>(GetId(), info->entry, info->spawnId);
2022 if (time_t linkedTime = GetLinkedRespawnTime(thisGUID))
2023 {
2024 time_t now = GameTime::GetGameTime();
2025 time_t respawnTime;
2026 if (linkedTime == std::numeric_limits<time_t>::max())
2027 respawnTime = linkedTime;
2028 else if (sObjectMgr->GetLinkedRespawnGuid(thisGUID) == thisGUID) // never respawn, save "something" in DB
2029 respawnTime = now + WEEK;
2030 else // set us to check again shortly after linked unit
2031 respawnTime = std::max<time_t>(now, linkedTime) + urand(5, 15);
2032 info->respawnTime = respawnTime;
2033 return false;
2034 }
2035 // everything ok, let's spawn
2036 return true;
2037}
2038
2040{
2041 if (info->respawnTime <= GameTime::GetGameTime())
2042 return;
2044 _respawnTimes->increase(static_cast<RespawnInfoWithHandle*>(info)->handle);
2045 SaveRespawnInfoDB(*info, dbTrans);
2046}
2047
2049{
2050 std::vector<WorldObject*> toUnload;
2051 switch (type)
2052 {
2054 for (auto const& pair : Trinity::Containers::MapEqualRange(GetCreatureBySpawnIdStore(), spawnId))
2055 toUnload.push_back(pair.second);
2056 break;
2058 for (auto const& pair : Trinity::Containers::MapEqualRange(GetGameObjectBySpawnIdStore(), spawnId))
2059 toUnload.push_back(pair.second);
2060 break;
2061 default:
2062 break;
2063 }
2064
2065 for (WorldObject* o : toUnload)
2067
2068 return toUnload.size();
2069}
2070
2072{
2073 if (!info.spawnId)
2074 {
2075 TC_LOG_ERROR("maps", "Attempt to insert respawn info for zero spawn id (type {})", uint32(info.type));
2076 return false;
2077 }
2078
2079 RespawnInfoMap* bySpawnIdMap = GetRespawnMapForType(info.type);
2080 if (!bySpawnIdMap)
2081 return false;
2082
2083 // check if we already have the maximum possible number of respawns scheduled
2084 if (SpawnData::TypeHasData(info.type))
2085 {
2086 auto it = bySpawnIdMap->find(info.spawnId);
2087 if (it != bySpawnIdMap->end()) // spawnid already has a respawn scheduled
2088 {
2089 RespawnInfo* const existing = it->second;
2090 if (info.respawnTime <= existing->respawnTime) // delete existing in this case
2091 DeleteRespawnInfo(existing);
2092 else
2093 return false;
2094 }
2095 ASSERT(bySpawnIdMap->find(info.spawnId) == bySpawnIdMap->end(), "Insertion of respawn info with id (%u," UI64FMTD ") into spawn id map failed - state desync.", uint32(info.type), info.spawnId);
2096 }
2097 else
2098 ABORT_MSG("Invalid respawn info for spawn id (%u," UI64FMTD ") being inserted", uint32(info.type), info.spawnId);
2099
2101 ri->handle = _respawnTimes->push(ri);
2102 bySpawnIdMap->emplace(ri->spawnId, ri);
2103 return true;
2104}
2105
2106static void PushRespawnInfoFrom(std::vector<RespawnInfo const*>& data, RespawnInfoMap const& map)
2107{
2108 data.reserve(data.size() + map.size());
2109 for (auto const& pair : map)
2110 data.push_back(pair.second);
2111}
2112
2113void Map::GetRespawnInfo(std::vector<RespawnInfo const*>& respawnData, SpawnObjectTypeMask types) const
2114{
2115 if (types & SPAWN_TYPEMASK_CREATURE)
2117 if (types & SPAWN_TYPEMASK_GAMEOBJECT)
2119}
2120
2122{
2123 RespawnInfoMap const* map = GetRespawnMapForType(type);
2124 if (!map)
2125 return nullptr;
2126 auto it = map->find(spawnId);
2127 if (it == map->end())
2128 return nullptr;
2129 return it->second;
2130}
2131
2132void Map::UnloadAllRespawnInfos() // delete everything from memory
2133{
2134 for (RespawnInfo* info : *_respawnTimes)
2135 delete info;
2136 _respawnTimes->clear();
2139}
2140
2142{
2143 // Delete from all relevant containers to ensure consistency
2144 ASSERT(info);
2145
2146 // spawnid store
2147 auto spawnMap = GetRespawnMapForType(info->type);
2148 if (!spawnMap)
2149 return;
2150
2151 auto range = spawnMap->equal_range(info->spawnId);
2152 auto it = std::find_if(range.first, range.second, [info](RespawnInfoMap::value_type const& pair) { return (pair.second == info); });
2153 ASSERT(it != range.second, "Respawn stores inconsistent for map %u, spawnid " UI64FMTD " (type %u)", GetId(), info->spawnId, uint32(info->type));
2154 spawnMap->erase(it);
2155
2156 // respawn heap
2157 _respawnTimes->erase(static_cast<RespawnInfoWithHandle*>(info)->handle);
2158
2159 // database
2160 DeleteRespawnInfoFromDB(info->type, info->spawnId, dbTrans);
2161
2162 // then cleanup the object
2163 delete info;
2164}
2165
2167{
2168 if (Instanceable())
2169 return;
2170
2172 stmt->setUInt16(0, type);
2173 stmt->setUInt64(1, spawnId);
2174 stmt->setUInt16(2, GetId());
2175 stmt->setUInt32(3, GetInstanceId());
2176 CharacterDatabase.ExecuteOrAppend(dbTrans, stmt);
2177}
2178
2180{
2181 if (!IsGridLoaded(gridId)) // if grid isn't loaded, this will be processed in grid load handler
2182 return;
2183
2184 switch (type)
2185 {
2187 {
2188 Creature* obj = new Creature();
2189 if (!obj->LoadFromDB(spawnId, this, true, true))
2190 delete obj;
2191 break;
2192 }
2194 {
2195 GameObject* obj = new GameObject();
2196 if (!obj->LoadFromDB(spawnId, this, true))
2197 delete obj;
2198 break;
2199 }
2200 default:
2201 ABORT_MSG("Invalid spawn type %u (spawnid " UI64FMTD ") on map %u", uint32(type), spawnId, GetId());
2202 }
2203}
2204
2206{
2207 time_t now = GameTime::GetGameTime();
2208 while (!_respawnTimes->empty())
2209 {
2210 RespawnInfoWithHandle* next = _respawnTimes->top();
2211 if (now < next->respawnTime) // done for this tick
2212 break;
2213
2214 if (uint32 poolId = sPoolMgr->IsPartOfAPool(next->type, next->spawnId)) // is this part of a pool?
2215 { // if yes, respawn will be handled by (external) pooling logic, just delete the respawn time
2216 // step 1: remove entry from maps to avoid it being reachable by outside logic
2217 _respawnTimes->pop();
2218 ASSERT_NOTNULL(GetRespawnMapForType(next->type))->erase(next->spawnId);
2219
2220 // step 2: tell pooling logic to do its thing
2221 sPoolMgr->UpdatePool(GetPoolData(), poolId, next->type, next->spawnId);
2222
2223 // step 3: get rid of the actual entry
2224 RemoveRespawnTime(next->type, next->spawnId, nullptr, true);
2225 delete next;
2226 }
2227 else if (CheckRespawn(next)) // see if we're allowed to respawn
2228 { // ok, respawn
2229 // step 1: remove entry from maps to avoid it being reachable by outside logic
2230 _respawnTimes->pop();
2231 ASSERT_NOTNULL(GetRespawnMapForType(next->type))->erase(next->spawnId);
2232
2233 // step 2: do the respawn, which involves external logic
2234 DoRespawn(next->type, next->spawnId, next->gridId);
2235
2236 // step 3: get rid of the actual entry
2237 RemoveRespawnTime(next->type, next->spawnId, nullptr, true);
2238 delete next;
2239 }
2240 else if (!next->respawnTime)
2241 { // just remove this respawn entry without rescheduling
2242 _respawnTimes->pop();
2243 ASSERT_NOTNULL(GetRespawnMapForType(next->type))->erase(next->spawnId);
2244 RemoveRespawnTime(next->type, next->spawnId, nullptr, true);
2245 delete next;
2246 }
2247 else
2248 { // new respawn time, update heap position
2249 ASSERT(now < next->respawnTime); // infinite loop guard
2250 _respawnTimes->decrease(next->handle);
2251 SaveRespawnInfoDB(*next);
2252 }
2253 }
2254}
2255
2256void Map::ApplyDynamicModeRespawnScaling(WorldObject const* obj, ObjectGuid::LowType spawnId, uint32& respawnDelay, uint32 mode) const
2257{
2258 ASSERT(mode == 1);
2259 ASSERT(obj->GetMap() == this);
2260
2262 return;
2263
2264 SpawnObjectType type;
2265 switch (obj->GetTypeId())
2266 {
2267 case TYPEID_UNIT:
2268 type = SPAWN_TYPE_CREATURE;
2269 break;
2270 case TYPEID_GAMEOBJECT:
2271 type = SPAWN_TYPE_GAMEOBJECT;
2272 break;
2273 default:
2274 return;
2275 }
2276
2277 SpawnMetadata const* data = sObjectMgr->GetSpawnMetadata(type, spawnId);
2278 if (!data)
2279 return;
2280
2282 return;
2283
2284 auto it = _zonePlayerCountMap.find(obj->GetZoneId());
2285 if (it == _zonePlayerCountMap.end())
2286 return;
2287 uint32 const playerCount = it->second;
2288 if (!playerCount)
2289 return;
2290 double const adjustFactor = sWorld->getFloatConfig(type == SPAWN_TYPE_GAMEOBJECT ? CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT : CONFIG_RESPAWN_DYNAMICRATE_CREATURE) / playerCount;
2291 if (adjustFactor >= 1.0) // nothing to do here
2292 return;
2294 if (respawnDelay <= timeMinimum)
2295 return;
2296
2297 respawnDelay = std::max<uint32>(ceil(respawnDelay * adjustFactor), timeMinimum);
2298}
2299
2301{
2303 // check if the object is on its respawn timer
2304 if (GetRespawnTime(type, spawnId))
2305 return false;
2306
2307 SpawnMetadata const* spawnData = ASSERT_NOTNULL(sObjectMgr->GetSpawnMetadata(type, spawnId));
2308 // check if the object is part of a spawn group
2309 SpawnGroupTemplateData const* spawnGroup = ASSERT_NOTNULL(spawnData->spawnGroupData);
2310 if (!(spawnGroup->flags & SPAWNGROUP_FLAG_SYSTEM))
2311 if (!IsSpawnGroupActive(spawnGroup->groupId))
2312 return false;
2313
2314 if (spawnData->ToSpawnData()->poolId)
2315 if (!GetPoolData().IsSpawnedObject(type, spawnId))
2316 return false;
2317
2318 return true;
2319}
2320
2322{
2323 SpawnGroupTemplateData const* data = sObjectMgr->GetSpawnGroupData(groupId);
2324 if (data && (data->flags & SPAWNGROUP_FLAG_SYSTEM || data->mapId == GetId()))
2325 return data;
2326 return nullptr;
2327}
2328
2329bool Map::SpawnGroupSpawn(uint32 groupId, bool ignoreRespawn, bool force, std::vector<WorldObject*>* spawnedObjects)
2330{
2331 SpawnGroupTemplateData const* groupData = GetSpawnGroupData(groupId);
2332 if (!groupData || groupData->flags & SPAWNGROUP_FLAG_SYSTEM)
2333 {
2334 TC_LOG_ERROR("maps", "Tried to spawn non-existing (or system) spawn group {} on map {}. Blocked.", groupId, GetId());
2335 return false;
2336 }
2337
2338 SetSpawnGroupActive(groupId, true); // start processing respawns for the group
2339
2340 std::vector<SpawnData const*> toSpawn;
2341 for (auto& pair : sObjectMgr->GetSpawnMetadataForGroup(groupId))
2342 {
2343 SpawnMetadata const* data = pair.second;
2344 ASSERT(groupData->mapId == data->mapId);
2345
2346 auto respawnMap = GetRespawnMapForType(data->type);
2347 if (!respawnMap)
2348 continue;
2349
2350 if (force || ignoreRespawn)
2351 RemoveRespawnTime(data->type, data->spawnId);
2352
2353 uint32 nRespawnTimers = respawnMap->count(data->spawnId);
2354 if (SpawnData::TypeHasData(data->type))
2355 {
2356 // has a respawn timer
2357 if (nRespawnTimers)
2358 continue;
2359
2360 // has a spawn already active
2361 if (!force)
2362 if (WorldObject* obj = GetWorldObjectBySpawnId(data->type, data->spawnId))
2363 if ((data->type != SPAWN_TYPE_CREATURE) || obj->ToCreature()->IsAlive())
2364 continue;
2365
2366 toSpawn.push_back(ASSERT_NOTNULL(data->ToSpawnData()));
2367 }
2368 }
2369
2370 for (SpawnData const* data : toSpawn)
2371 {
2372 // don't spawn if the current map difficulty is not used by the spawn
2373 if (std::find(data->spawnDifficulties.begin(), data->spawnDifficulties.end(), GetDifficultyID()) == data->spawnDifficulties.end())
2374 continue;
2375
2376 // don't spawn if the grid isn't loaded (will be handled in grid loader)
2377 if (!IsGridLoaded(data->spawnPoint))
2378 continue;
2379
2380 // now do the actual (re)spawn
2381 switch (data->type)
2382 {
2384 {
2385 Creature* creature = new Creature();
2386 if (!creature->LoadFromDB(data->spawnId, this, true, force))
2387 delete creature;
2388 else if (spawnedObjects)
2389 spawnedObjects->push_back(creature);
2390 break;
2391 }
2393 {
2394 GameObject* gameobject = new GameObject();
2395 if (!gameobject->LoadFromDB(data->spawnId, this, true))
2396 delete gameobject;
2397 else if (spawnedObjects)
2398 spawnedObjects->push_back(gameobject);
2399 break;
2400 }
2402 {
2403 AreaTrigger* areaTrigger = new AreaTrigger();
2404 if (!areaTrigger->LoadFromDB(data->spawnId, this, true, false))
2405 delete areaTrigger;
2406 else if (spawnedObjects)
2407 spawnedObjects->push_back(areaTrigger);
2408 break;
2409 }
2410 default:
2411 ABORT_MSG("Invalid spawn type %u with spawnId " UI64FMTD, uint32(data->type), data->spawnId);
2412 return false;
2413 }
2414 }
2415 return true;
2416}
2417
2418bool Map::SpawnGroupDespawn(uint32 groupId, bool deleteRespawnTimes, size_t* count)
2419{
2420 SpawnGroupTemplateData const* groupData = GetSpawnGroupData(groupId);
2421 if (!groupData || groupData->flags & SPAWNGROUP_FLAG_SYSTEM)
2422 {
2423 TC_LOG_ERROR("maps", "Tried to despawn non-existing (or system) spawn group {} on map {}. Blocked.", groupId, GetId());
2424 return false;
2425 }
2426
2427 for (auto const& pair : sObjectMgr->GetSpawnMetadataForGroup(groupId))
2428 {
2429 SpawnMetadata const* data = pair.second;
2430 ASSERT(groupData->mapId == data->mapId);
2431 if (deleteRespawnTimes)
2432 RemoveRespawnTime(data->type, data->spawnId);
2433 size_t c = DespawnAll(data->type, data->spawnId);
2434 if (count)
2435 *count += c;
2436 }
2437 SetSpawnGroupActive(groupId, false); // stop processing respawns for the group, too
2438 return true;
2439}
2440
2441void Map::SetSpawnGroupActive(uint32 groupId, bool state)
2442{
2443 SpawnGroupTemplateData const* const data = GetSpawnGroupData(groupId);
2444 if (!data || data->flags & SPAWNGROUP_FLAG_SYSTEM)
2445 {
2446 TC_LOG_ERROR("maps", "Tried to set non-existing (or system) spawn group {} to {} on map {}. Blocked.", groupId, state ? "active" : "inactive", GetId());
2447 return;
2448 }
2449 if (state != !(data->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN)) // toggled
2450 _toggledSpawnGroupIds.insert(groupId);
2451 else
2452 _toggledSpawnGroupIds.erase(groupId);
2453}
2454
2456{
2457 SpawnGroupTemplateData const* const data = GetSpawnGroupData(groupId);
2458 if (!data)
2459 {
2460 TC_LOG_ERROR("maps", "Tried to query state of non-existing spawn group {} on map {}.", groupId, GetId());
2461 return false;
2462 }
2463 if (data->flags & SPAWNGROUP_FLAG_SYSTEM)
2464 return true;
2465 // either manual spawn group and toggled, or not manual spawn group and not toggled...
2466 return (_toggledSpawnGroupIds.find(groupId) != _toggledSpawnGroupIds.end()) != !(data->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN);
2467}
2468
2470{
2471 std::vector<uint32> const* spawnGroups = sObjectMgr->GetSpawnGroupsForMap(GetId());
2472 if (!spawnGroups)
2473 return;
2474
2475 for (uint32 spawnGroupId : *spawnGroups)
2476 {
2477 SpawnGroupTemplateData const* spawnGroupTemplate = ASSERT_NOTNULL(GetSpawnGroupData(spawnGroupId));
2478 if (spawnGroupTemplate->flags & (SPAWNGROUP_FLAG_SYSTEM | SPAWNGROUP_FLAG_MANUAL_SPAWN))
2479 continue;
2480
2481 SetSpawnGroupActive(spawnGroupId, sConditionMgr->IsMapMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_SPAWN_GROUP, spawnGroupId, this));
2482 }
2483}
2484
2486{
2487 std::vector<uint32> const* spawnGroups = sObjectMgr->GetSpawnGroupsForMap(GetId());
2488 if (!spawnGroups)
2489 return;
2490
2491 for (uint32 spawnGroupId : *spawnGroups)
2492 {
2493 SpawnGroupTemplateData const* spawnGroupTemplate = ASSERT_NOTNULL(GetSpawnGroupData(spawnGroupId));
2494
2495 bool isActive = IsSpawnGroupActive(spawnGroupId);
2496 bool shouldBeActive = sConditionMgr->IsMapMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_SPAWN_GROUP, spawnGroupId, this);
2497
2498 if (spawnGroupTemplate->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN)
2499 {
2500 // Only despawn the group if it isn't meeting conditions
2501 if (isActive && !shouldBeActive && spawnGroupTemplate->flags & SPAWNGROUP_FLAG_DESPAWN_ON_CONDITION_FAILURE)
2502 SpawnGroupDespawn(spawnGroupId, true);
2503
2504 continue;
2505 }
2506
2507 if (isActive == shouldBeActive)
2508 continue;
2509
2510 if (shouldBeActive)
2511 SpawnGroupSpawn(spawnGroupId);
2512 else if (spawnGroupTemplate->flags & SPAWNGROUP_FLAG_DESPAWN_ON_CONDITION_FAILURE)
2513 SpawnGroupDespawn(spawnGroupId, true);
2514 else
2515 SetSpawnGroupInactive(spawnGroupId);
2516 }
2517}
2518
2520{
2521 auto itr = _guidGenerators.find(high);
2522 if (itr == _guidGenerators.end())
2523 itr = _guidGenerators.insert(std::make_pair(high, std::make_unique<ObjectGuidGenerator>(high))).first;
2524
2525 return *itr->second;
2526}
2527
2529{
2530 _farSpellCallbacks.Enqueue(new FarSpellCallback(std::move(callback)));
2531}
2532
2534{
2535 {
2536 FarSpellCallback* callback;
2537 while (_farSpellCallbacks.Dequeue(callback))
2538 {
2539 (*callback)(this);
2540 delete callback;
2541 }
2542 }
2543
2545
2546 // Don't unload grids if it's battleground, since we may have manually added GOs, creatures, those doesn't load from DB at grid re-load !
2547 // This isn't really bother us, since as soon as we have instanced BG-s, the whole map unloads as the BG gets ended
2548 if (!IsBattlegroundOrArena())
2549 {
2551 {
2552 NGridType *grid = i->GetSource();
2553 GridInfo* info = i->GetSource()->getGridInfoRef();
2554 ++i; // The update might delete the map and we need the next map before the iterator gets invalid
2555 ASSERT(grid->GetGridState() >= 0 && grid->GetGridState() < MAX_GRID_STATE);
2556 si_GridStates[grid->GetGridState()]->Update(*this, *grid, *info, t_diff);
2557 }
2558 }
2559}
2560
2562{
2563 ASSERT(obj->GetMapId() == GetId() && obj->GetInstanceId() == GetInstanceId());
2564
2565 obj->SetDestroyedObject(true);
2566 obj->CleanupsBeforeDelete(false); // remove or simplify at least cross referenced links
2567
2568 i_objectsToRemove.insert(obj);
2569}
2570
2572{
2573 ASSERT(obj->GetMapId() == GetId() && obj->GetInstanceId() == GetInstanceId());
2574 // i_objectsToSwitch is iterated only in Map::RemoveAllObjectsInRemoveList() and it uses
2575 // the contained objects only if GetTypeId() == TYPEID_UNIT , so we can return in all other cases
2576 if (obj->GetTypeId() != TYPEID_UNIT)
2577 return;
2578
2579 std::map<WorldObject*, bool>::iterator itr = i_objectsToSwitch.find(obj);
2580 if (itr == i_objectsToSwitch.end())
2581 i_objectsToSwitch.insert(itr, std::make_pair(obj, on));
2582 else if (itr->second != on)
2583 i_objectsToSwitch.erase(itr);
2584 else
2585 ABORT();
2586}
2587
2589{
2590 while (!i_objectsToSwitch.empty())
2591 {
2592 std::map<WorldObject*, bool>::iterator itr = i_objectsToSwitch.begin();
2593 WorldObject* obj = itr->first;
2594 bool on = itr->second;
2595 i_objectsToSwitch.erase(itr);
2596
2598 {
2599 switch (obj->GetTypeId())
2600 {
2601 case TYPEID_UNIT:
2602 SwitchGridContainers<Creature>(obj->ToCreature(), on);
2603 break;
2604 default:
2605 break;
2606 }
2607 }
2608 }
2609
2610 //TC_LOG_DEBUG("maps", "Object remover 1 check.");
2611 while (!i_objectsToRemove.empty())
2612 {
2613 std::set<WorldObject*>::iterator itr = i_objectsToRemove.begin();
2614 WorldObject* obj = *itr;
2615
2616 switch (obj->GetTypeId())
2617 {
2618 case TYPEID_CORPSE:
2619 {
2620 Corpse* corpse = ObjectAccessor::GetCorpse(*obj, obj->GetGUID());
2621 if (!corpse)
2622 TC_LOG_ERROR("maps", "Tried to delete corpse/bones {} that is not in map.", obj->GetGUID().ToString());
2623 else
2624 RemoveFromMap(corpse, true);
2625 break;
2626 }
2628 RemoveFromMap(obj->ToDynObject(), true);
2629 break;
2630 case TYPEID_AREATRIGGER:
2631 RemoveFromMap((AreaTrigger*)obj, true);
2632 break;
2634 RemoveFromMap((Conversation*)obj, true);
2635 break;
2636 case TYPEID_GAMEOBJECT:
2637 {
2638 GameObject* go = obj->ToGameObject();
2639 if (Transport* transport = go->ToTransport())
2640 RemoveFromMap(transport, true);
2641 else
2642 RemoveFromMap(go, true);
2643 break;
2644 }
2645 case TYPEID_UNIT:
2646 // in case triggered sequence some spell can continue casting after prev CleanupsBeforeDelete call
2647 // make sure that like sources auras/etc removed before destructor start
2649 RemoveFromMap(obj->ToCreature(), true);
2650 break;
2651 default:
2652 TC_LOG_ERROR("maps", "Non-grid object (TypeId: {}) is in grid object remove list, ignored.", obj->GetTypeId());
2653 break;
2654 }
2655
2656 i_objectsToRemove.erase(itr);
2657 }
2658
2659 //TC_LOG_DEBUG("maps", "Object remover 2 check.");
2660}
2661
2663{
2664 uint32 count = 0;
2666 if (!itr->GetSource()->IsGameMaster())
2667 ++count;
2668 return count;
2669}
2670
2671void Map::SendToPlayers(WorldPacket const* data) const
2672{
2674 itr->GetSource()->SendDirectMessage(data);
2675}
2676
2678{
2679 CellCoord cell_min(ngrid.getX() * MAX_NUMBER_OF_CELLS, ngrid.getY() * MAX_NUMBER_OF_CELLS);
2680 CellCoord cell_max(cell_min.x_coord + MAX_NUMBER_OF_CELLS, cell_min.y_coord+MAX_NUMBER_OF_CELLS);
2681
2682 //we must find visible range in cells so we unload only non-visible cells...
2683 float viewDist = GetVisibilityRange();
2684 int cell_range = (int)ceilf(viewDist / SIZE_OF_GRID_CELL) + 1;
2685
2686 cell_min.dec_x(cell_range);
2687 cell_min.dec_y(cell_range);
2688 cell_max.inc_x(cell_range);
2689 cell_max.inc_y(cell_range);
2690
2692 {
2693 Player* player = iter->GetSource();
2694
2696 if ((cell_min.x_coord <= p.x_coord && p.x_coord <= cell_max.x_coord) &&
2697 (cell_min.y_coord <= p.y_coord && p.y_coord <= cell_max.y_coord))
2698 return true;
2699 }
2700
2701 for (ActiveNonPlayers::const_iterator iter = m_activeNonPlayers.begin(); iter != m_activeNonPlayers.end(); ++iter)
2702 {
2703 WorldObject* obj = *iter;
2704
2706 if ((cell_min.x_coord <= p.x_coord && p.x_coord <= cell_max.x_coord) &&
2707 (cell_min.y_coord <= p.y_coord && p.y_coord <= cell_max.y_coord))
2708 return true;
2709 }
2710
2711 return false;
2712}
2713
2715{
2716 AddToActiveHelper(obj);
2717
2718 Optional<Position> respawnLocation;
2719 switch (obj->GetTypeId())
2720 {
2721 case TYPEID_UNIT:
2722 if (Creature* creature = obj->ToCreature(); !creature->IsPet() && creature->GetSpawnId())
2723 {
2724 respawnLocation.emplace();
2725 creature->GetRespawnPosition(respawnLocation->m_positionX, respawnLocation->m_positionY, respawnLocation->m_positionZ);
2726 }
2727 break;
2728 case TYPEID_GAMEOBJECT:
2729 if (GameObject* gameObject = obj->ToGameObject(); gameObject->GetSpawnId())
2730 {
2731 respawnLocation.emplace();
2732 gameObject->GetRespawnPosition(respawnLocation->m_positionX, respawnLocation->m_positionY, respawnLocation->m_positionZ);
2733 }
2734 break;
2735 default:
2736 break;
2737 }
2738
2739 if (respawnLocation)
2740 {
2741 GridCoord p = Trinity::ComputeGridCoord(respawnLocation->GetPositionX(), respawnLocation->GetPositionY());
2742 if (getNGrid(p.x_coord, p.y_coord))
2744 else
2745 {
2747 TC_LOG_ERROR("maps", "Active object {} added to grid[{}, {}] but spawn grid[{}, {}] was not loaded.",
2748 obj->GetGUID().ToString(), p.x_coord, p.y_coord, p2.x_coord, p2.y_coord);
2749 }
2750 }
2751}
2752
2754{
2756
2757 Optional<Position> respawnLocation;
2758 switch (obj->GetTypeId())
2759 {
2760 case TYPEID_UNIT:
2761 if (Creature* creature = obj->ToCreature(); !creature->IsPet() && creature->GetSpawnId())
2762 {
2763 respawnLocation.emplace();
2764 creature->GetRespawnPosition(respawnLocation->m_positionX, respawnLocation->m_positionY, respawnLocation->m_positionZ);
2765 }
2766 break;
2767 case TYPEID_GAMEOBJECT:
2768 if (GameObject* gameObject = obj->ToGameObject(); gameObject->GetSpawnId())
2769 {
2770 respawnLocation.emplace();
2771 gameObject->GetRespawnPosition(respawnLocation->m_positionX, respawnLocation->m_positionY, respawnLocation->m_positionZ);
2772 }
2773 break;
2774 default:
2775 break;
2776 }
2777
2778 if (respawnLocation)
2779 {
2780 GridCoord p = Trinity::ComputeGridCoord(respawnLocation->GetPositionX(), respawnLocation->GetPositionY());
2781 if (getNGrid(p.x_coord, p.y_coord))
2783 else
2784 {
2786 TC_LOG_ERROR("maps", "Active object {} removed from to grid[{}, {}] but spawn grid[{}, {}] was not loaded.",
2787 obj->GetGUID().ToString(), p.x_coord, p.y_coord, p2.x_coord, p2.y_coord);
2788 }
2789 }
2790}
2791
2792template TC_GAME_API bool Map::AddToMap(Corpse*);
2793template TC_GAME_API bool Map::AddToMap(Creature*);
2794template TC_GAME_API bool Map::AddToMap(GameObject*);
2796template TC_GAME_API bool Map::AddToMap(AreaTrigger*);
2797template TC_GAME_API bool Map::AddToMap(SceneObject*);
2798template TC_GAME_API bool Map::AddToMap(Conversation*);
2799
2800template TC_GAME_API void Map::RemoveFromMap(Corpse*, bool);
2801template TC_GAME_API void Map::RemoveFromMap(Creature*, bool);
2802template TC_GAME_API void Map::RemoveFromMap(GameObject*, bool);
2803template TC_GAME_API void Map::RemoveFromMap(DynamicObject*, bool);
2804template TC_GAME_API void Map::RemoveFromMap(AreaTrigger*, bool);
2805template TC_GAME_API void Map::RemoveFromMap(SceneObject*, bool);
2806template TC_GAME_API void Map::RemoveFromMap(Conversation*, bool);
2807
2808/* ******* Dungeon Instance Maps ******* */
2809
2810InstanceMap::InstanceMap(uint32 id, time_t expiry, uint32 InstanceId, Difficulty SpawnMode, TeamId InstanceTeam, InstanceLock* instanceLock,
2811 Optional<uint32> lfgDungeonsId)
2812 : Map(id, expiry, InstanceId, SpawnMode),
2813 i_data(nullptr), i_script_id(0), i_scenario(nullptr), i_instanceLock(instanceLock), i_lfgDungeonsId(lfgDungeonsId)
2814{
2815 //lets initialize visibility distance for dungeons
2817
2818 // the timer is started by default, and stopped when the first player joins
2819 // this make sure it gets unloaded if for some reason no player joins
2821
2822 sWorldStateMgr->SetValue(WS_TEAM_IN_INSTANCE_ALLIANCE, InstanceTeam == TEAM_ALLIANCE, false, this);
2823 sWorldStateMgr->SetValue(WS_TEAM_IN_INSTANCE_HORDE, InstanceTeam == TEAM_HORDE, false, this);
2824
2825 if (i_instanceLock)
2826 {
2827 i_instanceLock->SetInUse(true);
2828 i_instanceExpireEvent = i_instanceLock->GetExpiryTime(); // ignore extension state for reset event (will ask players to accept extended save on expiration)
2829 }
2830}
2831
2833{
2834 if (i_instanceLock)
2835 i_instanceLock->SetInUse(false);
2836
2837 delete i_data;
2838 delete i_scenario;
2839}
2840
2842{
2843 //init visibility distance for instances
2846}
2847
2848/*
2849 Do map specific checks to see if the player can enter
2850*/
2852{
2853 if (player->GetMapRef().getTarget() == this)
2854 {
2855 TC_LOG_ERROR("maps", "InstanceMap::CannotEnter - player {} {} already in map {}, {}, {}!", player->GetName(), player->GetGUID().ToString(), GetId(), GetInstanceId(), GetDifficultyID());
2856 ABORT();
2857 return TRANSFER_ABORT_ERROR;
2858 }
2859
2860 // allow GM's to enter
2861 if (player->IsGameMaster())
2862 return Map::CannotEnter(player);
2863
2864 // cannot enter if the instance is full (player cap), GMs don't count
2865 uint32 maxPlayers = GetMaxPlayers();
2866 if (GetPlayersCountExceptGMs() >= maxPlayers)
2867 {
2868 TC_LOG_WARN("maps", "MAP: Instance '{}' of map '{}' cannot have more than '{}' players. Player '{}' rejected", GetInstanceId(), GetMapName(), maxPlayers, player->GetName());
2870 }
2871
2872 // cannot enter while an encounter is in progress (unless this is a relog, in which case it is permitted)
2873 if (!player->IsLoading() && IsRaid() && GetInstanceScript() && GetInstanceScript()->IsEncounterInProgress())
2875
2876 if (i_instanceLock)
2877 {
2878 // cannot enter if player is permanent saved to a different instance id
2879 TransferAbortReason lockError = sInstanceLockMgr.CanJoinInstanceLock(player->GetGUID(), { GetEntry(), GetMapDifficulty() }, i_instanceLock);
2880 if (lockError != TRANSFER_ABORT_NONE)
2881 return lockError;
2882 }
2883
2884 return Map::CannotEnter(player);
2885}
2886
2887/*
2888 Do map specific checks and add the player to the map if successful.
2889*/
2890bool InstanceMap::AddPlayerToMap(Player* player, bool initPlayer /*= true*/)
2891{
2892 // increase current instances (hourly limit)
2894
2895 MapDb2Entries entries{ GetEntry(), GetMapDifficulty() };
2896 if (entries.MapDifficulty->HasResetSchedule() && i_instanceLock && !i_instanceLock->IsNew() && i_data)
2897 {
2898 if (!entries.MapDifficulty->IsUsingEncounterLocks())
2899 {
2900 InstanceLock const* playerLock = sInstanceLockMgr.FindActiveInstanceLock(player->GetGUID(), entries);
2901 if (!playerLock || (playerLock->IsExpired() && playerLock->IsExtended()) ||
2903 {
2905 pendingRaidLock.TimeUntilLock = 60000;
2907 pendingRaidLock.Extending = playerLock && playerLock->IsExtended();
2908 pendingRaidLock.WarningOnly = entries.Map->IsFlexLocking(); // events it triggers: 1 : INSTANCE_LOCK_WARNING 0 : INSTANCE_LOCK_STOP / INSTANCE_LOCK_START
2909 player->GetSession()->SendPacket(pendingRaidLock.Write());
2910 if (!entries.Map->IsFlexLocking())
2911 player->SetPendingBind(GetInstanceId(), 60000);
2912 }
2913 }
2914 }
2915
2916 TC_LOG_DEBUG("maps", "MAP: Player '{}' entered instance '{}' of map '{}'", player->GetName(), GetInstanceId(), GetMapName());
2917 // initialize unload state
2918 m_unloadTimer = 0;
2919
2920 // this will acquire the same mutex so it cannot be in the previous block
2921 Map::AddPlayerToMap(player, initPlayer);
2922
2923 if (i_data)
2924 i_data->OnPlayerEnter(player);
2925
2926 if (i_scenario)
2927 i_scenario->OnPlayerEnter(player);
2928
2929 return true;
2930}
2931
2933{
2934 Map::Update(t_diff);
2935
2936 if (i_data)
2937 {
2938 i_data->Update(t_diff);
2940 }
2941
2942 if (i_scenario)
2943 i_scenario->Update(t_diff);
2944
2946 {
2949 }
2950}
2951
2953{
2954 TC_LOG_DEBUG("maps", "MAP: Removing player '{}' from instance '{}' of map '{}' before relocating to another map", player->GetName(), GetInstanceId(), GetMapName());
2955
2956 if (i_data)
2957 i_data->OnPlayerLeave(player);
2958
2959 // if last player set unload timer
2960 if (!m_unloadTimer && m_mapRefManager.getSize() == 1)
2962
2963 if (i_scenario)
2964 i_scenario->OnPlayerExit(player);
2965
2966 Map::RemovePlayerFromMap(player, remove);
2967}
2968
2970{
2971 if (i_data != nullptr)
2972 return;
2973
2974 InstanceTemplate const* mInstance = sObjectMgr->GetInstanceTemplate(GetId());
2975 if (mInstance)
2976 {
2977 i_script_id = mInstance->ScriptId;
2978 i_data = sScriptMgr->CreateInstanceData(this);
2979 }
2980
2981 if (!i_data)
2982 return;
2983
2985 {
2986 i_data->Create();
2987 return;
2988 }
2989
2990 MapDb2Entries entries{ GetEntry(), GetMapDifficulty() };
2991 if (!entries.IsInstanceIdBound() && !IsRaid() && !entries.MapDifficulty->IsRestoringDungeonState() && i_owningGroupRef.isValid())
2992 {
2993 i_data->Create();
2994 return;
2995 }
2996
2999 if (!lockData->Data.empty())
3000 {
3001 TC_LOG_DEBUG("maps", "Loading instance data for `{}` with id {}", sObjectMgr->GetScriptName(i_script_id), i_InstanceId);
3002 i_data->Load(lockData->Data.c_str());
3003 }
3004 else
3005 i_data->Create();
3006}
3007
3009{
3011 i_owningGroupRef.link(group, this);
3012}
3013
3014/*
3015 Returns true if there are no players in the instance
3016*/
3018{
3019 // raids can be reset if no boss was killed
3022
3023 if (HavePlayers())
3024 {
3025 switch (method)
3026 {
3028 // notify the players to leave the instance so it can be reset
3029 for (MapReference const& ref : m_mapRefManager)
3030 ref.GetSource()->SendResetFailedNotify(GetId());
3031 break;
3033 // no client notification
3034 break;
3036 {
3038 raidInstanceMessage.Type = RAID_INSTANCE_EXPIRED;
3039 raidInstanceMessage.MapID = GetId();
3040 raidInstanceMessage.DifficultyID = GetDifficultyID();
3041 raidInstanceMessage.Write();
3042
3043 for (MapReference const& ref : m_mapRefManager)
3044 ref.GetSource()->SendDirectMessage(raidInstanceMessage.GetRawPacket());
3045
3046 if (i_data)
3047 {
3049 pendingRaidLock.TimeUntilLock = 60000;
3051 pendingRaidLock.Extending = true;
3052 pendingRaidLock.WarningOnly = GetEntry()->IsFlexLocking();
3053 pendingRaidLock.Write();
3054
3055 for (MapReference const& ref : m_mapRefManager)
3056 {
3057 ref.GetSource()->SendDirectMessage(pendingRaidLock.GetRawPacket());
3058
3059 if (!pendingRaidLock.WarningOnly)
3060 ref.GetSource()->SetPendingBind(GetInstanceId(), 60000);
3061 }
3062 }
3063 break;
3064 }
3065 default:
3066 break;
3067 }
3068
3070 }
3071 else
3072 {
3073 // unloaded at next update
3075 }
3076
3078}
3079
3080std::string const& InstanceMap::GetScriptName() const
3081{
3082 return sObjectMgr->GetScriptName(i_script_id);
3083}
3084
3086{
3087 if (i_instanceLock)
3088 {
3089 uint32 instanceCompletedEncounters = i_instanceLock->GetData()->CompletedEncountersMask | (1u << updateSaveDataEvent.DungeonEncounter->Bit);
3090
3091 MapDb2Entries entries{ GetEntry(), GetMapDifficulty() };
3092
3093 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
3094
3095 if (entries.IsInstanceIdBound())
3096 sInstanceLockMgr.UpdateSharedInstanceLock(trans, InstanceLockUpdateEvent(GetInstanceId(), i_data->GetSaveData(),
3097 instanceCompletedEncounters, updateSaveDataEvent.DungeonEncounter, i_data->GetEntranceLocationForCompletedEncounters(instanceCompletedEncounters)));
3098
3099 for (MapReference& mapReference : m_mapRefManager)
3100 {
3101 Player* player = mapReference.GetSource();
3102 // never instance bind GMs with GM mode enabled
3103 if (player->IsGameMaster())
3104 continue;
3105
3106 InstanceLock const* playerLock = sInstanceLockMgr.FindActiveInstanceLock(player->GetGUID(), entries);
3107 std::string const* oldData = nullptr;
3108 uint32 playerCompletedEncounters = 0;
3109 if (playerLock)
3110 {
3111 oldData = &playerLock->GetData()->Data;
3112 playerCompletedEncounters = playerLock->GetData()->CompletedEncountersMask | (1u << updateSaveDataEvent.DungeonEncounter->Bit);
3113 }
3114
3115 bool isNewLock = !playerLock || playerLock->IsNew() || playerLock->IsExpired();
3116
3117 InstanceLock const* newLock = sInstanceLockMgr.UpdateInstanceLockForPlayer(trans, player->GetGUID(), entries,
3118 InstanceLockUpdateEvent(GetInstanceId(), i_data->UpdateBossStateSaveData(oldData ? *oldData : "", updateSaveDataEvent),
3119 instanceCompletedEncounters, updateSaveDataEvent.DungeonEncounter, i_data->GetEntranceLocationForCompletedEncounters(playerCompletedEncounters)));
3120
3121 if (isNewLock)
3122 {
3124 data.Gm = player->IsGameMaster();
3125 player->SendDirectMessage(data.Write());
3126
3127 player->GetSession()->SendCalendarRaidLockoutAdded(newLock);
3128 }
3129 }
3130
3131 CharacterDatabase.CommitTransaction(trans);
3132 }
3133}
3134
3136{
3137 if (i_instanceLock)
3138 {
3139 uint32 instanceCompletedEncounters = i_instanceLock->GetData()->CompletedEncountersMask;
3140
3141 MapDb2Entries entries{ GetEntry(), GetMapDifficulty() };
3142
3143 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
3144
3145 if (entries.IsInstanceIdBound())
3146 sInstanceLockMgr.UpdateSharedInstanceLock(trans, InstanceLockUpdateEvent(GetInstanceId(), i_data->GetSaveData(),
3147 instanceCompletedEncounters, nullptr, {}));
3148
3149 for (MapReference& mapReference : m_mapRefManager)
3150 {
3151 Player* player = mapReference.GetSource();
3152 // never instance bind GMs with GM mode enabled
3153 if (player->IsGameMaster())
3154 continue;
3155
3156 InstanceLock const* playerLock = sInstanceLockMgr.FindActiveInstanceLock(player->GetGUID(), entries);
3157 std::string const* oldData = nullptr;
3158 if (playerLock)
3159 oldData = &playerLock->GetData()->Data;
3160
3161 bool isNewLock = !playerLock || playerLock->IsNew() || playerLock->IsExpired();
3162
3163 InstanceLock const* newLock = sInstanceLockMgr.UpdateInstanceLockForPlayer(trans, player->GetGUID(), entries,
3164 InstanceLockUpdateEvent(GetInstanceId(), i_data->UpdateAdditionalSaveData(oldData ? *oldData : "", updateSaveDataEvent),
3165 instanceCompletedEncounters, nullptr, {}));
3166
3167 if (isNewLock)
3168 {
3170 data.Gm = player->IsGameMaster();
3171 player->SendDirectMessage(data.Write());
3172
3173 player->GetSession()->SendCalendarRaidLockoutAdded(newLock);
3174 }
3175 }
3176
3177 CharacterDatabase.CommitTransaction(trans);
3178 }
3179}
3180
3182{
3183 MapDb2Entries entries{ GetEntry(), GetMapDifficulty() };
3184 InstanceLock const* playerLock = sInstanceLockMgr.FindActiveInstanceLock(player->GetGUID(), entries);
3185
3186 bool isNewLock = !playerLock || playerLock->IsNew() || playerLock->IsExpired();
3187
3188 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
3189
3190 InstanceLock const* newLock = sInstanceLockMgr.UpdateInstanceLockForPlayer(trans, player->GetGUID(), entries,
3192
3193 CharacterDatabase.CommitTransaction(trans);
3194
3195 if (isNewLock)
3196 {
3198 data.Gm = player->IsGameMaster();
3199 player->SendDirectMessage(data.Write());
3200
3201 player->GetSession()->SendCalendarRaidLockoutAdded(newLock);
3202 }
3203}
3204
3206{
3207 return sDB2Manager.GetMapDifficultyData(GetId(), GetDifficultyID());
3208}
3209
3211{
3212 return i_mapEntry->ID;
3213}
3214
3216{
3217 return i_mapEntry && i_mapEntry->Instanceable();
3218}
3219
3220bool Map::IsDungeon() const
3221{
3222 return i_mapEntry && i_mapEntry->IsDungeon();
3223}
3224
3226{
3228}
3229
3230bool Map::IsRaid() const
3231{
3232 return i_mapEntry && i_mapEntry->IsRaid();
3233}
3234
3235bool Map::IsLFR() const
3236{
3237 switch (i_spawnMode)
3238 {
3239 case DIFFICULTY_LFR:
3240 case DIFFICULTY_LFR_NEW:
3242 return true;
3243 default:
3244 return false;
3245 }
3246}
3247
3248bool Map::IsNormal() const
3249{
3250 switch (i_spawnMode)
3251 {
3252 case DIFFICULTY_NORMAL:
3253 case DIFFICULTY_10_N:
3254 case DIFFICULTY_25_N:
3258 return true;
3259 default:
3260 return false;
3261 }
3262}
3263
3264bool Map::IsHeroic() const
3265{
3266 if (DifficultyEntry const* difficulty = sDifficultyStore.LookupEntry(i_spawnMode))
3267 {
3268 if (difficulty->Flags & DIFFICULTY_FLAG_DISPLAY_HEROIC)
3269 return true;
3270 }
3271
3272 // compatibility purposes of old difficulties
3273 switch (i_spawnMode)
3274 {
3275 case DIFFICULTY_10_HC:
3276 case DIFFICULTY_25_HC:
3277 case DIFFICULTY_HEROIC:
3279 return true;
3280 default:
3281 return false;
3282 }
3283}
3284
3285bool Map::IsMythic() const
3286{
3287 if (DifficultyEntry const* difficulty = sDifficultyStore.LookupEntry(i_spawnMode))
3288 return difficulty->Flags & DIFFICULTY_FLAG_DISPLAY_MYTHIC;
3289 return false;
3290}
3291
3293{
3295}
3296
3298{
3299 return IsHeroic() || IsMythic() || IsMythicPlus();
3300}
3301
3303{
3305}
3306
3308{
3310}
3311
3313{
3315}
3316
3318{
3319 return i_mapEntry && i_mapEntry->IsBattleArena();
3320}
3321
3323{
3325}
3326
3328{
3329 return i_mapEntry && i_mapEntry->IsScenario();
3330}
3331
3333{
3334 return i_mapEntry && i_mapEntry->IsGarrison();
3335}
3336
3338{
3339 return IsBattlegroundOrArena();
3340}
3341
3342bool Map::GetEntrancePos(int32 &mapid, float &x, float &y)
3343{
3344 if (!i_mapEntry)
3345 return false;
3346 return i_mapEntry->GetEntrancePos(mapid, x, y);
3347}
3348
3350{
3351 MapDifficultyEntry const* mapDiff = GetMapDifficulty();
3352 if (mapDiff && mapDiff->MaxPlayers)
3353 return mapDiff->MaxPlayers;
3354
3355 return GetEntry()->MaxPlayers;
3356}
3357
3359{
3360 if (sWorldStateMgr->GetValue(WS_TEAM_IN_INSTANCE_ALLIANCE, this))
3361 return TEAM_ALLIANCE;
3362 if (sWorldStateMgr->GetValue(WS_TEAM_IN_INSTANCE_HORDE, this))
3363 return TEAM_HORDE;
3364 return TEAM_NEUTRAL;
3365}
3366
3367/* ******* Battleground Instance Maps ******* */
3368
3369BattlegroundMap::BattlegroundMap(uint32 id, time_t expiry, uint32 InstanceId, Difficulty spawnMode)
3370 : Map(id, expiry, InstanceId, spawnMode), m_bg(nullptr), _battlegroundScript(nullptr), _scriptId(0)
3371{
3372 //lets initialize visibility distance for BG/Arenas
3374}
3375
3377{
3378 if (m_bg)
3379 {
3380 //unlink to prevent crash, always unlink all pointer reference before destruction
3381 m_bg->SetBgMap(nullptr);
3382 m_bg = nullptr;
3383 }
3384}
3385
3387{
3388 //init visibility distance for BG/Arenas
3391}
3392
3393std::string const& BattlegroundMap::GetScriptName() const
3394{
3395 return sObjectMgr->GetScriptName(_scriptId);
3396}
3397
3399{
3401 return;
3402
3403 ASSERT(GetBG(), "Battleground not set yet!");
3404
3405 if (BattlegroundScriptTemplate const* scriptTemplate = sBattlegroundMgr->FindBattlegroundScriptTemplate(GetId(), GetBG()->GetTypeID()))
3406 {
3407 _scriptId = scriptTemplate->ScriptId;
3408 _battlegroundScript.reset(sScriptMgr->CreateBattlegroundData(this));
3409 }
3410
3411 // Make sure every battleground has a default script
3413 {
3414 if (IsBattleArena())
3415 _battlegroundScript = std::make_unique<ArenaScript>(this);
3416 else
3417 _battlegroundScript = std::make_unique<BattlegroundScript>(this);
3418 }
3419
3420 _battlegroundScript->OnInit();
3421}
3422
3424{
3425 if (player->GetMapRef().getTarget() == this)
3426 {
3427 TC_LOG_ERROR("maps", "BGMap::CannotEnter - player {} is already in map!", player->GetGUID().ToString());
3428 ABORT();
3429 return TRANSFER_ABORT_ERROR;
3430 }
3431
3432 if (player->GetBattlegroundId() != GetInstanceId())
3434
3435 // player number limit is checked in bgmgr, no need to do it here
3436
3437 return Map::CannotEnter(player);
3438}
3439
3440bool BattlegroundMap::AddPlayerToMap(Player* player, bool initPlayer /*= true*/)
3441{
3442 player->m_InstanceValid = true;
3443 return Map::AddPlayerToMap(player, initPlayer);
3444}
3445
3447{
3448 TC_LOG_DEBUG("maps", "MAP: Removing player '{}' from bg '{}' of map '{}' before relocating to another map", player->GetName(), GetInstanceId(), GetMapName());
3449 Map::RemovePlayerFromMap(player, remove);
3450}
3451
3453{
3455}
3456
3458{
3459 if (HavePlayers())
3460 for (MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
3461 if (Player* player = itr->GetSource())
3462 if (!player->IsBeingTeleportedFar())
3463 player->TeleportTo(player->GetBattlegroundEntryPoint());
3464}
3465
3467{
3468 Map::Update(diff);
3469 _battlegroundScript->OnUpdate(diff);
3470}
3471
3473{
3474 return _objectsStore.Find<AreaTrigger>(guid);
3475}
3476
3478{
3479 return _objectsStore.Find<SceneObject>(guid);
3480}
3481
3483{
3484 return _objectsStore.Find<Conversation>(guid);
3485}
3486
3488{
3489 return ObjectAccessor::GetPlayer(this, guid);
3490}
3491
3493{
3494 return _objectsStore.Find<Corpse>(guid);
3495}
3496
3498{
3499 return _objectsStore.Find<Creature>(guid);
3500}
3501
3503{
3504 return _objectsStore.Find<DynamicObject>(guid);
3505}
3506
3508{
3509 return _objectsStore.Find<GameObject>(guid);
3510}
3511
3513{
3514 return _objectsStore.Find<Pet>(guid);
3515}
3516
3518{
3519 if (!guid.IsMOTransport())
3520 return nullptr;
3521
3522 GameObject* go = GetGameObject(guid);
3523 return go ? go->ToTransport() : nullptr;
3524}
3525
3527{
3528 auto const bounds = GetCreatureBySpawnIdStore().equal_range(spawnId);
3529 if (bounds.first == bounds.second)
3530 return nullptr;
3531
3532 std::unordered_multimap<ObjectGuid::LowType, Creature*>::const_iterator creatureItr = std::find_if(bounds.first, bounds.second, [](Map::CreatureBySpawnIdContainer::value_type const& pair)
3533 {
3534 return pair.second->IsAlive();
3535 });
3536
3537 return creatureItr != bounds.second ? creatureItr->second : bounds.first->second;
3538}
3539
3541{
3542 auto const bounds = GetGameObjectBySpawnIdStore().equal_range(spawnId);
3543 if (bounds.first == bounds.second)
3544 return nullptr;
3545
3546 std::unordered_multimap<ObjectGuid::LowType, GameObject*>::const_iterator creatureItr = std::find_if(bounds.first, bounds.second, [](Map::GameObjectBySpawnIdContainer::value_type const& pair)
3547 {
3548 return pair.second->isSpawned();
3549 });
3550
3551 return creatureItr != bounds.second ? creatureItr->second : bounds.first->second;
3552}
3553
3555{
3556 auto const bounds = GetAreaTriggerBySpawnIdStore().equal_range(spawnId);
3557 if (bounds.first == bounds.second)
3558 return nullptr;
3559
3560 return bounds.first->second;
3561}
3562
3564{
3565 if (&*m_mapRefIter == &player->GetMapRef())
3566 m_mapRefIter = m_mapRefIter->nocheck_prev();
3567}
3568
3569void Map::SaveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 entry, time_t respawnTime, uint32 gridId, CharacterDatabaseTransaction dbTrans, bool startup)
3570{
3571 SpawnMetadata const* data = sObjectMgr->GetSpawnMetadata(type, spawnId);
3572 if (!data)
3573 {
3574 TC_LOG_ERROR("maps", "Map {} attempt to save respawn time for nonexistant spawnid ({},{}).", GetId(), type, spawnId);
3575 return;
3576 }
3577
3578 if (!respawnTime)
3579 {
3580 // Delete only
3581 RemoveRespawnTime(data->type, data->spawnId, dbTrans);
3582 return;
3583 }
3584
3585 RespawnInfo ri;
3586 ri.type = data->type;
3587 ri.spawnId = data->spawnId;
3588 ri.entry = entry;
3589 ri.respawnTime = respawnTime;
3590 ri.gridId = gridId;
3591 bool success = AddRespawnInfo(ri);
3592
3593 if (startup)
3594 {
3595 if (!success)
3596 TC_LOG_ERROR("maps", "Attempt to load saved respawn {} for ({},{}) failed - duplicate respawn? Skipped.", respawnTime, uint32(type), spawnId);
3597 }
3598 else if (success)
3599 SaveRespawnInfoDB(ri, dbTrans);
3600}
3601
3603{
3604 if (Instanceable())
3605 return;
3606
3608 stmt->setUInt16(0, info.type);
3609 stmt->setUInt64(1, info.spawnId);
3610 stmt->setInt64(2, info.respawnTime);
3611 stmt->setUInt16(3, GetId());
3612 stmt->setUInt32(4, GetInstanceId());
3613 CharacterDatabase.ExecuteOrAppend(dbTrans, stmt);
3614}
3615
3617{
3618 if (Instanceable())
3619 return;
3620
3622 stmt->setUInt16(0, GetId());
3623 stmt->setUInt32(1, GetInstanceId());
3624 if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
3625 {
3626 do
3627 {
3628 Field* fields = result->Fetch();
3629 SpawnObjectType type = SpawnObjectType(fields[0].GetUInt16());
3630 ObjectGuid::LowType spawnId = fields[1].GetUInt64();
3631 time_t respawnTime = fields[2].GetInt64();
3632
3633 if (SpawnData::TypeHasData(type))
3634 {
3635 if (SpawnData const* data = sObjectMgr->GetSpawnData(type, spawnId))
3636 SaveRespawnTime(type, spawnId, data->id, time_t(respawnTime), Trinity::ComputeGridCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY()).GetId(), nullptr, true);
3637 else
3638 TC_LOG_ERROR("maps", "Loading saved respawn time of {} for spawnid ({},{}) - spawn does not exist, ignoring", respawnTime, uint32(type), spawnId);
3639 }
3640 else
3641 {
3642 TC_LOG_ERROR("maps", "Loading saved respawn time of {} for spawnid ({},{}) - invalid spawn type, ignoring", respawnTime, uint32(type), spawnId);
3643 }
3644
3645 } while (result->NextRow());
3646 }
3647}
3648
3650{
3651 if (Instanceable())
3652 return;
3653
3655 stmt->setUInt16(0, GetId());
3656 stmt->setUInt32(1, GetInstanceId());
3657 CharacterDatabase.Execute(stmt);
3658}
3659
3661{
3662 ObjectGuid linkedGuid = sObjectMgr->GetLinkedRespawnGuid(guid);
3663 switch (linkedGuid.GetHigh())
3664 {
3665 case HighGuid::Creature:
3666 return GetCreatureRespawnTime(linkedGuid.GetCounter());
3668 return GetGORespawnTime(linkedGuid.GetCounter());
3669 default:
3670 break;
3671 }
3672
3673 return time_t(0);
3674}
3675
3677{
3679 stmt->setUInt32(0, GetId());
3680 stmt->setUInt32(1, GetInstanceId());
3681
3682 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
3683 // SELECT posX, posY, posZ, orientation, mapId, displayId, itemCache, race, class, gender, flags, dynFlags, time, corpseType, instanceId, guid FROM corpse WHERE mapId = ? AND instanceId = ?
3684 PreparedQueryResult result = CharacterDatabase.Query(stmt);
3685 if (!result)
3686 return;
3687
3688 std::unordered_map<ObjectGuid::LowType, std::unordered_set<uint32>> phases;
3689 std::unordered_map<ObjectGuid::LowType, std::vector<UF::ChrCustomizationChoice>> customizations;
3690
3691 stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CORPSE_PHASES);
3692 stmt->setUInt32(0, GetId());
3693 stmt->setUInt32(1, GetInstanceId());
3694
3695 // 0 1
3696 // SELECT OwnerGuid, PhaseId FROM corpse_phases cp LEFT JOIN corpse c ON cp.OwnerGuid = c.guid WHERE c.mapId = ? AND c.instanceId = ?
3697 if (PreparedQueryResult phaseResult = CharacterDatabase.Query(stmt))
3698 {
3699 do
3700 {
3701 Field* fields = phaseResult->Fetch();
3702 ObjectGuid::LowType guid = fields[0].GetUInt64();
3703 uint32 phaseId = fields[1].GetUInt32();
3704
3705 phases[guid].insert(phaseId);
3706
3707 } while (phaseResult->NextRow());
3708 }
3709
3710 stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CORPSE_CUSTOMIZATIONS);
3711 stmt->setUInt32(0, GetId());
3712 stmt->setUInt32(1, GetInstanceId());
3713
3714 // 0 1 2
3715 // SELECT cc.ownerGuid, cc.chrCustomizationOptionID, cc.chrCustomizationChoiceID FROM corpse_customizations cc LEFT JOIN corpse c ON cc.ownerGuid = c.guid WHERE c.mapId = ? AND c.instanceId = ?
3716 if (PreparedQueryResult customizationResult = CharacterDatabase.Query(stmt))
3717 {
3718 do
3719 {
3720 Field* fields = customizationResult->Fetch();
3721 ObjectGuid::LowType guid = fields[0].GetUInt64();
3722 std::vector<UF::ChrCustomizationChoice>& customizationsForCorpse = customizations[guid];
3723
3724 customizationsForCorpse.emplace_back();
3725 UF::ChrCustomizationChoice& choice = customizationsForCorpse.back();
3726 choice.ChrCustomizationOptionID = fields[1].GetUInt32();
3727 choice.ChrCustomizationChoiceID = fields[2].GetUInt32();
3728
3729 } while (customizationResult->NextRow());
3730 }
3731
3732 do
3733 {
3734 Field* fields = result->Fetch();
3735 CorpseType type = CorpseType(fields[13].GetUInt8());
3736 ObjectGuid::LowType guid = fields[15].GetUInt64();
3737 if (type >= MAX_CORPSE_TYPE || type == CORPSE_BONES)
3738 {
3739 TC_LOG_ERROR("misc", "Corpse (guid: {}) have wrong corpse type ({}), not loading.", guid, type);
3740 continue;
3741 }
3742
3743 Corpse* corpse = new Corpse(type);
3744 if (!corpse->LoadCorpseFromDB(GenerateLowGuid<HighGuid::Corpse>(), fields))
3745 {
3746 delete corpse;
3747 continue;
3748 }
3749
3750 for (uint32 phaseId : phases[guid])
3751 PhasingHandler::AddPhase(corpse, phaseId, false);
3752
3753 corpse->SetCustomizations(Trinity::Containers::MakeIteratorPair(customizations[guid].begin(), customizations[guid].end()));
3754
3755 AddCorpse(corpse);
3756
3757 } while (result->NextRow());
3758}
3759
3761{
3762 // DELETE cp, c FROM corpse_phases cp INNER JOIN corpse c ON cp.OwnerGuid = c.guid WHERE c.mapId = ? AND c.instanceId = ?
3764 stmt->setUInt32(0, GetId());
3765 stmt->setUInt32(1, GetInstanceId());
3766 CharacterDatabase.Execute(stmt);
3767}
3768
3770{
3771 corpse->SetMap(this);
3772
3773 _corpsesByCell[corpse->GetCellCoord().GetId()].insert(corpse);
3774 if (corpse->GetType() != CORPSE_BONES)
3775 _corpsesByPlayer[corpse->GetOwnerGUID()] = corpse;
3776 else
3777 _corpseBones.insert(corpse);
3778}
3779
3781{
3782 ASSERT(corpse);
3783
3785 if (corpse->IsInGrid())
3786 RemoveFromMap(corpse, false);
3787 else
3788 {
3789 corpse->RemoveFromWorld();
3790 corpse->ResetMap();
3791 }
3792
3793 _corpsesByCell[corpse->GetCellCoord().GetId()].erase(corpse);
3794 if (corpse->GetType() != CORPSE_BONES)
3795 _corpsesByPlayer.erase(corpse->GetOwnerGUID());
3796 else
3797 _corpseBones.erase(corpse);
3798}
3799
3800Corpse* Map::ConvertCorpseToBones(ObjectGuid const& ownerGuid, bool insignia /*= false*/)
3801{
3802 Corpse* corpse = GetCorpseByPlayer(ownerGuid);
3803 if (!corpse)
3804 return nullptr;
3805
3806 RemoveCorpse(corpse);
3807
3808 // remove corpse from DB
3809 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
3810 corpse->DeleteFromDB(trans);
3811 CharacterDatabase.CommitTransaction(trans);
3812
3813 Corpse* bones = nullptr;
3814
3815 // create the bones only if the map and the grid is loaded at the corpse's location
3816 // ignore bones creating option in case insignia
3817 if ((insignia ||
3819 !IsRemovalGrid(corpse->GetPositionX(), corpse->GetPositionY()))
3820 {
3821 // Create bones, don't change Corpse
3822 bones = new Corpse();
3823 bones->Create(corpse->GetGUID().GetCounter(), this);
3824
3826 bones->SetOwnerGUID(corpse->m_corpseData->Owner);
3827 bones->SetPartyGUID(corpse->m_corpseData->PartyGUID);
3828 bones->SetGuildGUID(corpse->m_corpseData->GuildGUID);
3829 bones->SetDisplayId(corpse->m_corpseData->DisplayID);
3830 bones->SetRace(corpse->m_corpseData->RaceID);
3831 bones->SetSex(corpse->m_corpseData->Sex);
3832 bones->SetClass(corpse->m_corpseData->Class);
3833 bones->SetCustomizations(Trinity::Containers::MakeIteratorPair(corpse->m_corpseData->Customizations.begin(), corpse->m_corpseData->Customizations.end()));
3834 bones->ReplaceAllFlags(corpse->m_corpseData->Flags | CORPSE_FLAG_BONES);
3835 bones->SetFactionTemplate(corpse->m_corpseData->FactionTemplate);
3836
3837 bones->SetCellCoord(corpse->GetCellCoord());
3838 bones->Relocate(corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetOrientation());
3839
3840 PhasingHandler::InheritPhaseShift(bones, corpse);
3841
3842 AddCorpse(bones);
3843
3844 bones->UpdatePositionData();
3845 bones->SetZoneScript();
3846
3847 // add bones in grid store if grid loaded where corpse placed
3848 AddToMap(bones);
3849 }
3850
3851 // all references to the corpse should be removed at this point
3852 delete corpse;
3853
3854 return bones;
3855}
3856
3858{
3859 time_t now = GameTime::GetGameTime();
3860
3861 std::vector<ObjectGuid> corpses;
3862 corpses.reserve(_corpsesByPlayer.size());
3863
3864 for (auto const& p : _corpsesByPlayer)
3865 if (p.second->IsExpired(now))
3866 corpses.push_back(p.first);
3867
3868 for (ObjectGuid const& ownerGuid : corpses)
3869 ConvertCorpseToBones(ownerGuid);
3870
3871 std::vector<Corpse*> expiredBones;
3872 for (Corpse* bones : _corpseBones)
3873 if (bones->IsExpired(now))
3874 expiredBones.push_back(bones);
3875
3876 for (Corpse* bones : expiredBones)
3877 {
3878 RemoveCorpse(bones);
3879 delete bones;
3880 }
3881}
3882
3883void Map::SendZoneDynamicInfo(uint32 zoneId, Player* player) const
3884{
3885 auto itr = _zoneDynamicInfo.find(zoneId);
3886 if (itr == _zoneDynamicInfo.end())
3887 return;
3888
3889 if (uint32 music = itr->second.MusicId)
3890 player->SendDirectMessage(WorldPackets::Misc::PlayMusic(music).Write());
3891
3892 SendZoneWeather(itr->second, player);
3893
3894 for (ZoneDynamicInfo::LightOverride const& lightOverride : itr->second.LightOverrides)
3895 {
3897 overrideLight.AreaLightID = lightOverride.AreaLightId;
3898 overrideLight.OverrideLightID = lightOverride.OverrideLightId;
3899 overrideLight.TransitionMilliseconds = lightOverride.TransitionMilliseconds;
3900 player->SendDirectMessage(overrideLight.Write());
3901 }
3902}
3903
3904void Map::SendZoneWeather(uint32 zoneId, Player* player) const
3905{
3907 {
3908 auto itr = _zoneDynamicInfo.find(zoneId);
3909 if (itr == _zoneDynamicInfo.end())
3910 return;
3911
3912 SendZoneWeather(itr->second, player);
3913 }
3914}
3915
3916void Map::SendZoneWeather(ZoneDynamicInfo const& zoneDynamicInfo, Player* player) const
3917{
3918 if (WeatherState weatherId = zoneDynamicInfo.WeatherId)
3919 {
3920 WorldPackets::Misc::Weather weather(weatherId, zoneDynamicInfo.Intensity);
3921 player->SendDirectMessage(weather.Write());
3922 }
3923 else if (zoneDynamicInfo.DefaultWeather)
3924 {
3925 zoneDynamicInfo.DefaultWeather->SendWeatherUpdateToPlayer(player);
3926 }
3927 else
3929}
3930
3931void Map::SetZoneMusic(uint32 zoneId, uint32 musicId)
3932{
3933 _zoneDynamicInfo[zoneId].MusicId = musicId;
3934
3935 Map::PlayerList const& players = GetPlayers();
3936 if (!players.isEmpty())
3937 {
3938 WorldPackets::Misc::PlayMusic playMusic(musicId);
3939 playMusic.Write();
3940
3941 for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
3942 if (Player* player = itr->GetSource())
3943 if (player->GetZoneId() == zoneId && !player->HasAuraType(SPELL_AURA_FORCE_WEATHER))
3944 player->SendDirectMessage(playMusic.GetRawPacket());
3945 }
3946}
3947
3949{
3950 WeatherData const* weatherData = WeatherMgr::GetWeatherData(zoneId);
3951 if (!weatherData)
3952 return nullptr;
3953
3954 ZoneDynamicInfo& info = _zoneDynamicInfo[zoneId];
3955 if (!info.DefaultWeather)
3956 {
3957 info.DefaultWeather = std::make_unique<Weather>(zoneId, weatherData);
3958 info.DefaultWeather->ReGenerate();
3959 info.DefaultWeather->UpdateWeather();
3960 }
3961
3962 return info.DefaultWeather.get();
3963}
3964
3966{
3968 if (zoneDynamicInfo)
3969 {
3970 if (WeatherState weatherId = zoneDynamicInfo->WeatherId)
3971 return weatherId;
3972
3973 if (zoneDynamicInfo->DefaultWeather)
3974 return zoneDynamicInfo->DefaultWeather->GetWeatherState();
3975 }
3976
3977 return WEATHER_STATE_FINE;
3978}
3979
3980void Map::SetZoneWeather(uint32 zoneId, WeatherState weatherId, float intensity)
3981{
3982 ZoneDynamicInfo& info = _zoneDynamicInfo[zoneId];
3983 info.WeatherId = weatherId;
3984 info.Intensity = intensity;
3985
3986 Map::PlayerList const& players = GetPlayers();
3987 if (!players.isEmpty())
3988 {
3989 WorldPackets::Misc::Weather weather(weatherId, intensity);
3990 weather.Write();
3991
3992 for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
3993 if (Player* player = itr->GetSource())
3994 if (player->GetZoneId() == zoneId)
3995 player->SendDirectMessage(weather.GetRawPacket());
3996 }
3997}
3998
3999void Map::SetZoneOverrideLight(uint32 zoneId, uint32 areaLightId, uint32 overrideLightId, Milliseconds transitionTime)
4000{
4001 ZoneDynamicInfo& info = _zoneDynamicInfo[zoneId];
4002 // client can support only one override for each light (zone independent)
4003 info.LightOverrides.erase(std::remove_if(info.LightOverrides.begin(), info.LightOverrides.end(), [areaLightId](ZoneDynamicInfo::LightOverride const& lightOverride)
4004 {
4005 return lightOverride.AreaLightId == areaLightId;
4006 }), info.LightOverrides.end());
4007
4008 // set new override (if any)
4009 if (overrideLightId)
4010 {
4011 ZoneDynamicInfo::LightOverride& lightOverride = info.LightOverrides.emplace_back();
4012 lightOverride.AreaLightId = areaLightId;
4013 lightOverride.OverrideLightId = overrideLightId;
4014 lightOverride.TransitionMilliseconds = static_cast<uint32>(transitionTime.count());
4015 }
4016
4017 Map::PlayerList const& players = GetPlayers();
4018 if (!players.isEmpty())
4019 {
4021 overrideLight.AreaLightID = areaLightId;
4022 overrideLight.OverrideLightID = overrideLightId;
4023 overrideLight.TransitionMilliseconds = static_cast<uint32>(transitionTime.count());
4024 overrideLight.Write();
4025
4026 for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
4027 if (Player* player = itr->GetSource())
4028 if (player->GetZoneId() == zoneId)
4029 player->SendDirectMessage(overrideLight.GetRawPacket());
4030 }
4031}
4032
4034{
4035 Map::PlayerList const& players = GetPlayers();
4036 for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
4037 {
4038 if (Player* player = itr->GetSource())
4039 {
4040 if (player->IsInWorld())
4041 {
4042 player->UpdateAreaDependentAuras(player->GetAreaId());
4043 player->UpdateZoneDependentAuras(player->GetZoneId());
4044 }
4045 }
4046 }
4047}
4048
4049std::string Map::GetDebugInfo() const
4050{
4051 std::stringstream sstr;
4052 sstr << std::boolalpha
4053 << "Id: " << GetId() << " InstanceId: " << GetInstanceId() << " Difficulty: " << std::to_string(GetDifficultyID())
4054 << " HasPlayers: " << HavePlayers();
4055 return sstr.str();
4056}
4057
4058std::string InstanceMap::GetDebugInfo() const
4059{
4060 std::stringstream sstr;
4061 sstr << Map::GetDebugInfo() << "\n"
4062 << std::boolalpha
4063 << "ScriptId: " << GetScriptId() << " ScriptName: " << GetScriptName();
4064 return sstr.str();
4065}
4066
#define sBattlegroundMgr
@ CHAR_SEL_CORPSE_CUSTOMIZATIONS
@ CHAR_DEL_ALL_RESPAWNS
@ CHAR_REP_RESPAWN
@ CHAR_SEL_CORPSE_PHASES
@ CHAR_DEL_RESPAWN
@ CHAR_SEL_CORPSES
@ CHAR_DEL_CORPSES_FROM_MAP
@ CHAR_SEL_RESPAWNS
@ IN_MILLISECONDS
Definition: Common.h:35
@ WEEK
Definition: Common.h:32
#define sConditionMgr
Definition: ConditionMgr.h:368
@ CONDITION_SOURCE_TYPE_SPAWN_GROUP
Definition: ConditionMgr.h:188
CorpseType
Definition: Corpse.h:30
@ CORPSE_BONES
Definition: Corpse.h:31
@ CORPSE_FLAG_BONES
Definition: Corpse.h:43
#define MAX_CORPSE_TYPE
Definition: Corpse.h:35
DB2Storage< DifficultyEntry > sDifficultyStore("Difficulty.db2", &DifficultyLoadInfo::Instance)
DB2Storage< MapEntry > sMapStore("Map.db2", &MapLoadInfo::Instance)
#define sDB2Manager
Definition: DB2Stores.h:553
Difficulty
Definition: DBCEnums.h:918
@ DIFFICULTY_25_HC
Definition: DBCEnums.h:925
@ DIFFICULTY_NORMAL_RAID
Definition: DBCEnums.h:931
@ DIFFICULTY_NORMAL
Definition: DBCEnums.h:920
@ DIFFICULTY_LFR_15TH_ANNIVERSARY
Definition: DBCEnums.h:954
@ DIFFICULTY_HEROIC
Definition: DBCEnums.h:921
@ DIFFICULTY_MYTHIC_KEYSTONE
Definition: DBCEnums.h:927
@ DIFFICULTY_3_MAN_SCENARIO_HC
Definition: DBCEnums.h:929
@ DIFFICULTY_TIMEWALKING_RAID
Definition: DBCEnums.h:946
@ DIFFICULTY_TIMEWALKING
Definition: DBCEnums.h:939
@ DIFFICULTY_10_N
Definition: DBCEnums.h:922
@ DIFFICULTY_25_N
Definition: DBCEnums.h:923
@ DIFFICULTY_NORMAL_WARFRONT
Definition: DBCEnums.h:952
@ DIFFICULTY_NORMAL_ISLAND
Definition: DBCEnums.h:948
@ DIFFICULTY_LFR_NEW
Definition: DBCEnums.h:934
@ DIFFICULTY_10_HC
Definition: DBCEnums.h:924
@ DIFFICULTY_LFR
Definition: DBCEnums.h:926
@ DIFFICULTY_FLAG_DISPLAY_MYTHIC
Definition: DBCEnums.h:968
@ DIFFICULTY_FLAG_DISPLAY_HEROIC
Definition: DBCEnums.h:967
@ IgnoreInstanceFarmLimit
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
Definition: DatabaseEnv.cpp:21
#define UI64FMTD
Definition: Define.h:126
#define TC_GAME_API
Definition: Define.h:123
int32_t int32
Definition: Define.h:138
uint64_t uint64
Definition: Define.h:141
uint32_t uint32
Definition: Define.h:142
std::unordered_set< uint32 > params[2]
Definition: DisableMgr.cpp:50
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition: Duration.h:24
#define ABORT_MSG
Definition: Errors.h:75
#define ABORT
Definition: Errors.h:74
#define ASSERT_NOTNULL(pointer)
Definition: Errors.h:84
#define ASSERT
Definition: Errors.h:68
NGrid< MAX_NUMBER_OF_CELLS, Player, WorldTypeMapContainer, GridTypeMapContainer > NGridType
Definition: GridDefines.h:98
#define MAX_NUMBER_OF_CELLS
Definition: GridDefines.h:36
#define TOTAL_NUMBER_OF_CELLS_PER_MAP
Definition: GridDefines.h:53
#define SIZE_OF_GRIDS
Definition: GridDefines.h:40
CoordPair< MAX_NUMBER_OF_GRIDS > GridCoord
Definition: GridDefines.h:173
#define MAX_NUMBER_OF_GRIDS
Definition: GridDefines.h:38
#define CENTER_GRID_CELL_ID
Definition: GridDefines.h:50
#define SIZE_OF_GRID_CELL
Definition: GridDefines.h:48
#define CENTER_GRID_ID
Definition: GridDefines.h:41
#define CENTER_GRID_OFFSET
Definition: GridDefines.h:43
#define sInstanceLockMgr
@ LOG_LEVEL_DEBUG
Definition: LogCommon.h:28
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition: Log.h:179
#define TC_LOG_ERROR(filterType__, message__,...)
Definition: Log.h:188
#define sLog
Definition: Log.h:154
#define TC_LOG_WARN(filterType__, message__,...)
Definition: Log.h:185
ZLiquidStatus
Definition: MapDefines.h:125
#define sMapMgr
Definition: MapManager.h:186
@ MAP_OBJECT_CELL_MOVE_INACTIVE
Definition: MapObject.h:32
@ MAP_OBJECT_CELL_MOVE_ACTIVE
Definition: MapObject.h:31
@ MAP_OBJECT_CELL_MOVE_NONE
Definition: MapObject.h:30
static void PushRespawnInfoFrom(std::vector< RespawnInfo const * > &data, RespawnInfoMap const &map)
Definition: Map.cpp:2106
GridState * si_GridStates[MAX_GRID_STATE]
Definition: Map.cpp:72
#define MIN_UNLOAD_DELAY
Definition: Map.h:153
InstanceResetMethod
Definition: Map.h:878
std::unordered_map< ObjectGuid::LowType, RespawnInfo * > RespawnInfoMap
Definition: Map.h:163
InstanceResetResult
Definition: Map.h:885
#define MAP_INVALID_ZONE
Definition: Map.h:154
TransferAbortReason
Definition: Map.h:87
@ TRANSFER_ABORT_TOO_MANY_INSTANCES
Definition: Map.h:92
@ TRANSFER_ABORT_DIFFICULTY
Definition: Map.h:95
@ TRANSFER_ABORT_MAP_NOT_ALLOWED
Definition: Map.h:103
@ TRANSFER_ABORT_MAX_PLAYERS
Definition: Map.h:90
@ TRANSFER_ABORT_ZONE_IN_COMBAT
Definition: Map.h:93
@ TRANSFER_ABORT_NONE
Definition: Map.h:88
@ TRANSFER_ABORT_LOCKED_TO_DIFFERENT_INSTANCE
Definition: Map.h:104
@ TRANSFER_ABORT_NEED_GROUP
Definition: Map.h:98
@ TRANSFER_ABORT_ERROR
Definition: Map.h:89
#define TC_METRIC_VALUE(category, value,...)
Definition: Metric.h:214
#define TC_METRIC_TAG(name, value)
Definition: Metric.h:180
@ GRID_STATE_REMOVAL
Definition: NGrid.h:59
@ GRID_STATE_INVALID
Definition: NGrid.h:56
@ GRID_STATE_IDLE
Definition: NGrid.h:58
@ GRID_STATE_ACTIVE
Definition: NGrid.h:57
@ MAX_GRID_STATE
Definition: NGrid.h:60
#define DEFAULT_VISIBILITY_NOTIFY_PERIOD
Definition: NGrid.h:29
#define MAX_VISIBILITY_DISTANCE
Definition: ObjectDefines.h:28
#define DEFAULT_VISIBILITY_DISTANCE
Definition: ObjectDefines.h:35
@ TYPEID_AREATRIGGER
Definition: ObjectGuid.h:48
@ TYPEID_DYNAMICOBJECT
Definition: ObjectGuid.h:46
@ TYPEID_GAMEOBJECT
Definition: ObjectGuid.h:45
@ TYPEID_UNIT
Definition: ObjectGuid.h:42
@ TYPEID_CORPSE
Definition: ObjectGuid.h:47
@ TYPEID_CONVERSATION
Definition: ObjectGuid.h:50
@ TYPEID_PLAYER
Definition: ObjectGuid.h:43
HighGuid
Definition: ObjectGuid.h:77
#define sObjectMgr
Definition: ObjectMgr.h:1995
std::unordered_map< Player *, UpdateData > UpdateDataMapType
Definition: Object.h:81
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
@ PET_SAVE_NOT_IN_SLOT
Definition: PetDefines.h:48
@ RAID_INSTANCE_EXPIRED
Definition: Player.h:822
#define sPoolMgr
Definition: PoolMgr.h:179
uint32 urand(uint32 min, uint32 max)
Definition: Random.cpp:42
#define sScriptMgr
Definition: ScriptMgr.h:1417
TeamId
@ TEAM_NEUTRAL
@ TEAM_ALLIANCE
@ TEAM_HORDE
LineOfSightChecks
@ LINEOFSIGHT_CHECK_VMAP
@ LINEOFSIGHT_CHECK_GOBJECT
@ WS_TEAM_IN_INSTANCE_ALLIANCE
@ WS_TEAM_IN_INSTANCE_HORDE
@ SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE
Definition: SpawnData.h:58
@ SPAWNGROUP_FLAG_MANUAL_SPAWN
Definition: SpawnData.h:57
@ SPAWNGROUP_FLAG_DESPAWN_ON_CONDITION_FAILURE
Definition: SpawnData.h:60
@ SPAWNGROUP_FLAG_SYSTEM
Definition: SpawnData.h:55
@ SPAWNGROUP_FLAG_ESCORTQUESTNPC
Definition: SpawnData.h:59
SpawnObjectTypeMask
Definition: SpawnData.h:43
@ SPAWN_TYPEMASK_CREATURE
Definition: SpawnData.h:44
@ SPAWN_TYPEMASK_GAMEOBJECT
Definition: SpawnData.h:45
SpawnObjectType
Definition: SpawnData.h:34
@ SPAWN_TYPE_GAMEOBJECT
Definition: SpawnData.h:36
@ SPAWN_TYPE_AREATRIGGER
Definition: SpawnData.h:37
@ SPAWN_TYPE_CREATURE
Definition: SpawnData.h:35
@ SPELL_AURA_FORCE_WEATHER
#define sTerrainMgr
Definition: TerrainMgr.h:165
#define sTransportMgr
Definition: TransportMgr.h:183
#define sWorldStateMgr
Definition: WorldStateMgr.h:50
bool LoadFromDB(ObjectGuid::LowType spawnId, Map *map, bool addToMap, bool allowDuplicate)
void UpdateShape()
TransferAbortParams CannotEnter(Player *player) override
Definition: Map.cpp:3423
BattlegroundMap(uint32 id, time_t, uint32 InstanceId, Difficulty spawnMode)
Definition: Map.cpp:3369
~BattlegroundMap()
Definition: Map.cpp:3376
void SetUnload()
Definition: Map.cpp:3452
void RemoveAllPlayers() override
Definition: Map.cpp:3457
std::unique_ptr< BattlegroundScript > _battlegroundScript
Definition: Map.h:962
Battleground * GetBG() const
Definition: Map.h:951
void InitScriptData()
Definition: Map.cpp:3398
std::string const & GetScriptName() const
Definition: Map.cpp:3393
bool AddPlayerToMap(Player *player, bool initPlayer=true) override
Definition: Map.cpp:3440
Battleground * m_bg
Definition: Map.h:961
void RemovePlayerFromMap(Player *, bool) override
Definition: Map.cpp:3446
virtual void InitVisibilityDistance() override
Definition: Map.cpp:3386
uint32 _scriptId
Definition: Map.h:963
void Update(uint32 diff) override
Definition: Map.cpp:3466
void SetBgMap(BattlegroundMap *map)
void clear()
Definition: ByteBuffer.h:127
std::unordered_map< ObjectGuid, CombatReference * > const & GetPvECombatRefs() const
Definition: Corpse.h:53
void ReplaceAllFlags(uint32 flags)
Definition: Corpse.h:106
void SetRace(uint8 race)
Definition: Corpse.h:103
void SetClass(uint8 playerClass)
Definition: Corpse.h:104
void SetSex(uint8 sex)
Definition: Corpse.h:105
void SetGuildGUID(ObjectGuid guildGuid)
Definition: Corpse.h:101
void SetDisplayId(uint32 displayId)
Definition: Corpse.h:102
void SetCustomizations(Trinity::IteratorPair< Iter > customizations)
Definition: Corpse.h:113
CorpseDynFlags GetCorpseDynamicFlags() const
Definition: Corpse.h:92
void SetFactionTemplate(int32 factionTemplate)
Definition: Corpse.h:107
void RemoveFromWorld() override
Definition: Corpse.cpp:58
void SetOwnerGUID(ObjectGuid owner)
Definition: Corpse.h:99
void SetPartyGUID(ObjectGuid partyGuid)
Definition: Corpse.h:100
UF::UpdateField< UF::CorpseData, int32(WowCS::EntityFragment::CGObject), TYPEID_CORPSE > m_corpseData
Definition: Corpse.h:138
CellCoord const & GetCellCoord() const
Definition: Corpse.h:128
void ReplaceAllCorpseDynamicFlags(CorpseDynFlags dynamicFlags)
Definition: Corpse.h:95
ObjectGuid GetOwnerGUID() const override
Definition: Corpse.h:98
bool LoadCorpseFromDB(ObjectGuid::LowType guid, Field *fields)
Definition: Corpse.cpp:183
void SetCellCoord(CellCoord const &cellCoord)
Definition: Corpse.h:129
bool Create(ObjectGuid::LowType guidlow, Map *map)
Definition: Corpse.cpp:67
CorpseType GetType() const
Definition: Corpse.h:126
void DeleteFromDB(CharacterDatabaseTransaction trans)
Definition: Corpse.cpp:158
void GetRespawnPosition(float &x, float &y, float &z, float *ori=nullptr, float *dist=nullptr) const
Definition: Creature.cpp:2892
bool LoadFromDB(ObjectGuid::LowType spawnId, Map *map, bool addToMap, bool allowDuplicate)
Definition: Creature.cpp:1847
bool IsEscorted() const
Definition: Creature.cpp:3791
bool m_isTempWorldObject
Definition: Creature.h:429
bool isInLineOfSight(G3D::Vector3 const &startPos, G3D::Vector3 const &endPos, PhaseShift const &phaseShift) const
bool getObjectHitPos(G3D::Vector3 const &startPos, G3D::Vector3 const &endPos, G3D::Vector3 &resultHitPos, float modifyDist, PhaseShift const &phaseShift) const
void update(uint32 diff)
Class used to access individual fields of database query result.
Definition: Field.h:93
int64 GetInt64() const
Definition: Field.cpp:85
uint64 GetUInt64() const
Definition: Field.cpp:77
uint32 GetUInt32() const
Definition: Field.cpp:61
void GetRespawnPosition(float &x, float &y, float &z, float *ori=nullptr) const
bool LoadFromDB(ObjectGuid::LowType spawnId, Map *map, bool addToMap, bool=true)
void RemoveFromWorld() override
Definition: GameObject.cpp:957
ObjectGuid::LowType GetSpawnId() const
Definition: GameObject.h:211
void AfterRelocation()
Transport * ToTransport()
Definition: GameObject.h:405
void AddToWorld() override
Definition: GameObject.cpp:930
Definition: NGrid.h:32
PeriodicTimer & getRelocationTimer()
Definition: NGrid.h:45
void RemoveFromGrid()
Definition: GridObject.h:32
bool IsInGrid() const
Definition: GridObject.h:30
virtual void Update(Map &, NGridType &, GridInfo &, uint32 t_diff) const =0
Definition: Grid.h:46
void AddGridObject(SPECIFIC_OBJECT *obj)
Definition: Grid.h:111
void AddWorldObject(SPECIFIC_OBJECT *obj)
Definition: Grid.h:58
Definition: Group.h:205
bool isRaidGroup() const
Definition: Group.cpp:1635
virtual InstanceLockData const * GetInstanceInitializationData() const
bool IsExtended() const
void SetInUse(bool inUse)
InstanceResetTimePoint GetExpiryTime() const
bool IsNew() const
bool IsExpired() const
InstanceLockData * GetData()
Optional< SystemTimePoint > i_instanceExpireEvent
Definition: Map.h:927
InstanceLock * i_instanceLock
Definition: Map.h:931
TransferAbortParams CannotEnter(Player *player) override
Definition: Map.cpp:2851
void Update(uint32) override
Definition: Map.cpp:2932
InstanceResetResult Reset(InstanceResetMethod method)
Definition: Map.cpp:3017
TeamId GetTeamIdInInstance() const
Definition: Map.cpp:3358
InstanceScenario * i_scenario
Definition: Map.h:930
void TrySetOwningGroup(Group *group)
Definition: Map.cpp:3008
void CreateInstanceLockForPlayer(Player *player)
Definition: Map.cpp:3181
uint32 GetMaxPlayers() const
Definition: Map.cpp:3349
virtual void InitVisibilityDistance() override
Definition: Map.cpp:2841
uint32 GetScriptId() const
Definition: Map.h:902
void RemovePlayerFromMap(Player *, bool) override
Definition: Map.cpp:2952
InstanceMap(uint32 id, time_t, uint32 InstanceId, Difficulty SpawnMode, TeamId InstanceTeam, InstanceLock *instanceLock, Optional< uint32 > lfgDungeonsId)
Definition: Map.cpp:2810
GroupInstanceReference i_owningGroupRef
Definition: Map.h:932
bool AddPlayerToMap(Player *player, bool initPlayer=true) override
Definition: Map.cpp:2890
InstanceScript * i_data
Definition: Map.h:928
std::string const & GetScriptName() const
Definition: Map.cpp:3080
std::string GetDebugInfo() const override
Definition: Map.cpp:4058
void CreateInstanceData()
Definition: Map.cpp:2969
uint32 i_script_id
Definition: Map.h:929
InstanceScript * GetInstanceScript()
Definition: Map.h:904
void UpdateInstanceLock(UpdateBossStateSaveDataEvent const &updateSaveDataEvent)
Definition: Map.cpp:3085
~InstanceMap()
Definition: Map.cpp:2832
std::string GetSaveData()
Optional< uint32 > GetEntranceLocationForCompletedEncounters(uint32 completedEncountersMask) const
void Load(char const *data)
virtual void OnPlayerLeave(Player *)
void UpdateCombatResurrection(uint32 diff)
virtual void Create()
void SetEntranceLocation(uint32 worldSafeLocationId)
std::string UpdateAdditionalSaveData(std::string const &oldData, UpdateAdditionalSaveDataEvent const &event)
virtual void OnPlayerEnter(Player *)
virtual void Update(uint32)
std::string UpdateBossStateSaveData(std::string const &oldData, UpdateBossStateSaveDataEvent const &event)
bool isEmpty() const
Definition: LinkedList.h:110
uint32 getSize() const
Definition: LinkedList.h:128
MapObjectCellMoveState _moveState
Definition: MapObject.h:51
Position _newPosition
Definition: MapObject.h:52
void SetNewCellPosition(float x, float y, float z, float o)
Definition: MapObject.h:53
Cell const & GetCurrentCell() const
Definition: MapObject.h:48
Definition: Map.h:222
MapEntry const * i_mapEntry
Definition: Map.h:642
void DynamicObjectRelocation(DynamicObject *go, float x, float y, float z, float orientation)
Definition: Map.cpp:1113
bool _creatureToMoveLock
Definition: Map.h:606
std::map< WorldObject *, bool > i_objectsToSwitch
Definition: Map.h:690
WorldObject * GetWorldObjectBySpawnId(SpawnObjectType type, ObjectGuid::LowType spawnId) const
Definition: Map.h:440
std::unordered_map< uint32, uint32 > _zonePlayerCountMap
Definition: Map.h:822
std::vector< Creature * > _creaturesToMove
Definition: Map.h:607
void AddFarSpellCallback(FarSpellCallback &&callback)
Definition: Map.cpp:2528
void GameObjectRelocation(GameObject *go, float x, float y, float z, float orientation, bool respawnRelocationOnFail=true)
Definition: Map.cpp:1084
void AddGameObjectToMoveList(GameObject *go, float x, float y, float z, float ang)
Definition: Map.cpp:1192
void RemoveDynamicObjectFromMoveList(DynamicObject *go)
Definition: Map.cpp:1221
bool IsDungeon() const
Definition: Map.cpp:3220
void CreatureRelocation(Creature *creature, float x, float y, float z, float ang, bool respawnRelocationOnFail=true)
Definition: Map.cpp:1052
ZLiquidStatus GetLiquidStatus(PhaseShift const &phaseShift, float x, float y, float z, Optional< map_liquidHeaderTypeFlags > ReqLiquidType={}, LiquidData *data=nullptr, float collisionHeight=2.03128f)
Definition: Map.cpp:1698
void SetZoneMusic(uint32 zoneId, uint32 musicId)
Definition: Map.cpp:3931
bool IsBattlegroundOrArena() const
Definition: Map.cpp:3322
void RemoveInfiniteAOIVignette(Vignettes::VignetteData *vignette)
Definition: Map.cpp:487
bool UnloadGrid(NGridType &ngrid, bool pForce)
Definition: Map.cpp:1571
void SetSpawnGroupInactive(uint32 groupId)
Definition: Map.h:746
std::unordered_map< ObjectGuid, Corpse * > _corpsesByPlayer
Definition: Map.h:836
void UpdateSpawnGroupConditions()
Definition: Map.cpp:2485
void SetZoneWeather(uint32 zoneId, WeatherState weatherId, float intensity)
Definition: Map.cpp:3980
Pet * GetPet(ObjectGuid const &guid)
Definition: Map.cpp:3512
MapStoredObjectTypesContainer & GetObjectsStore()
Definition: Map.h:455
TransportsContainer::iterator _transportsUpdateIter
Definition: Map.h:662
bool IsNormal() const
Definition: Map.cpp:3248
void InitializeObject(T *obj)
Definition: Map.cpp:507
bool SpawnGroupSpawn(uint32 groupId, bool ignoreRespawn=false, bool force=false, std::vector< WorldObject * > *spawnedObjects=nullptr)
Definition: Map.cpp:2329
void SwitchGridContainers(T *obj, bool on)
Definition: Map.cpp:221
void AddObjectToRemoveList(WorldObject *obj)
Definition: Map.cpp:2561
void SendZoneWeather(uint32 zoneId, Player *player) const
Definition: Map.cpp:3904
virtual void RemovePlayerFromMap(Player *, bool)
Definition: Map.cpp:917
virtual bool AddPlayerToMap(Player *player, bool initPlayer=true)
Definition: Map.cpp:387
void MoveAllGameObjectsInMoveList()
Definition: Map.cpp:1306
Creature * GetCreatureBySpawnId(ObjectGuid::LowType spawnId) const
Definition: Map.cpp:3526
void RemoveAreaTriggerFromMoveList(AreaTrigger *at)
Definition: Map.cpp:1240
void RemoveFromActiveHelper(WorldObject *obj)
Definition: Map.h:770
void DeleteRespawnInfoFromDB(SpawnObjectType type, ObjectGuid::LowType spawnId, CharacterDatabaseTransaction dbTrans=nullptr)
Definition: Map.cpp:2166
size_t DespawnAll(SpawnObjectType type, ObjectGuid::LowType spawnId)
Definition: Map.cpp:2048
void AddAreaTriggerToMoveList(AreaTrigger *at, float x, float y, float z, float ang)
Definition: Map.cpp:1230
std::function< void(Map *)> FarSpellCallback
Definition: Map.h:751
void SetSpawnGroupActive(uint32 groupId, bool state)
Definition: Map.cpp:2441
void LoadGrid(float x, float y)
Definition: Map.cpp:377
bool IsUnderWater(PhaseShift const &phaseShift, float x, float y, float z)
Definition: Map.cpp:1744
void SendZoneDynamicInfo(uint32 zoneId, Player *player) const
Definition: Map.cpp:3883
void RemoveFromActive(WorldObject *obj)
Definition: Map.cpp:2753
MapDifficultyEntry const * GetMapDifficulty() const
Definition: Map.cpp:3205
PeriodicTimer _vignetteUpdateTimer
Definition: Map.h:874
bool AddToMap(T *)
Definition: Map.cpp:522