2010-10-07 15:35:36 +02:00
/*
2020-01-02 06:44:10 +01:00
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
2010-10-07 15:35:36 +02:00
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
2008-10-02 16:23:55 -05:00
*/
2009-10-17 15:51:44 -07:00
2010-09-22 18:13:57 +06:00
#include "Map.h"
2012-11-20 12:30:30 +01:00
#include "Battleground.h"
2008-10-02 16:23:55 -05:00
#include "CellImpl.h"
2020-12-01 21:08:06 +01:00
#include "CharacterPackets.h"
2023-01-06 16:44:11 +01:00
#include "Containers.h"
2017-05-14 11:40:55 +02:00
#include "Conversation.h"
2021-10-02 19:59:56 +02:00
#include "DB2Stores.h"
2024-03-14 20:32:03 +01:00
#include "DatabaseEnv.h"
2012-11-20 12:30:30 +01:00
#include "DynamicTree.h"
2017-05-21 23:18:43 +02:00
#include "GameObjectModel.h"
2017-05-07 21:48:41 +01:00
#include "GameTime.h"
2010-09-22 18:13:57 +06:00
#include "GridNotifiers.h"
2008-10-02 16:23:55 -05:00
#include "GridNotifiersImpl.h"
2012-11-20 12:30:30 +01:00
#include "GridStates.h"
#include "Group.h"
2018-07-22 13:22:35 +02:00
#include "InstanceLockMgr.h"
2015-07-27 19:44:08 +02:00
#include "InstancePackets.h"
2016-11-06 23:30:49 +01:00
#include "InstanceScenario.h"
2010-09-22 18:13:57 +06:00
#include "InstanceScript.h"
2017-05-18 23:52:58 +02:00
#include "Log.h"
#include "MapManager.h"
2020-06-19 22:04:01 +00:00
#include "Metric.h"
2015-01-28 13:58:40 +00:00
#include "MiscPackets.h"
2017-06-04 01:00:45 +02:00
#include "MotionMaster.h"
2012-11-20 12:30:30 +01:00
#include "ObjectAccessor.h"
2017-06-04 01:00:45 +02:00
#include "ObjectGridLoader.h"
2008-10-02 16:23:55 -05:00
#include "ObjectMgr.h"
2012-11-20 12:30:30 +01:00
#include "Pet.h"
2018-01-28 11:37:20 +01:00
#include "PhasingHandler.h"
2017-05-07 21:48:41 +01:00
#include "PoolMgr.h"
2012-11-20 12:30:30 +01:00
#include "ScriptMgr.h"
2022-02-13 00:49:10 +01:00
#include "SpellAuras.h"
2022-07-23 19:13:33 +02:00
#include "TerrainMgr.h"
2012-11-20 12:30:30 +01:00
#include "Transport.h"
#include "VMapFactory.h"
2020-08-29 13:02:42 +02:00
#include "VMapManager2.h"
2024-03-14 20:32:03 +01:00
#include "Vehicle.h"
2024-03-09 00:54:27 +01:00
#include "Vignette.h"
#include "VignettePackets.h"
2015-01-28 13:58:40 +00:00
#include "Weather.h"
2017-09-14 22:33:40 +02:00
#include "WeatherMgr.h"
2017-05-21 23:18:43 +02:00
#include "World.h"
2017-06-04 01:00:45 +02:00
#include "WorldSession.h"
2022-06-25 22:48:27 +02:00
#include "WorldStateMgr.h"
#include "WorldStatePackets.h"
2023-01-06 16:54:16 +01:00
#include <boost/heap/fibonacci_heap.hpp>
2018-12-14 22:01:16 +01:00
#include <sstream>
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
#define DEFAULT_GRID_EXPIRY 300
#define MAX_GRID_LOAD_TIME 50
2010-12-23 23:25:44 +01:00
#define MAX_CREATURE_ATTACK_RADIUS (45.0f * sWorld->getRate(RATE_CREATURE_AGGRO))
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
GridState * si_GridStates [ MAX_GRID_STATE ];
2009-10-17 15:51:44 -07:00
2017-09-14 22:33:40 +02:00
ZoneDynamicInfo :: ZoneDynamicInfo () : MusicId ( 0 ), DefaultWeather ( nullptr ), WeatherId ( WEATHER_STATE_FINE ),
2020-03-06 17:44:10 +01:00
Intensity ( 0.0f ) { }
2015-01-28 13:58:40 +00:00
2023-08-22 12:38:55 +02:00
RespawnInfo ::~ RespawnInfo () = default ;
2023-01-06 16:54:16 +01:00
struct RespawnInfoWithHandle ;
struct RespawnListContainer : boost :: heap :: fibonacci_heap < RespawnInfoWithHandle * , boost :: heap :: compare < CompareRespawnInfo >>
{
};
2023-01-09 11:27:16 +02:00
2023-01-06 16:54:16 +01:00
struct RespawnInfoWithHandle : RespawnInfo
{
explicit RespawnInfoWithHandle ( RespawnInfo const & other ) : RespawnInfo ( other ) { }
RespawnListContainer :: handle_type handle ;
};
2008-10-02 16:23:55 -05:00
Map ::~ Map ()
{
2017-05-07 21:48:41 +01:00
// Delete all waiting spawns, else there will be a memory leak
// This doesn't delete from database.
2019-07-15 17:14:58 +02:00
UnloadAllRespawnInfos ();
2017-05-07 21:48:41 +01:00
2010-04-07 19:13:19 +02:00
while ( ! i_worldObjects . empty ())
2009-08-12 15:33:12 -05:00
{
2011-09-15 14:08:17 +02:00
WorldObject * obj = * i_worldObjects . begin ();
2024-03-14 11:01:58 +01:00
ASSERT ( obj -> IsStoredInWorldObjectGridContainer ());
2010-07-30 20:04:28 +02:00
//ASSERT(obj->GetTypeId() == TYPEID_CORPSE);
2009-08-12 15:33:12 -05:00
obj -> RemoveFromWorld ();
2009-08-12 23:12:33 -05:00
obj -> ResetMap ();
2009-08-12 15:33:12 -05:00
}
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( ! m_scriptSchedule . empty ())
2016-04-02 22:37:51 +02:00
sMapMgr -> DecreaseScheduledScriptCount ( m_scriptSchedule . size ());
2012-04-09 15:11:39 +02:00
2023-08-08 00:08:10 +02:00
m_terrain -> UnloadMMapInstance ( GetId (), GetInstanceId ());
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2016-03-14 20:16:42 +00:00
void Map :: LoadAllCells ()
{
for ( uint32 cellX = 0 ; cellX < TOTAL_NUMBER_OF_CELLS_PER_MAP ; cellX ++ )
for ( uint32 cellY = 0 ; cellY < TOTAL_NUMBER_OF_CELLS_PER_MAP ; cellY ++ )
LoadGrid (( cellX + 0.5f - CENTER_GRID_CELL_ID ) * SIZE_OF_GRID_CELL , ( cellY + 0.5f - CENTER_GRID_CELL_ID ) * SIZE_OF_GRID_CELL );
}
2008-10-02 16:23:55 -05:00
void Map :: InitStateMachine ()
{
2018-02-27 23:57:54 +01:00
si_GridStates [ GRID_STATE_INVALID ] = new InvalidState ();
si_GridStates [ GRID_STATE_ACTIVE ] = new ActiveState ();
si_GridStates [ GRID_STATE_IDLE ] = new IdleState ();
si_GridStates [ GRID_STATE_REMOVAL ] = new RemovalState ();
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
void Map :: DeleteStateMachine ()
{
delete si_GridStates [ GRID_STATE_INVALID ];
delete si_GridStates [ GRID_STATE_ACTIVE ];
delete si_GridStates [ GRID_STATE_IDLE ];
delete si_GridStates [ GRID_STATE_REMOVAL ];
}
2009-10-17 15:51:44 -07:00
2022-07-24 18:56:40 +02:00
Map :: Map ( uint32 id , time_t expiry , uint32 InstanceId , Difficulty SpawnMode ) :
2017-01-29 00:00:43 +01:00
_creatureToMoveLock ( false ), _gameObjectsToMoveLock ( false ), _dynamicObjectsToMoveLock ( false ), _areaTriggersToMoveLock ( false ),
2013-10-16 18:37:29 +02:00
i_mapEntry ( sMapStore . LookupEntry ( id )), i_spawnMode ( SpawnMode ), i_InstanceId ( InstanceId ),
2010-08-21 20:08:47 +02:00
m_unloadTimer ( 0 ), m_VisibleDistance ( DEFAULT_VISIBILITY_DISTANCE ),
m_VisibilityNotifyPeriod ( DEFAULT_VISIBILITY_NOTIFY_PERIOD ),
2013-10-16 18:37:29 +02:00
m_activeNonPlayersIter ( m_activeNonPlayers . end ()), _transportsUpdateIter ( _transports . end ()),
2023-10-16 11:57:24 +02:00
i_gridExpiry ( expiry ), m_terrain ( sTerrainMgr . LoadTerrain ( id )), m_forceEnabledNavMeshFilterFlags ( 0 ), m_forceDisabledNavMeshFilterFlags ( 0 ),
2024-03-09 00:54:27 +01:00
i_scriptLock ( false ), _respawnTimes ( std :: make_unique < RespawnListContainer > ()), _respawnCheckTimer ( 0 ), _vignetteUpdateTimer ( 5200 , 5200 )
2008-10-02 16:23:55 -05:00
{
2018-02-27 23:57:54 +01:00
for ( uint32 x = 0 ; x < MAX_NUMBER_OF_GRIDS ; ++ x )
{
for ( uint32 y = 0 ; y < MAX_NUMBER_OF_GRIDS ; ++ y )
2008-10-02 16:23:55 -05:00
{
//z code
2018-02-27 23:57:54 +01:00
setNGrid ( nullptr , x , y );
2008-10-02 16:23:55 -05:00
}
}
2009-10-17 16:20:24 -07:00
2017-05-07 21:48:41 +01:00
_zonePlayerCountMap . clear ();
2009-09-27 02:24:25 -07:00
//lets initialize visibility distance for map
Map :: InitVisibilityDistance ();
2010-08-06 19:23:43 +02:00
2017-09-14 22:33:40 +02:00
_weatherUpdateTimer . SetInterval ( time_t ( 1 * IN_MILLISECONDS ));
2023-01-07 22:38:21 +01:00
GetGuidSequenceGenerator ( HighGuid :: Transport ). Set ( sObjectMgr -> GetGenerator < HighGuid :: Transport > (). GetNextAfterMaxUsed ());
2016-06-12 00:11:00 +02:00
2022-06-11 14:04:56 +02:00
_poolData = sPoolMgr -> InitPoolsForMap ( this );
2022-06-13 20:06:26 +02:00
sTransportMgr -> CreateTransportsForMap ( this );
2023-08-08 00:08:10 +02:00
m_terrain -> LoadMMapInstance ( GetId (), GetInstanceId ());
2018-03-03 14:29:53 +01:00
2022-06-25 22:48:27 +02:00
_worldStateValues = sWorldStateMgr -> GetInitialWorldStatesForMap ( this );
2009-09-27 02:24:25 -07:00
}
2009-10-17 16:20:24 -07:00
2009-09-27 02:24:25 -07:00
void Map :: InitVisibilityDistance ()
{
//init visibility for continents
2009-11-22 19:56:48 -08:00
m_VisibleDistance = World :: GetMaxVisibleDistanceOnContinents ();
2010-02-27 15:25:14 +02:00
m_VisibilityNotifyPeriod = World :: GetVisibilityNotifyPeriodOnContinents ();
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
// Template specialization of utility methods
template < class T >
2011-10-18 11:51:30 -04:00
void Map :: AddToGrid ( T * obj , Cell const & cell )
2008-10-02 16:23:55 -05:00
{
2011-10-18 11:51:30 -04:00
NGridType * grid = getNGrid ( cell . GridX (), cell . GridY ());
2024-03-14 11:01:58 +01:00
if ( obj -> IsStoredInWorldObjectGridContainer ())
2011-10-22 09:36:05 -04:00
grid -> GetGridType ( cell . CellX (), cell . CellY ()). template AddWorldObject < T > ( obj );
2009-08-11 16:14:45 -05:00
else
2011-10-22 09:36:05 -04:00
grid -> GetGridType ( cell . CellX (), cell . CellY ()). template AddGridObject < T > ( obj );
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
template <>
2011-10-18 11:51:30 -04:00
void Map :: AddToGrid ( Creature * obj , Cell const & cell )
2008-10-02 16:23:55 -05:00
{
2011-10-18 11:51:30 -04:00
NGridType * grid = getNGrid ( cell . GridX (), cell . GridY ());
2024-03-14 11:01:58 +01:00
if ( obj -> IsStoredInWorldObjectGridContainer ())
2011-10-22 09:36:05 -04:00
grid -> GetGridType ( cell . CellX (), cell . CellY ()). AddWorldObject ( obj );
2008-10-02 16:23:55 -05:00
else
2011-10-22 09:36:05 -04:00
grid -> GetGridType ( cell . CellX (), cell . CellY ()). AddGridObject ( obj );
2009-10-17 15:51:44 -07:00
2009-02-17 21:50:18 -06:00
obj -> SetCurrentCell ( cell );
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2013-10-16 18:37:29 +02:00
template <>
void Map :: AddToGrid ( GameObject * obj , Cell const & cell )
{
NGridType * grid = getNGrid ( cell . GridX (), cell . GridY ());
grid -> GetGridType ( cell . CellX (), cell . CellY ()). AddGridObject ( obj );
obj -> SetCurrentCell ( cell );
}
2014-03-16 20:00:09 +01:00
template <>
void Map :: AddToGrid ( DynamicObject * obj , Cell const & cell )
{
NGridType * grid = getNGrid ( cell . GridX (), cell . GridY ());
2024-03-14 11:01:58 +01:00
if ( obj -> IsStoredInWorldObjectGridContainer ())
2020-01-14 20:42:30 +02:00
grid -> GetGridType ( cell . CellX (), cell . CellY ()). AddWorldObject ( obj );
else
grid -> GetGridType ( cell . CellX (), cell . CellY ()). AddGridObject ( obj );
2014-03-16 20:00:09 +01:00
obj -> SetCurrentCell ( cell );
}
2017-01-29 00:00:43 +01:00
template <>
void Map :: AddToGrid ( AreaTrigger * obj , Cell const & cell )
{
NGridType * grid = getNGrid ( cell . GridX (), cell . GridY ());
grid -> GetGridType ( cell . CellX (), cell . CellY ()). AddGridObject ( obj );
obj -> SetCurrentCell ( cell );
}
2015-10-30 17:53:00 +01:00
template <>
void Map :: AddToGrid ( Corpse * obj , Cell const & cell )
{
NGridType * grid = getNGrid ( cell . GridX (), cell . GridY ());
2015-11-06 21:30:08 +01:00
// Corpses are a special object type - they can be added to grid via a call to AddToMap
// or loaded through ObjectGridLoader.
// Both corpses loaded from database and these freshly generated by Player::CreateCoprse are added to _corpsesByCell
// ObjectGridLoader loads all corpses from _corpsesByCell even if they were already added to grid before it was loaded
// so we need to explicitly check it here (Map::AddToGrid is only called from Player::BuildPlayerRepop, not from ObjectGridLoader)
// to avoid failing an assertion in GridObject::AddToGrid
if ( grid -> isGridObjectDataLoaded ())
2015-10-30 17:53:00 +01:00
{
2024-03-14 11:01:58 +01:00
if ( obj -> IsStoredInWorldObjectGridContainer ())
2015-10-30 17:53:00 +01:00
grid -> GetGridType ( cell . CellX (), cell . CellY ()). AddWorldObject ( obj );
2015-11-06 21:30:08 +01:00
else
grid -> GetGridType ( cell . CellX (), cell . CellY ()). AddGridObject ( obj );
2015-10-30 17:53:00 +01:00
}
}
2013-10-16 18:37:29 +02:00
template < class T >
2013-10-28 14:36:07 -04:00
void Map :: SwitchGridContainers ( T * /*obj*/ , bool /*on*/ ) { }
2013-10-16 18:37:29 +02:00
template <>
2011-12-20 17:05:08 -05:00
void Map :: SwitchGridContainers ( Creature * obj , bool on )
2008-11-05 20:51:05 -06:00
{
2024-03-14 11:01:58 +01:00
ASSERT ( ! obj -> IsAlwaysStoredInWorldObjectGridContainer ());
2011-10-18 10:53:34 -04:00
CellCoord p = Trinity :: ComputeCellCoord ( obj -> GetPositionX (), obj -> GetPositionY ());
2011-10-18 11:05:32 -04:00
if ( ! p . IsCoordValid ())
2009-04-08 12:20:39 -05:00
{
2023-01-08 21:16:53 +01:00
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 );
2009-04-08 12:20:39 -05:00
return ;
}
2009-10-17 15:51:44 -07:00
2009-04-08 12:20:39 -05:00
Cell cell ( p );
2011-10-18 11:51:30 -04:00
if ( ! IsGridLoaded ( GridCoord ( cell . data . Part . grid_x , cell . data . Part . grid_y )))
2009-04-08 12:20:39 -05:00
return ;
2009-10-17 15:51:44 -07:00
2015-07-21 23:17:00 +02:00
if ( sLog -> ShouldLog ( "maps" , LOG_LEVEL_DEBUG ))
{
// Extract bitfield values
uint32 const grid_x = cell . data . Part . grid_x ;
uint32 const grid_y = cell . data . Part . grid_y ;
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "maps" , "Switch object {} from grid[{}, {}] {}" , obj -> GetGUID (). ToString (), grid_x , grid_y , on );
2015-07-21 23:17:00 +02:00
}
2008-11-14 20:40:35 -06:00
NGridType * ngrid = getNGrid ( cell . GridX (), cell . GridY ());
2020-08-14 17:06:03 +02:00
ASSERT ( ngrid != nullptr );
2009-10-17 15:51:44 -07:00
2011-10-22 09:36:05 -04:00
GridType & grid = ngrid -> GetGridType ( cell . CellX (), cell . CellY ());
2009-10-17 15:51:44 -07:00
2011-11-10 17:31:45 -05:00
obj -> RemoveFromGrid (); //This step is not really necessary but we want to do ASSERT in remove/add
2013-10-16 18:37:29 +02:00
2010-04-07 19:14:10 +02:00
if ( on )
2011-12-20 17:05:08 -05:00
{
grid . AddWorldObject ( obj );
AddWorldObject ( obj );
}
2009-02-17 21:50:18 -06:00
else
2011-12-20 17:05:08 -05:00
{
grid . AddGridObject ( obj );
RemoveWorldObject ( obj );
}
2013-10-16 18:37:29 +02:00
2011-12-20 17:05:08 -05:00
obj -> m_isTempWorldObject = on ;
2008-11-05 20:51:05 -06:00
}
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
template < class T >
void Map :: DeleteFromWorld ( T * obj )
{
2008-11-07 09:36:46 -06:00
// Note: In case resurrectable corpse and pet its removed from global lists in own destructor
2008-10-02 16:23:55 -05:00
delete obj ;
}
2009-10-17 15:51:44 -07:00
2009-07-16 11:49:00 +08:00
template <>
2011-11-07 11:18:00 -06:00
void Map :: DeleteFromWorld ( Player * player )
2009-07-16 11:49:00 +08:00
{
2015-09-28 17:20:27 +02:00
ObjectAccessor :: RemoveObject ( player );
2015-02-25 00:13:14 +01:00
RemoveUpdateObject ( player ); /// @todo I do not know why we need this, it should be removed in ~Object anyway
2011-11-07 11:18:00 -06:00
delete player ;
2009-07-16 11:49:00 +08:00
}
2009-10-17 15:51:44 -07:00
2011-10-18 11:51:30 -04:00
//Create NGrid so the object can be added to it
//But object data is not loaded here
2022-07-24 18:56:40 +02:00
void Map :: EnsureGridCreated ( GridCoord const & p )
2008-10-02 16:23:55 -05:00
{
2010-04-07 19:14:10 +02:00
if ( ! getNGrid ( p . x_coord , p . y_coord ))
2008-10-02 16:23:55 -05:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "maps" , "Creating grid[{}, {}] for map {} instance {}" , p . x_coord , p . y_coord , GetId (), i_InstanceId );
2009-10-17 15:51:44 -07:00
2020-05-24 22:34:25 +02:00
NGridType * ngrid = new NGridType ( p . x_coord * MAX_NUMBER_OF_GRIDS + p . y_coord , p . x_coord , p . y_coord , i_gridExpiry , sWorld -> getBoolConfig ( CONFIG_GRID_UNLOAD ));
2018-05-06 00:39:13 +02:00
setNGrid ( ngrid , p . x_coord , p . y_coord );
2009-10-17 15:51:44 -07:00
2012-11-03 15:48:27 +01:00
// build a linkage between this map and NGridType
2018-05-06 00:39:13 +02:00
buildNGridLinkage ( ngrid );
2009-10-17 15:51:44 -07:00
2018-05-06 00:39:13 +02:00
ngrid -> SetGridState ( GRID_STATE_IDLE );
2009-10-17 15:51:44 -07:00
2012-11-03 15:48:27 +01:00
//z coord
int gx = ( MAX_NUMBER_OF_GRIDS - 1 ) - p . x_coord ;
int gy = ( MAX_NUMBER_OF_GRIDS - 1 ) - p . y_coord ;
2009-10-17 15:51:44 -07:00
2022-07-23 19:13:33 +02:00
m_terrain -> LoadMapAndVMap ( gx , gy );
2008-10-02 16:23:55 -05:00
}
}
2009-10-17 15:51:44 -07:00
2011-10-18 11:51:30 -04:00
//Load NGrid and make it active
2021-12-28 14:24:10 +02:00
void Map :: EnsureGridLoadedForActiveObject ( Cell const & cell , WorldObject const * object )
2008-10-02 16:23:55 -05:00
{
2010-01-16 11:43:47 +02:00
EnsureGridLoaded ( cell );
2010-04-07 18:09:10 +02:00
NGridType * grid = getNGrid ( cell . GridX (), cell . GridY ());
2020-08-14 17:06:03 +02:00
ASSERT ( grid != nullptr );
2009-10-17 15:51:44 -07:00
2021-12-28 14:24:10 +02:00
if ( object -> IsPlayer ())
GetMultiPersonalPhaseTracker (). LoadGrid ( object -> GetPhaseShift (), * grid , this , cell );
2010-01-16 11:43:47 +02:00
// refresh grid state & timer
2010-04-07 22:59:46 +02:00
if ( grid -> GetGridState () != GRID_STATE_ACTIVE )
2010-01-16 11:43:47 +02:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "maps" , "Active object {} triggers loading of grid [{}, {}] on map {}" , object -> GetGUID (). ToString (), cell . GridX (), cell . GridY (), GetId ());
2010-01-16 11:43:47 +02:00
ResetGridExpiry ( * grid , 0.1f );
grid -> SetGridState ( GRID_STATE_ACTIVE );
}
2009-04-02 16:33:35 -06:00
}
2009-10-17 15:51:44 -07:00
2011-10-18 11:51:30 -04:00
//Create NGrid and load the object data in it
2020-09-04 13:38:24 +02:00
bool Map :: EnsureGridLoaded ( Cell const & cell )
2009-04-02 16:33:35 -06:00
{
2011-10-18 10:53:34 -04:00
EnsureGridCreated ( GridCoord ( cell . GridX (), cell . GridY ()));
2009-04-02 16:33:35 -06:00
NGridType * grid = getNGrid ( cell . GridX (), cell . GridY ());
2009-10-17 15:51:44 -07:00
2020-08-14 17:06:03 +02:00
ASSERT ( grid != nullptr );
2010-04-07 22:59:46 +02:00
if ( ! isGridObjectDataLoaded ( cell . GridX (), cell . GridY ()))
2009-04-02 16:33:35 -06:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "maps" , "Loading grid[{}, {}] for map {} instance {}" , cell . GridX (), cell . GridY (), GetId (), i_InstanceId );
2009-10-17 15:51:44 -07:00
2011-04-29 20:47:02 +02:00
setGridObjectDataLoaded ( true , cell . GridX (), cell . GridY ());
2010-10-22 03:39:27 +02:00
2015-05-08 00:03:15 +02:00
LoadGridObjects ( grid , cell );
2009-10-17 15:51:44 -07:00
2012-02-03 16:03:52 -05:00
Balance ();
2009-04-02 16:33:35 -06:00
return true ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2009-04-02 16:33:35 -06:00
return false ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2015-05-08 00:03:15 +02:00
void Map :: LoadGridObjects ( NGridType * grid , Cell const & cell )
{
ObjectGridLoader loader ( * grid , this , cell );
loader . LoadN ();
}
2017-05-07 21:48:41 +01:00
void Map :: GridMarkNoUnload ( uint32 x , uint32 y )
{
// First make sure this grid is loaded
float gX = (( float ( x ) - 0.5f - CENTER_GRID_ID ) * SIZE_OF_GRIDS ) + ( CENTER_GRID_OFFSET * 2 );
float gY = (( float ( y ) - 0.5f - CENTER_GRID_ID ) * SIZE_OF_GRIDS ) + ( CENTER_GRID_OFFSET * 2 );
Cell cell = Cell ( gX , gY );
EnsureGridLoaded ( cell );
// Mark as don't unload
NGridType * grid = getNGrid ( x , y );
grid -> setUnloadExplicitLock ( true );
}
void Map :: GridUnmarkNoUnload ( uint32 x , uint32 y )
{
// If grid is loaded, clear unload lock
if ( IsGridLoaded ( GridCoord ( x , y )))
{
NGridType * grid = getNGrid ( x , y );
grid -> setUnloadExplicitLock ( false );
}
}
2008-12-20 14:10:53 -06:00
void Map :: LoadGrid ( float x , float y )
{
2011-10-18 11:18:49 -04:00
EnsureGridLoaded ( Cell ( x , y ));
2008-12-20 14:10:53 -06:00
}
2009-10-17 15:51:44 -07:00
2021-12-28 14:24:10 +02:00
void Map :: LoadGridForActiveObject ( float x , float y , WorldObject const * object )
{
EnsureGridLoadedForActiveObject ( Cell ( x , y ), object );
}
2015-05-01 15:44:43 +02:00
bool Map :: AddPlayerToMap ( Player * player , bool initPlayer /*= true*/ )
2008-10-02 16:23:55 -05:00
{
2011-11-11 17:50:26 -05:00
CellCoord cellCoord = Trinity :: ComputeCellCoord ( player -> GetPositionX (), player -> GetPositionY ());
if ( ! cellCoord . IsCoordValid ())
2010-01-16 11:43:47 +02:00
{
2023-01-08 21:16:53 +01:00
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 );
2010-01-16 11:43:47 +02:00
return false ;
}
2011-11-11 17:50:26 -05:00
Cell cell ( cellCoord );
2011-10-18 11:51:30 -04:00
EnsureGridLoadedForActiveObject ( cell , player );
AddToGrid ( player , cell );
2010-01-16 11:43:47 +02:00
2011-11-11 17:50:26 -05:00
// Check if we are adding to correct map
ASSERT ( player -> GetMap () == this );
player -> SetMap ( this );
2008-10-02 16:23:55 -05:00
player -> AddToWorld ();
2009-10-17 15:51:44 -07:00
2015-05-01 15:44:43 +02:00
if ( initPlayer )
SendInitSelf ( player );
2008-10-02 16:23:55 -05:00
SendInitTransports ( player );
2009-10-17 15:51:44 -07:00
2015-05-07 00:18:19 +02:00
if ( initPlayer )
player -> m_clientGUIDs . clear ();
2009-10-17 15:51:44 -07:00
2015-05-07 00:18:19 +02:00
player -> UpdateObjectVisibility ( false );
2018-01-28 11:37:20 +01:00
PhasingHandler :: SendToPlayer ( player );
2015-03-16 07:28:49 +01:00
2023-05-06 17:58:40 +02:00
if ( Instanceable ())
player -> RemoveAurasWithInterruptFlags ( SpellAuraInterruptFlags2 :: EnteringInstance );
2015-09-28 17:20:27 +02:00
if ( player -> IsAlive ())
ConvertCorpseToBones ( player -> GetGUID ());
2010-12-22 21:25:23 +01:00
sScriptMgr -> OnPlayerEnterMap ( this , player );
2008-10-02 16:23:55 -05:00
return true ;
}
2009-10-17 15:51:44 -07:00
2021-12-28 14:24:10 +02:00
void Map :: UpdatePersonalPhasesForPlayer ( Player const * player )
{
Cell cell ( player -> GetPositionX (), player -> GetPositionY ());
GetMultiPersonalPhaseTracker (). OnOwnerPhaseChanged ( player , getNGrid ( cell . GridX (), cell . GridY ()), this , cell );
}
2022-06-25 22:48:27 +02:00
int32 Map :: GetWorldStateValue ( int32 worldStateId ) const
{
if ( int32 const * value = Trinity :: Containers :: MapGetValuePtr ( _worldStateValues , worldStateId ))
return * value ;
return 0 ;
}
2022-07-07 23:07:20 +02:00
void Map :: SetWorldStateValue ( int32 worldStateId , int32 value , bool hidden )
2022-06-25 22:48:27 +02:00
{
2022-07-09 00:50:29 +02:00
auto [ itr , inserted ] = _worldStateValues . try_emplace ( worldStateId , 0 );
2022-06-25 22:48:27 +02:00
int32 oldValue = itr -> second ;
2022-07-09 00:50:29 +02:00
if ( oldValue == value && ! inserted )
return ;
2022-06-25 22:48:27 +02:00
itr -> second = value ;
2022-06-26 21:27:17 +02:00
WorldStateTemplate const * worldStateTemplate = sWorldStateMgr -> GetWorldStateTemplate ( worldStateId );
if ( worldStateTemplate )
2022-06-25 22:48:27 +02:00
sScriptMgr -> OnWorldStateValueChange ( worldStateTemplate , oldValue , value , this );
// Broadcast update to all players on the map
WorldPackets :: WorldState :: UpdateWorldState updateWorldState ;
updateWorldState . VariableID = worldStateId ;
updateWorldState . Value = value ;
2022-07-07 23:07:20 +02:00
updateWorldState . Hidden = hidden ;
2022-06-26 21:27:17 +02:00
updateWorldState . Write ();
for ( MapReference const & mapReference : m_mapRefManager )
{
if ( worldStateTemplate && ! worldStateTemplate -> AreaIds . empty ())
{
bool isInAllowedArea = std :: any_of ( worldStateTemplate -> AreaIds . begin (), worldStateTemplate -> AreaIds . end (),
[ playerAreaId = mapReference . GetSource () -> GetAreaId ()]( uint32 requiredAreaId ) { return DB2Manager :: IsInArea ( playerAreaId , requiredAreaId ); });
if ( ! isInAllowedArea )
continue ;
}
mapReference . GetSource () -> SendDirectMessage ( updateWorldState . GetRawPacket ());
}
2022-06-25 22:48:27 +02:00
}
2024-03-09 00:54:27 +01:00
void Map :: AddInfiniteAOIVignette ( Vignettes :: VignetteData * vignette )
{
_infiniteAOIVignettes . push_back ( vignette );
WorldPackets :: Vignette :: VignetteUpdate vignetteUpdate ;
vignette -> FillPacket ( vignetteUpdate . Added );
vignetteUpdate . Write ();
for ( MapReference const & ref : m_mapRefManager )
if ( Vignettes :: CanSee ( ref . GetSource (), * vignette ))
ref . GetSource () -> SendDirectMessage ( vignetteUpdate . GetRawPacket ());
}
void Map :: RemoveInfiniteAOIVignette ( Vignettes :: VignetteData * vignette )
{
if ( ! std :: erase ( _infiniteAOIVignettes , vignette ))
return ;
WorldPackets :: Vignette :: VignetteUpdate vignetteUpdate ;
vignetteUpdate . Removed . push_back ( vignette -> Guid );
vignetteUpdate . Write ();
if ( vignette -> Data -> GetFlags (). HasFlag ( VignetteFlags :: ZoneInfiniteAOI ))
{
for ( MapReference const & ref : m_mapRefManager )
if ( ref . GetSource () -> GetZoneId () == vignette -> ZoneID )
ref . GetSource () -> SendDirectMessage ( vignetteUpdate . GetRawPacket ());
}
else
SendToPlayers ( vignetteUpdate . GetRawPacket ());
}
2011-10-07 17:36:23 -04:00
template < class T >
2013-10-28 14:36:07 -04:00
void Map :: InitializeObject ( T * /*obj*/ ) { }
2011-10-07 17:36:23 -04:00
template <>
void Map :: InitializeObject ( Creature * obj )
{
2013-10-16 18:37:29 +02:00
obj -> _moveState = MAP_OBJECT_CELL_MOVE_NONE ;
}
template <>
void Map :: InitializeObject ( GameObject * obj )
{
obj -> _moveState = MAP_OBJECT_CELL_MOVE_NONE ;
2011-10-07 17:36:23 -04:00
}
2008-10-02 16:23:55 -05:00
template < class T >
2013-10-16 18:37:29 +02:00
bool Map :: AddToMap ( T * obj )
2008-10-02 16:23:55 -05:00
{
2013-03-08 21:41:30 +01:00
/// @todo Needs clean up. An object should not be added to map twice.
2011-11-11 17:50:26 -05:00
if ( obj -> IsInWorld ())
2008-10-02 16:23:55 -05:00
{
2011-11-11 17:50:26 -05:00
ASSERT ( obj -> IsInGrid ());
obj -> UpdateObjectVisibility ( true );
2011-11-28 13:23:20 -05:00
return true ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2011-11-11 17:50:26 -05:00
CellCoord cellCoord = Trinity :: ComputeCellCoord ( obj -> GetPositionX (), obj -> GetPositionY ());
2011-11-28 11:32:52 -05:00
//It will create many problems (including crashes) if an object is not added to grid after creation
//The correct way to fix it is to make AddToMap return false and delete the object if it is not added to grid
//But now AddToMap is used in too many places, I will just see how many ASSERT failures it will cause
ASSERT ( cellCoord . IsCoordValid ());
2011-11-11 17:50:26 -05:00
if ( ! cellCoord . IsCoordValid ())
2009-08-11 10:01:39 -05:00
{
2023-01-08 21:16:53 +01:00
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 );
2011-11-28 13:23:20 -05:00
return false ; //Should delete object
2009-08-11 10:01:39 -05:00
}
2009-10-17 15:51:44 -07:00
2023-11-14 18:40:39 +01:00
if ( IsAlwaysActive ())
obj -> setActive ( true );
2011-11-11 17:50:26 -05:00
Cell cell ( cellCoord );
2010-04-07 19:14:10 +02:00
if ( obj -> isActiveObject ())
2011-10-18 11:51:30 -04:00
EnsureGridLoadedForActiveObject ( cell , obj );
2009-02-25 20:52:20 -06:00
else
2011-10-18 10:53:34 -04:00
EnsureGridCreated ( GridCoord ( cell . GridX (), cell . GridY ()));
2011-10-18 11:51:30 -04:00
AddToGrid ( obj , cell );
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "maps" , "Object {} enters grid[{}, {}]" , obj -> GetGUID (). ToString (), cell . GridX (), cell . GridY ());
2011-11-11 17:50:26 -05:00
//Must already be set before AddToMap. Usually during obj->Create.
2009-08-11 18:23:54 -05:00
//obj->SetMap(this);
2008-10-02 16:23:55 -05:00
obj -> AddToWorld ();
2011-11-11 17:50:26 -05:00
2011-10-07 17:36:23 -04:00
InitializeObject ( obj );
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( obj -> isActiveObject ())
2009-02-25 20:52:20 -06:00
AddToActive ( obj );
2009-10-17 15:51:44 -07:00
2009-04-01 22:40:12 -06:00
//something, such as vehicle, needs to be update immediately
2009-05-03 11:06:48 -05:00
//also, trigger needs to cast spell, if not update, cannot see visual
2017-12-15 22:11:02 -02:00
obj -> SetIsNewObject ( true );
2016-03-02 23:54:37 +01:00
obj -> UpdateObjectVisibilityOnCreate ();
2017-12-15 22:11:02 -02:00
obj -> SetIsNewObject ( false );
2011-11-28 13:23:20 -05:00
return true ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2013-10-16 18:37:29 +02:00
template <>
bool Map :: AddToMap ( Transport * obj )
{
//TODO: Needs clean up. An object should not be added to map twice.
if ( obj -> IsInWorld ())
return true ;
CellCoord cellCoord = Trinity :: ComputeCellCoord ( obj -> GetPositionX (), obj -> GetPositionY ());
if ( ! cellCoord . IsCoordValid ())
{
2023-01-08 21:16:53 +01:00
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 );
2013-10-16 18:37:29 +02:00
return false ; //Should delete object
}
_transports . insert ( obj );
2022-06-13 20:06:26 +02:00
if ( obj -> GetExpectedMapId () == GetId ())
2013-12-28 12:44:22 +01:00
{
2022-06-13 20:06:26 +02:00
obj -> AddToWorld ();
// Broadcast creation to players
2013-12-28 12:44:22 +01:00
for ( Map :: PlayerList :: const_iterator itr = GetPlayers (). begin (); itr != GetPlayers (). end (); ++ itr )
{
2022-12-25 00:44:12 +01:00
if ( itr -> GetSource () -> GetTransport () != obj && itr -> GetSource () -> InSamePhase ( obj ))
2013-12-28 12:44:22 +01:00
{
2014-01-14 12:38:19 +01:00
UpdateData data ( GetId ());
2013-12-28 12:44:22 +01:00
obj -> BuildCreateUpdateBlockForPlayer ( & data , itr -> GetSource ());
2018-01-28 11:37:20 +01:00
itr -> GetSource () -> m_visibleTransports . insert ( obj -> GetGUID ());
2013-12-28 12:44:22 +01:00
WorldPacket packet ;
data . BuildPacket ( & packet );
itr -> GetSource () -> SendDirectMessage ( & packet );
}
}
}
2013-10-16 18:37:29 +02:00
return true ;
}
2020-09-04 13:38:24 +02:00
bool Map :: IsGridLoaded ( GridCoord const & p ) const
2008-10-02 16:23:55 -05:00
{
2010-04-07 22:59:46 +02:00
return ( getNGrid ( p . x_coord , p . y_coord ) && isGridObjectDataLoaded ( p . x_coord , p . y_coord ));
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2010-11-13 17:18:09 +01:00
void Map :: VisitNearbyCellsOf ( WorldObject * obj , TypeContainerVisitor < Trinity :: ObjectUpdater , GridTypeMapContainer > & gridVisitor , TypeContainerVisitor < Trinity :: ObjectUpdater , WorldTypeMapContainer > & worldVisitor )
2008-12-27 11:26:22 -06:00
{
2011-10-19 17:40:44 +01:00
// Check for valid position
if ( ! obj -> IsPositionValid ())
2010-11-13 17:18:09 +01:00
return ;
2011-10-19 17:40:44 +01:00
// Update mobs/objects in ALL visible cells around object!
CellArea area = Cell :: CalculateCellArea ( obj -> GetPositionX (), obj -> GetPositionY (), obj -> GetGridActivationRange ());
2010-11-13 17:18:09 +01:00
2011-10-19 17:40:44 +01:00
for ( uint32 x = area . low_bound . x_coord ; x <= area . high_bound . x_coord ; ++ x )
2009-12-17 07:34:00 +01:00
{
2011-10-19 17:40:44 +01:00
for ( uint32 y = area . low_bound . y_coord ; y <= area . high_bound . y_coord ; ++ y )
2010-11-13 17:18:09 +01:00
{
// marked cells are those that have been visited
// don't visit the same cell twice
uint32 cell_id = ( y * TOTAL_NUMBER_OF_CELLS_PER_MAP ) + x ;
if ( isCellMarked ( cell_id ))
continue ;
markCell ( cell_id );
2011-10-18 10:53:34 -04:00
CellCoord pair ( x , y );
2010-11-13 17:18:09 +01:00
Cell cell ( pair );
2012-05-30 08:01:02 +02:00
cell . SetNoCreate ();
2011-10-19 17:40:44 +01:00
Visit ( cell , gridVisitor );
Visit ( cell , worldVisitor );
2010-11-13 17:18:09 +01:00
}
2009-12-17 07:34:00 +01:00
}
2010-11-13 17:18:09 +01:00
}
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
void Map :: UpdatePlayerZoneStats ( uint32 oldZone , uint32 newZone )
{
// Nothing to do if no change
if ( oldZone == newZone )
return ;
if ( oldZone != MAP_INVALID_ZONE )
{
uint32 & oldZoneCount = _zonePlayerCountMap [ oldZone ];
ASSERT ( oldZoneCount , "A player left zone %u (went to %u) - but there were no players in the zone!" , oldZone , newZone );
-- oldZoneCount ;
}
++ _zonePlayerCountMap [ newZone ];
}
void Map :: Update ( uint32 t_diff )
2010-11-13 17:18:09 +01:00
{
2012-02-11 19:23:18 -05:00
_dynamicTree . update ( t_diff );
2010-12-13 22:37:56 +01:00
/// update worldsessions for existing players
2011-06-12 02:30:32 +02:00
for ( m_mapRefIter = m_mapRefManager . begin (); m_mapRefIter != m_mapRefManager . end (); ++ m_mapRefIter )
2010-12-13 22:37:56 +01:00
{
2013-06-11 21:25:12 -02:30
Player * player = m_mapRefIter -> GetSource ();
2011-11-07 11:06:39 -06:00
if ( player && player -> IsInWorld ())
2010-12-13 22:37:56 +01:00
{
2011-11-07 11:06:39 -06:00
//player->Update(t_diff);
2012-05-30 08:01:02 +02:00
WorldSession * session = player -> GetSession ();
MapSessionFilter updater ( session );
session -> Update ( t_diff , updater );
2010-12-13 22:37:56 +01:00
}
}
2017-05-07 21:48:41 +01:00
/// process any due respawns
if ( _respawnCheckTimer <= t_diff )
{
ProcessRespawns ();
2022-06-24 22:20:34 +02:00
UpdateSpawnGroupConditions ();
2017-05-07 21:48:41 +01:00
_respawnCheckTimer = sWorld -> getIntConfig ( CONFIG_RESPAWN_MINCHECKINTERVALMS );
}
else
_respawnCheckTimer -= t_diff ;
2009-06-17 18:37:53 -05:00
/// update active cells around players and active objects
2008-11-21 15:41:18 -06:00
resetMarkedCells ();
2009-10-17 15:51:44 -07:00
2008-11-23 11:01:26 -06:00
Trinity :: ObjectUpdater updater ( t_diff );
// for creature
TypeContainerVisitor < Trinity :: ObjectUpdater , GridTypeMapContainer > grid_object_update ( updater );
// for pets
TypeContainerVisitor < Trinity :: ObjectUpdater , WorldTypeMapContainer > world_object_update ( updater );
2009-10-17 15:51:44 -07:00
2009-02-25 20:52:20 -06:00
// the player iterator is stored in the map object
// to make sure calls to Map::Remove don't invalidate it
2009-10-17 16:20:24 -07:00
for ( m_mapRefIter = m_mapRefManager . begin (); m_mapRefIter != m_mapRefManager . end (); ++ m_mapRefIter )
2008-11-21 15:41:18 -06:00
{
2013-06-11 21:25:12 -02:30
Player * player = m_mapRefIter -> GetSource ();
2009-10-17 15:51:44 -07:00
2011-11-07 11:06:39 -06:00
if ( ! player || ! player -> IsInWorld ())
2009-02-25 20:52:20 -06:00
continue ;
2009-10-17 15:51:44 -07:00
2010-11-13 17:18:09 +01:00
// update players at tick
2011-11-07 11:06:39 -06:00
player -> Update ( t_diff );
2009-10-17 15:51:44 -07:00
2011-11-07 11:06:39 -06:00
VisitNearbyCellsOf ( player , grid_object_update , world_object_update );
2015-09-23 22:32:03 +02:00
2017-02-08 20:23:37 +01:00
// If player is using far sight or mind vision, visit that object too
2016-04-27 16:53:18 +02:00
if ( WorldObject * viewPoint = player -> GetViewpoint ())
2017-02-08 20:23:37 +01:00
VisitNearbyCellsOf ( viewPoint , grid_object_update , world_object_update );
2016-04-27 16:53:18 +02:00
2015-09-23 22:32:03 +02:00
// Handle updates for creatures in combat with player and are more than 60 yards away
if ( player -> IsInCombat ())
{
2018-01-04 15:19:37 +01:00
std :: vector < Unit *> toVisit ;
2018-01-03 20:04:19 +01:00
for ( auto const & pair : player -> GetCombatManager (). GetPvECombatRefs ())
if ( Creature * unit = pair . second -> GetOther ( player ) -> ToCreature ())
if ( unit -> GetMapId () == player -> GetMapId () && ! unit -> IsWithinDistInMap ( player , GetVisibilityRange (), false ))
2018-01-04 15:19:37 +01:00
toVisit . push_back ( unit );
for ( Unit * unit : toVisit )
VisitNearbyCellsOf ( unit , grid_object_update , world_object_update );
2015-09-23 22:32:03 +02:00
}
2018-07-24 10:42:41 +02:00
{ // Update any creatures that own auras the player has applications of
std :: unordered_set < Unit *> toVisit ;
for ( std :: pair < uint32 , AuraApplication *> pair : player -> GetAppliedAuras ())
{
if ( Unit * caster = pair . second -> GetBase () -> GetCaster ())
2018-07-24 10:46:14 +02:00
if ( caster -> GetTypeId () != TYPEID_PLAYER && ! caster -> IsWithinDistInMap ( player , GetVisibilityRange (), false ))
2018-07-24 10:42:41 +02:00
toVisit . insert ( caster );
}
for ( Unit * unit : toVisit )
VisitNearbyCellsOf ( unit , grid_object_update , world_object_update );
}
2020-09-06 21:38:12 +02:00
{ // Update player's summons
std :: vector < Unit *> toVisit ;
// Totems
for ( ObjectGuid const & summonGuid : player -> m_SummonSlot )
if ( ! summonGuid . IsEmpty ())
if ( Creature * unit = GetCreature ( summonGuid ))
if ( unit -> GetMapId () == player -> GetMapId () && ! unit -> IsWithinDistInMap ( player , GetVisibilityRange (), false ))
toVisit . push_back ( unit );
for ( Unit * unit : toVisit )
VisitNearbyCellsOf ( unit , grid_object_update , world_object_update );
}
2009-02-25 20:52:20 -06:00
}
2009-10-17 15:51:44 -07:00
2010-11-13 17:18:09 +01:00
// non-player active objects, increasing iterator in the loop in case of object removal
for ( m_activeNonPlayersIter = m_activeNonPlayers . begin (); m_activeNonPlayersIter != m_activeNonPlayers . end ();)
2009-02-25 20:52:20 -06:00
{
2010-11-13 17:18:09 +01:00
WorldObject * obj = * m_activeNonPlayersIter ;
++ m_activeNonPlayersIter ;
2009-10-17 15:51:44 -07:00
2011-08-04 19:41:11 +02:00
if ( ! obj || ! obj -> IsInWorld ())
2010-11-13 17:18:09 +01:00
continue ;
2009-10-17 15:51:44 -07:00
2010-11-13 17:18:09 +01:00
VisitNearbyCellsOf ( obj , grid_object_update , world_object_update );
2008-11-21 15:41:18 -06:00
}
2009-10-17 15:51:44 -07:00
2013-10-16 18:37:29 +02:00
for ( _transportsUpdateIter = _transports . begin (); _transportsUpdateIter != _transports . end ();)
{
WorldObject * obj = * _transportsUpdateIter ;
++ _transportsUpdateIter ;
obj -> Update ( t_diff );
}
2024-03-09 00:54:27 +01:00
if ( _vignetteUpdateTimer . Update ( t_diff ))
{
for ( Vignettes :: VignetteData * vignette : _infiniteAOIVignettes )
{
if ( vignette -> NeedUpdate )
{
WorldPackets :: Vignette :: VignetteUpdate vignetteUpdate ;
vignette -> FillPacket ( vignetteUpdate . Updated );
vignetteUpdate . Write ();
for ( MapReference const & ref : m_mapRefManager )
if ( Vignettes :: CanSee ( ref . GetSource (), * vignette ))
ref . GetSource () -> SendDirectMessage ( vignetteUpdate . GetRawPacket ());
vignette -> NeedUpdate = false ;
}
}
}
2015-02-25 00:13:14 +01:00
SendObjectUpdates ();
2009-07-31 10:48:34 +08:00
///- Process necessary scripts
if ( ! m_scriptSchedule . empty ())
2009-08-19 17:42:56 -05:00
{
i_scriptLock = true ;
2009-07-31 10:48:34 +08:00
ScriptsProcess ();
2009-08-19 17:42:56 -05:00
i_scriptLock = false ;
}
2009-10-17 15:51:44 -07:00
2019-07-10 23:16:58 +02:00
_weatherUpdateTimer . Update ( t_diff );
2017-09-14 22:33:40 +02:00
if ( _weatherUpdateTimer . Passed ())
{
for ( auto && zoneInfo : _zoneDynamicInfo )
if ( zoneInfo . second . DefaultWeather && ! zoneInfo . second . DefaultWeather -> Update ( _weatherUpdateTimer . GetInterval ()))
zoneInfo . second . DefaultWeather . reset ();
_weatherUpdateTimer . Reset ();
}
2021-12-28 14:24:10 +02:00
// update phase shift objects
GetMultiPersonalPhaseTracker (). Update ( this , t_diff );
2009-08-25 15:12:22 -05:00
MoveAllCreaturesInMoveList ();
2013-10-16 18:37:29 +02:00
MoveAllGameObjectsInMoveList ();
2017-01-29 00:00:43 +01:00
MoveAllAreaTriggersInMoveList ();
2009-12-19 10:12:40 +01:00
2010-04-07 19:14:10 +02:00
if ( ! m_mapRefManager . isEmpty () || ! m_activeNonPlayers . empty ())
2010-02-27 15:25:14 +02:00
ProcessRelocationNotifies ( t_diff );
2010-08-06 19:23:43 +02:00
2010-12-22 21:25:23 +01:00
sScriptMgr -> OnMapUpdate ( this , t_diff );
2020-06-19 22:04:01 +00:00
TC_METRIC_VALUE ( "map_creatures" , uint64 ( GetObjectsStore (). Size < Creature > ()),
TC_METRIC_TAG ( "map_id" , std :: to_string ( GetId ())),
TC_METRIC_TAG ( "map_instanceid" , std :: to_string ( GetInstanceId ())));
TC_METRIC_VALUE ( "map_gameobjects" , uint64 ( GetObjectsStore (). Size < GameObject > ()),
TC_METRIC_TAG ( "map_id" , std :: to_string ( GetId ())),
TC_METRIC_TAG ( "map_instanceid" , std :: to_string ( GetInstanceId ())));
2009-12-19 10:12:40 +01:00
}
2010-02-27 15:25:14 +02:00
struct ResetNotifier
2009-12-19 10:12:40 +01:00
{
2010-02-27 15:25:14 +02:00
template < class T > inline void resetNotify ( GridRefManager < T > & m )
2009-12-19 10:12:40 +01:00
{
2010-04-07 19:12:44 +02:00
for ( typename GridRefManager < T >:: iterator iter = m . begin (); iter != m . end (); ++ iter )
2013-06-11 21:25:12 -02:30
iter -> GetSource () -> ResetAllNotifies ();
2009-12-19 10:12:40 +01:00
}
2013-10-27 22:27:46 +01:00
template < class T > void Visit ( GridRefManager < T > & ) { }
2010-02-27 15:25:14 +02:00
void Visit ( CreatureMapType & m ) { resetNotify < Creature > ( m );}
void Visit ( PlayerMapType & m ) { resetNotify < Player > ( m );}
};
2009-12-19 10:12:40 +01:00
2011-08-01 14:23:27 +02:00
void Map :: ProcessRelocationNotifies ( const uint32 diff )
2009-12-19 10:12:40 +01:00
{
2010-04-07 19:12:44 +02:00
for ( GridRefManager < NGridType >:: iterator i = GridRefManager < NGridType >:: begin (); i != GridRefManager < NGridType >:: end (); ++ i )
2009-12-19 10:12:40 +01:00
{
2013-06-11 21:25:12 -02:30
NGridType * grid = i -> GetSource ();
2009-12-19 10:12:40 +01:00
2010-02-27 15:25:14 +02:00
if ( grid -> GetGridState () != GRID_STATE_ACTIVE )
continue ;
grid -> getGridInfoRef () -> getRelocationTimer (). TUpdate ( diff );
if ( ! grid -> getGridInfoRef () -> getRelocationTimer (). TPassed ())
2009-12-19 10:12:40 +01:00
continue ;
uint32 gx = grid -> getX (), gy = grid -> getY ();
2011-10-18 10:53:34 -04:00
CellCoord cell_min ( gx * MAX_NUMBER_OF_CELLS , gy * MAX_NUMBER_OF_CELLS );
CellCoord cell_max ( cell_min . x_coord + MAX_NUMBER_OF_CELLS , cell_min . y_coord + MAX_NUMBER_OF_CELLS );
2009-12-19 10:12:40 +01:00
2010-04-07 19:12:44 +02:00
for ( uint32 x = cell_min . x_coord ; x < cell_max . x_coord ; ++ x )
2009-12-19 10:12:40 +01:00
{
2010-04-07 19:12:44 +02:00
for ( uint32 y = cell_min . y_coord ; y < cell_max . y_coord ; ++ y )
2009-12-19 10:12:40 +01:00
{
uint32 cell_id = ( y * TOTAL_NUMBER_OF_CELLS_PER_MAP ) + x ;
2010-04-07 19:14:10 +02:00
if ( ! isCellMarked ( cell_id ))
2009-12-19 10:12:40 +01:00
continue ;
2011-10-18 10:53:34 -04:00
CellCoord pair ( x , y );
2009-12-19 10:12:40 +01:00
Cell cell ( pair );
cell . SetNoCreate ();
2010-11-13 17:18:09 +01:00
Trinity :: DelayedUnitRelocation cell_relocation ( cell , pair , * this , MAX_VISIBILITY_DISTANCE );
2009-12-19 10:12:40 +01:00
TypeContainerVisitor < Trinity :: DelayedUnitRelocation , GridTypeMapContainer > grid_object_relocation ( cell_relocation );
TypeContainerVisitor < Trinity :: DelayedUnitRelocation , WorldTypeMapContainer > world_object_relocation ( cell_relocation );
2010-02-27 15:25:14 +02:00
Visit ( cell , grid_object_relocation );
Visit ( cell , world_object_relocation );
2009-12-19 10:12:40 +01:00
}
}
}
2010-02-27 15:25:14 +02:00
ResetNotifier reset ;
TypeContainerVisitor < ResetNotifier , GridTypeMapContainer > grid_notifier ( reset );
TypeContainerVisitor < ResetNotifier , WorldTypeMapContainer > world_notifier ( reset );
2010-04-07 19:12:44 +02:00
for ( GridRefManager < NGridType >:: iterator i = GridRefManager < NGridType >:: begin (); i != GridRefManager < NGridType >:: end (); ++ i )
2009-12-19 10:12:40 +01:00
{
2013-06-11 21:25:12 -02:30
NGridType * grid = i -> GetSource ();
2009-12-19 10:12:40 +01:00
2010-02-27 15:25:14 +02:00
if ( grid -> GetGridState () != GRID_STATE_ACTIVE )
continue ;
if ( ! grid -> getGridInfoRef () -> getRelocationTimer (). TPassed ())
2009-12-19 10:12:40 +01:00
continue ;
2010-02-27 15:25:14 +02:00
grid -> getGridInfoRef () -> getRelocationTimer (). TReset ( diff , m_VisibilityNotifyPeriod );
2009-12-19 10:12:40 +01:00
uint32 gx = grid -> getX (), gy = grid -> getY ();
2011-10-18 10:53:34 -04:00
CellCoord cell_min ( gx * MAX_NUMBER_OF_CELLS , gy * MAX_NUMBER_OF_CELLS );
CellCoord cell_max ( cell_min . x_coord + MAX_NUMBER_OF_CELLS , cell_min . y_coord + MAX_NUMBER_OF_CELLS );
2009-12-19 10:12:40 +01:00
2010-04-07 19:12:44 +02:00
for ( uint32 x = cell_min . x_coord ; x < cell_max . x_coord ; ++ x )
2009-12-19 10:12:40 +01:00
{
2010-04-07 19:12:44 +02:00
for ( uint32 y = cell_min . y_coord ; y < cell_max . y_coord ; ++ y )
2009-12-19 10:12:40 +01:00
{
uint32 cell_id = ( y * TOTAL_NUMBER_OF_CELLS_PER_MAP ) + x ;
2010-04-07 19:14:10 +02:00
if ( ! isCellMarked ( cell_id ))
2009-12-19 10:12:40 +01:00
continue ;
2011-10-18 10:53:34 -04:00
CellCoord pair ( x , y );
2009-12-19 10:12:40 +01:00
Cell cell ( pair );
cell . SetNoCreate ();
2010-02-27 15:25:14 +02:00
Visit ( cell , grid_notifier );
Visit ( cell , world_notifier );
2009-12-19 10:12:40 +01:00
}
}
}
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2011-11-11 17:50:26 -05:00
void Map :: RemovePlayerFromMap ( Player * player , bool remove )
2008-10-02 16:23:55 -05:00
{
2017-05-07 21:48:41 +01:00
// Before leaving map, update zone/area for stats
player -> UpdateZone ( MAP_INVALID_ZONE , 0 );
2013-05-17 21:30:02 +02:00
sScriptMgr -> OnPlayerLeaveMap ( this , player );
2021-12-28 14:24:10 +02:00
GetMultiPersonalPhaseTracker (). MarkAllPhasesForDeletion ( player -> GetGUID ());
2018-01-03 20:04:19 +01:00
player -> CombatStop ();
2017-01-28 05:00:28 +01:00
bool const inWorld = player -> IsInWorld ();
2009-08-14 14:09:57 -05:00
player -> RemoveFromWorld ();
SendRemoveTransports ( player );
2009-10-17 15:51:44 -07:00
2017-01-28 05:00:28 +01:00
if ( ! inWorld ) // if was in world, RemoveFromWorld() called DestroyForNearbyPlayers()
2022-01-29 15:18:27 +01:00
player -> UpdateObjectVisibilityOnDestroy ();
2017-01-28 05:00:28 +01:00
2011-11-12 08:16:52 -05:00
if ( player -> IsInGrid ())
player -> RemoveFromGrid ();
else
ASSERT ( remove ); //maybe deleted in logoutplayer when player is not in a map
2009-10-17 15:51:44 -07:00
2009-10-31 16:00:47 -07:00
if ( remove )
2008-10-02 16:23:55 -05:00
DeleteFromWorld ( player );
}
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
template < class T >
2011-11-28 13:23:20 -05:00
void Map :: RemoveFromMap ( T * obj , bool remove )
2008-10-02 16:23:55 -05:00
{
2017-01-28 05:00:28 +01:00
bool const inWorld = obj -> IsInWorld () && obj -> GetTypeId () >= TYPEID_UNIT && obj -> GetTypeId () <= TYPEID_GAMEOBJECT ;
2008-10-02 16:23:55 -05:00
obj -> RemoveFromWorld ();
2009-10-31 16:00:47 -07:00
if ( obj -> isActiveObject ())
2009-02-25 20:52:20 -06:00
RemoveFromActive ( obj );
2009-10-17 15:51:44 -07:00
2021-12-28 14:24:10 +02:00
GetMultiPersonalPhaseTracker (). UnregisterTrackedObject ( obj );
2017-01-28 05:00:28 +01:00
if ( ! inWorld ) // if was in world, RemoveFromWorld() called DestroyForNearbyPlayers()
2022-01-29 15:18:27 +01:00
obj -> UpdateObjectVisibilityOnDestroy ();
2017-01-28 05:00:28 +01:00
2011-11-10 17:31:45 -05:00
obj -> RemoveFromGrid ();
2009-10-17 15:51:44 -07:00
2009-07-16 11:49:00 +08:00
obj -> ResetMap ();
2009-10-17 15:51:44 -07:00
2009-10-31 16:00:47 -07:00
if ( remove )
2008-10-02 16:23:55 -05:00
DeleteFromWorld ( obj );
}
2009-10-17 15:51:44 -07:00
2013-10-16 18:37:29 +02:00
template <>
void Map :: RemoveFromMap ( Transport * obj , bool remove )
{
2022-06-13 20:06:26 +02:00
if ( obj -> IsInWorld ())
2013-12-28 12:44:22 +01:00
{
2022-06-13 20:06:26 +02:00
obj -> RemoveFromWorld ();
2014-01-14 12:38:19 +01:00
UpdateData data ( GetId ());
2022-06-13 20:06:26 +02:00
if ( obj -> IsDestroyedObject ())
obj -> BuildDestroyUpdateBlock ( & data );
else
obj -> BuildOutOfRangeUpdateBlock ( & data );
2013-12-28 12:44:22 +01:00
WorldPacket packet ;
data . BuildPacket ( & packet );
2022-06-13 20:06:26 +02:00
for ( Map :: PlayerList :: const_iterator itr = GetPlayers (). begin (); itr != GetPlayers (). end (); ++ itr )
2018-01-28 11:37:20 +01:00
{
2022-06-13 20:06:26 +02:00
if ( itr -> GetSource () -> GetTransport () != obj && itr -> GetSource () -> m_visibleTransports . count ( obj -> GetGUID ()))
2018-01-28 11:37:20 +01:00
{
2014-01-14 12:38:19 +01:00
itr -> GetSource () -> SendDirectMessage ( & packet );
2018-01-28 11:37:20 +01:00
itr -> GetSource () -> m_visibleTransports . erase ( obj -> GetGUID ());
}
}
2013-12-28 12:44:22 +01:00
}
2013-10-16 18:37:29 +02:00
if ( _transportsUpdateIter != _transports . end ())
{
TransportsContainer :: iterator itr = _transports . find ( obj );
if ( itr == _transports . end ())
return ;
if ( itr == _transportsUpdateIter )
++ _transportsUpdateIter ;
_transports . erase ( itr );
}
else
_transports . erase ( obj );
obj -> ResetMap ();
if ( remove )
DeleteFromWorld ( obj );
}
2021-03-28 16:26:28 +03:00
template < typename T >
/*static*/ bool Map :: CheckGridIntegrity ( T * object , bool moved , char const * objType )
{
Cell const & cur_cell = object -> GetCurrentCell ();
Cell xy_cell ( object -> GetPositionX (), object -> GetPositionY ());
if ( xy_cell != cur_cell )
{
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "maps" , "{} {} X: {} Y: {} ({}) is in grid[{}, {}]cell[{}, {}] instead of grid[{}, {}]cell[{}, {}]" ,
objType , object -> GetGUID (). ToString (),
2021-03-28 16:26:28 +03:00
object -> GetPositionX (), object -> GetPositionY (), ( moved ? "final" : "original" ),
cur_cell . GridX (), cur_cell . GridY (), cur_cell . CellX (), cur_cell . CellY (),
xy_cell . GridX (), xy_cell . GridY (), xy_cell . CellX (), xy_cell . CellY ());
return true ; // not crash at error, just output error in debug mode
}
return true ;
}
2011-12-10 13:48:41 +01:00
void Map :: PlayerRelocation ( Player * player , float x , float y , float z , float orientation )
2008-10-02 16:23:55 -05:00
{
2010-07-30 20:04:28 +02:00
ASSERT ( player );
2009-10-17 15:51:44 -07:00
2011-10-18 11:18:49 -04:00
Cell old_cell ( player -> GetPositionX (), player -> GetPositionY ());
Cell new_cell ( x , y );
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
player -> Relocate ( x , y , z , orientation );
2012-05-30 08:01:02 +02:00
if ( player -> IsVehicle ())
2012-09-10 15:44:12 +02:00
player -> GetVehicleKit () -> RelocatePassengers ();
2009-10-17 15:51:44 -07:00
2009-10-31 16:00:47 -07:00
if ( old_cell . DiffGrid ( new_cell ) || old_cell . DiffCell ( new_cell ))
2008-10-02 16:23:55 -05:00
{
2023-01-08 21:16:53 +01:00
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 ());
2009-10-17 15:51:44 -07:00
2011-11-10 17:31:45 -05:00
player -> RemoveFromGrid ();
2010-01-16 11:43:47 +02:00
2010-04-07 22:59:46 +02:00
if ( old_cell . DiffGrid ( new_cell ))
2011-10-18 11:51:30 -04:00
EnsureGridLoadedForActiveObject ( new_cell , player );
2010-01-16 11:43:47 +02:00
2011-10-18 11:51:30 -04:00
AddToGrid ( player , new_cell );
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2017-06-08 00:25:06 +02:00
player -> UpdatePositionData ();
2010-02-27 15:25:14 +02:00
player -> UpdateObjectVisibility ( false );
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2011-12-10 13:48:41 +01:00
void Map :: CreatureRelocation ( Creature * creature , float x , float y , float z , float ang , bool respawnRelocationOnFail )
2008-10-02 16:23:55 -05:00
{
2021-03-28 16:26:28 +03:00
ASSERT ( CheckGridIntegrity ( creature , false , "Creature" ));
2009-10-17 15:51:44 -07:00
2011-10-18 11:18:49 -04:00
Cell new_cell ( x , y );
2009-10-17 15:51:44 -07:00
2010-10-25 02:30:50 +02:00
if ( ! respawnRelocationOnFail && ! getNGrid ( new_cell . GridX (), new_cell . GridY ()))
return ;
2021-03-28 16:26:28 +03:00
Cell old_cell = creature -> GetCurrentCell ();
2008-10-02 16:23:55 -05:00
// delay creature move for grid/cell to grid/cell moves
2009-10-31 16:00:47 -07:00
if ( old_cell . DiffCell ( new_cell ) || old_cell . DiffGrid ( new_cell ))
2008-10-02 16:23:55 -05:00
{
2021-03-28 16:26:28 +03:00
#ifdef TRINITY_DEBUG
2023-01-08 21:16:53 +01:00
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 ());
2021-03-28 16:26:28 +03:00
#endif
2009-10-31 16:00:47 -07:00
AddCreatureToMoveList ( creature , x , y , z , ang );
2008-10-02 16:23:55 -05:00
// in diffcell/diffgrid case notifiers called at finishing move creature in Map::MoveAllCreaturesInMoveList
}
else
{
creature -> Relocate ( x , y , z , ang );
2012-05-30 08:01:02 +02:00
if ( creature -> IsVehicle ())
2012-09-10 15:44:12 +02:00
creature -> GetVehicleKit () -> RelocatePassengers ();
2010-02-27 15:25:14 +02:00
creature -> UpdateObjectVisibility ( false );
2017-06-08 00:25:06 +02:00
creature -> UpdatePositionData ();
2011-10-07 17:36:23 -04:00
RemoveCreatureFromMoveList ( creature );
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2021-03-28 16:26:28 +03:00
ASSERT ( CheckGridIntegrity ( creature , true , "Creature" ));
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2013-10-16 18:37:29 +02:00
void Map :: GameObjectRelocation ( GameObject * go , float x , float y , float z , float orientation , bool respawnRelocationOnFail )
{
2021-03-28 16:26:28 +03:00
ASSERT ( CheckGridIntegrity ( go , false , "GameObject" ));
2013-10-16 18:37:29 +02:00
Cell new_cell ( x , y );
if ( ! respawnRelocationOnFail && ! getNGrid ( new_cell . GridX (), new_cell . GridY ()))
return ;
2021-03-28 16:26:28 +03:00
Cell old_cell = go -> GetCurrentCell ();
2013-10-16 18:37:29 +02:00
// delay creature move for grid/cell to grid/cell moves
if ( old_cell . DiffCell ( new_cell ) || old_cell . DiffGrid ( new_cell ))
{
#ifdef TRINITY_DEBUG
2023-01-08 21:16:53 +01:00
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 ());
2013-10-16 18:37:29 +02:00
#endif
AddGameObjectToMoveList ( go , x , y , z , orientation );
// in diffcell/diffgrid case notifiers called at finishing move go in Map::MoveAllGameObjectsInMoveList
}
else
{
go -> Relocate ( x , y , z , orientation );
2022-05-25 22:14:32 +02:00
go -> AfterRelocation ();
2013-10-16 18:37:29 +02:00
RemoveGameObjectFromMoveList ( go );
}
2021-03-28 16:26:28 +03:00
ASSERT ( CheckGridIntegrity ( go , true , "GameObject" ));
2013-10-16 18:37:29 +02:00
}
2014-03-16 20:00:09 +01:00
void Map :: DynamicObjectRelocation ( DynamicObject * dynObj , float x , float y , float z , float orientation )
{
2021-03-28 16:26:28 +03:00
ASSERT ( CheckGridIntegrity ( dynObj , false , "DynamicObject" ));
2014-03-16 20:00:09 +01:00
Cell new_cell ( x , y );
if ( ! getNGrid ( new_cell . GridX (), new_cell . GridY ()))
return ;
2021-03-28 16:26:28 +03:00
Cell old_cell = dynObj -> GetCurrentCell ();
2014-03-16 20:00:09 +01:00
// delay creature move for grid/cell to grid/cell moves
if ( old_cell . DiffCell ( new_cell ) || old_cell . DiffGrid ( new_cell ))
{
#ifdef TRINITY_DEBUG
2023-01-08 21:16:53 +01:00
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 ());
2014-03-16 20:00:09 +01:00
#endif
AddDynamicObjectToMoveList ( dynObj , x , y , z , orientation );
// in diffcell/diffgrid case notifiers called at finishing move dynObj in Map::MoveAllGameObjectsInMoveList
}
else
{
dynObj -> Relocate ( x , y , z , orientation );
2017-06-08 00:25:06 +02:00
dynObj -> UpdatePositionData ();
2014-03-16 20:00:09 +01:00
dynObj -> UpdateObjectVisibility ( false );
RemoveDynamicObjectFromMoveList ( dynObj );
}
2021-03-28 16:26:28 +03:00
ASSERT ( CheckGridIntegrity ( dynObj , true , "DynamicObject" ));
2014-03-16 20:00:09 +01:00
}
2017-01-29 00:00:43 +01:00
void Map :: AreaTriggerRelocation ( AreaTrigger * at , float x , float y , float z , float orientation )
{
2021-03-28 16:26:28 +03:00
ASSERT ( CheckGridIntegrity ( at , false , "AreaTrigger" ));
2017-01-29 00:00:43 +01:00
Cell new_cell ( x , y );
if ( ! getNGrid ( new_cell . GridX (), new_cell . GridY ()))
return ;
2021-03-28 16:26:28 +03:00
Cell old_cell = at -> GetCurrentCell ();
2017-01-29 00:00:43 +01:00
// delay areatrigger move for grid/cell to grid/cell moves
if ( old_cell . DiffCell ( new_cell ) || old_cell . DiffGrid ( new_cell ))
{
#ifdef TRINITY_DEBUG
2023-01-08 21:16:53 +01:00
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 ());
2017-01-29 00:00:43 +01:00
#endif
AddAreaTriggerToMoveList ( at , x , y , z , orientation );
// in diffcell/diffgrid case notifiers called at finishing move at in Map::MoveAllAreaTriggersInMoveList
}
else
{
at -> Relocate ( x , y , z , orientation );
at -> UpdateShape ();
at -> UpdateObjectVisibility ( false );
RemoveAreaTriggerFromMoveList ( at );
}
2021-03-28 16:26:28 +03:00
ASSERT ( CheckGridIntegrity ( at , true , "AreaTrigger" ));
2017-01-29 00:00:43 +01:00
}
2011-09-15 14:08:17 +02:00
void Map :: AddCreatureToMoveList ( Creature * c , float x , float y , float z , float ang )
2008-10-02 16:23:55 -05:00
{
2011-10-07 17:36:23 -04:00
if ( _creatureToMoveLock ) //can this happen?
return ;
2013-10-16 18:37:29 +02:00
if ( c -> _moveState == MAP_OBJECT_CELL_MOVE_NONE )
2011-10-07 17:36:23 -04:00
_creaturesToMove . push_back ( c );
c -> SetNewCellPosition ( x , y , z , ang );
}
void Map :: RemoveCreatureFromMoveList ( Creature * c )
{
if ( _creatureToMoveLock ) //can this happen?
2008-10-02 16:23:55 -05:00
return ;
2009-10-17 15:51:44 -07:00
2013-10-16 18:37:29 +02:00
if ( c -> _moveState == MAP_OBJECT_CELL_MOVE_ACTIVE )
c -> _moveState = MAP_OBJECT_CELL_MOVE_INACTIVE ;
}
void Map :: AddGameObjectToMoveList ( GameObject * go , float x , float y , float z , float ang )
{
if ( _gameObjectsToMoveLock ) //can this happen?
return ;
if ( go -> _moveState == MAP_OBJECT_CELL_MOVE_NONE )
_gameObjectsToMove . push_back ( go );
go -> SetNewCellPosition ( x , y , z , ang );
}
void Map :: RemoveGameObjectFromMoveList ( GameObject * go )
{
if ( _gameObjectsToMoveLock ) //can this happen?
return ;
if ( go -> _moveState == MAP_OBJECT_CELL_MOVE_ACTIVE )
go -> _moveState = MAP_OBJECT_CELL_MOVE_INACTIVE ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2014-03-16 20:00:09 +01:00
void Map :: AddDynamicObjectToMoveList ( DynamicObject * dynObj , float x , float y , float z , float ang )
{
if ( _dynamicObjectsToMoveLock ) //can this happen?
return ;
if ( dynObj -> _moveState == MAP_OBJECT_CELL_MOVE_NONE )
_dynamicObjectsToMove . push_back ( dynObj );
dynObj -> SetNewCellPosition ( x , y , z , ang );
}
void Map :: RemoveDynamicObjectFromMoveList ( DynamicObject * dynObj )
{
if ( _dynamicObjectsToMoveLock ) //can this happen?
return ;
if ( dynObj -> _moveState == MAP_OBJECT_CELL_MOVE_ACTIVE )
dynObj -> _moveState = MAP_OBJECT_CELL_MOVE_INACTIVE ;
}
2017-01-29 00:00:43 +01:00
void Map :: AddAreaTriggerToMoveList ( AreaTrigger * at , float x , float y , float z , float ang )
{
if ( _areaTriggersToMoveLock ) //can this happen?
return ;
if ( at -> _moveState == MAP_OBJECT_CELL_MOVE_NONE )
_areaTriggersToMove . push_back ( at );
at -> SetNewCellPosition ( x , y , z , ang );
}
void Map :: RemoveAreaTriggerFromMoveList ( AreaTrigger * at )
{
if ( _areaTriggersToMoveLock ) //can this happen?
return ;
if ( at -> _moveState == MAP_OBJECT_CELL_MOVE_ACTIVE )
at -> _moveState = MAP_OBJECT_CELL_MOVE_INACTIVE ;
}
2008-10-02 16:23:55 -05:00
void Map :: MoveAllCreaturesInMoveList ()
{
2011-10-07 17:36:23 -04:00
_creatureToMoveLock = true ;
2012-05-30 08:01:02 +02:00
for ( std :: vector < Creature *>:: iterator itr = _creaturesToMove . begin (); itr != _creaturesToMove . end (); ++ itr )
2008-10-02 16:23:55 -05:00
{
2011-10-07 17:36:23 -04:00
Creature * c = * itr ;
2012-05-30 08:01:02 +02:00
if ( c -> FindMap () != this ) //pet is teleported to another map
2011-10-07 17:36:23 -04:00
continue ;
2013-10-16 18:37:29 +02:00
if ( c -> _moveState != MAP_OBJECT_CELL_MOVE_ACTIVE )
2011-10-07 17:36:23 -04:00
{
2013-10-16 18:37:29 +02:00
c -> _moveState = MAP_OBJECT_CELL_MOVE_NONE ;
2011-10-07 17:36:23 -04:00
continue ;
}
2013-10-16 18:37:29 +02:00
c -> _moveState = MAP_OBJECT_CELL_MOVE_NONE ;
2012-05-30 08:01:02 +02:00
if ( ! c -> IsInWorld ())
2011-10-07 17:36:23 -04:00
continue ;
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
// do move or do move to respawn or remove creature if previous all fail
2011-10-18 11:18:49 -04:00
if ( CreatureCellRelocation ( c , Cell ( c -> _newPosition . m_positionX , c -> _newPosition . m_positionY )))
2008-10-02 16:23:55 -05:00
{
// update pos
2011-10-07 17:36:23 -04:00
c -> Relocate ( c -> _newPosition );
2013-07-08 18:01:41 +02:00
if ( c -> IsVehicle ())
c -> GetVehicleKit () -> RelocatePassengers ();
2011-10-18 10:53:34 -04:00
//CreatureRelocationNotify(c, new_cell, new_cell.cellCoord());
2017-06-08 00:25:06 +02:00
c -> UpdatePositionData ();
2010-02-27 15:25:14 +02:00
c -> UpdateObjectVisibility ( false );
2008-10-02 16:23:55 -05:00
}
else
{
// if creature can't be move in new cell/grid (not loaded) move it to repawn cell/grid
// creature coordinates will be updated and notifiers send
2011-10-07 17:36:23 -04:00
if ( ! CreatureRespawnRelocation ( c , false ))
2008-10-02 16:23:55 -05:00
{
// ... or unload (if respawn grid also not loaded)
2020-04-04 19:16:37 +02:00
#ifdef TRINITY_DEBUG
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "maps" , "Creature {} cannot be move to unloaded respawn grid." , c -> GetGUID (). ToString ());
2020-04-04 19:16:37 +02:00
#endif
2011-09-13 11:54:55 -04:00
//AddObjectToRemoveList(Pet*) should only be called in Pet::Remove
//This may happen when a player just logs in and a pet moves to a nearby unloaded cell
//To avoid this, we can load nearby cells when player log in
//But this check is always needed to ensure safety
2013-03-08 21:41:30 +01:00
/// @todo pets will disappear if this is outside CreatureRespawnRelocation
2011-09-13 11:54:55 -04:00
//need to check why pet is frequently relocated to an unloaded cell
2013-06-11 18:39:51 -02:30
if ( c -> IsPet ())
2011-09-13 11:54:55 -04:00
(( Pet * ) c ) -> Remove ( PET_SAVE_NOT_IN_SLOT , true );
else
AddObjectToRemoveList ( c );
2008-10-02 16:23:55 -05:00
}
}
}
2011-10-07 17:36:23 -04:00
_creaturesToMove . clear ();
_creatureToMoveLock = false ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2013-10-16 18:37:29 +02:00
void Map :: MoveAllGameObjectsInMoveList ()
{
_gameObjectsToMoveLock = true ;
for ( std :: vector < GameObject *>:: iterator itr = _gameObjectsToMove . begin (); itr != _gameObjectsToMove . end (); ++ itr )
{
GameObject * go = * itr ;
if ( go -> FindMap () != this ) //transport is teleported to another map
continue ;
if ( go -> _moveState != MAP_OBJECT_CELL_MOVE_ACTIVE )
{
go -> _moveState = MAP_OBJECT_CELL_MOVE_NONE ;
continue ;
}
go -> _moveState = MAP_OBJECT_CELL_MOVE_NONE ;
if ( ! go -> IsInWorld ())
continue ;
// do move or do move to respawn or remove creature if previous all fail
if ( GameObjectCellRelocation ( go , Cell ( go -> _newPosition . m_positionX , go -> _newPosition . m_positionY )))
{
// update pos
go -> Relocate ( go -> _newPosition );
2022-05-25 22:14:32 +02:00
go -> AfterRelocation ();
2013-10-16 18:37:29 +02:00
}
else
{
// if GameObject can't be move in new cell/grid (not loaded) move it to repawn cell/grid
// GameObject coordinates will be updated and notifiers send
if ( ! GameObjectRespawnRelocation ( go , false ))
{
// ... or unload (if respawn grid also not loaded)
#ifdef TRINITY_DEBUG
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "maps" , "GameObject {} cannot be move to unloaded respawn grid." , go -> GetGUID (). ToString ());
2013-10-16 18:37:29 +02:00
#endif
AddObjectToRemoveList ( go );
}
}
}
_gameObjectsToMove . clear ();
_gameObjectsToMoveLock = false ;
}
2014-03-16 20:00:09 +01:00
void Map :: MoveAllDynamicObjectsInMoveList ()
{
_dynamicObjectsToMoveLock = true ;
for ( std :: vector < DynamicObject *>:: iterator itr = _dynamicObjectsToMove . begin (); itr != _dynamicObjectsToMove . end (); ++ itr )
{
DynamicObject * dynObj = * itr ;
if ( dynObj -> FindMap () != this ) //transport is teleported to another map
continue ;
if ( dynObj -> _moveState != MAP_OBJECT_CELL_MOVE_ACTIVE )
{
dynObj -> _moveState = MAP_OBJECT_CELL_MOVE_NONE ;
continue ;
}
dynObj -> _moveState = MAP_OBJECT_CELL_MOVE_NONE ;
if ( ! dynObj -> IsInWorld ())
continue ;
// do move or do move to respawn or remove creature if previous all fail
if ( DynamicObjectCellRelocation ( dynObj , Cell ( dynObj -> _newPosition . m_positionX , dynObj -> _newPosition . m_positionY )))
{
// update pos
dynObj -> Relocate ( dynObj -> _newPosition );
2017-06-08 00:25:06 +02:00
dynObj -> UpdatePositionData ();
2014-03-16 20:00:09 +01:00
dynObj -> UpdateObjectVisibility ( false );
}
else
{
#ifdef TRINITY_DEBUG
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "maps" , "DynamicObject {} cannot be moved to unloaded grid." , dynObj -> GetGUID (). ToString ());
2014-03-16 20:00:09 +01:00
#endif
}
}
_dynamicObjectsToMove . clear ();
_dynamicObjectsToMoveLock = false ;
}
2017-01-29 00:00:43 +01:00
void Map :: MoveAllAreaTriggersInMoveList ()
{
_areaTriggersToMoveLock = true ;
for ( std :: vector < AreaTrigger *>:: iterator itr = _areaTriggersToMove . begin (); itr != _areaTriggersToMove . end (); ++ itr )
{
AreaTrigger * at = * itr ;
if ( at -> FindMap () != this ) //transport is teleported to another map
continue ;
if ( at -> _moveState != MAP_OBJECT_CELL_MOVE_ACTIVE )
{
at -> _moveState = MAP_OBJECT_CELL_MOVE_NONE ;
continue ;
}
at -> _moveState = MAP_OBJECT_CELL_MOVE_NONE ;
if ( ! at -> IsInWorld ())
continue ;
// do move or do move to respawn or remove creature if previous all fail
if ( AreaTriggerCellRelocation ( at , Cell ( at -> _newPosition . m_positionX , at -> _newPosition . m_positionY )))
{
// update pos
at -> Relocate ( at -> _newPosition );
2018-08-05 18:57:35 +02:00
at -> UpdateShape ();
2017-01-29 00:00:43 +01:00
at -> UpdateObjectVisibility ( false );
}
else
{
#ifdef TRINITY_DEBUG
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "maps" , "AreaTrigger {} cannot be moved to unloaded grid." , at -> GetGUID (). ToString ());
2017-01-29 00:00:43 +01:00
#endif
}
}
_areaTriggersToMove . clear ();
_areaTriggersToMoveLock = false ;
}
2021-03-28 16:26:28 +03:00
template < typename T >
2020-04-04 19:16:37 +02:00
bool Map :: MapObjectCellRelocation ( T * object , Cell new_cell , [[maybe_unused]] char const * objType )
2008-10-02 16:23:55 -05:00
{
2021-03-28 16:26:28 +03:00
Cell const & old_cell = object -> GetCurrentCell ();
2009-10-31 16:00:47 -07:00
if ( ! old_cell . DiffGrid ( new_cell )) // in same grid
2008-10-02 16:23:55 -05:00
{
// if in same cell then none do
2009-10-31 16:00:47 -07:00
if ( old_cell . DiffCell ( new_cell ))
2008-10-02 16:23:55 -05:00
{
2021-03-28 16:26:28 +03:00
#ifdef TRINITY_DEBUG
2023-01-08 21:16:53 +01:00
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 ());
2021-03-28 16:26:28 +03:00
#endif
2009-10-17 15:51:44 -07:00
2021-03-28 16:26:28 +03:00
object -> RemoveFromGrid ();
AddToGrid ( object , new_cell );
2008-10-02 16:23:55 -05:00
}
else
{
2021-03-28 16:26:28 +03:00
#ifdef TRINITY_DEBUG
2023-01-08 21:16:53 +01:00
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 ());
2021-03-28 16:26:28 +03:00
#endif
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2009-02-25 20:52:20 -06:00
return true ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2009-02-25 20:52:20 -06:00
// in diff. grids but active creature
2021-03-28 16:26:28 +03:00
if ( object -> isActiveObject ())
2009-02-25 20:52:20 -06:00
{
2021-03-28 16:26:28 +03:00
EnsureGridLoadedForActiveObject ( new_cell , object );
2009-10-17 15:51:44 -07:00
2021-03-28 16:26:28 +03:00
#ifdef TRINITY_DEBUG
2023-01-08 21:16:53 +01:00
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 ());
2021-03-28 16:26:28 +03:00
#endif
2009-10-17 15:51:44 -07:00
2021-03-28 16:26:28 +03:00
object -> RemoveFromGrid ();
AddToGrid ( object , new_cell );
2009-10-17 15:51:44 -07:00
2009-02-25 20:52:20 -06:00
return true ;
}
2009-10-17 15:51:44 -07:00
2019-12-20 20:38:05 +01:00
if ( Creature * c = object -> ToCreature ())
if ( c -> GetCharmerOrOwnerGUID (). IsPlayer ())
EnsureGridLoaded ( new_cell );
2021-03-28 16:26:28 +03:00
// in diff. loaded grid normal object
2011-10-18 11:51:30 -04:00
if ( IsGridLoaded ( GridCoord ( new_cell . GridX (), new_cell . GridY ())))
2008-10-02 16:23:55 -05:00
{
2021-03-28 16:26:28 +03:00
#ifdef TRINITY_DEBUG
2023-01-08 21:16:53 +01:00
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 ());
2021-03-28 16:26:28 +03:00
#endif
2009-10-17 15:51:44 -07:00
2021-03-28 16:26:28 +03:00
object -> RemoveFromGrid ();
2011-10-18 10:53:34 -04:00
EnsureGridCreated ( GridCoord ( new_cell . GridX (), new_cell . GridY ()));
2021-03-28 16:26:28 +03:00
AddToGrid ( object , new_cell );
2009-10-17 15:51:44 -07:00
2009-02-25 20:52:20 -06:00
return true ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2021-03-28 16:26:28 +03:00
// fail to move: normal object attempt move to unloaded grid
#ifdef TRINITY_DEBUG
2023-01-08 21:16:53 +01:00
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 ());
2021-03-28 16:26:28 +03:00
#endif
2009-02-25 20:52:20 -06:00
return false ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2021-03-28 16:26:28 +03:00
bool Map :: CreatureCellRelocation ( Creature * c , Cell new_cell )
2013-10-16 18:37:29 +02:00
{
2021-03-28 16:26:28 +03:00
return MapObjectCellRelocation ( c , new_cell , "Creature" );
}
2013-10-16 18:37:29 +02:00
2021-03-28 16:26:28 +03:00
bool Map :: GameObjectCellRelocation ( GameObject * go , Cell new_cell )
{
return MapObjectCellRelocation ( go , new_cell , "GameObject" );
2013-10-16 18:37:29 +02:00
}
2014-03-16 20:00:09 +01:00
bool Map :: DynamicObjectCellRelocation ( DynamicObject * go , Cell new_cell )
{
2021-03-28 16:26:28 +03:00
return MapObjectCellRelocation ( go , new_cell , "DynamicObject" );
2014-03-16 20:00:09 +01:00
}
2017-01-29 00:00:43 +01:00
bool Map :: AreaTriggerCellRelocation ( AreaTrigger * at , Cell new_cell )
{
2021-03-28 16:26:28 +03:00
return MapObjectCellRelocation ( at , new_cell , "AreaTrigger" );
2017-01-29 00:00:43 +01:00
}
2011-10-07 17:36:23 -04:00
bool Map :: CreatureRespawnRelocation ( Creature * c , bool diffGridOnly )
2008-10-02 16:23:55 -05:00
{
float resp_x , resp_y , resp_z , resp_o ;
2011-10-18 10:53:34 -04:00
c -> GetRespawnPosition ( resp_x , resp_y , resp_z , & resp_o );
2011-10-18 11:18:49 -04:00
Cell resp_cell ( resp_x , resp_y );
2009-10-17 15:51:44 -07:00
2011-10-07 17:36:23 -04:00
//creature will be unloaded with grid
2011-10-18 11:42:40 +02:00
if ( diffGridOnly && ! c -> GetCurrentCell (). DiffGrid ( resp_cell ))
2011-10-07 17:36:23 -04:00
return true ;
2008-10-02 16:23:55 -05:00
c -> CombatStop ();
c -> GetMotionMaster () -> Clear ();
2009-10-17 15:51:44 -07:00
2020-04-04 19:16:37 +02:00
#ifdef TRINITY_DEBUG
2023-01-08 21:16:53 +01:00
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 ());
2020-04-04 19:16:37 +02:00
#endif
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
// teleport it to respawn point (like normal respawn if player see)
2011-04-29 20:47:02 +02:00
if ( CreatureCellRelocation ( c , resp_cell ))
2008-10-02 16:23:55 -05:00
{
c -> Relocate ( resp_x , resp_y , resp_z , resp_o );
2018-06-03 10:06:57 -07:00
c -> GetMotionMaster () -> Initialize (); // prevent possible problems with default move generators
2011-10-18 10:53:34 -04:00
//CreatureRelocationNotify(c, resp_cell, resp_cell.GetCellCoord());
2017-06-08 00:25:06 +02:00
c -> UpdatePositionData ();
2010-02-27 15:25:14 +02:00
c -> UpdateObjectVisibility ( false );
2008-10-02 16:23:55 -05:00
return true ;
}
2011-09-13 11:54:55 -04:00
return false ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2013-10-16 18:37:29 +02:00
bool Map :: GameObjectRespawnRelocation ( GameObject * go , bool diffGridOnly )
{
float resp_x , resp_y , resp_z , resp_o ;
go -> GetRespawnPosition ( resp_x , resp_y , resp_z , & resp_o );
Cell resp_cell ( resp_x , resp_y );
//GameObject will be unloaded with grid
if ( diffGridOnly && ! go -> GetCurrentCell (). DiffGrid ( resp_cell ))
return true ;
2020-04-04 19:16:37 +02:00
#ifdef TRINITY_DEBUG
2023-01-08 21:16:53 +01:00
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 ());
2020-04-04 19:16:37 +02:00
#endif
2013-10-16 18:37:29 +02:00
// teleport it to respawn point (like normal respawn if player see)
if ( GameObjectCellRelocation ( go , resp_cell ))
{
go -> Relocate ( resp_x , resp_y , resp_z , resp_o );
2017-06-08 00:25:06 +02:00
go -> UpdatePositionData ();
2013-10-16 18:37:29 +02:00
go -> UpdateObjectVisibility ( false );
return true ;
}
return false ;
}
2011-10-22 10:01:29 -04:00
bool Map :: UnloadGrid ( NGridType & ngrid , bool unloadAll )
2008-10-02 16:23:55 -05:00
{
2011-10-22 10:01:29 -04:00
const uint32 x = ngrid . getX ();
const uint32 y = ngrid . getY ();
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
{
2011-12-20 17:05:08 -05:00
if ( ! unloadAll )
{
//pets, possessed creatures (must be active), transport passengers
2011-12-22 10:15:17 -05:00
if ( ngrid . GetWorldObjectCountInNGrid < Creature > ())
2011-12-20 17:05:08 -05:00
return false ;
if ( ActiveObjectsNearGrid ( ngrid ))
return false ;
}
2009-10-17 15:51:44 -07:00
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "maps" , "Unloading grid[{}, {}] for map {}" , x , y , GetId ());
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( ! unloadAll )
2009-02-25 17:53:05 -06:00
{
// Finish creature moves, remove and delete all creatures with delayed remove before moving to respawn grids
// Must know real mob position before move
2009-03-29 11:58:19 -06:00
MoveAllCreaturesInMoveList ();
2013-10-16 18:37:29 +02:00
MoveAllGameObjectsInMoveList ();
2017-01-29 00:00:43 +01:00
MoveAllAreaTriggersInMoveList ();
2009-10-17 15:51:44 -07:00
2009-02-25 17:53:05 -06:00
// move creatures to respawn grids if this is diff.grid or to remove list
2011-10-18 14:31:34 -04:00
ObjectGridEvacuator worker ;
TypeContainerVisitor < ObjectGridEvacuator , GridTypeMapContainer > visitor ( worker );
2011-10-22 10:01:29 -04:00
ngrid . VisitAllGrids ( visitor );
2009-10-17 15:51:44 -07:00
2009-02-25 17:53:05 -06:00
// Finish creature moves, remove and delete all creatures with delayed remove before unload
2009-03-29 11:58:19 -06:00
MoveAllCreaturesInMoveList ();
2013-10-16 18:37:29 +02:00
MoveAllGameObjectsInMoveList ();
2017-01-29 00:00:43 +01:00
MoveAllAreaTriggersInMoveList ();
2009-02-25 17:53:05 -06:00
}
2009-10-17 15:51:44 -07:00
2011-10-18 14:31:34 -04:00
{
ObjectGridCleaner worker ;
TypeContainerVisitor < ObjectGridCleaner , GridTypeMapContainer > visitor ( worker );
2011-10-22 10:01:29 -04:00
ngrid . VisitAllGrids ( visitor );
2011-10-18 14:31:34 -04:00
}
2009-10-17 15:51:44 -07:00
2009-03-29 11:58:19 -06:00
RemoveAllObjectsInRemoveList ();
2009-10-17 15:51:44 -07:00
2021-12-28 14:24:10 +02:00
// After removing all objects from the map, purge empty tracked phases
GetMultiPersonalPhaseTracker (). UnloadGrid ( ngrid );
2011-10-18 14:31:34 -04:00
{
ObjectGridUnloader worker ;
TypeContainerVisitor < ObjectGridUnloader , GridTypeMapContainer > visitor ( worker );
2011-10-22 10:01:29 -04:00
ngrid . VisitAllGrids ( visitor );
2011-10-18 14:31:34 -04:00
}
2009-10-17 15:51:44 -07:00
2010-07-30 20:04:28 +02:00
ASSERT ( i_objectsToRemove . empty ());
2009-10-17 15:51:44 -07:00
2011-10-22 10:01:29 -04:00
delete & ngrid ;
2020-08-14 17:06:03 +02:00
setNGrid ( nullptr , x , y );
2008-10-02 16:23:55 -05:00
}
2009-09-27 02:24:25 -07:00
int gx = ( MAX_NUMBER_OF_GRIDS - 1 ) - x ;
int gy = ( MAX_NUMBER_OF_GRIDS - 1 ) - y ;
2009-10-17 15:51:44 -07:00
2022-07-23 19:13:33 +02:00
m_terrain -> UnloadMap ( gx , gy );
2018-05-06 00:39:13 +02:00
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "maps" , "Unloading grid[{}, {}] for map {} finished" , x , y , GetId ());
2008-10-02 16:23:55 -05:00
return true ;
}
2009-10-17 15:51:44 -07:00
2009-08-06 16:14:13 -05:00
void Map :: RemoveAllPlayers ()
{
2010-04-07 19:14:10 +02:00
if ( HavePlayers ())
2009-08-06 16:14:13 -05:00
{
2009-10-17 16:20:24 -07:00
for ( MapRefManager :: iterator itr = m_mapRefManager . begin (); itr != m_mapRefManager . end (); ++ itr )
2009-08-06 16:14:13 -05:00
{
2013-06-11 21:25:12 -02:30
Player * player = itr -> GetSource ();
2011-11-07 11:06:39 -06:00
if ( ! player -> IsBeingTeleportedFar ())
2009-08-29 15:29:14 -05:00
{
// this is happening for bg
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "maps" , "Map::UnloadAll: player {} is still in map {} during unload, this should not happen!" , player -> GetName (), GetId ());
2021-10-16 01:17:47 +02:00
player -> TeleportTo ( player -> m_homebind );
2009-08-29 15:29:14 -05:00
}
2009-08-06 16:14:13 -05:00
}
}
}
2009-10-17 15:51:44 -07:00
2009-02-25 17:53:05 -06:00
void Map :: UnloadAll ()
2008-10-02 16:23:55 -05:00
{
// clear all delayed moves, useless anyway do this moves before map unload.
2011-10-07 17:36:23 -04:00
_creaturesToMove . clear ();
2013-10-16 18:37:29 +02:00
_gameObjectsToMove . clear ();
2009-10-17 15:51:44 -07:00
2009-11-20 19:11:28 -08:00
for ( GridRefManager < NGridType >:: iterator i = GridRefManager < NGridType >:: begin (); i != GridRefManager < NGridType >:: end ();)
2008-10-02 16:23:55 -05:00
{
2013-06-11 21:25:12 -02:30
NGridType & grid ( * i -> GetSource ());
2008-10-02 16:23:55 -05:00
++ i ;
2011-10-22 10:01:29 -04:00
UnloadGrid ( grid , true ); // deletes the grid and removes it from the GridRefManager
2008-10-02 16:23:55 -05:00
}
2013-12-25 22:52:35 +01:00
for ( TransportsContainer :: iterator itr = _transports . begin (); itr != _transports . end ();)
{
Transport * transport = * itr ;
++ itr ;
2014-04-28 22:26:43 +02:00
RemoveFromMap < Transport > ( transport , true );
2013-12-25 22:52:35 +01:00
}
2015-10-28 21:43:44 +01:00
for ( auto & cellCorpsePair : _corpsesByCell )
2015-10-29 18:46:37 +01:00
{
2015-10-28 21:43:44 +01:00
for ( Corpse * corpse : cellCorpsePair . second )
2015-10-29 18:46:37 +01:00
{
corpse -> RemoveFromWorld ();
corpse -> ResetMap ();
2015-10-28 21:43:44 +01:00
delete corpse ;
2015-10-29 18:46:37 +01:00
}
}
2015-10-28 21:43:44 +01:00
_corpsesByCell . clear ();
_corpsesByPlayer . clear ();
2015-10-29 18:46:37 +01:00
_corpseBones . clear ();
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2022-07-23 19:13:33 +02:00
void Map :: GetFullTerrainStatusForPosition ( PhaseShift const & phaseShift , float x , float y , float z , PositionFullTerrainStatus & data ,
2024-03-07 00:23:52 +01:00
Optional < map_liquidHeaderTypeFlags > reqLiquidType , float collisionHeight )
2009-03-09 18:06:19 -06:00
{
2022-12-31 18:22:40 +01:00
m_terrain -> GetFullTerrainStatusForPosition ( phaseShift , GetId (), x , y , z , data , reqLiquidType , collisionHeight , & _dynamicTree );
2009-03-09 18:06:19 -06:00
}
2009-10-17 15:51:44 -07:00
2024-03-07 00:23:52 +01:00
ZLiquidStatus Map :: GetLiquidStatus ( PhaseShift const & phaseShift , float x , float y , float z , Optional < map_liquidHeaderTypeFlags > ReqLiquidType , LiquidData * data ,
2022-07-23 19:13:33 +02:00
float collisionHeight )
2009-03-09 18:06:19 -06:00
{
2022-12-31 18:22:40 +01:00
return m_terrain -> GetLiquidStatus ( phaseShift , GetId (), x , y , z , ReqLiquidType , data , collisionHeight );
2009-03-09 18:06:19 -06:00
}
2009-10-17 15:51:44 -07:00
2022-07-23 19:13:33 +02:00
uint32 Map :: GetAreaId ( PhaseShift const & phaseShift , float x , float y , float z )
2012-01-14 15:34:41 +01:00
{
2022-12-31 18:22:40 +01:00
return m_terrain -> GetAreaId ( phaseShift , GetId (), x , y , z , & _dynamicTree );
2012-01-14 15:34:41 +01:00
}
2022-07-23 19:13:33 +02:00
uint32 Map :: GetZoneId ( PhaseShift const & phaseShift , float x , float y , float z )
2009-03-09 18:06:19 -06:00
{
2022-12-31 18:22:40 +01:00
return m_terrain -> GetZoneId ( phaseShift , GetId (), x , y , z , & _dynamicTree );
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2022-07-23 19:13:33 +02:00
void Map :: GetZoneAndAreaId ( PhaseShift const & phaseShift , uint32 & zoneid , uint32 & areaid , float x , float y , float z )
2019-04-07 20:15:40 +02:00
{
2022-12-31 18:22:40 +01:00
return m_terrain -> GetZoneAndAreaId ( phaseShift , GetId (), zoneid , areaid , x , y , z , & _dynamicTree );
2019-04-07 20:15:40 +02:00
}
2020-05-24 22:34:25 +02:00
float Map :: GetMinHeight ( PhaseShift const & phaseShift , float x , float y )
2016-02-09 00:14:58 +01:00
{
2022-12-31 18:22:40 +01:00
return m_terrain -> GetMinHeight ( phaseShift , GetId (), x , y );
2016-02-09 00:14:58 +01:00
}
2022-07-23 19:13:33 +02:00
float Map :: GetGridHeight ( PhaseShift const & phaseShift , float x , float y )
2009-01-16 19:05:30 -06:00
{
2022-12-31 18:22:40 +01:00
return m_terrain -> GetGridHeight ( phaseShift , GetId (), x , y );
2009-01-16 19:05:30 -06:00
}
2009-10-17 15:51:44 -07:00
2022-07-23 19:13:33 +02:00
float Map :: GetStaticHeight ( PhaseShift const & phaseShift , float x , float y , float z , bool checkVMap , float maxSearchDist )
2010-06-05 00:59:25 +02:00
{
2022-12-31 18:22:40 +01:00
return m_terrain -> GetStaticHeight ( phaseShift , GetId (), x , y , z , checkVMap , maxSearchDist );
2010-06-05 00:59:25 +02:00
}
2009-10-17 15:51:44 -07:00
2022-07-23 19:13:33 +02:00
float Map :: GetWaterLevel ( PhaseShift const & phaseShift , float x , float y )
2016-02-02 19:13:04 +01:00
{
2022-12-31 18:22:40 +01:00
return m_terrain -> GetWaterLevel ( phaseShift , GetId (), x , y );
2016-02-02 19:13:04 +01:00
}
2022-07-23 19:13:33 +02:00
bool Map :: IsInWater ( PhaseShift const & phaseShift , float x , float y , float z , LiquidData * data )
2016-02-02 19:13:04 +01:00
{
2022-12-31 18:22:40 +01:00
return m_terrain -> IsInWater ( phaseShift , GetId (), x , y , z , data );
2016-02-02 19:13:04 +01:00
}
2009-10-17 15:51:44 -07:00
2022-07-23 19:13:33 +02:00
bool Map :: IsUnderWater ( PhaseShift const & phaseShift , float x , float y , float z )
2017-06-08 00:25:06 +02:00
{
2022-12-31 18:22:40 +01:00
return m_terrain -> IsUnderWater ( phaseShift , GetId (), x , y , z );
2017-06-08 00:25:06 +02:00
}
2022-07-23 19:13:33 +02:00
float Map :: GetWaterOrGroundLevel ( PhaseShift const & phaseShift , float x , float y , float z , float * ground , bool swim , float collisionHeight )
2009-03-09 18:06:19 -06:00
{
2022-12-31 18:22:40 +01:00
return m_terrain -> GetWaterOrGroundLevel ( phaseShift , GetId (), x , y , z , ground , swim , collisionHeight , & _dynamicTree );
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2017-06-10 15:08:35 +02:00
bool Map :: isInLineOfSight ( PhaseShift const & phaseShift , float x1 , float y1 , float z1 , float x2 , float y2 , float z2 , LineOfSightChecks checks , VMAP :: ModelIgnoreFlags ignoreFlags ) const
2012-02-03 16:03:52 -05:00
{
2017-06-10 15:08:35 +02:00
if (( checks & LINEOFSIGHT_CHECK_VMAP )
2022-12-31 18:22:40 +01:00
&& ! VMAP :: VMapFactory :: createOrGetVMapManager () -> isInLineOfSight ( PhasingHandler :: GetTerrainMapId ( phaseShift , GetId (), m_terrain . get (), x1 , y1 ), x1 , y1 , z1 , x2 , y2 , z2 , ignoreFlags ))
2017-06-10 15:08:35 +02:00
return false ;
if ( sWorld -> getBoolConfig ( CONFIG_CHECK_GOBJECT_LOS ) && ( checks & LINEOFSIGHT_CHECK_GOBJECT )
&& ! _dynamicTree . isInLineOfSight ({ x1 , y1 , z1 }, { x2 , y2 , z2 }, phaseShift ))
return false ;
return true ;
2012-02-11 19:23:18 -05:00
}
2018-01-28 11:37:20 +01:00
bool 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 )
2012-02-11 19:23:18 -05:00
{
2013-01-30 09:06:19 +01:00
G3D :: Vector3 startPos ( x1 , y1 , z1 );
G3D :: Vector3 dstPos ( x2 , y2 , z2 );
2012-02-11 19:23:18 -05:00
2013-01-30 09:06:19 +01:00
G3D :: Vector3 resultPos ;
2018-01-28 11:37:20 +01:00
bool result = _dynamicTree . getObjectHitPos ( startPos , dstPos , resultPos , modifyDist , phaseShift );
2012-02-11 19:23:18 -05:00
rx = resultPos . x ;
ry = resultPos . y ;
rz = resultPos . z ;
return result ;
2012-02-03 16:03:52 -05:00
}
2022-10-08 19:11:34 +02:00
TransferAbortParams Map :: PlayerCannotEnter ( uint32 mapid , Player * player )
2022-07-24 18:56:40 +02:00
{
MapEntry const * entry = sMapStore . LookupEntry ( mapid );
if ( ! entry )
2022-10-08 19:11:34 +02:00
return TRANSFER_ABORT_MAP_NOT_ALLOWED ;
2022-07-24 18:56:40 +02:00
if ( ! entry -> IsDungeon ())
2022-10-08 19:11:34 +02:00
return TRANSFER_ABORT_NONE ;
2022-07-24 18:56:40 +02:00
2022-10-08 19:11:34 +02:00
Difficulty targetDifficulty = player -> GetDifficultyID ( entry );
2022-07-24 18:56:40 +02:00
// Get the highest available difficulty if current setting is higher than the instance allows
MapDifficultyEntry const * mapDiff = sDB2Manager . GetDownscaledMapDifficultyData ( mapid , targetDifficulty );
if ( ! mapDiff )
2022-10-08 19:11:34 +02:00
return TRANSFER_ABORT_DIFFICULTY ;
2022-07-24 18:56:40 +02:00
//Bypass checks for GMs
if ( player -> IsGameMaster ())
2022-10-08 19:11:34 +02:00
return TRANSFER_ABORT_NONE ;
2022-07-24 18:56:40 +02:00
//Other requirements
2022-10-08 19:11:34 +02:00
{
TransferAbortParams params ( TRANSFER_ABORT_NONE );
if ( ! player -> Satisfy ( sObjectMgr -> GetAccessRequirement ( mapid , targetDifficulty ), mapid , & params , true ))
return params ;
}
2022-07-24 18:56:40 +02:00
Group * group = player -> GetGroup ();
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
if (( ! group || ! group -> isRaidGroup ()) && ! sWorld -> getBoolConfig ( CONFIG_INSTANCE_IGNORE_RAID ))
2022-10-08 19:11:34 +02:00
return TRANSFER_ABORT_NEED_GROUP ;
2022-07-24 18:56:40 +02:00
2018-07-22 13:22:35 +02:00
if ( entry -> Instanceable ())
2022-07-24 18:56:40 +02:00
{
2018-07-22 13:22:35 +02:00
//Get instance where player's group is bound & its map
2020-01-02 14:19:35 +01:00
uint32 instanceIdToCheck = sMapMgr -> FindInstanceIdForPlayer ( mapid , player );
if ( Map * boundMap = sMapMgr -> FindMap ( mapid , instanceIdToCheck ))
2022-10-08 19:11:34 +02:00
if ( TransferAbortParams denyReason = boundMap -> CannotEnter ( player ))
2020-01-02 14:19:35 +01:00
return denyReason ;
2022-07-24 18:56:40 +02:00
2020-01-02 14:19:35 +01:00
// players are only allowed to enter 10 instances per hour
2022-08-15 22:18:17 +02:00
if ( ! entry -> GetFlags2 (). HasFlag ( MapFlags2 :: IgnoreInstanceFarmLimit ) && entry -> IsDungeon () && ! player -> CheckInstanceCount ( instanceIdToCheck ) && ! player -> isDead ())
2022-10-08 19:11:34 +02:00
return TRANSFER_ABORT_TOO_MANY_INSTANCES ;
2022-07-24 18:56:40 +02:00
}
2022-10-08 19:11:34 +02:00
return TRANSFER_ABORT_NONE ;
2022-07-24 18:56:40 +02:00
}
2011-12-10 13:48:41 +01:00
char const * Map :: GetMapName () const
2008-10-02 16:23:55 -05:00
{
2022-07-23 19:13:33 +02:00
return i_mapEntry -> MapName [ sWorld -> GetDefaultDbcLocale ()];
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2011-06-11 22:35:29 +02:00
void Map :: SendInitSelf ( Player * player )
2008-10-02 16:23:55 -05:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "maps" , "Creating player data for himself {}" , player -> GetGUID (). ToString ());
2009-10-17 15:51:44 -07:00
2011-11-27 16:06:29 +01:00
UpdateData data ( player -> GetMapId ());
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
// attach to player data current transport data
2022-05-25 22:14:32 +02:00
if ( Transport * transport = dynamic_cast < Transport *> ( player -> GetTransport ()))
2008-10-02 16:23:55 -05:00
{
transport -> BuildCreateUpdateBlockForPlayer ( & data , player );
2018-01-28 11:37:20 +01:00
player -> m_visibleTransports . insert ( transport -> GetGUID ());
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
// build data for self presence in world at own client (one time for map)
player -> BuildCreateUpdateBlockForPlayer ( & data , player );
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
// build other passengers at transport also (they always visible and marked as visible and will not send at visibility update at add to map
2022-05-25 22:14:32 +02:00
if ( Transport * transport = dynamic_cast < Transport *> ( player -> GetTransport ()))
2018-01-28 11:37:20 +01:00
for ( WorldObject * passenger : transport -> GetPassengers ())
if ( player != passenger && player -> HaveAtClient ( passenger ))
passenger -> BuildCreateUpdateBlockForPlayer ( & data , player );
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
WorldPacket packet ;
2009-05-31 15:56:51 -05:00
data . BuildPacket ( & packet );
2020-09-04 13:38:24 +02:00
player -> SendDirectMessage ( & packet );
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2011-06-11 22:35:29 +02:00
void Map :: SendInitTransports ( Player * player )
2008-10-02 16:23:55 -05:00
{
// Hack to send out transports
2022-06-13 20:06:26 +02:00
UpdateData transData ( GetId ());
2018-01-28 11:37:20 +01:00
for ( Transport * transport : _transports )
{
2022-12-25 00:44:12 +01:00
if ( transport -> IsInWorld () && transport != player -> GetTransport () && player -> InSamePhase ( transport ))
2018-01-28 11:37:20 +01:00
{
transport -> BuildCreateUpdateBlockForPlayer ( & transData , player );
player -> m_visibleTransports . insert ( transport -> GetGUID ());
}
}
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
WorldPacket packet ;
2009-05-31 15:56:51 -05:00
transData . BuildPacket ( & packet );
2020-09-04 13:38:24 +02:00
player -> SendDirectMessage ( & packet );
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2011-06-11 22:35:29 +02:00
void Map :: SendRemoveTransports ( Player * player )
2008-10-02 16:23:55 -05:00
{
// Hack to send out transports
2011-11-27 16:06:29 +01:00
UpdateData transData ( player -> GetMapId ());
2018-01-28 11:37:20 +01:00
for ( Transport * transport : _transports )
{
2022-06-13 20:06:26 +02:00
if ( player -> m_visibleTransports . count ( transport -> GetGUID ()) && transport != player -> GetTransport ())
2018-01-28 11:37:20 +01:00
{
transport -> BuildOutOfRangeUpdateBlock ( & transData );
player -> m_visibleTransports . erase ( transport -> GetGUID ());
}
}
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
WorldPacket packet ;
transData . BuildPacket ( & packet );
2020-09-04 13:38:24 +02:00
player -> SendDirectMessage ( & packet );
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2018-01-28 11:37:20 +01:00
void Map :: SendUpdateTransportVisibility ( Player * player )
2014-06-03 13:08:54 -05:00
{
// Hack to send out transports
UpdateData transData ( player -> GetMapId ());
2018-01-28 11:37:20 +01:00
for ( Transport * transport : _transports )
2014-06-03 13:08:54 -05:00
{
2022-06-13 20:06:26 +02:00
if ( ! transport -> IsInWorld ())
continue ;
2018-01-28 11:37:20 +01:00
auto transportItr = player -> m_visibleTransports . find ( transport -> GetGUID ());
2022-12-25 00:44:12 +01:00
if ( player -> InSamePhase ( transport ))
2018-01-28 11:37:20 +01:00
{
if ( transportItr == player -> m_visibleTransports . end ())
{
transport -> BuildCreateUpdateBlockForPlayer ( & transData , player );
player -> m_visibleTransports . insert ( transport -> GetGUID ());
}
}
else if ( transportItr != player -> m_visibleTransports . end ())
{
transport -> BuildOutOfRangeUpdateBlock ( & transData );
player -> m_visibleTransports . erase ( transportItr );
}
2014-06-03 13:08:54 -05:00
}
WorldPacket packet ;
transData . BuildPacket ( & packet );
player -> GetSession () -> SendPacket ( & packet );
}
2008-10-02 16:23:55 -05:00
inline void Map :: setNGrid ( NGridType * grid , uint32 x , uint32 y )
{
2010-04-07 19:14:10 +02:00
if ( x >= MAX_NUMBER_OF_GRIDS || y >= MAX_NUMBER_OF_GRIDS )
2008-10-02 16:23:55 -05:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "maps" , "map::setNGrid() Invalid grid coordinates found: {}, {}!" , x , y );
2015-09-21 15:17:05 +02:00
ABORT ();
2008-10-02 16:23:55 -05:00
}
i_grids [ x ][ y ] = grid ;
}
2009-10-17 15:51:44 -07:00
2015-02-25 00:13:14 +01:00
void Map :: SendObjectUpdates ()
{
UpdateDataMapType update_players ;
while ( ! _updateObjects . empty ())
{
Object * obj = * _updateObjects . begin ();
2015-05-15 00:08:40 +02:00
ASSERT ( obj -> IsInWorld ());
2015-02-25 00:13:14 +01:00
_updateObjects . erase ( _updateObjects . begin ());
obj -> BuildUpdate ( update_players );
}
WorldPacket packet ; // here we allocate a std::vector with a size of 0x10000
for ( UpdateDataMapType :: iterator iter = update_players . begin (); iter != update_players . end (); ++ iter )
{
iter -> second . BuildPacket ( & packet );
2020-09-04 13:38:24 +02:00
iter -> first -> SendDirectMessage ( & packet );
2015-02-25 00:13:14 +01:00
packet . clear (); // clean the string
}
}
2018-02-22 20:31:42 +01:00
// CheckRespawn MUST do one of the following:
// -) return true
// -) set info->respawnTime to zero, which indicates the respawn time should be deleted (and will never be processed again without outside intervention)
// -) set info->respawnTime to a new respawn time, which must be strictly GREATER than the current time (GameTime::GetGameTime())
2017-05-07 21:48:41 +01:00
bool Map :: CheckRespawn ( RespawnInfo * info )
{
2018-02-22 20:31:42 +01:00
SpawnData const * data = sObjectMgr -> GetSpawnData ( info -> type , info -> spawnId );
ASSERT ( data , "Invalid respawn info with type %u, spawnID " UI64FMTD " in respawn queue." , info -> type , info -> spawnId );
// First, check if this creature's spawn group is inactive
if ( ! IsSpawnGroupActive ( data -> spawnGroupData -> groupId ))
{
info -> respawnTime = 0 ;
return false ;
}
// Next, check if there's already an instance of this object that would block the respawn
2020-12-30 21:08:22 +01:00
bool alreadyExists = false ;
switch ( info -> type )
2017-05-07 21:48:41 +01:00
{
2020-12-30 21:08:22 +01:00
case SPAWN_TYPE_CREATURE :
2017-05-07 21:48:41 +01:00
{
2020-12-30 21:08:22 +01:00
// escort check for creatures only (if the world config boolean is set)
bool const isEscort = ( sWorld -> getBoolConfig ( CONFIG_RESPAWN_DYNAMIC_ESCORTNPC ) && data -> spawnGroupData -> flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC );
2017-05-07 21:48:41 +01:00
2020-12-30 21:08:22 +01:00
auto range = _creatureBySpawnIdStore . equal_range ( info -> spawnId );
for ( auto it = range . first ; it != range . second ; ++ it )
{
Creature * creature = it -> second ;
if ( ! creature -> IsAlive ())
continue ;
// escort NPCs are allowed to respawn as long as all other instances are already escorting
if ( isEscort && creature -> IsEscorted ())
continue ;
alreadyExists = true ;
2017-05-07 21:48:41 +01:00
break ;
}
2020-12-30 21:08:22 +01:00
break ;
2017-05-07 21:48:41 +01:00
}
2020-12-30 21:08:22 +01:00
case SPAWN_TYPE_GAMEOBJECT :
// gameobject check is simpler - they cannot be dead or escorting
if ( _gameobjectBySpawnIdStore . find ( info -> spawnId ) != _gameobjectBySpawnIdStore . end ())
alreadyExists = true ;
break ;
default :
ABORT_MSG ( "Invalid spawn type %u with spawnId " UI64FMTD " on map %u" , uint32 ( info -> type ), info -> spawnId , GetId ());
return true ;
}
if ( alreadyExists )
{
info -> respawnTime = 0 ;
return false ;
2017-05-07 21:48:41 +01:00
}
// next, check linked respawn time
ObjectGuid thisGUID = info -> type == SPAWN_TYPE_GAMEOBJECT
? ObjectGuid :: Create < HighGuid :: GameObject > ( GetId (), info -> entry , info -> spawnId )
: ObjectGuid :: Create < HighGuid :: Creature > ( GetId (), info -> entry , info -> spawnId );
if ( time_t linkedTime = GetLinkedRespawnTime ( thisGUID ))
{
2017-12-10 12:39:37 +01:00
time_t now = GameTime :: GetGameTime ();
2017-05-07 21:48:41 +01:00
time_t respawnTime ;
2017-11-15 00:59:23 +01:00
if ( linkedTime == std :: numeric_limits < time_t >:: max ())
respawnTime = linkedTime ;
else if ( sObjectMgr -> GetLinkedRespawnGuid ( thisGUID ) == thisGUID ) // never respawn, save "something" in DB
2017-05-07 21:48:41 +01:00
respawnTime = now + WEEK ;
else // set us to check again shortly after linked unit
respawnTime = std :: max < time_t > ( now , linkedTime ) + urand ( 5 , 15 );
info -> respawnTime = respawnTime ;
return false ;
}
2018-02-22 20:31:42 +01:00
// everything ok, let's spawn
2017-05-07 21:48:41 +01:00
return true ;
}
2021-01-01 04:03:19 +01:00
void Map :: Respawn ( RespawnInfo * info , CharacterDatabaseTransaction dbTrans )
2017-05-07 21:48:41 +01:00
{
2020-12-31 18:56:53 +01:00
if ( info -> respawnTime <= GameTime :: GetGameTime ())
return ;
2021-01-01 04:03:19 +01:00
info -> respawnTime = GameTime :: GetGameTime ();
2023-01-06 16:54:16 +01:00
_respawnTimes -> increase ( static_cast < RespawnInfoWithHandle *> ( info ) -> handle );
2020-02-08 20:22:37 +01:00
SaveRespawnInfoDB ( * info , dbTrans );
}
2017-05-07 21:48:41 +01:00
2020-02-08 20:22:37 +01:00
size_t Map :: DespawnAll ( SpawnObjectType type , ObjectGuid :: LowType spawnId )
{
std :: vector < WorldObject *> toUnload ;
2017-05-07 21:48:41 +01:00
switch ( type )
{
case SPAWN_TYPE_CREATURE :
2020-02-08 20:22:37 +01:00
for ( auto const & pair : Trinity :: Containers :: MapEqualRange ( GetCreatureBySpawnIdStore (), spawnId ))
toUnload . push_back ( pair . second );
2017-05-07 21:48:41 +01:00
break ;
case SPAWN_TYPE_GAMEOBJECT :
2020-02-08 20:22:37 +01:00
for ( auto const & pair : Trinity :: Containers :: MapEqualRange ( GetGameObjectBySpawnIdStore (), spawnId ))
toUnload . push_back ( pair . second );
2020-07-18 20:43:16 +02:00
break ;
2017-05-07 21:48:41 +01:00
default :
2020-02-08 20:22:37 +01:00
break ;
2017-05-07 21:48:41 +01:00
}
2020-02-08 20:22:37 +01:00
for ( WorldObject * o : toUnload )
AddObjectToRemoveList ( o );
2017-05-07 21:48:41 +01:00
2020-02-08 20:22:37 +01:00
return toUnload . size ();
2017-05-07 21:48:41 +01:00
}
2019-07-15 17:14:58 +02:00
bool Map :: AddRespawnInfo ( RespawnInfo const & info )
2017-05-07 21:48:41 +01:00
{
2020-02-08 20:22:37 +01:00
if ( ! info . spawnId )
{
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "maps" , "Attempt to insert respawn info for zero spawn id (type {})" , uint32 ( info . type ));
2020-02-08 20:22:37 +01:00
return false ;
}
2017-05-07 21:48:41 +01:00
2020-02-08 20:22:37 +01:00
RespawnInfoMap * bySpawnIdMap = GetRespawnMapForType ( info . type );
if ( ! bySpawnIdMap )
return false ;
2017-05-07 21:48:41 +01:00
2020-02-08 20:22:37 +01:00
// check if we already have the maximum possible number of respawns scheduled
if ( SpawnData :: TypeHasData ( info . type ))
2017-05-07 21:48:41 +01:00
{
2020-02-08 20:22:37 +01:00
auto it = bySpawnIdMap -> find ( info . spawnId );
if ( it != bySpawnIdMap -> end ()) // spawnid already has a respawn scheduled
{
RespawnInfo * const existing = it -> second ;
if ( info . respawnTime <= existing -> respawnTime ) // delete existing in this case
DeleteRespawnInfo ( existing );
else
return false ;
}
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 );
2017-05-07 21:48:41 +01:00
}
2020-02-08 20:22:37 +01:00
else
2020-06-26 23:28:33 +02:00
ABORT_MSG ( "Invalid respawn info for spawn id (%u," UI64FMTD ") being inserted ", uint32(info.type), info.spawnId) ;
2017-05-07 21:48:41 +01:00
2023-01-06 16:54:16 +01:00
RespawnInfoWithHandle * ri = new RespawnInfoWithHandle ( info );
ri -> handle = _respawnTimes -> push ( ri );
2020-02-08 20:22:37 +01:00
bySpawnIdMap -> emplace ( ri -> spawnId , ri );
2019-07-15 17:14:58 +02:00
return true ;
2017-05-07 21:48:41 +01:00
}
2020-12-31 18:56:53 +01:00
static void PushRespawnInfoFrom ( std :: vector < RespawnInfo const *>& data , RespawnInfoMap const & map )
2017-05-07 21:48:41 +01:00
{
2019-08-17 22:30:27 +02:00
data . reserve ( data . size () + map . size ());
2017-05-07 21:48:41 +01:00
for ( auto const & pair : map )
2019-08-17 22:30:27 +02:00
data . push_back ( pair . second );
2017-05-07 21:48:41 +01:00
}
2019-08-17 22:30:27 +02:00
2020-12-31 18:56:53 +01:00
void Map :: GetRespawnInfo ( std :: vector < RespawnInfo const *>& respawnData , SpawnObjectTypeMask types ) const
2017-05-07 21:48:41 +01:00
{
if ( types & SPAWN_TYPEMASK_CREATURE )
2019-08-17 22:30:27 +02:00
PushRespawnInfoFrom ( respawnData , _creatureRespawnTimesBySpawnId );
2017-05-07 21:48:41 +01:00
if ( types & SPAWN_TYPEMASK_GAMEOBJECT )
2019-08-17 22:30:27 +02:00
PushRespawnInfoFrom ( respawnData , _gameObjectRespawnTimesBySpawnId );
2017-05-07 21:48:41 +01:00
}
2021-01-01 04:03:19 +01:00
RespawnInfo * Map :: GetRespawnInfo ( SpawnObjectType type , ObjectGuid :: LowType spawnId ) const
2017-05-07 21:48:41 +01:00
{
2020-02-08 20:22:37 +01:00
RespawnInfoMap const * map = GetRespawnMapForType ( type );
if ( ! map )
return nullptr ;
auto it = map -> find ( spawnId );
if ( it == map -> end ())
2017-05-07 21:48:41 +01:00
return nullptr ;
return it -> second ;
}
2019-07-15 17:14:58 +02:00
void Map :: UnloadAllRespawnInfos () // delete everything from memory
2017-05-07 21:48:41 +01:00
{
2023-01-06 16:54:16 +01:00
for ( RespawnInfo * info : * _respawnTimes )
2017-05-07 21:48:41 +01:00
delete info ;
2023-01-06 16:54:16 +01:00
_respawnTimes -> clear ();
2017-05-07 21:48:41 +01:00
_creatureRespawnTimesBySpawnId . clear ();
_gameObjectRespawnTimesBySpawnId . clear ();
}
2019-07-15 17:14:58 +02:00
void Map :: DeleteRespawnInfo ( RespawnInfo * info , CharacterDatabaseTransaction dbTrans )
2017-05-07 21:48:41 +01:00
{
// Delete from all relevant containers to ensure consistency
ASSERT ( info );
// spawnid store
2020-02-08 20:22:37 +01:00
auto spawnMap = GetRespawnMapForType ( info -> type );
if ( ! spawnMap )
return ;
auto range = spawnMap -> equal_range ( info -> spawnId );
auto it = std :: find_if ( range . first , range . second , [ info ]( RespawnInfoMap :: value_type const & pair ) { return ( pair . second == info ); });
ASSERT ( it != range . second , "Respawn stores inconsistent for map %u, spawnid " UI64FMTD " (type %u)" , GetId (), info -> spawnId , uint32 ( info -> type ));
spawnMap -> erase ( it );
2017-05-07 21:48:41 +01:00
2019-07-15 17:14:58 +02:00
// respawn heap
2023-01-06 16:54:16 +01:00
_respawnTimes -> erase ( static_cast < RespawnInfoWithHandle *> ( info ) -> handle );
2017-05-07 21:48:41 +01:00
2019-07-15 17:14:58 +02:00
// database
2020-04-11 12:07:57 +00:00
DeleteRespawnInfoFromDB ( info -> type , info -> spawnId , dbTrans );
// then cleanup the object
delete info ;
}
void Map :: DeleteRespawnInfoFromDB ( SpawnObjectType type , ObjectGuid :: LowType spawnId , CharacterDatabaseTransaction dbTrans )
{
2020-01-02 14:19:35 +01:00
if ( Instanceable ())
return ;
2019-07-14 18:49:38 +02:00
CharacterDatabasePreparedStatement * stmt = CharacterDatabase . GetPreparedStatement ( CHAR_DEL_RESPAWN );
2020-04-11 12:07:57 +00:00
stmt -> setUInt16 ( 0 , type );
stmt -> setUInt64 ( 1 , spawnId );
2019-07-14 18:49:38 +02:00
stmt -> setUInt16 ( 2 , GetId ());
stmt -> setUInt32 ( 3 , GetInstanceId ());
2017-05-07 21:48:41 +01:00
CharacterDatabase . ExecuteOrAppend ( dbTrans , stmt );
}
2020-02-08 20:22:37 +01:00
void Map :: DoRespawn ( SpawnObjectType type , ObjectGuid :: LowType spawnId , uint32 gridId )
{
if ( ! IsGridLoaded ( gridId )) // if grid isn't loaded, this will be processed in grid load handler
return ;
switch ( type )
{
case SPAWN_TYPE_CREATURE :
{
Creature * obj = new Creature ();
if ( ! obj -> LoadFromDB ( spawnId , this , true , true ))
delete obj ;
break ;
}
case SPAWN_TYPE_GAMEOBJECT :
{
GameObject * obj = new GameObject ();
if ( ! obj -> LoadFromDB ( spawnId , this , true ))
delete obj ;
break ;
}
default :
2020-06-26 23:28:33 +02:00
ABORT_MSG ( "Invalid spawn type %u (spawnid " UI64FMTD ") on map %u" , uint32 ( type ), spawnId , GetId ());
2020-02-08 20:22:37 +01:00
}
}
2017-05-07 21:48:41 +01:00
void Map :: ProcessRespawns ()
{
2017-12-10 12:39:37 +01:00
time_t now = GameTime :: GetGameTime ();
2023-01-06 16:54:16 +01:00
while ( ! _respawnTimes -> empty ())
2017-05-07 21:48:41 +01:00
{
2023-01-06 16:54:16 +01:00
RespawnInfoWithHandle * next = _respawnTimes -> top ();
2017-05-07 21:48:41 +01:00
if ( now < next -> respawnTime ) // done for this tick
break ;
2020-12-30 21:08:22 +01:00
if ( uint32 poolId = sPoolMgr -> IsPartOfAPool ( next -> type , next -> spawnId )) // is this part of a pool?
{ // if yes, respawn will be handled by (external) pooling logic, just delete the respawn time
// step 1: remove entry from maps to avoid it being reachable by outside logic
2023-01-06 16:54:16 +01:00
_respawnTimes -> pop ();
2020-12-30 21:08:22 +01:00
ASSERT_NOTNULL ( GetRespawnMapForType ( next -> type )) -> erase ( next -> spawnId );
// step 2: tell pooling logic to do its thing
2022-06-11 14:04:56 +02:00
sPoolMgr -> UpdatePool ( GetPoolData (), poolId , next -> type , next -> spawnId );
2020-12-30 21:08:22 +01:00
// step 3: get rid of the actual entry
2021-06-26 14:21:18 +02:00
RemoveRespawnTime ( next -> type , next -> spawnId , nullptr , true );
2020-12-30 21:08:22 +01:00
delete next ;
}
else if ( CheckRespawn ( next )) // see if we're allowed to respawn
{ // ok, respawn
// step 1: remove entry from maps to avoid it being reachable by outside logic
2023-01-06 16:54:16 +01:00
_respawnTimes -> pop ();
2020-02-08 20:22:37 +01:00
ASSERT_NOTNULL ( GetRespawnMapForType ( next -> type )) -> erase ( next -> spawnId );
2020-12-30 21:08:22 +01:00
// step 2: do the respawn, which involves external logic
2017-05-07 21:48:41 +01:00
DoRespawn ( next -> type , next -> spawnId , next -> gridId );
2020-12-30 21:08:22 +01:00
// step 3: get rid of the actual entry
2021-06-26 14:21:18 +02:00
RemoveRespawnTime ( next -> type , next -> spawnId , nullptr , true );
2017-05-07 21:48:41 +01:00
delete next ;
}
2020-12-30 21:08:22 +01:00
else if ( ! next -> respawnTime )
{ // just remove this respawn entry without rescheduling
2023-01-06 16:54:16 +01:00
_respawnTimes -> pop ();
2020-02-08 20:22:37 +01:00
ASSERT_NOTNULL ( GetRespawnMapForType ( next -> type )) -> erase ( next -> spawnId );
2021-06-26 14:21:18 +02:00
RemoveRespawnTime ( next -> type , next -> spawnId , nullptr , true );
2017-05-07 21:48:41 +01:00
delete next ;
}
2020-12-30 21:08:22 +01:00
else
{ // new respawn time, update heap position
2017-05-07 21:48:41 +01:00
ASSERT ( now < next -> respawnTime ); // infinite loop guard
2023-01-06 16:54:16 +01:00
_respawnTimes -> decrease ( next -> handle );
2019-07-15 17:14:58 +02:00
SaveRespawnInfoDB ( * next );
2017-05-07 21:48:41 +01:00
}
}
}
void Map :: ApplyDynamicModeRespawnScaling ( WorldObject const * obj , ObjectGuid :: LowType spawnId , uint32 & respawnDelay , uint32 mode ) const
{
ASSERT ( mode == 1 );
ASSERT ( obj -> GetMap () == this );
2017-10-05 00:39:43 +01:00
if ( IsBattlegroundOrArena ())
return ;
2017-05-07 21:48:41 +01:00
SpawnObjectType type ;
switch ( obj -> GetTypeId ())
{
case TYPEID_UNIT :
type = SPAWN_TYPE_CREATURE ;
break ;
case TYPEID_GAMEOBJECT :
type = SPAWN_TYPE_GAMEOBJECT ;
break ;
default :
return ;
}
2020-02-08 20:22:37 +01:00
SpawnMetadata const * data = sObjectMgr -> GetSpawnMetadata ( type , spawnId );
if ( ! data )
return ;
if ( ! ( data -> spawnGroupData -> flags & SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE ))
2017-05-07 21:48:41 +01:00
return ;
auto it = _zonePlayerCountMap . find ( obj -> GetZoneId ());
if ( it == _zonePlayerCountMap . end ())
return ;
uint32 const playerCount = it -> second ;
if ( ! playerCount )
return ;
double const adjustFactor = sWorld -> getFloatConfig ( type == SPAWN_TYPE_GAMEOBJECT ? CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT : CONFIG_RESPAWN_DYNAMICRATE_CREATURE ) / playerCount ;
if ( adjustFactor >= 1.0 ) // nothing to do here
return ;
uint32 const timeMinimum = sWorld -> getIntConfig ( type == SPAWN_TYPE_GAMEOBJECT ? CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT : CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE );
if ( respawnDelay <= timeMinimum )
return ;
respawnDelay = std :: max < uint32 > ( ceil ( respawnDelay * adjustFactor ), timeMinimum );
}
2020-02-08 20:22:37 +01:00
bool Map :: ShouldBeSpawnedOnGridLoad ( SpawnObjectType type , ObjectGuid :: LowType spawnId ) const
{
ASSERT ( SpawnData :: TypeHasData ( type ));
// check if the object is on its respawn timer
if ( GetRespawnTime ( type , spawnId ))
return false ;
SpawnMetadata const * spawnData = ASSERT_NOTNULL ( sObjectMgr -> GetSpawnMetadata ( type , spawnId ));
// check if the object is part of a spawn group
SpawnGroupTemplateData const * spawnGroup = ASSERT_NOTNULL ( spawnData -> spawnGroupData );
if ( ! ( spawnGroup -> flags & SPAWNGROUP_FLAG_SYSTEM ))
if ( ! IsSpawnGroupActive ( spawnGroup -> groupId ))
return false ;
2022-06-11 14:04:56 +02:00
if ( spawnData -> ToSpawnData () -> poolId )
if ( ! GetPoolData (). IsSpawnedObject ( type , spawnId ))
return false ;
2020-02-08 20:22:37 +01:00
return true ;
}
2017-08-26 13:14:25 +02:00
SpawnGroupTemplateData const * Map :: GetSpawnGroupData ( uint32 groupId ) const
{
SpawnGroupTemplateData const * data = sObjectMgr -> GetSpawnGroupData ( groupId );
2018-01-04 04:46:22 +01:00
if ( data && ( data -> flags & SPAWNGROUP_FLAG_SYSTEM || data -> mapId == GetId ()))
2017-08-26 13:14:25 +02:00
return data ;
return nullptr ;
}
bool Map :: SpawnGroupSpawn ( uint32 groupId , bool ignoreRespawn , bool force , std :: vector < WorldObject *>* spawnedObjects )
{
SpawnGroupTemplateData const * groupData = GetSpawnGroupData ( groupId );
if ( ! groupData || groupData -> flags & SPAWNGROUP_FLAG_SYSTEM )
{
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "maps" , "Tried to spawn non-existing (or system) spawn group {} on map {}. Blocked." , groupId , GetId ());
2017-08-26 13:14:25 +02:00
return false ;
}
2018-08-29 18:07:31 +02:00
SetSpawnGroupActive ( groupId , true ); // start processing respawns for the group
2017-08-26 13:14:25 +02:00
2020-02-08 20:22:37 +01:00
std :: vector < SpawnData const *> toSpawn ;
for ( auto & pair : sObjectMgr -> GetSpawnMetadataForGroup ( groupId ))
{
SpawnMetadata const * data = pair . second ;
ASSERT ( groupData -> mapId == data -> mapId );
auto respawnMap = GetRespawnMapForType ( data -> type );
if ( ! respawnMap )
continue ;
if ( force || ignoreRespawn )
RemoveRespawnTime ( data -> type , data -> spawnId );
uint32 nRespawnTimers = respawnMap -> count ( data -> spawnId );
if ( SpawnData :: TypeHasData ( data -> type ))
2017-08-26 13:14:25 +02:00
{
2020-02-08 20:22:37 +01:00
// has a respawn timer
if ( nRespawnTimers )
2017-08-26 13:14:25 +02:00
continue ;
2020-02-08 20:22:37 +01:00
// has a spawn already active
if ( ! force )
if ( WorldObject * obj = GetWorldObjectBySpawnId ( data -> type , data -> spawnId ))
if (( data -> type != SPAWN_TYPE_CREATURE ) || obj -> ToCreature () -> IsAlive ())
continue ;
toSpawn . push_back ( ASSERT_NOTNULL ( data -> ToSpawnData ()));
2017-08-26 13:14:25 +02:00
}
2020-02-08 20:22:37 +01:00
}
2017-08-26 13:14:25 +02:00
2020-02-08 20:22:37 +01:00
for ( SpawnData const * data : toSpawn )
{
2021-07-24 14:49:21 +02:00
// don't spawn if the current map difficulty is not used by the spawn
if ( std :: find ( data -> spawnDifficulties . begin (), data -> spawnDifficulties . end (), GetDifficultyID ()) == data -> spawnDifficulties . end ())
continue ;
2017-08-26 13:14:25 +02:00
// don't spawn if the grid isn't loaded (will be handled in grid loader)
if ( ! IsGridLoaded ( data -> spawnPoint ))
continue ;
2020-02-08 20:22:37 +01:00
// now do the actual (re)spawn
2017-08-26 13:14:25 +02:00
switch ( data -> type )
{
case SPAWN_TYPE_CREATURE :
{
Creature * creature = new Creature ();
if ( ! creature -> LoadFromDB ( data -> spawnId , this , true , force ))
delete creature ;
else if ( spawnedObjects )
spawnedObjects -> push_back ( creature );
break ;
}
case SPAWN_TYPE_GAMEOBJECT :
{
GameObject * gameobject = new GameObject ();
if ( ! gameobject -> LoadFromDB ( data -> spawnId , this , true ))
delete gameobject ;
else if ( spawnedObjects )
spawnedObjects -> push_back ( gameobject );
break ;
}
2020-02-08 20:22:37 +01:00
case SPAWN_TYPE_AREATRIGGER :
{
AreaTrigger * areaTrigger = new AreaTrigger ();
if ( ! areaTrigger -> LoadFromDB ( data -> spawnId , this , true , false ))
delete areaTrigger ;
else if ( spawnedObjects )
spawnedObjects -> push_back ( areaTrigger );
break ;
}
2017-08-26 13:14:25 +02:00
default :
2020-06-26 23:28:33 +02:00
ABORT_MSG ( "Invalid spawn type %u with spawnId " UI64FMTD , uint32 ( data -> type ), data -> spawnId );
2017-08-26 13:14:25 +02:00
return false ;
}
}
return true ;
}
2018-01-25 13:45:11 +01:00
bool Map :: SpawnGroupDespawn ( uint32 groupId , bool deleteRespawnTimes , size_t * count )
2017-08-26 13:14:25 +02:00
{
SpawnGroupTemplateData const * groupData = GetSpawnGroupData ( groupId );
if ( ! groupData || groupData -> flags & SPAWNGROUP_FLAG_SYSTEM )
{
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "maps" , "Tried to despawn non-existing (or system) spawn group {} on map {}. Blocked." , groupId , GetId ());
2017-08-26 13:14:25 +02:00
return false ;
}
2020-02-08 20:22:37 +01:00
for ( auto const & pair : sObjectMgr -> GetSpawnMetadataForGroup ( groupId ))
2017-08-26 13:14:25 +02:00
{
2020-02-08 20:22:37 +01:00
SpawnMetadata const * data = pair . second ;
ASSERT ( groupData -> mapId == data -> mapId );
2017-08-26 13:14:25 +02:00
if ( deleteRespawnTimes )
RemoveRespawnTime ( data -> type , data -> spawnId );
2020-02-08 20:22:37 +01:00
size_t c = DespawnAll ( data -> type , data -> spawnId );
if ( count )
* count += c ;
2017-08-26 13:14:25 +02:00
}
SetSpawnGroupActive ( groupId , false ); // stop processing respawns for the group, too
return true ;
}
void Map :: SetSpawnGroupActive ( uint32 groupId , bool state )
{
SpawnGroupTemplateData const * const data = GetSpawnGroupData ( groupId );
if ( ! data || data -> flags & SPAWNGROUP_FLAG_SYSTEM )
{
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "maps" , "Tried to set non-existing (or system) spawn group {} to {} on map {}. Blocked." , groupId , state ? "active" : "inactive" , GetId ());
2017-08-26 13:14:25 +02:00
return ;
}
if ( state != ! ( data -> flags & SPAWNGROUP_FLAG_MANUAL_SPAWN )) // toggled
_toggledSpawnGroupIds . insert ( groupId );
else
_toggledSpawnGroupIds . erase ( groupId );
}
bool Map :: IsSpawnGroupActive ( uint32 groupId ) const
{
SpawnGroupTemplateData const * const data = GetSpawnGroupData ( groupId );
if ( ! data )
{
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "maps" , "Tried to query state of non-existing spawn group {} on map {}." , groupId , GetId ());
2017-08-26 13:14:25 +02:00
return false ;
}
if ( data -> flags & SPAWNGROUP_FLAG_SYSTEM )
return true ;
2018-01-04 04:46:22 +01:00
// either manual spawn group and toggled, or not manual spawn group and not toggled...
2017-08-26 13:14:25 +02:00
return ( _toggledSpawnGroupIds . find ( groupId ) != _toggledSpawnGroupIds . end ()) != ! ( data -> flags & SPAWNGROUP_FLAG_MANUAL_SPAWN );
}
2023-12-29 14:02:37 +01:00
void Map :: InitSpawnGroupState ()
{
std :: vector < uint32 > const * spawnGroups = sObjectMgr -> GetSpawnGroupsForMap ( GetId ());
if ( ! spawnGroups )
return ;
for ( uint32 spawnGroupId : * spawnGroups )
2023-12-29 20:41:25 +01:00
{
SpawnGroupTemplateData const * spawnGroupTemplate = ASSERT_NOTNULL ( GetSpawnGroupData ( spawnGroupId ));
if ( spawnGroupTemplate -> flags & SPAWNGROUP_FLAG_SYSTEM )
continue ;
2023-12-29 14:02:37 +01:00
SetSpawnGroupActive ( spawnGroupId , sConditionMgr -> IsMapMeetingNotGroupedConditions ( CONDITION_SOURCE_TYPE_SPAWN_GROUP , spawnGroupId , this ));
2023-12-29 20:41:25 +01:00
}
2023-12-29 14:02:37 +01:00
}
2022-06-24 22:20:34 +02:00
void Map :: UpdateSpawnGroupConditions ()
{
std :: vector < uint32 > const * spawnGroups = sObjectMgr -> GetSpawnGroupsForMap ( GetId ());
if ( ! spawnGroups )
return ;
for ( uint32 spawnGroupId : * spawnGroups )
{
2022-10-02 18:51:37 +02:00
SpawnGroupTemplateData const * spawnGroupTemplate = ASSERT_NOTNULL ( GetSpawnGroupData ( spawnGroupId ));
2022-06-24 22:20:34 +02:00
bool isActive = IsSpawnGroupActive ( spawnGroupId );
bool shouldBeActive = sConditionMgr -> IsMapMeetingNotGroupedConditions ( CONDITION_SOURCE_TYPE_SPAWN_GROUP , spawnGroupId , this );
2023-01-29 19:50:34 +01:00
if ( spawnGroupTemplate -> flags & SPAWNGROUP_FLAG_MANUAL_SPAWN )
{
// Only despawn the group if it isn't meeting conditions
if ( isActive && ! shouldBeActive && spawnGroupTemplate -> flags & SPAWNGROUP_FLAG_DESPAWN_ON_CONDITION_FAILURE )
SpawnGroupDespawn ( spawnGroupId , true );
continue ;
}
2022-06-24 22:20:34 +02:00
if ( isActive == shouldBeActive )
continue ;
if ( shouldBeActive )
SpawnGroupSpawn ( spawnGroupId );
2022-10-02 18:51:37 +02:00
else if ( spawnGroupTemplate -> flags & SPAWNGROUP_FLAG_DESPAWN_ON_CONDITION_FAILURE )
2022-07-18 20:21:58 +02:00
SpawnGroupDespawn ( spawnGroupId , true );
2022-06-24 22:20:34 +02:00
else
SetSpawnGroupInactive ( spawnGroupId );
}
}
2023-01-07 22:38:21 +01:00
ObjectGuidGenerator & Map :: GetGuidSequenceGenerator ( HighGuid high )
{
auto itr = _guidGenerators . find ( high );
if ( itr == _guidGenerators . end ())
itr = _guidGenerators . insert ( std :: make_pair ( high , std :: make_unique < ObjectGuidGenerator > ( high ))). first ;
return * itr -> second ;
}
2018-02-01 13:10:51 -03:00
void Map :: AddFarSpellCallback ( FarSpellCallback && callback )
{
_farSpellCallbacks . Enqueue ( new FarSpellCallback ( std :: move ( callback )));
}
2017-05-07 21:48:41 +01:00
void Map :: DelayedUpdate ( uint32 t_diff )
2009-08-25 15:12:22 -05:00
{
2018-02-01 13:10:51 -03:00
{
FarSpellCallback * callback ;
while ( _farSpellCallbacks . Dequeue ( callback ))
{
( * callback )( this );
delete callback ;
}
}
2009-08-25 15:12:22 -05:00
RemoveAllObjectsInRemoveList ();
2009-10-17 15:51:44 -07:00
2011-04-29 20:47:02 +02:00
// 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 !
2009-08-25 15:12:22 -05:00
// This isn't really bother us, since as soon as we have instanced BG-s, the whole map unloads as the BG gets ended
2010-08-08 04:37:24 +02:00
if ( ! IsBattlegroundOrArena ())
2009-08-25 15:12:22 -05:00
{
2009-11-20 19:11:28 -08:00
for ( GridRefManager < NGridType >:: iterator i = GridRefManager < NGridType >:: begin (); i != GridRefManager < NGridType >:: end ();)
2009-08-25 15:12:22 -05:00
{
2013-06-11 21:25:12 -02:30
NGridType * grid = i -> GetSource ();
GridInfo * info = i -> GetSource () -> getGridInfoRef ();
2009-08-25 15:12:22 -05:00
++ i ; // The update might delete the map and we need the next map before the iterator gets invalid
2010-07-30 20:04:28 +02:00
ASSERT ( grid -> GetGridState () >= 0 && grid -> GetGridState () < MAX_GRID_STATE );
2011-10-22 10:01:29 -04:00
si_GridStates [ grid -> GetGridState ()] -> Update ( * this , * grid , * info , t_diff );
2009-08-25 15:12:22 -05:00
}
}
}
2009-10-17 15:51:44 -07:00
2011-09-15 14:08:17 +02:00
void Map :: AddObjectToRemoveList ( WorldObject * obj )
2008-10-02 16:23:55 -05:00
{
2010-07-30 20:04:28 +02:00
ASSERT ( obj -> GetMapId () == GetId () && obj -> GetInstanceId () == GetInstanceId ());
2009-10-17 15:51:44 -07:00
2022-01-29 16:01:57 +01:00
obj -> SetDestroyedObject ( true );
2010-01-22 20:03:47 +01:00
obj -> CleanupsBeforeDelete ( false ); // remove or simplify at least cross referenced links
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
i_objectsToRemove . insert ( obj );
}
2009-10-17 15:51:44 -07:00
2011-09-15 14:08:17 +02:00
void Map :: AddObjectToSwitchList ( WorldObject * obj , bool on )
2009-02-26 17:01:02 -06:00
{
2010-07-30 20:04:28 +02:00
ASSERT ( obj -> GetMapId () == GetId () && obj -> GetInstanceId () == GetInstanceId ());
2013-08-26 16:42:25 +02:00
// i_objectsToSwitch is iterated only in Map::RemoveAllObjectsInRemoveList() and it uses
// the contained objects only if GetTypeId() == TYPEID_UNIT , so we can return in all other cases
2021-03-06 22:17:12 +01:00
if ( obj -> GetTypeId () != TYPEID_UNIT )
2013-08-26 16:42:25 +02:00
return ;
2009-10-17 15:51:44 -07:00
2009-02-26 17:01:02 -06:00
std :: map < WorldObject * , bool >:: iterator itr = i_objectsToSwitch . find ( obj );
2010-04-07 19:14:10 +02:00
if ( itr == i_objectsToSwitch . end ())
2009-02-26 17:01:02 -06:00
i_objectsToSwitch . insert ( itr , std :: make_pair ( obj , on ));
2010-04-07 19:14:10 +02:00
else if ( itr -> second != on )
2009-02-26 17:01:02 -06:00
i_objectsToSwitch . erase ( itr );
else
2015-09-21 15:17:05 +02:00
ABORT ();
2009-02-26 17:01:02 -06:00
}
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
void Map :: RemoveAllObjectsInRemoveList ()
{
2010-04-07 19:13:19 +02:00
while ( ! i_objectsToSwitch . empty ())
2009-02-26 17:01:02 -06:00
{
std :: map < WorldObject * , bool >:: iterator itr = i_objectsToSwitch . begin ();
2011-09-15 14:08:17 +02:00
WorldObject * obj = itr -> first ;
2009-02-26 17:01:02 -06:00
bool on = itr -> second ;
i_objectsToSwitch . erase ( itr );
2009-10-17 15:51:44 -07:00
2024-03-14 11:01:58 +01:00
if ( ! obj -> IsAlwaysStoredInWorldObjectGridContainer ())
2013-11-02 13:22:14 +01:00
{
switch ( obj -> GetTypeId ())
{
case TYPEID_UNIT :
SwitchGridContainers < Creature > ( obj -> ToCreature (), on );
break ;
default :
break ;
}
}
2009-02-26 17:01:02 -06:00
}
2009-10-17 15:51:44 -07:00
2013-11-08 10:50:51 +01:00
//TC_LOG_DEBUG("maps", "Object remover 1 check.");
2010-04-07 19:13:19 +02:00
while ( ! i_objectsToRemove . empty ())
2008-10-02 16:23:55 -05:00
{
2009-03-24 17:55:05 -06:00
std :: set < WorldObject *>:: iterator itr = i_objectsToRemove . begin ();
WorldObject * obj = * itr ;
2009-10-17 15:51:44 -07:00
2011-09-29 12:43:05 +02:00
switch ( obj -> GetTypeId ())
2008-10-02 16:23:55 -05:00
{
case TYPEID_CORPSE :
{
2010-12-23 23:25:44 +01:00
Corpse * corpse = ObjectAccessor :: GetCorpse ( * obj , obj -> GetGUID ());
2008-10-02 16:23:55 -05:00
if ( ! corpse )
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "maps" , "Tried to delete corpse/bones {} that is not in map." , obj -> GetGUID (). ToString ());
2008-10-02 16:23:55 -05:00
else
2011-10-10 17:39:34 -04:00
RemoveFromMap ( corpse , true );
2008-10-02 16:23:55 -05:00
break ;
}
2014-04-28 22:26:43 +02:00
case TYPEID_DYNAMICOBJECT :
RemoveFromMap ( obj -> ToDynObject (), true );
break ;
2014-05-11 23:08:54 +02:00
case TYPEID_AREATRIGGER :
RemoveFromMap (( AreaTrigger * ) obj , true );
break ;
2017-05-14 11:40:55 +02:00
case TYPEID_CONVERSATION :
RemoveFromMap (( Conversation * ) obj , true );
break ;
2014-04-28 22:26:43 +02:00
case TYPEID_GAMEOBJECT :
{
GameObject * go = obj -> ToGameObject ();
if ( Transport * transport = go -> ToTransport ())
RemoveFromMap ( transport , true );
else
RemoveFromMap ( go , true );
break ;
}
case TYPEID_UNIT :
// in case triggered sequence some spell can continue casting after prev CleanupsBeforeDelete call
// make sure that like sources auras/etc removed before destructor start
obj -> ToCreature () -> CleanupsBeforeDelete ();
RemoveFromMap ( obj -> ToCreature (), true );
break ;
default :
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "maps" , "Non-grid object (TypeId: {}) is in grid object remove list, ignored." , obj -> GetTypeId ());
2014-04-28 22:26:43 +02:00
break ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2009-03-24 17:55:05 -06:00
i_objectsToRemove . erase ( itr );
2008-10-02 16:23:55 -05:00
}
2009-12-17 11:30:18 +01:00
2013-11-08 10:50:51 +01:00
//TC_LOG_DEBUG("maps", "Object remover 2 check.");
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2008-11-18 19:40:06 -06:00
uint32 Map :: GetPlayersCountExceptGMs () const
{
uint32 count = 0 ;
2009-10-17 16:20:24 -07:00
for ( MapRefManager :: const_iterator itr = m_mapRefManager . begin (); itr != m_mapRefManager . end (); ++ itr )
2013-06-11 21:25:12 -02:30
if ( ! itr -> GetSource () -> IsGameMaster ())
2008-11-18 19:40:06 -06:00
++ count ;
return count ;
}
2009-10-17 15:51:44 -07:00
2015-01-28 13:58:40 +00:00
void Map :: SendToPlayers ( WorldPacket const * data ) const
2008-11-18 19:40:06 -06:00
{
2009-10-17 16:20:24 -07:00
for ( MapRefManager :: const_iterator itr = m_mapRefManager . begin (); itr != m_mapRefManager . end (); ++ itr )
2020-09-04 13:38:24 +02:00
itr -> GetSource () -> SendDirectMessage ( data );
2008-11-18 19:40:06 -06:00
}
2009-10-17 15:51:44 -07:00
2011-10-22 10:01:29 -04:00
bool Map :: ActiveObjectsNearGrid ( NGridType const & ngrid ) const
2008-11-18 19:40:06 -06:00
{
2011-10-22 10:01:29 -04:00
CellCoord cell_min ( ngrid . getX () * MAX_NUMBER_OF_CELLS , ngrid . getY () * MAX_NUMBER_OF_CELLS );
2011-10-18 10:53:34 -04:00
CellCoord cell_max ( cell_min . x_coord + MAX_NUMBER_OF_CELLS , cell_min . y_coord + MAX_NUMBER_OF_CELLS );
2009-10-17 16:20:24 -07:00
2009-09-27 02:24:25 -07:00
//we must find visible range in cells so we unload only non-visible cells...
2010-11-13 17:18:09 +01:00
float viewDist = GetVisibilityRange ();
2009-09-27 02:24:25 -07:00
int cell_range = ( int ) ceilf ( viewDist / SIZE_OF_GRID_CELL ) + 1 ;
2009-10-17 16:20:24 -07:00
2011-10-18 11:42:40 +02:00
cell_min . dec_x ( cell_range );
cell_min . dec_y ( cell_range );
cell_max . inc_x ( cell_range );
cell_max . inc_y ( cell_range );
2009-10-17 15:51:44 -07:00
2009-10-17 16:20:24 -07:00
for ( MapRefManager :: const_iterator iter = m_mapRefManager . begin (); iter != m_mapRefManager . end (); ++ iter )
2008-11-18 19:40:06 -06:00
{
2013-06-11 21:25:12 -02:30
Player * player = iter -> GetSource ();
2009-10-17 15:51:44 -07:00
2011-11-07 11:06:39 -06:00
CellCoord p = Trinity :: ComputeCellCoord ( player -> GetPositionX (), player -> GetPositionY ());
2009-11-22 19:56:48 -08:00
if (( cell_min . x_coord <= p . x_coord && p . x_coord <= cell_max . x_coord ) &&
( cell_min . y_coord <= p . y_coord && p . y_coord <= cell_max . y_coord ))
2008-11-18 19:40:06 -06:00
return true ;
}
2009-10-17 15:51:44 -07:00
2009-10-17 16:20:24 -07:00
for ( ActiveNonPlayers :: const_iterator iter = m_activeNonPlayers . begin (); iter != m_activeNonPlayers . end (); ++ iter )
2008-11-21 15:41:18 -06:00
{
2009-02-25 20:52:20 -06:00
WorldObject * obj = * iter ;
2009-10-17 15:51:44 -07:00
2011-10-18 10:53:34 -04:00
CellCoord p = Trinity :: ComputeCellCoord ( obj -> GetPositionX (), obj -> GetPositionY ());
2009-11-22 19:56:48 -08:00
if (( cell_min . x_coord <= p . x_coord && p . x_coord <= cell_max . x_coord ) &&
( cell_min . y_coord <= p . y_coord && p . y_coord <= cell_max . y_coord ))
2008-11-21 15:41:18 -06:00
return true ;
}
2009-10-17 15:51:44 -07:00
2009-02-25 20:52:20 -06:00
return false ;
}
2009-10-17 15:51:44 -07:00
2022-05-25 22:07:47 +02:00
void Map :: AddToActive ( WorldObject * obj )
2013-10-16 18:37:29 +02:00
{
AddToActiveHelper ( obj );
2022-05-25 22:07:47 +02:00
Optional < Position > respawnLocation ;
switch ( obj -> GetTypeId ())
{
case TYPEID_UNIT :
if ( Creature * creature = obj -> ToCreature (); ! creature -> IsPet () && creature -> GetSpawnId ())
{
respawnLocation . emplace ();
creature -> GetRespawnPosition ( respawnLocation -> m_positionX , respawnLocation -> m_positionY , respawnLocation -> m_positionZ );
}
break ;
case TYPEID_GAMEOBJECT :
if ( GameObject * gameObject = obj -> ToGameObject (); gameObject -> GetSpawnId ())
{
respawnLocation . emplace ();
gameObject -> GetRespawnPosition ( respawnLocation -> m_positionX , respawnLocation -> m_positionY , respawnLocation -> m_positionZ );
}
break ;
default :
break ;
}
2009-10-17 15:51:44 -07:00
2022-05-25 22:07:47 +02:00
if ( respawnLocation )
2008-11-21 15:41:18 -06:00
{
2022-05-25 22:07:47 +02:00
GridCoord p = Trinity :: ComputeGridCoord ( respawnLocation -> GetPositionX (), respawnLocation -> GetPositionY ());
2010-04-07 19:14:10 +02:00
if ( getNGrid ( p . x_coord , p . y_coord ))
2009-02-25 20:52:20 -06:00
getNGrid ( p . x_coord , p . y_coord ) -> incUnloadActiveLock ();
else
{
2022-05-25 22:07:47 +02:00
GridCoord p2 = Trinity :: ComputeGridCoord ( obj -> GetPositionX (), obj -> GetPositionY ());
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "maps" , "Active object {} added to grid[{}, {}] but spawn grid[{}, {}] was not loaded." ,
obj -> GetGUID (). ToString (), p . x_coord , p . y_coord , p2 . x_coord , p2 . y_coord );
2009-02-25 20:52:20 -06:00
}
2008-11-21 15:41:18 -06:00
}
2009-02-25 20:52:20 -06:00
}
2009-10-17 15:51:44 -07:00
2022-05-25 22:07:47 +02:00
void Map :: RemoveFromActive ( WorldObject * obj )
2013-10-17 00:55:50 +02:00
{
2022-05-25 22:07:47 +02:00
RemoveFromActiveHelper ( obj );
2013-10-16 18:37:29 +02:00
2022-05-25 22:07:47 +02:00
Optional < Position > respawnLocation ;
switch ( obj -> GetTypeId ())
{
case TYPEID_UNIT :
if ( Creature * creature = obj -> ToCreature (); ! creature -> IsPet () && creature -> GetSpawnId ())
{
respawnLocation . emplace ();
creature -> GetRespawnPosition ( respawnLocation -> m_positionX , respawnLocation -> m_positionY , respawnLocation -> m_positionZ );
}
break ;
case TYPEID_GAMEOBJECT :
if ( GameObject * gameObject = obj -> ToGameObject (); gameObject -> GetSpawnId ())
{
respawnLocation . emplace ();
gameObject -> GetRespawnPosition ( respawnLocation -> m_positionX , respawnLocation -> m_positionY , respawnLocation -> m_positionZ );
}
break ;
default :
break ;
}
2009-10-17 15:51:44 -07:00
2022-05-25 22:07:47 +02:00
if ( respawnLocation )
2009-02-25 20:52:20 -06:00
{
2022-05-25 22:07:47 +02:00
GridCoord p = Trinity :: ComputeGridCoord ( respawnLocation -> GetPositionX (), respawnLocation -> GetPositionY ());
2010-04-07 19:14:10 +02:00
if ( getNGrid ( p . x_coord , p . y_coord ))
2009-02-25 20:52:20 -06:00
getNGrid ( p . x_coord , p . y_coord ) -> decUnloadActiveLock ();
else
{
2022-05-25 22:07:47 +02:00
GridCoord p2 = Trinity :: ComputeGridCoord ( obj -> GetPositionX (), obj -> GetPositionY ());
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "maps" , "Active object {} removed from to grid[{}, {}] but spawn grid[{}, {}] was not loaded." ,
obj -> GetGUID (). ToString (), p . x_coord , p . y_coord , p2 . x_coord , p2 . y_coord );
2009-02-25 20:52:20 -06:00
}
}
2008-11-21 15:41:18 -06:00
}
2009-10-17 15:51:44 -07:00
2016-03-11 16:37:14 +01:00
template TC_GAME_API bool Map :: AddToMap ( Corpse * );
template TC_GAME_API bool Map :: AddToMap ( Creature * );
template TC_GAME_API bool Map :: AddToMap ( GameObject * );
template TC_GAME_API bool Map :: AddToMap ( DynamicObject * );
template TC_GAME_API bool Map :: AddToMap ( AreaTrigger * );
2021-10-11 12:59:19 +02:00
template TC_GAME_API bool Map :: AddToMap ( SceneObject * );
2017-05-14 11:40:55 +02:00
template TC_GAME_API bool Map :: AddToMap ( Conversation * );
2016-03-11 16:37:14 +01:00
template TC_GAME_API void Map :: RemoveFromMap ( Corpse * , bool );
template TC_GAME_API void Map :: RemoveFromMap ( Creature * , bool );
template TC_GAME_API void Map :: RemoveFromMap ( GameObject * , bool );
template TC_GAME_API void Map :: RemoveFromMap ( DynamicObject * , bool );
template TC_GAME_API void Map :: RemoveFromMap ( AreaTrigger * , bool );
2021-10-11 12:59:19 +02:00
template TC_GAME_API void Map :: RemoveFromMap ( SceneObject * , bool );
2017-05-14 11:40:55 +02:00
template TC_GAME_API void Map :: RemoveFromMap ( Conversation * , bool );
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
/* ******* Dungeon Instance Maps ******* */
2009-10-17 15:51:44 -07:00
2018-07-22 13:22:35 +02:00
InstanceMap :: InstanceMap ( uint32 id , time_t expiry , uint32 InstanceId , Difficulty SpawnMode , TeamId InstanceTeam , InstanceLock * instanceLock )
2022-07-24 18:56:40 +02:00
: Map ( id , expiry , InstanceId , SpawnMode ),
2018-07-22 13:22:35 +02:00
i_data ( nullptr ), i_script_id ( 0 ), i_scenario ( nullptr ), i_instanceLock ( instanceLock )
2008-10-02 16:23:55 -05:00
{
2009-09-27 02:24:25 -07:00
//lets initialize visibility distance for dungeons
InstanceMap :: InitVisibilityDistance ();
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
// the timer is started by default, and stopped when the first player joins
// this make sure it gets unloaded if for some reason no player joins
2010-12-23 23:25:44 +01:00
m_unloadTimer = std :: max ( sWorld -> getIntConfig ( CONFIG_INSTANCE_UNLOAD_DELAY ), ( uint32 ) MIN_UNLOAD_DELAY );
2022-07-24 20:11:24 +02:00
sWorldStateMgr -> SetValue ( WS_TEAM_IN_INSTANCE_ALLIANCE , InstanceTeam == TEAM_ALLIANCE , false , this );
sWorldStateMgr -> SetValue ( WS_TEAM_IN_INSTANCE_HORDE , InstanceTeam == TEAM_HORDE , false , this );
2021-01-19 22:48:46 +01:00
if ( i_instanceLock )
2022-08-15 18:39:58 +02:00
{
2021-01-19 22:48:46 +01:00
i_instanceLock -> SetInUse ( true );
2022-08-15 18:39:58 +02:00
i_instanceExpireEvent = i_instanceLock -> GetExpiryTime (); // ignore extension state for reset event (will ask players to accept extended save on expiration)
}
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
InstanceMap ::~ InstanceMap ()
{
2021-01-19 22:48:46 +01:00
if ( i_instanceLock )
i_instanceLock -> SetInUse ( false );
2010-06-21 23:20:58 +02:00
delete i_data ;
2016-11-06 23:30:49 +01:00
delete i_scenario ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2009-09-27 02:24:25 -07:00
void InstanceMap :: InitVisibilityDistance ()
{
//init visibility distance for instances
2009-11-22 19:56:48 -08:00
m_VisibleDistance = World :: GetMaxVisibleDistanceInInstances ();
2010-02-27 15:25:14 +02:00
m_VisibilityNotifyPeriod = World :: GetVisibilityNotifyPeriodInInstances ();
2009-09-27 02:24:25 -07:00
}
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
/*
Do map specific checks to see if the player can enter
*/
2022-10-08 19:11:34 +02:00
TransferAbortParams InstanceMap :: CannotEnter ( Player * player )
2008-10-02 16:23:55 -05:00
{
2010-04-07 19:14:10 +02:00
if ( player -> GetMapRef (). getTarget () == this )
2008-10-02 16:23:55 -05:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "maps" , "InstanceMap::CannotEnter - player {} {} already in map {}, {}, {}!" , player -> GetName (), player -> GetGUID (). ToString (), GetId (), GetInstanceId (), GetDifficultyID ());
2015-09-21 15:17:05 +02:00
ABORT ();
2022-10-08 19:11:34 +02:00
return TRANSFER_ABORT_ERROR ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2009-10-31 19:34:02 +01:00
// allow GM's to enter
2013-06-11 21:25:12 -02:30
if ( player -> IsGameMaster ())
2015-12-31 19:51:07 +01:00
return Map :: CannotEnter ( player );
2009-10-31 19:34:02 +01:00
2008-10-02 16:23:55 -05:00
// cannot enter if the instance is full (player cap), GMs don't count
2009-01-22 22:44:27 -06:00
uint32 maxPlayers = GetMaxPlayers ();
2009-10-31 19:34:02 +01:00
if ( GetPlayersCountExceptGMs () >= maxPlayers )
2008-10-02 16:23:55 -05:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_WARN ( "maps" , "MAP: Instance '{}' of map '{}' cannot have more than '{}' players. Player '{}' rejected" , GetInstanceId (), GetMapName (), maxPlayers , player -> GetName ());
2022-10-08 19:11:34 +02:00
return TRANSFER_ABORT_MAX_PLAYERS ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2015-12-31 19:51:07 +01:00
// cannot enter while an encounter is in progress (unless this is a relog, in which case it is permitted)
2014-06-03 00:57:36 +01:00
if ( ! player -> IsLoading () && IsRaid () && GetInstanceScript () && GetInstanceScript () -> IsEncounterInProgress ())
2022-10-08 19:11:34 +02:00
return TRANSFER_ABORT_ZONE_IN_COMBAT ;
2009-10-17 15:51:44 -07:00
2018-07-22 13:22:35 +02:00
if ( i_instanceLock )
{
// cannot enter if player is permanent saved to a different instance id
TransferAbortReason lockError = sInstanceLockMgr . CanJoinInstanceLock ( player -> GetGUID (), { GetEntry (), GetMapDifficulty () }, i_instanceLock );
2022-10-08 19:11:34 +02:00
if ( lockError != TRANSFER_ABORT_NONE )
return lockError ;
2018-07-22 13:22:35 +02:00
}
2010-07-07 22:22:16 +02:00
2015-12-31 19:51:07 +01:00
return Map :: CannotEnter ( player );
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
/*
Do map specific checks and add the player to the map if successful.
*/
2015-05-01 15:44:43 +02:00
bool InstanceMap :: AddPlayerToMap ( Player * player , bool initPlayer /*= true*/ )
2008-10-02 16:23:55 -05:00
{
2022-07-24 18:56:40 +02:00
// increase current instances (hourly limit)
2018-07-22 13:22:35 +02:00
player -> AddInstanceEnterTime ( GetInstanceId (), GameTime :: GetGameTime ());
2009-10-17 15:51:44 -07:00
2018-07-22 13:22:35 +02:00
MapDb2Entries entries { GetEntry (), GetMapDifficulty () };
2023-11-28 16:24:38 +01:00
if ( entries . MapDifficulty -> HasResetSchedule () && i_instanceLock && ! i_instanceLock -> IsNew () && i_data )
2022-07-24 18:56:40 +02:00
{
2018-07-22 13:22:35 +02:00
if ( ! entries . MapDifficulty -> IsUsingEncounterLocks ())
2022-07-24 18:56:40 +02:00
{
2018-07-22 13:22:35 +02:00
InstanceLock const * playerLock = sInstanceLockMgr . FindActiveInstanceLock ( player -> GetGUID (), entries );
if ( ! playerLock || ( playerLock -> IsExpired () && playerLock -> IsExtended ()) ||
playerLock -> GetData () -> CompletedEncountersMask != i_instanceLock -> GetData () -> CompletedEncountersMask )
2009-03-18 20:41:49 -06:00
{
2018-07-22 13:22:35 +02:00
WorldPackets :: Instance :: PendingRaidLock pendingRaidLock ;
pendingRaidLock . TimeUntilLock = 60000 ;
pendingRaidLock . CompletedMask = i_instanceLock -> GetData () -> CompletedEncountersMask ;
pendingRaidLock . Extending = playerLock && playerLock -> IsExtended ();
pendingRaidLock . WarningOnly = entries . Map -> IsFlexLocking (); // events it triggers: 1 : INSTANCE_LOCK_WARNING 0 : INSTANCE_LOCK_STOP / INSTANCE_LOCK_START
player -> GetSession () -> SendPacket ( pendingRaidLock . Write ());
if ( ! entries . Map -> IsFlexLocking ())
player -> SetPendingBind ( GetInstanceId (), 60000 );
2008-10-02 16:23:55 -05:00
}
}
2022-07-24 18:56:40 +02:00
}
2009-10-17 15:51:44 -07:00
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "maps" , "MAP: Player '{}' entered instance '{}' of map '{}'" , player -> GetName (), GetInstanceId (), GetMapName ());
2022-07-24 18:56:40 +02:00
// initialize unload state
m_unloadTimer = 0 ;
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
// this will acquire the same mutex so it cannot be in the previous block
2015-05-01 15:44:43 +02:00
Map :: AddPlayerToMap ( player , initPlayer );
2010-02-14 19:13:14 -07:00
if ( i_data )
i_data -> OnPlayerEnter ( player );
2016-11-06 23:30:49 +01:00
if ( i_scenario )
i_scenario -> OnPlayerEnter ( player );
2008-10-02 16:23:55 -05:00
return true ;
}
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
void InstanceMap :: Update ( uint32 t_diff )
2008-11-02 16:53:46 -06:00
{
2008-11-07 09:36:46 -06:00
Map :: Update ( t_diff );
2009-10-17 15:51:44 -07:00
2009-10-30 22:52:37 -07:00
if ( i_data )
2016-04-14 22:43:21 +02:00
{
2008-11-07 09:36:46 -06:00
i_data -> Update ( t_diff );
2016-04-14 22:43:21 +02:00
i_data -> UpdateCombatResurrection ( t_diff );
}
2016-11-06 23:30:49 +01:00
if ( i_scenario )
i_scenario -> Update ( t_diff );
2022-08-15 18:39:58 +02:00
if ( i_instanceExpireEvent && i_instanceExpireEvent < GameTime :: GetSystemTime ())
{
Reset ( InstanceResetMethod :: Expire );
i_instanceExpireEvent = sInstanceLockMgr . GetNextResetTime ({ GetEntry (), GetMapDifficulty () });
}
2008-11-02 16:53:46 -06:00
}
2009-10-17 15:51:44 -07:00
2011-11-11 17:50:26 -05:00
void InstanceMap :: RemovePlayerFromMap ( Player * player , bool remove )
2008-10-02 16:23:55 -05:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "maps" , "MAP: Removing player '{}' from instance '{}' of map '{}' before relocating to another map" , player -> GetName (), GetInstanceId (), GetMapName ());
2019-06-29 19:24:05 +02:00
if ( i_data )
i_data -> OnPlayerLeave ( player );
// if last player set unload timer
2009-10-30 22:52:37 -07:00
if ( ! m_unloadTimer && m_mapRefManager . getSize () == 1 )
2022-08-15 18:39:58 +02:00
m_unloadTimer = ( i_instanceLock && i_instanceLock -> IsExpired ()) ? MIN_UNLOAD_DELAY : std :: max ( sWorld -> getIntConfig ( CONFIG_INSTANCE_UNLOAD_DELAY ), ( uint32 ) MIN_UNLOAD_DELAY );
2019-06-29 19:24:05 +02:00
2016-11-06 23:30:49 +01:00
if ( i_scenario )
i_scenario -> OnPlayerExit ( player );
2019-06-29 19:24:05 +02:00
2011-11-11 17:50:26 -05:00
Map :: RemovePlayerFromMap ( player , remove );
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2018-07-22 13:22:35 +02:00
void InstanceMap :: CreateInstanceData ()
2008-10-02 16:23:55 -05:00
{
2020-08-14 17:06:03 +02:00
if ( i_data != nullptr )
2008-10-02 16:23:55 -05:00
return ;
2009-10-17 15:51:44 -07:00
2011-04-28 22:16:13 +02:00
InstanceTemplate const * mInstance = sObjectMgr -> GetInstanceTemplate ( GetId ());
2008-10-02 16:23:55 -05:00
if ( mInstance )
{
2011-04-28 22:16:13 +02:00
i_script_id = mInstance -> ScriptId ;
2010-12-22 21:25:23 +01:00
i_data = sScriptMgr -> CreateInstanceData ( this );
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2009-10-30 22:52:37 -07:00
if ( ! i_data )
2008-10-02 16:23:55 -05:00
return ;
2009-10-17 15:51:44 -07:00
2023-03-21 12:07:54 +01:00
if ( ! i_instanceLock || i_instanceLock -> IsNew ())
2008-10-02 16:23:55 -05:00
{
2022-10-06 18:34:42 +02:00
i_data -> Create ();
return ;
}
MapDb2Entries entries { GetEntry (), GetMapDifficulty () };
if ( ! entries . IsInstanceIdBound () && ! IsRaid () && ! entries . MapDifficulty -> IsRestoringDungeonState () && i_owningGroupRef . isValid ())
{
i_data -> Create ();
return ;
}
InstanceLockData const * lockData = i_instanceLock -> GetInstanceInitializationData ();
i_data -> SetEntranceLocation ( lockData -> EntranceWorldSafeLocId );
if ( ! lockData -> Data . empty ())
{
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "maps" , "Loading instance data for `{}` with id {}" , sObjectMgr -> GetScriptName ( i_script_id ), i_InstanceId );
2022-10-06 18:34:42 +02:00
i_data -> Load ( lockData -> Data . c_str ());
2008-10-02 16:23:55 -05:00
}
2017-08-04 00:23:40 +02:00
else
i_data -> Create ();
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2019-12-26 13:30:32 +01:00
void InstanceMap :: TrySetOwningGroup ( Group * group )
{
if ( ! i_owningGroupRef . isValid ())
i_owningGroupRef . link ( group , this );
}
2008-10-02 16:23:55 -05:00
/*
Returns true if there are no players in the instance
*/
2020-01-11 12:49:50 +01:00
InstanceResetResult InstanceMap :: Reset ( InstanceResetMethod method )
2008-10-02 16:23:55 -05:00
{
2020-01-11 12:49:50 +01:00
// raids can be reset if no boss was killed
2023-03-21 12:07:54 +01:00
if ( method != InstanceResetMethod :: Expire && i_instanceLock && ! i_instanceLock -> IsNew ())
2020-01-11 12:49:50 +01:00
return InstanceResetResult :: CannotReset ;
2009-10-17 15:51:44 -07:00
2009-10-30 22:52:37 -07:00
if ( HavePlayers ())
2008-10-02 16:23:55 -05:00
{
2020-01-11 12:49:50 +01:00
switch ( method )
2008-10-02 16:23:55 -05:00
{
2020-01-11 12:49:50 +01:00
case InstanceResetMethod :: Manual :
// notify the players to leave the instance so it can be reset
2023-11-28 16:24:38 +01:00
for ( MapReference const & ref : m_mapRefManager )
2020-01-11 12:49:50 +01:00
ref . GetSource () -> SendResetFailedNotify ( GetId ());
break ;
case InstanceResetMethod :: OnChangeDifficulty :
// no client notification
break ;
case InstanceResetMethod :: Expire :
2022-08-15 18:39:58 +02:00
{
WorldPackets :: Instance :: RaidInstanceMessage raidInstanceMessage ;
raidInstanceMessage . Type = RAID_INSTANCE_EXPIRED ;
raidInstanceMessage . MapID = GetId ();
raidInstanceMessage . DifficultyID = GetDifficultyID ();
raidInstanceMessage . Write ();
2023-11-28 16:24:38 +01:00
for ( MapReference const & ref : m_mapRefManager )
2022-08-15 18:39:58 +02:00
ref . GetSource () -> SendDirectMessage ( raidInstanceMessage . GetRawPacket ());
2009-10-17 15:51:44 -07:00
2023-11-28 16:24:38 +01:00
if ( i_data )
{
WorldPackets :: Instance :: PendingRaidLock pendingRaidLock ;
pendingRaidLock . TimeUntilLock = 60000 ;
pendingRaidLock . CompletedMask = i_instanceLock -> GetData () -> CompletedEncountersMask ;
pendingRaidLock . Extending = true ;
pendingRaidLock . WarningOnly = GetEntry () -> IsFlexLocking ();
pendingRaidLock . Write ();
for ( MapReference const & ref : m_mapRefManager )
{
ref . GetSource () -> SendDirectMessage ( pendingRaidLock . GetRawPacket ());
if ( ! pendingRaidLock . WarningOnly )
ref . GetSource () -> SetPendingBind ( GetInstanceId (), 60000 );
}
2022-08-15 18:39:58 +02:00
}
2020-01-11 12:49:50 +01:00
break ;
2022-08-15 18:39:58 +02:00
}
2020-01-11 12:49:50 +01:00
default :
break ;
2008-10-02 16:23:55 -05:00
}
2020-01-11 12:49:50 +01:00
return InstanceResetResult :: NotEmpty ;
2008-10-02 16:23:55 -05:00
}
else
{
// unloaded at next update
m_unloadTimer = MIN_UNLOAD_DELAY ;
}
2009-10-17 15:51:44 -07:00
2020-01-11 12:49:50 +01:00
return InstanceResetResult :: Success ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2017-05-28 16:34:44 +02:00
std :: string const & InstanceMap :: GetScriptName () const
{
return sObjectMgr -> GetScriptName ( i_script_id );
}
2020-01-03 21:55:45 +01:00
void InstanceMap :: UpdateInstanceLock ( UpdateBossStateSaveDataEvent const & updateSaveDataEvent )
{
if ( i_instanceLock )
{
uint32 instanceCompletedEncounters = i_instanceLock -> GetData () -> CompletedEncountersMask | ( 1u << updateSaveDataEvent . DungeonEncounter -> Bit );
MapDb2Entries entries { GetEntry (), GetMapDifficulty () };
CharacterDatabaseTransaction trans = CharacterDatabase . BeginTransaction ();
if ( entries . IsInstanceIdBound ())
2021-01-16 22:17:11 +01:00
sInstanceLockMgr . UpdateSharedInstanceLock ( trans , InstanceLockUpdateEvent ( GetInstanceId (), i_data -> GetSaveData (),
instanceCompletedEncounters , updateSaveDataEvent . DungeonEncounter , i_data -> GetEntranceLocationForCompletedEncounters ( instanceCompletedEncounters )));
2020-01-03 21:55:45 +01:00
for ( MapReference & mapReference : m_mapRefManager )
{
Player * player = mapReference . GetSource ();
// never instance bind GMs with GM mode enabled
if ( player -> IsGameMaster ())
continue ;
InstanceLock const * playerLock = sInstanceLockMgr . FindActiveInstanceLock ( player -> GetGUID (), entries );
std :: string const * oldData = nullptr ;
2021-01-16 22:17:11 +01:00
uint32 playerCompletedEncounters = 0 ;
2020-01-03 21:55:45 +01:00
if ( playerLock )
2021-01-16 22:17:11 +01:00
{
2020-01-03 21:55:45 +01:00
oldData = & playerLock -> GetData () -> Data ;
2021-01-16 22:17:11 +01:00
playerCompletedEncounters = playerLock -> GetData () -> CompletedEncountersMask | ( 1u << updateSaveDataEvent . DungeonEncounter -> Bit );
}
2020-01-03 21:55:45 +01:00
2023-03-21 12:07:54 +01:00
bool isNewLock = ! playerLock || playerLock -> IsNew () || playerLock -> IsExpired ();
2020-01-03 21:55:45 +01:00
InstanceLock const * newLock = sInstanceLockMgr . UpdateInstanceLockForPlayer ( trans , player -> GetGUID (), entries ,
2021-01-16 22:17:11 +01:00
InstanceLockUpdateEvent ( GetInstanceId (), i_data -> UpdateBossStateSaveData ( oldData ? * oldData : "" , updateSaveDataEvent ),
instanceCompletedEncounters , updateSaveDataEvent . DungeonEncounter , i_data -> GetEntranceLocationForCompletedEncounters ( playerCompletedEncounters )));
2020-01-03 21:55:45 +01:00
if ( isNewLock )
{
WorldPackets :: Instance :: InstanceSaveCreated data ;
data . Gm = player -> IsGameMaster ();
player -> SendDirectMessage ( data . Write ());
player -> GetSession () -> SendCalendarRaidLockoutAdded ( newLock );
}
}
CharacterDatabase . CommitTransaction ( trans );
}
}
void InstanceMap :: UpdateInstanceLock ( UpdateAdditionalSaveDataEvent const & updateSaveDataEvent )
2008-10-02 16:23:55 -05:00
{
2018-07-22 13:22:35 +02:00
if ( i_instanceLock )
2008-10-02 16:23:55 -05:00
{
2018-07-22 13:22:35 +02:00
uint32 instanceCompletedEncounters = i_instanceLock -> GetData () -> CompletedEncountersMask ;
2009-10-17 15:51:44 -07:00
2018-07-22 13:22:35 +02:00
MapDb2Entries entries { GetEntry (), GetMapDifficulty () };
CharacterDatabaseTransaction trans = CharacterDatabase . BeginTransaction ();
2016-09-16 13:09:16 +02:00
2018-07-22 13:22:35 +02:00
if ( entries . IsInstanceIdBound ())
2021-01-16 22:17:11 +01:00
sInstanceLockMgr . UpdateSharedInstanceLock ( trans , InstanceLockUpdateEvent ( GetInstanceId (), i_data -> GetSaveData (),
instanceCompletedEncounters , nullptr , {}));
2018-07-22 13:22:35 +02:00
for ( MapReference & mapReference : m_mapRefManager )
2016-09-16 13:09:16 +02:00
{
2018-07-22 13:22:35 +02:00
Player * player = mapReference . GetSource ();
// never instance bind GMs with GM mode enabled
if ( player -> IsGameMaster ())
continue ;
InstanceLock const * playerLock = sInstanceLockMgr . FindActiveInstanceLock ( player -> GetGUID (), entries );
std :: string const * oldData = nullptr ;
if ( playerLock )
oldData = & playerLock -> GetData () -> Data ;
2023-03-21 12:07:54 +01:00
bool isNewLock = ! playerLock || playerLock -> IsNew () || playerLock -> IsExpired ();
2018-07-22 13:22:35 +02:00
InstanceLock const * newLock = sInstanceLockMgr . UpdateInstanceLockForPlayer ( trans , player -> GetGUID (), entries ,
2021-01-16 22:17:11 +01:00
InstanceLockUpdateEvent ( GetInstanceId (), i_data -> UpdateAdditionalSaveData ( oldData ? * oldData : "" , updateSaveDataEvent ),
instanceCompletedEncounters , nullptr , {}));
2018-07-22 13:22:35 +02:00
if ( isNewLock )
2016-09-21 15:36:21 +02:00
{
2018-07-22 13:22:35 +02:00
WorldPackets :: Instance :: InstanceSaveCreated data ;
data . Gm = player -> IsGameMaster ();
player -> SendDirectMessage ( data . Write ());
player -> GetSession () -> SendCalendarRaidLockoutAdded ( newLock );
2016-09-21 15:36:21 +02:00
}
2016-09-16 13:09:16 +02:00
}
2018-07-22 13:22:35 +02:00
CharacterDatabase . CommitTransaction ( trans );
}
}
void InstanceMap :: CreateInstanceLockForPlayer ( Player * player )
{
MapDb2Entries entries { GetEntry (), GetMapDifficulty () };
InstanceLock const * playerLock = sInstanceLockMgr . FindActiveInstanceLock ( player -> GetGUID (), entries );
2023-03-21 12:07:54 +01:00
bool isNewLock = ! playerLock || playerLock -> IsNew () || playerLock -> IsExpired ();
2018-07-22 13:22:35 +02:00
CharacterDatabaseTransaction trans = CharacterDatabase . BeginTransaction ();
InstanceLock const * newLock = sInstanceLockMgr . UpdateInstanceLockForPlayer ( trans , player -> GetGUID (), entries ,
2021-01-16 22:17:11 +01:00
InstanceLockUpdateEvent ( GetInstanceId (), i_data -> GetSaveData (), i_instanceLock -> GetData () -> CompletedEncountersMask , nullptr , {}));
2018-07-22 13:22:35 +02:00
CharacterDatabase . CommitTransaction ( trans );
if ( isNewLock )
{
WorldPackets :: Instance :: InstanceSaveCreated data ;
data . Gm = player -> IsGameMaster ();
player -> SendDirectMessage ( data . Write ());
player -> GetSession () -> SendCalendarRaidLockoutAdded ( newLock );
2008-10-02 16:23:55 -05:00
}
}
2009-10-17 15:51:44 -07:00
2015-01-24 20:28:42 +01:00
MapDifficultyEntry const * Map :: GetMapDifficulty () const
2009-12-17 11:30:18 +01:00
{
2016-04-25 22:03:34 +02:00
return sDB2Manager . GetMapDifficultyData ( GetId (), GetDifficultyID ());
2015-01-10 01:35:47 +01:00
}
2016-04-25 22:03:34 +02:00
uint32 Map :: GetId () const
{
return i_mapEntry -> ID ;
}
bool Map :: Instanceable () const
{
return i_mapEntry && i_mapEntry -> Instanceable ();
}
bool Map :: IsDungeon () const
{
return i_mapEntry && i_mapEntry -> IsDungeon ();
}
bool Map :: IsNonRaidDungeon () const
{
return i_mapEntry && i_mapEntry -> IsNonRaidDungeon ();
}
bool Map :: IsRaid () const
{
return i_mapEntry && i_mapEntry -> IsRaid ();
}
2023-10-22 11:24:48 +02:00
bool Map :: IsLFR () const
{
switch ( i_spawnMode )
{
case DIFFICULTY_LFR :
case DIFFICULTY_LFR_NEW :
case DIFFICULTY_LFR_15TH_ANNIVERSARY :
return true ;
default :
return false ;
}
}
bool Map :: IsNormal () const
{
switch ( i_spawnMode )
{
case DIFFICULTY_NORMAL :
case DIFFICULTY_10_N :
case DIFFICULTY_25_N :
case DIFFICULTY_NORMAL_RAID :
case DIFFICULTY_NORMAL_ISLAND :
case DIFFICULTY_NORMAL_WARFRONT :
return true ;
default :
return false ;
}
}
2015-01-10 01:35:47 +01:00
bool Map :: IsHeroic () const
{
if ( DifficultyEntry const * difficulty = sDifficultyStore . LookupEntry ( i_spawnMode ))
2023-10-22 11:24:48 +02:00
{
if ( difficulty -> Flags & DIFFICULTY_FLAG_DISPLAY_HEROIC )
return true ;
}
// compatibility purposes of old difficulties
switch ( i_spawnMode )
{
case DIFFICULTY_10_HC :
case DIFFICULTY_25_HC :
case DIFFICULTY_HEROIC :
case DIFFICULTY_3_MAN_SCENARIO_HC :
return true ;
default :
return false ;
}
}
bool Map :: IsMythic () const
{
if ( DifficultyEntry const * difficulty = sDifficultyStore . LookupEntry ( i_spawnMode ))
return difficulty -> Flags & DIFFICULTY_FLAG_DISPLAY_MYTHIC ;
2015-01-10 01:35:47 +01:00
return false ;
2009-12-17 11:30:18 +01:00
}
2023-10-22 11:24:48 +02:00
bool Map :: IsMythicPlus () const
{
return IsDungeon () && i_spawnMode == DIFFICULTY_MYTHIC_KEYSTONE ;
}
bool Map :: IsHeroicOrHigher () const
{
return IsHeroic () || IsMythic () || IsMythicPlus ();
}
2016-04-25 22:03:34 +02:00
bool Map :: Is25ManRaid () const
{
return IsRaid () && ( i_spawnMode == DIFFICULTY_25_N || i_spawnMode == DIFFICULTY_25_HC );
}
2023-10-22 11:24:48 +02:00
bool Map :: IsTimewalking () const
{
return ( IsDungeon () && i_spawnMode == DIFFICULTY_TIMEWALKING ) || ( IsRaid () && i_spawnMode == DIFFICULTY_TIMEWALKING_RAID );
}
2016-04-25 22:03:34 +02:00
bool Map :: IsBattleground () const
{
return i_mapEntry && i_mapEntry -> IsBattleground ();
}
bool Map :: IsBattleArena () const
{
return i_mapEntry && i_mapEntry -> IsBattleArena ();
}
bool Map :: IsBattlegroundOrArena () const
{
return i_mapEntry && i_mapEntry -> IsBattlegroundOrArena ();
}
2022-04-24 13:59:51 +02:00
bool Map :: IsScenario () const
{
return i_mapEntry && i_mapEntry -> IsScenario ();
}
2016-04-25 22:03:34 +02:00
bool Map :: IsGarrison () const
{
return i_mapEntry && i_mapEntry -> IsGarrison ();
}
2023-11-14 18:40:39 +01:00
bool Map :: IsAlwaysActive () const
{
return IsBattlegroundOrArena ();
}
2016-04-25 22:03:34 +02:00
bool Map :: GetEntrancePos ( int32 & mapid , float & x , float & y )
{
if ( ! i_mapEntry )
return false ;
return i_mapEntry -> GetEntrancePos ( mapid , x , y );
}
2009-01-22 22:44:27 -06:00
uint32 InstanceMap :: GetMaxPlayers () const
{
2015-01-24 20:28:42 +01:00
MapDifficultyEntry const * mapDiff = GetMapDifficulty ();
if ( mapDiff && mapDiff -> MaxPlayers )
return mapDiff -> MaxPlayers ;
2014-03-30 15:42:05 +02:00
2014-11-05 23:33:56 +02:00
return GetEntry () -> MaxPlayers ;
2009-12-17 11:30:18 +01:00
}
2022-07-24 20:11:24 +02:00
TeamId InstanceMap :: GetTeamIdInInstance () const
{
if ( sWorldStateMgr -> GetValue ( WS_TEAM_IN_INSTANCE_ALLIANCE , this ))
return TEAM_ALLIANCE ;
if ( sWorldStateMgr -> GetValue ( WS_TEAM_IN_INSTANCE_HORDE , this ))
return TEAM_HORDE ;
return TEAM_NEUTRAL ;
}
2008-10-02 16:23:55 -05:00
/* ******* Battleground Instance Maps ******* */
2009-10-17 15:51:44 -07:00
2022-07-24 18:56:40 +02:00
BattlegroundMap :: BattlegroundMap ( uint32 id , time_t expiry , uint32 InstanceId , Difficulty spawnMode )
: Map ( id , expiry , InstanceId , spawnMode ), m_bg ( nullptr )
2008-10-02 16:23:55 -05:00
{
2009-09-27 02:24:25 -07:00
//lets initialize visibility distance for BG/Arenas
2010-08-08 04:37:24 +02:00
BattlegroundMap :: InitVisibilityDistance ();
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2010-08-08 04:37:24 +02:00
BattlegroundMap ::~ BattlegroundMap ()
2008-10-02 16:23:55 -05:00
{
2011-09-29 12:43:05 +02:00
if ( m_bg )
2011-09-27 17:26:13 -04:00
{
//unlink to prevent crash, always unlink all pointer reference before destruction
2020-08-14 17:06:03 +02:00
m_bg -> SetBgMap ( nullptr );
m_bg = nullptr ;
2011-09-27 17:26:13 -04:00
}
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2010-08-08 04:37:24 +02:00
void BattlegroundMap :: InitVisibilityDistance ()
2009-09-27 02:24:25 -07:00
{
//init visibility distance for BG/Arenas
2020-12-26 23:30:16 +01:00
m_VisibleDistance = IsBattleArena () ? World :: GetMaxVisibleDistanceInArenas () : World :: GetMaxVisibleDistanceInBG ();
m_VisibilityNotifyPeriod = IsBattleArena () ? World :: GetVisibilityNotifyPeriodInArenas () : World :: GetVisibilityNotifyPeriodInBG ();
2009-09-27 02:24:25 -07:00
}
2009-10-17 15:51:44 -07:00
2022-10-08 19:11:34 +02:00
TransferAbortParams BattlegroundMap :: CannotEnter ( Player * player )
2008-10-02 16:23:55 -05:00
{
2009-10-30 22:52:37 -07:00
if ( player -> GetMapRef (). getTarget () == this )
2008-10-02 16:23:55 -05:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "maps" , "BGMap::CannotEnter - player {} is already in map!" , player -> GetGUID (). ToString ());
2015-09-21 15:17:05 +02:00
ABORT ();
2022-10-08 19:11:34 +02:00
return TRANSFER_ABORT_ERROR ;
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2010-08-08 04:37:24 +02:00
if ( player -> GetBattlegroundId () != GetInstanceId ())
2022-10-08 19:11:34 +02:00
return TRANSFER_ABORT_LOCKED_TO_DIFFERENT_INSTANCE ;
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
// player number limit is checked in bgmgr, no need to do it here
2009-10-17 15:51:44 -07:00
2015-12-31 19:51:07 +01:00
return Map :: CannotEnter ( player );
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2015-05-01 15:44:43 +02:00
bool BattlegroundMap :: AddPlayerToMap ( Player * player , bool initPlayer /*= true*/ )
2008-10-02 16:23:55 -05:00
{
2022-07-24 18:56:40 +02:00
player -> m_InstanceValid = true ;
2015-05-01 15:44:43 +02:00
return Map :: AddPlayerToMap ( player , initPlayer );
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2011-11-11 17:50:26 -05:00
void BattlegroundMap :: RemovePlayerFromMap ( Player * player , bool remove )
2008-10-02 16:23:55 -05:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "maps" , "MAP: Removing player '{}' from bg '{}' of map '{}' before relocating to another map" , player -> GetName (), GetInstanceId (), GetMapName ());
2011-11-11 17:50:26 -05:00
Map :: RemovePlayerFromMap ( player , remove );
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2010-08-08 04:37:24 +02:00
void BattlegroundMap :: SetUnload ()
2008-10-02 16:23:55 -05:00
{
m_unloadTimer = MIN_UNLOAD_DELAY ;
}
2009-10-17 15:51:44 -07:00
2010-08-08 04:37:24 +02:00
void BattlegroundMap :: RemoveAllPlayers ()
2008-10-02 16:23:55 -05:00
{
2009-10-30 22:52:37 -07:00
if ( HavePlayers ())
2009-10-17 16:20:24 -07:00
for ( MapRefManager :: iterator itr = m_mapRefManager . begin (); itr != m_mapRefManager . end (); ++ itr )
2013-06-11 21:25:12 -02:30
if ( Player * player = itr -> GetSource ())
2011-11-07 11:06:39 -06:00
if ( ! player -> IsBeingTeleportedFar ())
player -> TeleportTo ( player -> GetBattlegroundEntryPoint ());
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2015-02-25 00:13:14 +01:00
AreaTrigger * Map :: GetAreaTrigger ( ObjectGuid const & guid )
{
return _objectsStore . Find < AreaTrigger > ( guid );
}
2021-10-11 12:59:19 +02:00
SceneObject * Map :: GetSceneObject ( ObjectGuid const & guid )
{
return _objectsStore . Find < SceneObject > ( guid );
}
2017-05-14 11:40:55 +02:00
Conversation * Map :: GetConversation ( ObjectGuid const & guid )
{
return _objectsStore . Find < Conversation > ( guid );
}
2017-02-05 23:42:31 +01:00
Player * Map :: GetPlayer ( ObjectGuid const & guid )
{
return ObjectAccessor :: GetPlayer ( this , guid );
}
2015-02-25 00:13:14 +01:00
Corpse * Map :: GetCorpse ( ObjectGuid const & guid )
2009-04-20 20:28:19 -05:00
{
2015-02-25 00:13:14 +01:00
return _objectsStore . Find < Corpse > ( guid );
2009-04-20 20:28:19 -05:00
}
2009-10-17 15:51:44 -07:00
2015-02-25 00:13:14 +01:00
Creature * Map :: GetCreature ( ObjectGuid const & guid )
2009-04-20 20:28:19 -05:00
{
2015-02-25 00:13:14 +01:00
return _objectsStore . Find < Creature > ( guid );
2009-04-20 20:28:19 -05:00
}
2009-10-17 15:51:44 -07:00
2015-02-25 00:13:14 +01:00
DynamicObject * Map :: GetDynamicObject ( ObjectGuid const & guid )
{
return _objectsStore . Find < DynamicObject > ( guid );
}
GameObject * Map :: GetGameObject ( ObjectGuid const & guid )
{
return _objectsStore . Find < GameObject > ( guid );
}
Pet * Map :: GetPet ( ObjectGuid const & guid )
{
return _objectsStore . Find < Pet > ( guid );
}
Transport * Map :: GetTransport ( ObjectGuid const & guid )
2013-10-16 18:37:29 +02:00
{
2014-09-14 16:14:12 +02:00
if ( ! guid . IsMOTransport ())
2020-08-14 17:06:03 +02:00
return nullptr ;
2013-10-16 18:37:29 +02:00
GameObject * go = GetGameObject ( guid );
2020-08-14 17:06:03 +02:00
return go ? go -> ToTransport () : nullptr ;
2013-10-16 18:37:29 +02:00
}
2017-05-07 21:48:41 +01:00
Creature * Map :: GetCreatureBySpawnId ( ObjectGuid :: LowType spawnId ) const
2009-08-01 00:20:32 +02:00
{
2017-05-07 21:48:41 +01:00
auto const bounds = GetCreatureBySpawnIdStore (). equal_range ( spawnId );
if ( bounds . first == bounds . second )
return nullptr ;
std :: unordered_multimap < ObjectGuid :: LowType , Creature *>:: const_iterator creatureItr = std :: find_if ( bounds . first , bounds . second , []( Map :: CreatureBySpawnIdContainer :: value_type const & pair )
{
return pair . second -> IsAlive ();
});
return creatureItr != bounds . second ? creatureItr -> second : bounds . first -> second ;
2009-08-01 00:20:32 +02:00
}
2012-06-07 23:20:57 +02:00
2017-05-07 21:48:41 +01:00
GameObject * Map :: GetGameObjectBySpawnId ( ObjectGuid :: LowType spawnId ) const
2012-06-07 23:20:57 +02:00
{
2017-05-07 21:48:41 +01:00
auto const bounds = GetGameObjectBySpawnIdStore (). equal_range ( spawnId );
if ( bounds . first == bounds . second )
return nullptr ;
2012-06-07 23:20:57 +02:00
2017-05-07 21:48:41 +01:00
std :: unordered_multimap < ObjectGuid :: LowType , GameObject *>:: const_iterator creatureItr = std :: find_if ( bounds . first , bounds . second , []( Map :: GameObjectBySpawnIdContainer :: value_type const & pair )
{
return pair . second -> isSpawned ();
});
2012-06-07 23:20:57 +02:00
2017-05-07 21:48:41 +01:00
return creatureItr != bounds . second ? creatureItr -> second : bounds . first -> second ;
2012-06-07 23:20:57 +02:00
}
2020-02-08 20:22:37 +01:00
AreaTrigger * Map :: GetAreaTriggerBySpawnId ( ObjectGuid :: LowType spawnId ) const
{
auto const bounds = GetAreaTriggerBySpawnIdStore (). equal_range ( spawnId );
if ( bounds . first == bounds . second )
return nullptr ;
return bounds . first -> second ;
}
2017-05-07 21:48:41 +01:00
void Map :: UpdateIteratorBack ( Player * player )
2012-06-07 23:20:57 +02:00
{
2023-10-18 20:21:32 +02:00
if ( &* m_mapRefIter == & player -> GetMapRef ())
2017-05-07 21:48:41 +01:00
m_mapRefIter = m_mapRefIter -> nocheck_prev ();
2012-06-07 23:20:57 +02:00
}
2019-08-17 22:30:27 +02:00
void Map :: SaveRespawnTime ( SpawnObjectType type , ObjectGuid :: LowType spawnId , uint32 entry , time_t respawnTime , uint32 gridId , CharacterDatabaseTransaction dbTrans , bool startup )
2012-06-07 23:20:57 +02:00
{
2020-02-08 20:22:37 +01:00
SpawnMetadata const * data = sObjectMgr -> GetSpawnMetadata ( type , spawnId );
if ( ! data )
{
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "maps" , "Map {} attempt to save respawn time for nonexistant spawnid ({},{})." , GetId (), type , spawnId );
2019-07-15 17:14:58 +02:00
return ;
2020-02-08 20:22:37 +01:00
}
2019-07-15 17:14:58 +02:00
2012-06-07 23:20:57 +02:00
if ( ! respawnTime )
{
// Delete only
2020-02-08 20:22:37 +01:00
RemoveRespawnTime ( data -> type , data -> spawnId , dbTrans );
2012-06-07 23:20:57 +02:00
return ;
}
2017-05-07 21:48:41 +01:00
RespawnInfo ri ;
2020-02-08 20:22:37 +01:00
ri . type = data -> type ;
ri . spawnId = data -> spawnId ;
2017-05-07 21:48:41 +01:00
ri . entry = entry ;
ri . respawnTime = respawnTime ;
ri . gridId = gridId ;
2019-07-15 17:14:58 +02:00
bool success = AddRespawnInfo ( ri );
2012-06-07 23:20:57 +02:00
2019-07-15 17:14:58 +02:00
if ( startup )
{
if ( ! success )
2023-08-27 12:50:26 +02:00
TC_LOG_ERROR ( "maps" , "Attempt to load saved respawn {} for ({},{}) failed - duplicate respawn? Skipped." , respawnTime , uint32 ( type ), spawnId );
2019-07-15 17:14:58 +02:00
}
else if ( success )
SaveRespawnInfoDB ( ri , dbTrans );
2012-06-07 23:20:57 +02:00
}
2019-07-15 17:14:58 +02:00
void Map :: SaveRespawnInfoDB ( RespawnInfo const & info , CharacterDatabaseTransaction dbTrans )
2012-06-07 23:20:57 +02:00
{
2020-01-02 14:19:35 +01:00
if ( Instanceable ())
return ;
2019-07-14 18:49:38 +02:00
CharacterDatabasePreparedStatement * stmt = CharacterDatabase . GetPreparedStatement ( CHAR_REP_RESPAWN );
2019-07-15 17:14:58 +02:00
stmt -> setUInt16 ( 0 , info . type );
stmt -> setUInt64 ( 1 , info . spawnId );
stmt -> setInt64 ( 2 , info . respawnTime );
2019-07-14 18:49:38 +02:00
stmt -> setUInt16 ( 3 , GetId ());
stmt -> setUInt32 ( 4 , GetInstanceId ());
2017-05-07 21:48:41 +01:00
CharacterDatabase . ExecuteOrAppend ( dbTrans , stmt );
2012-06-07 23:20:57 +02:00
}
void Map :: LoadRespawnTimes ()
{
2020-01-02 14:19:35 +01:00
if ( Instanceable ())
return ;
2019-07-14 18:49:38 +02:00
CharacterDatabasePreparedStatement * stmt = CharacterDatabase . GetPreparedStatement ( CHAR_SEL_RESPAWNS );
2012-06-07 23:20:57 +02:00
stmt -> setUInt16 ( 0 , GetId ());
stmt -> setUInt32 ( 1 , GetInstanceId ());
if ( PreparedQueryResult result = CharacterDatabase . Query ( stmt ))
{
do
{
Field * fields = result -> Fetch ();
2019-07-14 18:49:38 +02:00
SpawnObjectType type = SpawnObjectType ( fields [ 0 ]. GetUInt16 ());
ObjectGuid :: LowType spawnId = fields [ 1 ]. GetUInt64 ();
time_t respawnTime = fields [ 2 ]. GetInt64 ();
2012-06-07 23:20:57 +02:00
2020-02-08 20:22:37 +01:00
if ( SpawnData :: TypeHasData ( type ))
2019-07-15 17:14:58 +02:00
{
if ( SpawnData const * data = sObjectMgr -> GetSpawnData ( type , spawnId ))
2019-08-17 22:30:27 +02:00
SaveRespawnTime ( type , spawnId , data -> id , time_t ( respawnTime ), Trinity :: ComputeGridCoord ( data -> spawnPoint . GetPositionX (), data -> spawnPoint . GetPositionY ()). GetId (), nullptr , true );
2019-07-15 17:14:58 +02:00
else
2023-08-27 12:50:26 +02:00
TC_LOG_ERROR ( "maps" , "Loading saved respawn time of {} for spawnid ({},{}) - spawn does not exist , ignoring ", respawnTime, uint32(type), spawnId) ;
2019-07-15 17:14:58 +02:00
}
2019-07-14 18:49:38 +02:00
else
2019-07-15 17:14:58 +02:00
{
2023-08-27 12:50:26 +02:00
TC_LOG_ERROR ( "maps" , "Loading saved respawn time of {} for spawnid ({},{}) - invalid spawn type, ignoring" , respawnTime , uint32 ( type ), spawnId );
2019-07-15 17:14:58 +02:00
}
2017-05-07 21:48:41 +01:00
2012-06-07 23:20:57 +02:00
} while ( result -> NextRow ());
}
}
2020-01-02 14:19:35 +01:00
void Map :: DeleteRespawnTimesInDB ()
2012-06-08 14:52:52 +02:00
{
2020-01-02 14:19:35 +01:00
if ( Instanceable ())
return ;
2019-07-14 18:49:38 +02:00
CharacterDatabasePreparedStatement * stmt = CharacterDatabase . GetPreparedStatement ( CHAR_DEL_ALL_RESPAWNS );
2020-01-02 14:19:35 +01:00
stmt -> setUInt16 ( 0 , GetId ());
stmt -> setUInt32 ( 1 , GetInstanceId ());
2012-06-07 23:20:57 +02:00
CharacterDatabase . Execute ( stmt );
}
2014-09-14 16:14:12 +02:00
time_t Map :: GetLinkedRespawnTime ( ObjectGuid guid ) const
2012-06-07 23:20:57 +02:00
{
2014-09-14 16:14:12 +02:00
ObjectGuid linkedGuid = sObjectMgr -> GetLinkedRespawnGuid ( guid );
switch ( linkedGuid . GetHigh ())
2012-06-07 23:20:57 +02:00
{
2014-10-27 12:03:35 +01:00
case HighGuid :: Creature :
2014-09-14 16:14:12 +02:00
return GetCreatureRespawnTime ( linkedGuid . GetCounter ());
2014-10-27 12:03:35 +01:00
case HighGuid :: GameObject :
2014-09-14 16:14:12 +02:00
return GetGORespawnTime ( linkedGuid . GetCounter ());
2012-06-07 23:20:57 +02:00
default :
break ;
}
return time_t ( 0 );
}
2012-06-08 14:52:52 +02:00
2015-02-25 00:13:14 +01:00
void Map :: LoadCorpseData ()
{
2020-12-01 21:08:06 +01:00
CharacterDatabasePreparedStatement * stmt = CharacterDatabase . GetPreparedStatement ( CHAR_SEL_CORPSES );
stmt -> setUInt32 ( 0 , GetId ());
stmt -> setUInt32 ( 1 , GetInstanceId ());
2020-12-13 18:38:31 +01:00
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// SELECT posX, posY, posZ, orientation, mapId, displayId, itemCache, race, class, gender, flags, dynFlags, time, corpseType, instanceId, guid FROM corpse WHERE mapId = ? AND instanceId = ?
2020-12-01 21:08:06 +01:00
PreparedQueryResult result = CharacterDatabase . Query ( stmt );
if ( ! result )
return ;
2015-09-28 17:20:27 +02:00
std :: unordered_map < ObjectGuid :: LowType , std :: unordered_set < uint32 >> phases ;
2020-12-01 21:08:06 +01:00
std :: unordered_map < ObjectGuid :: LowType , std :: vector < UF :: ChrCustomizationChoice >> customizations ;
2015-02-25 00:13:14 +01:00
2020-12-01 21:08:06 +01:00
stmt = CharacterDatabase . GetPreparedStatement ( CHAR_SEL_CORPSE_PHASES );
2015-02-25 00:13:14 +01:00
stmt -> setUInt32 ( 0 , GetId ());
2015-08-27 01:07:14 +01:00
stmt -> setUInt32 ( 1 , GetInstanceId ());
2015-02-25 00:13:14 +01:00
// 0 1
2015-08-27 01:07:14 +01:00
// SELECT OwnerGuid, PhaseId FROM corpse_phases cp LEFT JOIN corpse c ON cp.OwnerGuid = c.guid WHERE c.mapId = ? AND c.instanceId = ?
2020-12-01 21:08:06 +01:00
if ( PreparedQueryResult phaseResult = CharacterDatabase . Query ( stmt ))
2015-02-25 00:13:14 +01:00
{
do
{
Field * fields = phaseResult -> Fetch ();
2015-09-28 17:20:27 +02:00
ObjectGuid :: LowType guid = fields [ 0 ]. GetUInt64 ();
2015-02-25 00:13:14 +01:00
uint32 phaseId = fields [ 1 ]. GetUInt32 ();
phases [ guid ]. insert ( phaseId );
} while ( phaseResult -> NextRow ());
}
2020-12-22 16:11:34 +01:00
stmt = CharacterDatabase . GetPreparedStatement ( CHAR_SEL_CORPSE_CUSTOMIZATIONS );
stmt -> setUInt32 ( 0 , GetId ());
stmt -> setUInt32 ( 1 , GetInstanceId ());
2020-12-01 21:08:06 +01:00
// 0 1 2
// 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 = ?
if ( PreparedQueryResult customizationResult = CharacterDatabase . Query ( stmt ))
{
do
{
Field * fields = customizationResult -> Fetch ();
ObjectGuid :: LowType guid = fields [ 0 ]. GetUInt64 ();
std :: vector < UF :: ChrCustomizationChoice >& customizationsForCorpse = customizations [ guid ];
2015-02-25 00:13:14 +01:00
2020-12-01 21:08:06 +01:00
customizationsForCorpse . emplace_back ();
UF :: ChrCustomizationChoice & choice = customizationsForCorpse . back ();
choice . ChrCustomizationOptionID = fields [ 1 ]. GetUInt32 ();
choice . ChrCustomizationChoiceID = fields [ 2 ]. GetUInt32 ();
} while ( customizationResult -> NextRow ());
}
2015-02-25 00:13:14 +01:00
do
{
Field * fields = result -> Fetch ();
2020-12-13 18:38:31 +01:00
CorpseType type = CorpseType ( fields [ 13 ]. GetUInt8 ());
ObjectGuid :: LowType guid = fields [ 15 ]. GetUInt64 ();
2015-02-25 00:13:14 +01:00
if ( type >= MAX_CORPSE_TYPE || type == CORPSE_BONES )
{
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "misc" , "Corpse (guid: {}) have wrong corpse type ({}), not loading." , guid , type );
2015-02-25 00:13:14 +01:00
continue ;
}
Corpse * corpse = new Corpse ( type );
if ( ! corpse -> LoadCorpseFromDB ( GenerateLowGuid < HighGuid :: Corpse > (), fields ))
{
delete corpse ;
continue ;
}
2018-01-28 11:37:20 +01:00
for ( uint32 phaseId : phases [ guid ])
PhasingHandler :: AddPhase ( corpse , phaseId , false );
2015-02-25 00:13:14 +01:00
2020-12-01 21:08:06 +01:00
corpse -> SetCustomizations ( Trinity :: Containers :: MakeIteratorPair ( customizations [ guid ]. begin (), customizations [ guid ]. end ()));
2015-09-28 17:20:27 +02:00
AddCorpse ( corpse );
2015-02-25 00:13:14 +01:00
} while ( result -> NextRow ());
}
void Map :: DeleteCorpseData ()
{
2015-08-27 01:07:14 +01:00
// DELETE cp, c FROM corpse_phases cp INNER JOIN corpse c ON cp.OwnerGuid = c.guid WHERE c.mapId = ? AND c.instanceId = ?
2019-07-27 01:00:37 +02:00
CharacterDatabasePreparedStatement * stmt = CharacterDatabase . GetPreparedStatement ( CHAR_DEL_CORPSES_FROM_MAP );
2015-02-25 00:13:14 +01:00
stmt -> setUInt32 ( 0 , GetId ());
2015-08-27 01:07:14 +01:00
stmt -> setUInt32 ( 1 , GetInstanceId ());
2015-02-25 00:13:14 +01:00
CharacterDatabase . Execute ( stmt );
}
2015-09-28 17:20:27 +02:00
void Map :: AddCorpse ( Corpse * corpse )
{
corpse -> SetMap ( this );
2015-10-28 21:43:44 +01:00
_corpsesByCell [ corpse -> GetCellCoord (). GetId ()]. insert ( corpse );
if ( corpse -> GetType () != CORPSE_BONES )
_corpsesByPlayer [ corpse -> GetOwnerGUID ()] = corpse ;
else
_corpseBones . insert ( corpse );
2015-09-28 17:20:27 +02:00
}
void Map :: RemoveCorpse ( Corpse * corpse )
{
2015-10-28 21:43:44 +01:00
ASSERT ( corpse );
2015-09-28 17:20:27 +02:00
2022-01-29 15:18:27 +01:00
corpse -> UpdateObjectVisibilityOnDestroy ();
2015-09-28 17:20:27 +02:00
if ( corpse -> IsInGrid ())
RemoveFromMap ( corpse , false );
else
{
corpse -> RemoveFromWorld ();
corpse -> ResetMap ();
}
2015-10-28 21:43:44 +01:00
_corpsesByCell [ corpse -> GetCellCoord (). GetId ()]. erase ( corpse );
if ( corpse -> GetType () != CORPSE_BONES )
_corpsesByPlayer . erase ( corpse -> GetOwnerGUID ());
else
_corpseBones . erase ( corpse );
2015-09-28 17:20:27 +02:00
}
Corpse * Map :: ConvertCorpseToBones ( ObjectGuid const & ownerGuid , bool insignia /*= false*/ )
{
Corpse * corpse = GetCorpseByPlayer ( ownerGuid );
if ( ! corpse )
return nullptr ;
RemoveCorpse ( corpse );
// remove corpse from DB
2019-07-27 10:52:33 +02:00
CharacterDatabaseTransaction trans = CharacterDatabase . BeginTransaction ();
2015-09-28 17:20:27 +02:00
corpse -> DeleteFromDB ( trans );
CharacterDatabase . CommitTransaction ( trans );
2020-08-14 17:06:03 +02:00
Corpse * bones = nullptr ;
2015-09-28 17:20:27 +02:00
// create the bones only if the map and the grid is loaded at the corpse's location
// ignore bones creating option in case insignia
if (( insignia ||
( IsBattlegroundOrArena () ? sWorld -> getBoolConfig ( CONFIG_DEATH_BONES_BG_OR_ARENA ) : sWorld -> getBoolConfig ( CONFIG_DEATH_BONES_WORLD ))) &&
! IsRemovalGrid ( corpse -> GetPositionX (), corpse -> GetPositionY ()))
{
// Create bones, don't change Corpse
bones = new Corpse ();
2015-10-31 00:05:09 +01:00
bones -> Create ( corpse -> GetGUID (). GetCounter (), this );
2015-09-28 17:20:27 +02:00
2022-03-26 18:59:44 +01:00
bones -> ReplaceAllCorpseDynamicFlags ( corpse -> GetCorpseDynamicFlags ());
2019-06-03 20:40:34 +02:00
bones -> SetOwnerGUID ( corpse -> m_corpseData -> Owner );
bones -> SetPartyGUID ( corpse -> m_corpseData -> PartyGUID );
bones -> SetGuildGUID ( corpse -> m_corpseData -> GuildGUID );
bones -> SetDisplayId ( corpse -> m_corpseData -> DisplayID );
bones -> SetRace ( corpse -> m_corpseData -> RaceID );
bones -> SetSex ( corpse -> m_corpseData -> Sex );
2020-12-13 18:38:31 +01:00
bones -> SetClass ( corpse -> m_corpseData -> Class );
2020-12-01 21:08:06 +01:00
bones -> SetCustomizations ( Trinity :: Containers :: MakeIteratorPair ( corpse -> m_corpseData -> Customizations . begin (), corpse -> m_corpseData -> Customizations . end ()));
2022-03-26 18:59:44 +01:00
bones -> ReplaceAllFlags ( corpse -> m_corpseData -> Flags | CORPSE_FLAG_BONES );
2019-06-03 20:40:34 +02:00
bones -> SetFactionTemplate ( corpse -> m_corpseData -> FactionTemplate );
2015-10-28 21:43:44 +01:00
bones -> SetCellCoord ( corpse -> GetCellCoord ());
2015-09-28 17:20:27 +02:00
bones -> Relocate ( corpse -> GetPositionX (), corpse -> GetPositionY (), corpse -> GetPositionZ (), corpse -> GetOrientation ());
2018-01-28 11:37:20 +01:00
PhasingHandler :: InheritPhaseShift ( bones , corpse );
2015-09-28 17:20:27 +02:00
2022-04-24 14:31:55 +02:00
AddCorpse ( bones );
2022-04-23 23:03:22 +02:00
bones -> UpdatePositionData ();
bones -> SetZoneScript ();
2015-10-28 21:43:44 +01:00
2015-09-28 17:20:27 +02:00
// add bones in grid store if grid loaded where corpse placed
AddToMap ( bones );
}
// all references to the corpse should be removed at this point
delete corpse ;
return bones ;
}
void Map :: RemoveOldCorpses ()
{
2017-12-10 12:39:37 +01:00
time_t now = GameTime :: GetGameTime ();
2015-09-28 17:20:27 +02:00
std :: vector < ObjectGuid > corpses ;
corpses . reserve ( _corpsesByPlayer . size ());
for ( auto const & p : _corpsesByPlayer )
if ( p . second -> IsExpired ( now ))
corpses . push_back ( p . first );
for ( ObjectGuid const & ownerGuid : corpses )
ConvertCorpseToBones ( ownerGuid );
2015-10-28 21:43:44 +01:00
std :: vector < Corpse *> expiredBones ;
for ( Corpse * bones : _corpseBones )
if ( bones -> IsExpired ( now ))
expiredBones . push_back ( bones );
for ( Corpse * bones : expiredBones )
{
RemoveCorpse ( bones );
delete bones ;
}
2015-09-28 17:20:27 +02:00
}
2017-09-14 22:33:40 +02:00
void Map :: SendZoneDynamicInfo ( uint32 zoneId , Player * player ) const
2014-03-22 19:40:36 +01:00
{
2017-09-14 22:33:40 +02:00
auto itr = _zoneDynamicInfo . find ( zoneId );
2014-03-22 19:40:36 +01:00
if ( itr == _zoneDynamicInfo . end ())
return ;
if ( uint32 music = itr -> second . MusicId )
2015-03-11 20:04:57 +01:00
player -> SendDirectMessage ( WorldPackets :: Misc :: PlayMusic ( music ). Write ());
2014-03-22 19:40:36 +01:00
2017-09-14 22:33:40 +02:00
SendZoneWeather ( itr -> second , player );
2014-03-22 19:40:36 +01:00
2020-03-06 17:44:10 +01:00
for ( ZoneDynamicInfo :: LightOverride const & lightOverride : itr -> second . LightOverrides )
2014-03-22 19:40:36 +01:00
{
2016-02-29 23:54:20 +01:00
WorldPackets :: Misc :: OverrideLight overrideLight ;
2020-03-06 17:44:10 +01:00
overrideLight . AreaLightID = lightOverride . AreaLightId ;
overrideLight . OverrideLightID = lightOverride . OverrideLightId ;
overrideLight . TransitionMilliseconds = lightOverride . TransitionMilliseconds ;
2016-02-29 23:54:20 +01:00
player -> SendDirectMessage ( overrideLight . Write ());
2014-03-22 19:40:36 +01:00
}
}
2017-09-14 22:33:40 +02:00
void Map :: SendZoneWeather ( uint32 zoneId , Player * player ) const
2014-03-22 19:40:36 +01:00
{
2017-09-14 22:33:40 +02:00
if ( ! player -> HasAuraType ( SPELL_AURA_FORCE_WEATHER ))
{
auto itr = _zoneDynamicInfo . find ( zoneId );
if ( itr == _zoneDynamicInfo . end ())
return ;
SendZoneWeather ( itr -> second , player );
}
}
void Map :: SendZoneWeather ( ZoneDynamicInfo const & zoneDynamicInfo , Player * player ) const
{
if ( WeatherState weatherId = zoneDynamicInfo . WeatherId )
{
2020-02-28 22:23:20 +04:00
WorldPackets :: Misc :: Weather weather ( weatherId , zoneDynamicInfo . Intensity );
2017-09-14 22:33:40 +02:00
player -> SendDirectMessage ( weather . Write ());
}
else if ( zoneDynamicInfo . DefaultWeather )
{
zoneDynamicInfo . DefaultWeather -> SendWeatherUpdateToPlayer ( player );
}
else
Weather :: SendFineWeatherUpdateToPlayer ( player );
}
2014-03-22 19:40:36 +01:00
2017-09-14 22:33:40 +02:00
void Map :: SetZoneMusic ( uint32 zoneId , uint32 musicId )
{
2014-03-22 19:40:36 +01:00
_zoneDynamicInfo [ zoneId ]. MusicId = musicId ;
Map :: PlayerList const & players = GetPlayers ();
if ( ! players . isEmpty ())
{
2017-09-14 22:33:40 +02:00
WorldPackets :: Misc :: PlayMusic playMusic ( musicId );
playMusic . Write ();
2014-03-22 19:40:36 +01:00
for ( Map :: PlayerList :: const_iterator itr = players . begin (); itr != players . end (); ++ itr )
if ( Player * player = itr -> GetSource ())
2017-09-14 22:33:40 +02:00
if ( player -> GetZoneId () == zoneId && ! player -> HasAuraType ( SPELL_AURA_FORCE_WEATHER ))
player -> SendDirectMessage ( playMusic . GetRawPacket ());
2014-03-22 19:40:36 +01:00
}
}
2017-09-14 22:33:40 +02:00
Weather * Map :: GetOrGenerateZoneDefaultWeather ( uint32 zoneId )
2014-03-22 19:40:36 +01:00
{
2017-09-14 22:33:40 +02:00
WeatherData const * weatherData = WeatherMgr :: GetWeatherData ( zoneId );
if ( ! weatherData )
return nullptr ;
2014-03-22 19:40:36 +01:00
2017-09-14 22:33:40 +02:00
ZoneDynamicInfo & info = _zoneDynamicInfo [ zoneId ];
if ( ! info . DefaultWeather )
{
2020-06-23 08:54:12 +02:00
info . DefaultWeather = std :: make_unique < Weather > ( zoneId , weatherData );
2017-09-14 22:33:40 +02:00
info . DefaultWeather -> ReGenerate ();
info . DefaultWeather -> UpdateWeather ();
}
return info . DefaultWeather . get ();
}
2021-06-04 21:47:43 +02:00
WeatherState Map :: GetZoneWeather ( uint32 zoneId ) const
{
ZoneDynamicInfo const * zoneDynamicInfo = Trinity :: Containers :: MapGetValuePtr ( _zoneDynamicInfo , zoneId );
if ( zoneDynamicInfo )
{
if ( WeatherState weatherId = zoneDynamicInfo -> WeatherId )
return weatherId ;
if ( zoneDynamicInfo -> DefaultWeather )
return zoneDynamicInfo -> DefaultWeather -> GetWeatherState ();
}
return WEATHER_STATE_FINE ;
}
2020-02-28 22:23:20 +04:00
void Map :: SetZoneWeather ( uint32 zoneId , WeatherState weatherId , float intensity )
2017-09-14 22:33:40 +02:00
{
2014-03-22 19:40:36 +01:00
ZoneDynamicInfo & info = _zoneDynamicInfo [ zoneId ];
info . WeatherId = weatherId ;
2020-02-28 22:23:20 +04:00
info . Intensity = intensity ;
2014-03-22 19:40:36 +01:00
2017-09-14 22:33:40 +02:00
Map :: PlayerList const & players = GetPlayers ();
2014-03-22 19:40:36 +01:00
if ( ! players . isEmpty ())
{
2020-02-28 22:23:20 +04:00
WorldPackets :: Misc :: Weather weather ( weatherId , intensity );
2017-09-14 22:33:40 +02:00
weather . Write ();
2014-03-22 19:40:36 +01:00
for ( Map :: PlayerList :: const_iterator itr = players . begin (); itr != players . end (); ++ itr )
if ( Player * player = itr -> GetSource ())
if ( player -> GetZoneId () == zoneId )
2017-09-14 22:33:40 +02:00
player -> SendDirectMessage ( weather . GetRawPacket ());
2014-03-22 19:40:36 +01:00
}
}
2021-05-11 05:33:05 -05:00
void Map :: SetZoneOverrideLight ( uint32 zoneId , uint32 areaLightId , uint32 overrideLightId , Milliseconds transitionTime )
2014-03-22 19:40:36 +01:00
{
ZoneDynamicInfo & info = _zoneDynamicInfo [ zoneId ];
2020-03-06 17:44:10 +01:00
// client can support only one override for each light (zone independent)
info . LightOverrides . erase ( std :: remove_if ( info . LightOverrides . begin (), info . LightOverrides . end (), [ areaLightId ]( ZoneDynamicInfo :: LightOverride const & lightOverride )
{
return lightOverride . AreaLightId == areaLightId ;
}), info . LightOverrides . end ());
// set new override (if any)
if ( overrideLightId )
{
ZoneDynamicInfo :: LightOverride & lightOverride = info . LightOverrides . emplace_back ();
lightOverride . AreaLightId = areaLightId ;
lightOverride . OverrideLightId = overrideLightId ;
2021-05-11 05:33:05 -05:00
lightOverride . TransitionMilliseconds = static_cast < uint32 > ( transitionTime . count ());
2020-03-06 17:44:10 +01:00
}
2014-03-22 19:40:36 +01:00
2017-09-14 22:33:40 +02:00
Map :: PlayerList const & players = GetPlayers ();
2014-03-22 19:40:36 +01:00
if ( ! players . isEmpty ())
{
2016-02-29 23:54:20 +01:00
WorldPackets :: Misc :: OverrideLight overrideLight ;
2020-03-06 17:44:10 +01:00
overrideLight . AreaLightID = areaLightId ;
2020-02-28 22:23:20 +04:00
overrideLight . OverrideLightID = overrideLightId ;
2021-05-11 05:33:05 -05:00
overrideLight . TransitionMilliseconds = static_cast < uint32 > ( transitionTime . count ());
2016-02-29 23:54:20 +01:00
overrideLight . Write ();
2014-03-22 19:40:36 +01:00
for ( Map :: PlayerList :: const_iterator itr = players . begin (); itr != players . end (); ++ itr )
if ( Player * player = itr -> GetSource ())
if ( player -> GetZoneId () == zoneId )
2016-02-29 23:54:20 +01:00
player -> SendDirectMessage ( overrideLight . GetRawPacket ());
2014-03-22 19:40:36 +01:00
}
}
2014-04-26 03:40:30 +02:00
void Map :: UpdateAreaDependentAuras ()
{
Map :: PlayerList const & players = GetPlayers ();
for ( Map :: PlayerList :: const_iterator itr = players . begin (); itr != players . end (); ++ itr )
2020-09-04 13:38:24 +02:00
{
2014-04-26 03:40:30 +02:00
if ( Player * player = itr -> GetSource ())
2020-09-04 13:38:24 +02:00
{
2014-04-26 03:40:30 +02:00
if ( player -> IsInWorld ())
{
player -> UpdateAreaDependentAuras ( player -> GetAreaId ());
player -> UpdateZoneDependentAuras ( player -> GetZoneId ());
}
2020-09-04 13:38:24 +02:00
}
}
2014-04-26 03:40:30 +02:00
}
2018-12-14 22:01:16 +01:00
std :: string Map :: GetDebugInfo () const
{
std :: stringstream sstr ;
sstr << std :: boolalpha
<< "Id: " << GetId () << " InstanceId: " << GetInstanceId () << " Difficulty: " << std :: to_string ( GetDifficultyID ())
<< " HasPlayers: " << HavePlayers ();
return sstr . str ();
}
std :: string InstanceMap :: GetDebugInfo () const
{
std :: stringstream sstr ;
sstr << Map :: GetDebugInfo () << " \n "
<< std :: boolalpha
<< "ScriptId: " << GetScriptId () << " ScriptName: " << GetScriptName ();
return sstr . str ();
}
2023-01-07 22:38:21 +01:00
template class TC_GAME_API TypeUnorderedMapContainer < AllMapStoredObjectTypes , ObjectGuid > ;