diff options
Diffstat (limited to 'src/game/Map.cpp')
-rw-r--r-- | src/game/Map.cpp | 1157 |
1 files changed, 820 insertions, 337 deletions
diff --git a/src/game/Map.cpp b/src/game/Map.cpp index b12c7b477c0..c6c74a74493 100644 --- a/src/game/Map.cpp +++ b/src/game/Map.cpp @@ -21,7 +21,6 @@ #include "MapManager.h" #include "Player.h" #include "GridNotifiers.h" -#include "WorldSession.h" #include "Log.h" #include "GridStates.h" #include "CellImpl.h" @@ -36,6 +35,7 @@ #include "ScriptCalls.h" #include "Group.h" #include "MapRefManager.h" +#include "Vehicle.h" #include "MapInstanced.h" #include "InstanceSaveMgr.h" @@ -44,9 +44,6 @@ #define DEFAULT_GRID_EXPIRY 300 #define MAX_GRID_LOAD_TIME 50 -// magic *.map header -const char MAP_MAGIC[] = "MAP_2.50"; - GridState* si_GridStates[MAX_GRID_STATE]; Map::~Map() @@ -54,11 +51,11 @@ Map::~Map() UnloadAll(); } -bool Map::ExistMap(uint32 mapid,int x,int y) +bool Map::ExistMap(uint32 mapid,int gx,int gy) { int len = sWorld.GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1; char* tmp = new char[len]; - snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),mapid,x,y); + snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),mapid,gx,gy); FILE *pf=fopen(tmp,"rb"); @@ -69,9 +66,10 @@ bool Map::ExistMap(uint32 mapid,int x,int y) return false; } - char magic[8]; - fread(magic,1,8,pf); - if(strncmp(MAP_MAGIC,magic,8)) + map_fileheader header; + fread(&header, sizeof(header), 1, pf); + if (header.mapMagic != uint32(MAP_MAGIC) || + header.versionMagic != uint32(MAP_VERSION_MAGIC)) { sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.",tmp); delete [] tmp; @@ -84,17 +82,17 @@ bool Map::ExistMap(uint32 mapid,int x,int y) return true; } -bool Map::ExistVMap(uint32 mapid,int x,int y) +bool Map::ExistVMap(uint32 mapid,int gx,int gy) { if(VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager()) { if(vmgr->isMapLoadingEnabled()) { // x and y are swapped !! => fixed now - bool exists = vmgr->existsMap((sWorld.GetDataPath()+ "vmaps").c_str(), mapid, x,y); + bool exists = vmgr->existsMap((sWorld.GetDataPath()+ "vmaps").c_str(), mapid, gx,gy); if(!exists) { - std::string name = vmgr->getDirFileName(mapid,x,y); + std::string name = vmgr->getDirFileName(mapid,gx,gy); sLog.outError("VMap file '%s' is missing or point to wrong version vmap file, redo vmaps with latest vmap_assembler.exe program", (sWorld.GetDataPath()+"vmaps/"+name).c_str()); return false; } @@ -104,91 +102,70 @@ bool Map::ExistVMap(uint32 mapid,int x,int y) return true; } -void Map::LoadVMap(int x,int y) +void Map::LoadVMap(int gx,int gy) { // x and y are swapped !! - int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapManager()->loadMap((sWorld.GetDataPath()+ "vmaps").c_str(), GetId(), x,y); + int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapManager()->loadMap((sWorld.GetDataPath()+ "vmaps").c_str(), GetId(), gx,gy); switch(vmapLoadResult) { case VMAP::VMAP_LOAD_RESULT_OK: - sLog.outDetail("VMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), x,y, x,y); + sLog.outDetail("VMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx,gy,gx,gy); break; case VMAP::VMAP_LOAD_RESULT_ERROR: - sLog.outDetail("Could not load VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), x,y, x,y); + sLog.outDetail("Could not load VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx,gy,gx,gy); break; case VMAP::VMAP_LOAD_RESULT_IGNORED: - DEBUG_LOG("Ignored VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), x,y, x,y); + DEBUG_LOG("Ignored VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx,gy,gx,gy); break; } } -void Map::LoadMap(uint32 mapid, uint32 instanceid, int x,int y) +void Map::LoadMap(int gx,int gy) { - if( instanceid != 0 ) + if( i_InstanceId != 0 ) { - if(GridMaps[x][y]) + if(GridMaps[gx][gy]) return; - Map* baseMap = const_cast<Map*>(MapManager::Instance().GetBaseMap(mapid)); - - // load gridmap for base map - if (!baseMap->GridMaps[x][y]) - baseMap->EnsureGridCreated(GridPair(63-x,63-y)); + Map* baseMap = const_cast<Map*>(MapManager::Instance().GetBaseMap(i_id)); -//+++ if (!baseMap->GridMaps[x][y]) don't check for GridMaps[gx][gy], we need the management for vmaps -// return; + // load grid map for base map + if (!baseMap->GridMaps[gx][gy]) + baseMap->EnsureGridCreated(GridPair(63-gx,63-gy)); - ((MapInstanced*)(baseMap))->AddGridMapReference(GridPair(x,y)); - GridMaps[x][y] = baseMap->GridMaps[x][y]; + ((MapInstanced*)(baseMap))->AddGridMapReference(GridPair(gx,gy)); + GridMaps[gx][gy] = baseMap->GridMaps[gx][gy]; return; } //map already load, delete it before reloading (Is it necessary? Do we really need the ability the reload maps during runtime?) - if(GridMaps[x][y]) + if(GridMaps[gx][gy]) { - sLog.outDetail("Unloading already loaded map %u before reloading.",mapid); - delete (GridMaps[x][y]); - GridMaps[x][y]=NULL; + sLog.outDetail("Unloading already loaded map %u before reloading.",i_id); + delete (GridMaps[gx][gy]); + GridMaps[gx][gy]=NULL; } // map file name char *tmp=NULL; - // Pihhan: dataPath length + "maps/" + 3+2+2+ ".map" length may be > 32 ! int len = sWorld.GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1; tmp = new char[len]; - snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),mapid,x,y); + snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),i_id,gx,gy); sLog.outDetail("Loading map %s",tmp); // loading data - FILE *pf=fopen(tmp,"rb"); - if(!pf) + GridMaps[gx][gy] = new GridMap(); + if (!GridMaps[gx][gy]->loadData(tmp)) { - delete [] tmp; - return; + sLog.outError("Error load map file: \n %s\n", tmp); } - - char magic[8]; - fread(magic,1,8,pf); - if(strncmp(MAP_MAGIC,magic,8)) - { - sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.",tmp); - delete [] tmp; - fclose(pf); //close file before return - return; - } - delete [] tmp; - - GridMap * buf= new GridMap; - fread(buf,1,sizeof(GridMap),pf); - fclose(pf); - - GridMaps[x][y] = buf; + delete [] tmp; } -void Map::LoadMapAndVMap(uint32 mapid, uint32 instanceid, int x,int y) +void Map::LoadMapAndVMap(int gx,int gy) { - LoadMap(mapid,instanceid,x,y); - if(instanceid == 0) - LoadVMap(x, y); // Only load the data for the base map + LoadMap(gx,gy); + if(i_InstanceId == 0) + LoadVMap(gx, gy); // Only load the data for the base map } void Map::InitStateMachine() @@ -256,7 +233,7 @@ template<> void Map::AddToGrid(Creature* obj, NGridType *grid, Cell const& cell) { // add to world object registry in grid - if(obj->isPet() || obj->IsTempWorldObject) + if(obj->isWorldCreature() || obj->IsTempWorldObject) { (*grid)(cell.CellX(), cell.CellY()).AddWorldObject<Creature>(obj, obj->GetGUID()); } @@ -308,7 +285,7 @@ template<> void Map::RemoveFromGrid(Creature* obj, NGridType *grid, Cell const& cell) { // remove from world object registry in grid - if(obj->isPet() || obj->IsTempWorldObject) + if(obj->isWorldCreature() || obj->IsTempWorldObject) { (*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject<Creature>(obj, obj->GetGUID()); } @@ -410,7 +387,7 @@ Map::EnsureGridCreated(const GridPair &p) Guard guard(*this); if(!getNGrid(p.x_coord, p.y_coord)) { - sLog.outDebug("Loading grid[%u,%u] for map %u", p.x_coord, p.y_coord, i_id); + sLog.outDebug("Creating grid[%u,%u] for map %u instance %u", p.x_coord, p.y_coord, i_id, i_InstanceId); setNGrid(new NGridType(p.x_coord*MAX_NUMBER_OF_GRIDS + p.y_coord, p.x_coord, p.y_coord, i_gridExpiry, sWorld.getConfig(CONFIG_GRID_UNLOAD)), p.x_coord, p.y_coord); @@ -425,20 +402,20 @@ Map::EnsureGridCreated(const GridPair &p) int gy=63-p.y_coord; if(!GridMaps[gx][gy]) - Map::LoadMapAndVMap(i_id,i_InstanceId,gx,gy); + LoadMapAndVMap(gx,gy); } } } void -Map::EnsureGridLoaded(const Cell &cell, Player *player) +Map::EnsureGridLoadedAtEnter(const Cell &cell, Player *player) { - EnsureGridCreated(GridPair(cell.GridX(), cell.GridY())); - NGridType *grid = getNGrid(cell.GridX(), cell.GridY()); + NGridType *grid; - assert(grid != NULL); - if (!isGridObjectDataLoaded(cell.GridX(), cell.GridY())) + if(EnsureGridLoaded(cell)) { + grid = getNGrid(cell.GridX(), cell.GridY()); + if (player) { player->SendDelayResponse(MAX_GRID_LOAD_TIME); @@ -449,19 +426,37 @@ Map::EnsureGridLoaded(const Cell &cell, Player *player) DEBUG_LOG("Active object nearby triggers of loading grid [%u,%u] on map %u", cell.GridX(), cell.GridY(), i_id); } + ResetGridExpiry(*getNGrid(cell.GridX(), cell.GridY()), 0.1f); + grid->SetGridState(GRID_STATE_ACTIVE); + } + else + grid = getNGrid(cell.GridX(), cell.GridY()); + + if (player) + AddToGrid(player,grid,cell); +} + +bool Map::EnsureGridLoaded(const Cell &cell) +{ + EnsureGridCreated(GridPair(cell.GridX(), cell.GridY())); + NGridType *grid = getNGrid(cell.GridX(), cell.GridY()); + + assert(grid != NULL); + if( !isGridObjectDataLoaded(cell.GridX(), cell.GridY()) ) + { + sLog.outDebug("Loading grid[%u,%u] for map %u instance %u", cell.GridX(), cell.GridY(), i_id, i_InstanceId); + ObjectGridLoader loader(*grid, this, cell); loader.LoadN(); - setGridObjectDataLoaded(true, cell.GridX(), cell.GridY()); // Add resurrectable corpses to world object list in grid ObjectAccessor::Instance().AddCorpsesToGrid(GridPair(cell.GridX(),cell.GridY()),(*grid)(cell.CellX(), cell.CellY()), this); - ResetGridExpiry(*getNGrid(cell.GridX(), cell.GridY()), 0.1f); - grid->SetGridState(GRID_STATE_ACTIVE); + setGridObjectDataLoaded(true,cell.GridX(), cell.GridY()); + return true; } - - if(player) - AddToGrid(player,grid,cell); + + return false; } void Map::LoadGrid(float x, float y) @@ -480,7 +475,7 @@ bool Map::Add(Player *player) // update player state for other player and visa-versa CellPair p = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY()); Cell cell(p); - EnsureGridLoaded(cell, player); + EnsureGridLoadedAtEnter(cell, player); player->AddToWorld(); SendInitSelf(player); @@ -508,7 +503,7 @@ Map::Add(T *obj) Cell cell(p); if(obj->isActiveObject()) - EnsureGridLoaded(cell); + EnsureGridLoadedAtEnter(cell); else EnsureGridCreated(GridPair(cell.GridX(), cell.GridY())); @@ -517,12 +512,14 @@ Map::Add(T *obj) AddToGrid(obj,grid,cell); obj->AddToWorld(); - + if(obj->isActiveObject()) AddToActive(obj); DEBUG_LOG("Object %u enters grid[%u,%u]", GUID_LOPART(obj->GetGUID()), cell.GridX(), cell.GridY()); + //something, such as vehicle, needs to be update immediately + //if(obj->GetTypeId() != TYPEID_UNIT) UpdateObjectVisibility(obj,cell,p); //AddNotifier(obj); @@ -730,6 +727,13 @@ void Map::Update(const uint32 &t_diff) } } } + + if(plr->m_seer != plr && !plr->hasUnitState(UNIT_STAT_ONVEHICLE)) + { + Trinity::PlayerVisibilityNotifier notifier(*plr); + VisitAll(plr->m_seer->GetPositionX(), plr->m_seer->GetPositionY(), World::GetMaxVisibleDistance(), notifier); + notifier.Notify(); + } } // non-player active objects @@ -747,33 +751,6 @@ void Map::Update(const uint32 &t_diff) if(!obj->IsInWorld()) continue; - // Update bindsight players - /*if(obj->isType(TYPEMASK_UNIT)) - { - if(!((Unit*)obj)->GetSharedVisionList().empty()) - for(SharedVisionList::const_iterator itr = ((Unit*)obj)->GetSharedVisionList().begin(); itr != ((Unit*)obj)->GetSharedVisionList().end(); ++itr) - { - if(!*itr) - { - sLog.outError("unit %u has invalid shared vision player, list size %u", obj->GetEntry(), ((Unit*)obj)->GetSharedVisionList().size()); - continue; - } - Trinity::PlayerVisibilityNotifier notifier(**itr); - VisitAll(obj->GetPositionX(), obj->GetPositionY(), World::GetMaxVisibleDistance(), notifier); - notifier.Notify(); - } - } - else */if(obj->GetTypeId() == TYPEID_DYNAMICOBJECT) - { - if(Unit *caster = ((DynamicObject*)obj)->GetCaster()) - if(caster->GetTypeId() == TYPEID_PLAYER && caster->GetUInt64Value(PLAYER_FARSIGHT) == obj->GetGUID()) - { - Trinity::PlayerVisibilityNotifier notifier(*((Player*)caster)); - VisitAll(obj->GetPositionX(), obj->GetPositionY(), World::GetMaxVisibleDistance(), notifier); - notifier.Notify(); - } - } - CellPair standing_cell(Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY())); // Check for correctness of standing_cell, it also avoids problems with update_cell @@ -948,7 +925,7 @@ Map::PlayerRelocation(Player *player, float x, float y, float z, float orientati if( !old_cell.DiffGrid(new_cell) ) AddToGrid(player, oldGrid,new_cell); else - EnsureGridLoaded(new_cell, player); + EnsureGridLoadedAtEnter(new_cell, player); } AddUnitToNotify(player); @@ -986,6 +963,17 @@ Map::CreatureRelocation(Creature *creature, float x, float y, float z, float ang creature->Relocate(x, y, z, ang); AddUnitToNotify(creature); } + + if(creature->isVehicle()) + { + for(SeatMap::iterator itr = ((Vehicle*)creature)->m_Seats.begin(); itr != ((Vehicle*)creature)->m_Seats.end(); ++itr) + if(itr->second.passenger) + if(itr->second.passenger->GetTypeId() == TYPEID_PLAYER) + PlayerRelocation((Player*)itr->second.passenger, x, y, z, ang); + else + CreatureRelocation((Creature*)itr->second.passenger, x, y, z, ang); + } + assert(CheckGridIntegrity(creature,true)); } @@ -1067,7 +1055,7 @@ bool Map::CreatureCellRelocation(Creature *c, Cell new_cell) // in diff. grids but active creature if(c->isActiveObject()) { - EnsureGridLoaded(new_cell); + EnsureGridLoadedAtEnter(new_cell); #ifdef TRINITY_DEBUG if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0) @@ -1178,7 +1166,11 @@ bool Map::UnloadGrid(const uint32 &x, const uint32 &y, bool unloadAll) { if (i_InstanceId == 0) { - if(GridMaps[gx][gy]) delete (GridMaps[gx][gy]); + if(GridMaps[gx][gy]) + { + GridMaps[gx][gy]->unloadData(); + delete GridMaps[gx][gy]; + } // x and y are swapped VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId(), gy, gx); } @@ -1203,103 +1195,529 @@ void Map::UnloadAll() } } -float Map::GetHeight(float x, float y, float z, bool pUseVmaps) const +//***************************** +// Grid function +//***************************** +GridMap::GridMap() { - GridPair p = Trinity::ComputeGridPair(x, y); + m_flags = 0; + // Area data + m_gridArea = 0; + m_area_map = NULL; + // Height level data + m_gridHeight = INVALID_HEIGHT; + m_gridGetHeight = &GridMap::getHeightFromFlat; + m_V9 = NULL; + m_V8 = NULL; + // Liquid data + m_liquidType = 0; + m_liquid_offX = 0; + m_liquid_offY = 0; + m_liquid_width = 0; + m_liquid_height = 0; + m_liquidLevel = INVALID_HEIGHT; + m_liquid_type = NULL; + m_liquid_map = NULL; +} - // half opt method - int gx=(int)(32-x/SIZE_OF_GRIDS); //grid x - int gy=(int)(32-y/SIZE_OF_GRIDS); //grid y +GridMap::~GridMap() +{ + unloadData(); +} - float lx=128*(32 -x/SIZE_OF_GRIDS - gx); - float ly=128*(32 -y/SIZE_OF_GRIDS - gy); +bool GridMap::loadData(char *filename) +{ + // Unload old data if exist + unloadData(); - // ensure GridMap is loaded - const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy)); + map_fileheader header; + // Not return error if file not found + FILE *in = fopen(filename, "rb"); + if (!in) + return true; + fread(&header, sizeof(header),1,in); + if (header.mapMagic == uint32(MAP_MAGIC) && + header.versionMagic == uint32(MAP_VERSION_MAGIC)) + { + // loadup area data + if (header.areaMapOffset && !loadAreaData(in, header.areaMapOffset, header.areaMapSize)) + { + sLog.outError("Error loading map area data\n"); + fclose(in); + return false; + } + // loadup height data + if (header.heightMapOffset && !loadHeihgtData(in, header.heightMapOffset, header.heightMapSize)) + { + sLog.outError("Error loading map height data\n"); + fclose(in); + return false; + } + // loadup liquid data + if (header.liquidMapOffset && !loadLiquidData(in, header.liquidMapOffset, header.liquidMapSize)) + { + sLog.outError("Error loading map liquids data\n"); + fclose(in); + return false; + } + fclose(in); + return true; + } + sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.", filename); + fclose(in); + return false; +} - // find raw .map surface under Z coordinates - float mapHeight; - if(GridMap* gmap = GridMaps[gx][gy]) +void GridMap::unloadData() +{ + if (m_area_map) delete[] m_area_map; + if (m_V9) delete[] m_V9; + if (m_V8) delete[] m_V8; + if (m_liquid_type) delete[] m_liquid_type; + if (m_liquid_map) delete[] m_liquid_map; + m_area_map = NULL; + m_V9 = NULL; + m_V8 = NULL; + m_liquid_type = NULL; + m_liquid_map = NULL; + m_gridGetHeight = &GridMap::getHeightFromFlat; +} + +bool GridMap::loadAreaData(FILE *in, uint32 offset, uint32 size) +{ + map_areaHeader header; + fseek(in, offset, SEEK_SET); + fread(&header, sizeof(header), 1, in); + if (header.fourcc != uint32(MAP_AREA_MAGIC)) + return false; + + m_gridArea = header.gridArea; + if (!(header.flags & MAP_AREA_NO_AREA)) { - int lx_int = (int)lx; - int ly_int = (int)ly; + m_area_map = new uint16 [16*16]; + fread(m_area_map, sizeof(uint16), 16*16, in); + } + return true; +} - lx -= lx_int; - ly -= ly_int; +bool GridMap::loadHeihgtData(FILE *in, uint32 offset, uint32 size) +{ + map_heightHeader header; + fseek(in, offset, SEEK_SET); + fread(&header, sizeof(header), 1, in); + if (header.fourcc != uint32(MAP_HEIGTH_MAGIC)) + return false; - float a,b,c; - if (lx+ly < 1) + m_gridHeight = header.gridHeight; + if (!(header.flags & MAP_HEIGHT_NO_HIGHT)) + { + if ((header.flags & MAP_HEIGHT_AS_INT16)) { - if (lx > ly) - { - // 1 - float h1 = gmap->v9[lx_int][ly_int]; - float h2 = gmap->v9[lx_int+1][ly_int]; - float h5 = 2 * gmap->v8[lx_int][ly_int]; - a = h2-h1; - b = h5-h1-h2; - c = h1; - } - else - { - // 2 - float h1 = gmap->v9[lx_int][ly_int]; - float h3 = gmap->v9[lx_int][ly_int+1]; - float h5 = 2 * gmap->v8[lx_int][ly_int]; - a = h5 - h1 - h3; - b = h3 - h1; - c = h1; - } + m_uint16_V9 = new uint16 [129*129]; + m_uint16_V8 = new uint16 [128*128]; + fread(m_uint16_V9, sizeof(uint16), 129*129, in); + fread(m_uint16_V8, sizeof(uint16), 128*128, in); + m_gridIntHeightMultiplier = (header.gridMaxHeight - header.gridHeight) / 65535; + m_gridGetHeight = &GridMap::getHeightFromUint16; + } + else if ((header.flags & MAP_HEIGHT_AS_INT8)) + { + m_uint8_V9 = new uint8 [129*129]; + m_uint8_V8 = new uint8 [128*128]; + fread(m_uint8_V9, sizeof(uint8), 129*129, in); + fread(m_uint8_V8, sizeof(uint8), 128*128, in); + m_gridIntHeightMultiplier = (header.gridMaxHeight - header.gridHeight) / 255; + m_gridGetHeight = &GridMap::getHeightFromUint8; } else { - if (lx > ly) - { - // 3 - float h2 = gmap->v9[lx_int+1][ly_int]; - float h4 = gmap->v9[lx_int+1][ly_int+1]; - float h5 = 2 * gmap->v8[lx_int][ly_int]; - a = h2 + h4 - h5; - b = h4 - h2; - c = h5 - h4; - } - else - { - // 4 - float h3 = gmap->v9[lx_int][ly_int+1]; - float h4 = gmap->v9[lx_int+1][ly_int+1]; - float h5 = 2 * gmap->v8[lx_int][ly_int]; - a = h4 - h3; - b = h3 + h4 - h5; - c = h5 - h4; - } + m_V9 = new float [129*129]; + m_V8 = new float [128*128]; + fread(m_V9, sizeof(float), 129*129, in); + fread(m_V8, sizeof(float), 128*128, in); + m_gridGetHeight = &GridMap::getHeightFromFloat; + } + } + else + m_gridGetHeight = &GridMap::getHeightFromFlat; + return true; +} + +bool GridMap::loadLiquidData(FILE *in, uint32 offset, uint32 size) +{ + map_liquidHeader header; + fseek(in, offset, SEEK_SET); + fread(&header, sizeof(header), 1, in); + if (header.fourcc != uint32(MAP_LIQUID_MAGIC)) + return false; + + m_liquidType = header.liquidType; + m_liquid_offX = header.offsetX; + m_liquid_offY = header.offsetY; + m_liquid_width = header.width; + m_liquid_height= header.height; + m_liquidLevel = header.liquidLevel; + + if (!(header.flags&MAP_LIQUID_NO_TYPE)) + { + m_liquid_type = new uint8 [16*16]; + fread(m_liquid_type, sizeof(uint8), 16*16, in); + } + if (!(header.flags&MAP_LIQUID_NO_HIGHT)) + { + m_liquid_map = new float [m_liquid_width*m_liquid_height]; + fread(m_liquid_map, sizeof(float), m_liquid_width*m_liquid_height, in); + } + return true; +} + +uint16 GridMap::getArea(float x, float y) +{ + if (!m_area_map) + return m_gridArea; + + x = 16 * (32 - x/SIZE_OF_GRIDS); + y = 16 * (32 - y/SIZE_OF_GRIDS); + int lx = (int)x & 15; + int ly = (int)y & 15; + return m_area_map[lx*16 + ly]; +} + +float GridMap::getHeightFromFlat(float x, float y) const +{ + return m_gridHeight; +} + +float GridMap::getHeightFromFloat(float x, float y) const +{ + if (!m_V8 || !m_V9) + return m_gridHeight; + + x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS); + y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS); + + int x_int = (int)x; + int y_int = (int)y; + x -= x_int; + y -= y_int; + x_int&=(MAP_RESOLUTION - 1); + y_int&=(MAP_RESOLUTION - 1); + + // Height stored as: h5 - its v8 grid, h1-h4 - its v9 grid + // +--------------> X + // | h1-------h2 Coordinates is: + // | | \ 1 / | h1 0,0 + // | | \ / | h2 0,1 + // | | 2 h5 3 | h3 1,0 + // | | / \ | h4 1,1 + // | | / 4 \ | h5 1/2,1/2 + // | h3-------h4 + // V Y + // For find height need + // 1 - detect triangle + // 2 - solve linear equation from triangle points + // Calculate coefficients for solve h = a*x + b*y + c + + float a,b,c; + // Select triangle: + if (x+y < 1) + { + if (x > y) + { + // 1 triangle (h1, h2, h5 points) + float h1 = m_V9[(x_int )*129 + y_int]; + float h2 = m_V9[(x_int+1)*129 + y_int]; + float h5 = 2 * m_V8[x_int*128 + y_int]; + a = h2-h1; + b = h5-h1-h2; + c = h1; + } + else + { + // 2 triangle (h1, h3, h5 points) + float h1 = m_V9[x_int*129 + y_int ]; + float h3 = m_V9[x_int*129 + y_int+1]; + float h5 = 2 * m_V8[x_int*128 + y_int]; + a = h5 - h1 - h3; + b = h3 - h1; + c = h1; + } + } + else + { + if (x > y) + { + // 3 triangle (h2, h4, h5 points) + float h2 = m_V9[(x_int+1)*129 + y_int ]; + float h4 = m_V9[(x_int+1)*129 + y_int+1]; + float h5 = 2 * m_V8[x_int*128 + y_int]; + a = h2 + h4 - h5; + b = h4 - h2; + c = h5 - h4; + } + else + { + // 4 triangle (h3, h4, h5 points) + float h3 = m_V9[(x_int )*129 + y_int+1]; + float h4 = m_V9[(x_int+1)*129 + y_int+1]; + float h5 = 2 * m_V8[x_int*128 + y_int]; + a = h4 - h3; + b = h3 + h4 - h5; + c = h5 - h4; + } + } + // Calculate height + return a * x + b * y + c; +} + +float GridMap::getHeightFromUint8(float x, float y) const +{ + if (!m_uint8_V8 || !m_uint8_V9) + return m_gridHeight; + + x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS); + y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS); + + int x_int = (int)x; + int y_int = (int)y; + x -= x_int; + y -= y_int; + x_int&=(MAP_RESOLUTION - 1); + y_int&=(MAP_RESOLUTION - 1); + + int32 a, b, c; + uint8 *V9_h1_ptr = &m_uint8_V9[x_int*128 + x_int + y_int]; + if (x+y < 1) + { + if (x > y) + { + // 1 triangle (h1, h2, h5 points) + int32 h1 = V9_h1_ptr[ 0]; + int32 h2 = V9_h1_ptr[129]; + int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int]; + a = h2-h1; + b = h5-h1-h2; + c = h1; } - float _mapheight = a * lx + b * ly + c; - - // In some very rare case this will happen. Need find a better way. - if(lx_int == MAP_RESOLUTION) --lx_int; - if(ly_int == MAP_RESOLUTION) --ly_int; - - /* - float zi[4]; - // Probe 4 nearest points (except border cases) - zi[0] = gmap->Z[lx_int][ly_int]; - zi[1] = lx < MAP_RESOLUTION-1 ? gmap->Z[lx_int+1][ly_int] : zi[0]; - zi[2] = ly < MAP_RESOLUTION-1 ? gmap->Z[lx_int][ly_int+1] : zi[0]; - zi[3] = lx < MAP_RESOLUTION-1 && ly < MAP_RESOLUTION-1 ? gmap->Z[lx_int+1][ly_int+1] : zi[0]; - // Recalculate them like if their x,y positions were in the range 0,1 - float b[4]; - b[0] = zi[0]; - b[1] = zi[1]-zi[0]; - b[2] = zi[2]-zi[0]; - b[3] = zi[0]-zi[1]-zi[2]+zi[3]; - // Normalize the dx and dy to be in range 0..1 - float fact_x = lx - lx_int; - float fact_y = ly - ly_int; - // Use the simplified bilinear equation, as described in [url="http://en.wikipedia.org/wiki/Bilinear_interpolation"]http://en.wikipedia.org/wiki/Bilinear_interpolation[/url] - float _mapheight = b[0] + (b[1]*fact_x) + (b[2]*fact_y) + (b[3]*fact_x*fact_y); - - */ + else + { + // 2 triangle (h1, h3, h5 points) + int32 h1 = V9_h1_ptr[0]; + int32 h3 = V9_h1_ptr[1]; + int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int]; + a = h5 - h1 - h3; + b = h3 - h1; + c = h1; + } + } + else + { + if (x > y) + { + // 3 triangle (h2, h4, h5 points) + int32 h2 = V9_h1_ptr[129]; + int32 h4 = V9_h1_ptr[130]; + int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int]; + a = h2 + h4 - h5; + b = h4 - h2; + c = h5 - h4; + } + else + { + // 4 triangle (h3, h4, h5 points) + int32 h3 = V9_h1_ptr[ 1]; + int32 h4 = V9_h1_ptr[130]; + int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int]; + a = h4 - h3; + b = h3 + h4 - h5; + c = h5 - h4; + } + } + // Calculate height + return (float)((a * x) + (b * y) + c)*m_gridIntHeightMultiplier + m_gridHeight; +} + +float GridMap::getHeightFromUint16(float x, float y) const +{ + if (!m_uint16_V8 || !m_uint16_V9) + return m_gridHeight; + + x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS); + y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS); + + int x_int = (int)x; + int y_int = (int)y; + x -= x_int; + y -= y_int; + x_int&=(MAP_RESOLUTION - 1); + y_int&=(MAP_RESOLUTION - 1); + + int32 a, b, c; + uint16 *V9_h1_ptr = &m_uint16_V9[x_int*128 + x_int + y_int]; + if (x+y < 1) + { + if (x > y) + { + // 1 triangle (h1, h2, h5 points) + int32 h1 = V9_h1_ptr[ 0]; + int32 h2 = V9_h1_ptr[129]; + int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int]; + a = h2-h1; + b = h5-h1-h2; + c = h1; + } + else + { + // 2 triangle (h1, h3, h5 points) + int32 h1 = V9_h1_ptr[0]; + int32 h3 = V9_h1_ptr[1]; + int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int]; + a = h5 - h1 - h3; + b = h3 - h1; + c = h1; + } + } + else + { + if (x > y) + { + // 3 triangle (h2, h4, h5 points) + int32 h2 = V9_h1_ptr[129]; + int32 h4 = V9_h1_ptr[130]; + int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int]; + a = h2 + h4 - h5; + b = h4 - h2; + c = h5 - h4; + } + else + { + // 4 triangle (h3, h4, h5 points) + int32 h3 = V9_h1_ptr[ 1]; + int32 h4 = V9_h1_ptr[130]; + int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int]; + a = h4 - h3; + b = h3 + h4 - h5; + c = h5 - h4; + } + } + // Calculate height + return (float)((a * x) + (b * y) + c)*m_gridIntHeightMultiplier + m_gridHeight; +} + +float GridMap::getLiquidLevel(float x, float y) +{ + if (!m_liquid_map) + return m_liquidLevel; + + x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS); + y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS); + + int cx_int = ((int)x & (MAP_RESOLUTION-1)) - m_liquid_offY; + int cy_int = ((int)y & (MAP_RESOLUTION-1)) - m_liquid_offX; + + if (cx_int < 0 || cx_int >=m_liquid_height) + return INVALID_HEIGHT; + if (cy_int < 0 || cy_int >=m_liquid_width ) + return INVALID_HEIGHT; + + return m_liquid_map[cx_int*m_liquid_width + cy_int]; +} + +uint8 GridMap::getTerrainType(float x, float y) +{ + if (!m_liquid_type) + return m_liquidType; + + x = 16 * (32 - x/SIZE_OF_GRIDS); + y = 16 * (32 - y/SIZE_OF_GRIDS); + int lx = (int)x & 15; + int ly = (int)y & 15; + return m_liquid_type[lx*16 + ly]; +} + +// Get water state on map +inline ZLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData *data) +{ + // Check water type (if no water return) + if (!m_liquid_type && !m_liquidType) + return LIQUID_MAP_NO_WATER; + + // Get cell + float cx = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS); + float cy = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS); + + int x_int = (int)cx & (MAP_RESOLUTION-1); + int y_int = (int)cy & (MAP_RESOLUTION-1); + + // Check water type in cell + uint8 type = m_liquid_type ? m_liquid_type[(x_int>>3)*16 + (y_int>>3)] : m_liquidType; + if (type == 0) + return LIQUID_MAP_NO_WATER; + + // Check req liquid type mask + if (ReqLiquidType && !(ReqLiquidType&type)) + return LIQUID_MAP_NO_WATER; + + // Check water level: + // Check water height map + int lx_int = x_int - m_liquid_offY; + int ly_int = y_int - m_liquid_offX; + if (lx_int < 0 || lx_int >=m_liquid_height) + return LIQUID_MAP_NO_WATER; + if (ly_int < 0 || ly_int >=m_liquid_width ) + return LIQUID_MAP_NO_WATER; + + // Get water level + float liquid_level = m_liquid_map ? m_liquid_map[lx_int*m_liquid_width + ly_int] : m_liquidLevel; + // Get ground level (sub 0.2 for fix some errors) + float ground_level = getHeight(x, y); + + // Check water level and ground level + if (liquid_level < ground_level || z < ground_level - 2) + return LIQUID_MAP_NO_WATER; + + // All ok in water -> store data + if (data) + { + data->type = type; + data->level = liquid_level; + data->depth_level = ground_level; + } + + // For speed check as int values + int delta = int((liquid_level - z) * 10); + + // Get position delta + if (delta > 20) // Under water + return LIQUID_MAP_UNDER_WATER; + if (delta > 0 ) // In water + return LIQUID_MAP_IN_WATER; + if (delta > -1) // Walk on water + return LIQUID_MAP_WATER_WALK; + // Above water + return LIQUID_MAP_ABOVE_WATER; +} + +inline GridMap *Map::GetGrid(float x, float y) +{ + // half opt method + int gx=(int)(32-x/SIZE_OF_GRIDS); //grid x + int gy=(int)(32-y/SIZE_OF_GRIDS); //grid y + + // ensure GridMap is loaded + EnsureGridCreated(GridPair(63-gx,63-gy)); + + return GridMaps[gx][gy]; +} + +float Map::GetHeight(float x, float y, float z, bool pUseVmaps) const +{ + // find raw .map surface under Z coordinates + float mapHeight; + if(GridMap *gmap = const_cast<Map*>(this)->GetGrid(x, y)) + { + float _mapheight = gmap->getHeight(x,y); + // look from a bit higher pos to find the floor, ignore under surface case if(z + 2.0f > _mapheight) mapHeight = _mapheight; @@ -1375,76 +1793,122 @@ float Map::GetVmapHeight(float x, float y, float z, bool useMaps) const return vmapHeight; } -uint16 Map::GetAreaFlag(float x, float y ) const +uint16 Map::GetAreaFlag(float x, float y, float z) const { - //local x,y coords - float lx,ly; - int gx,gy; - GridPair p = Trinity::ComputeGridPair(x, y); - - // half opt method - gx=(int)(32-x/SIZE_OF_GRIDS) ; //grid x - gy=(int)(32-y/SIZE_OF_GRIDS); //grid y - - lx=16*(32 -x/SIZE_OF_GRIDS - gx); - ly=16*(32 -y/SIZE_OF_GRIDS - gy); - //DEBUG_LOG("my %d %d si %d %d",gx,gy,p.x_coord,p.y_coord); - - // ensure GridMap is loaded - const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy)); - - if(GridMaps[gx][gy]) - return GridMaps[gx][gy]->area_flag[(int)(lx)][(int)(ly)]; + uint16 areaflag; + if(GridMap *gmap = const_cast<Map*>(this)->GetGrid(x, y)) + areaflag = gmap->getArea(x, y); // this used while not all *.map files generated (instances) else - return GetAreaFlagByMapId(i_id); + areaflag = GetAreaFlagByMapId(i_id); + + //FIXME: some hacks for areas above or underground for ground area + // required for area specific spells/etc, until map/vmap data + // not provided correct areaflag with this hacks + switch(areaflag) + { + // Acherus: The Ebon Hold (Plaguelands: The Scarlet Enclave) + case 1984: // Plaguelands: The Scarlet Enclave + case 2076: // Death's Breach (Plaguelands: The Scarlet Enclave) + case 2745: // The Noxious Pass (Plaguelands: The Scarlet Enclave) + if(z > 350.0f) areaflag = 2048; break; + // Acherus: The Ebon Hold (Eastern Plaguelands) + case 856: // The Noxious Glade (Eastern Plaguelands) + case 2456: // Death's Breach (Eastern Plaguelands) + if(z > 350.0f) areaflag = 1950; break; + // Dalaran + case 1593: // Crystalsong Forest + case 2484: // The Twilight Rivulet (Crystalsong Forest) + case 2492: // Forlorn Woods (Crystalsong Forest) + if (x > 5568.0f && x < 6116.0f && y > 282.0f && y < 982.0f && z > 563.0f) areaflag = 2153; break; + // Maw of Neltharion (cave) + case 164: // Dragonblight + case 1797: // Obsidian Dragonshrine (Dragonblight) + case 1827: // Wintergrasp + case 2591: // The Cauldron of Flames (Wintergrasp) + if (x > 4364.0f && x < 4632.0f && y > 1545.0f && y < 1886.0f && z < 200.0f) areaflag = 1853; break; + // Undercity (sewers enter and path) + case 179: // Tirisfal Glades + if (x > 1595.0f && x < 1699.0f && y > 535.0f && y < 643.5f && z < 30.5f) areaflag = 685; break; + // Undercity (Royal Quarter) + case 210: // Silverpine Forest + case 316: // The Shining Strand (Silverpine Forest) + case 438: // Lordamere Lake (Silverpine Forest) + if (x > 1237.0f && x < 1401.0f && y > 284.0f && y < 440.0f && z < -40.0f) areaflag = 685; break; + // Undercity (cave and ground zone, part of royal quarter) + case 607: // Ruins of Lordaeron (Tirisfal Glades) + // ground and near to ground (by city walls) + if(z > 0.0f) + { + if (x > 1510.0f && x < 1839.0f && y > 29.77f && y < 433.0f) areaflag = 685; + } + // more wide underground, part of royal quarter + else + { + if (x > 1299.0f && x < 1839.0f && y > 10.0f && y < 440.0f) areaflag = 685; + } + break; + // The Makers' Perch (ground) and Makers' Overlook (ground and cave) + case 1335: // Sholazar Basin + // The Makers' Perch ground (fast box) + if (x > 6100.0f && x < 6250.0f && y > 5650.0f && y < 5800.0f) + { + // nice slow circle + if ((x-6183.0f)*(x-6183.0f)+(y-5717.0f)*(y-5717.0f) < 2500.0f) + areaflag = 2189; + } + // Makers' Overlook (ground and cave) + else if (x > 5634.48f && x < 5774.53f && y < 3475.0f && z > 300.0f) + { + if(y > 3380.26f || y > 3265.0f && z < 360.0f) areaflag = 2187; + } + break; + // The Makers' Perch (underground) + case 2147: // The Stormwright's Shelf (Sholazar Basin) + if (x > 6199.0f && x < 6283.0f && y > 5705.0f && y < 5817.0f && z < 38.0f) areaflag = 2189; break; + // Makers' Overlook (deep cave) + case 267: // Icecrown + if (x > 5684.0f && x < 5798.0f && y > 3035.0f && y < 3367.0f && z < 358.0f) areaflag = 2187; break; + // Wyrmrest Temple (Dragonblight) + case 1814: // Path of the Titans (Dragonblight) + case 1897: // The Dragon Wastes (Dragonblight) + // fast box + if (x > 3400.0f && x < 3700.0f && y > 130.0f && y < 420.0f) + { + // nice slow circle + if ((x-3546.87f)*(x-3546.87f)+(y-272.71f)*(y-272.71f) < 19600.0f) areaflag = 1791; + } + break; + } + + return areaflag; } uint8 Map::GetTerrainType(float x, float y ) const { - //local x,y coords - float lx,ly; - int gx,gy; - - // half opt method - gx=(int)(32-x/SIZE_OF_GRIDS) ; //grid x - gy=(int)(32-y/SIZE_OF_GRIDS); //grid y - - lx=16*(32 -x/SIZE_OF_GRIDS - gx); - ly=16*(32 -y/SIZE_OF_GRIDS - gy); - - // ensure GridMap is loaded - const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy)); - - if(GridMaps[gx][gy]) - return GridMaps[gx][gy]->terrain_type[(int)(lx)][(int)(ly)]; + if(GridMap *gmap = const_cast<Map*>(this)->GetGrid(x, y)) + return gmap->getTerrainType(x, y); else return 0; } -float Map::GetWaterLevel(float x, float y ) const +ZLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData *data) const { - //local x,y coords - float lx,ly; - int gx,gy; - - // half opt method - gx=(int)(32-x/SIZE_OF_GRIDS) ; //grid x - gy=(int)(32-y/SIZE_OF_GRIDS); //grid y - - lx=128*(32 -x/SIZE_OF_GRIDS - gx); - ly=128*(32 -y/SIZE_OF_GRIDS - gy); - - // ensure GridMap is loaded - const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy)); + if(GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y)) + return gmap->getLiquidStatus(x, y, z, ReqLiquidType, data); + else + return LIQUID_MAP_NO_WATER; +} - if(GridMaps[gx][gy]) - return GridMaps[gx][gy]->liquid_level[(int)(lx)][(int)(ly)]; +float Map::GetWaterLevel(float x, float y ) const +{ + if(GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y)) + return gmap->getLiquidLevel(x, y); else return 0; } -uint32 Map::GetAreaId(uint16 areaflag,uint32 map_id) +uint32 Map::GetAreaIdByAreaFlag(uint16 areaflag,uint32 map_id) { AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id); @@ -1454,7 +1918,7 @@ uint32 Map::GetAreaId(uint16 areaflag,uint32 map_id) return 0; } -uint32 Map::GetZoneId(uint16 areaflag,uint32 map_id) +uint32 Map::GetZoneIdByAreaFlag(uint16 areaflag,uint32 map_id) { AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id); @@ -1464,26 +1928,37 @@ uint32 Map::GetZoneId(uint16 areaflag,uint32 map_id) return 0; } -bool Map::IsInWater(float x, float y, float pZ) const +void Map::GetZoneAndAreaIdByAreaFlag(uint32& zoneid, uint32& areaid, uint16 areaflag,uint32 map_id) { - // This method is called too often to use vamps for that (4. parameter = false). - // The pZ pos is taken anyway for future use - float z = GetHeight(x,y,pZ,false); // use .map base surface height + AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id); - // underground or instance without vmap - if(z <= INVALID_HEIGHT) - return false; + areaid = entry ? entry->ID : 0; + zoneid = entry ? (( entry->zone != 0 ) ? entry->zone : entry->ID) : 0; +} - float water_z = GetWaterLevel(x,y); - uint8 flag = GetTerrainType(x,y); - return (z < (water_z-2)) && (flag & 0x01); +bool Map::IsInWater(float x, float y, float pZ) const +{ + // Check surface in x, y point for liquid + if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y)) + { + LiquidData liquid_status; + if (getLiquidStatus(x, y, pZ, MAP_ALL_LIQUIDS, &liquid_status)) + { + if (liquid_status.level - liquid_status.depth_level > 2) + return true; + } + } + return false; } bool Map::IsUnderWater(float x, float y, float z) const { - float water_z = GetWaterLevel(x,y); - uint8 flag = GetTerrainType(x,y); - return (z < (water_z-2)) && (flag & 0x01); + if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y)) + { + if (getLiquidStatus(x, y, z, MAP_LIQUID_TYPE_WATER|MAP_LIQUID_TYPE_OCEAN)&LIQUID_MAP_UNDER_WATER) + return true; + } + return false; } bool Map::CheckGridIntegrity(Creature* c, bool moved) const @@ -1573,7 +2048,8 @@ void Map::SendInitTransports( Player * player ) for (MapManager::TransportSet::iterator i = tset.begin(); i != tset.end(); ++i) { - if((*i) != player->GetTransport()) // send data for current transport in other place + // send data for current transport in other place + if((*i) != player->GetTransport() && (*i)->GetMapId()==i_id) { hasTransport = true; (*i)->BuildCreateUpdateBlockForPlayer(&transData, player); @@ -1600,7 +2076,7 @@ void Map::SendRemoveTransports( Player * player ) // except used transport for (MapManager::TransportSet::iterator i = tset.begin(); i != tset.end(); ++i) - if(player->GetTransport() != (*i)) + if((*i) != player->GetTransport() && (*i)->GetMapId()!=i_id) (*i)->BuildOutOfRangeUpdateBlock(&transData); WorldPacket packet; @@ -1657,7 +2133,7 @@ void Map::RemoveAllObjectsInRemoveList() switch(obj->GetTypeId()) { case TYPEID_UNIT: - if(!((Creature*)obj)->isPet()) + if(!((Creature*)obj)->isWorldCreature()) SwitchGridContainers((Creature*)obj, on); break; } @@ -1804,8 +2280,9 @@ template void Map::Remove(DynamicObject *, bool); /* ******* Dungeon Instance Maps ******* */ InstanceMap::InstanceMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode) - : Map(id, expiry, InstanceId, SpawnMode), i_data(NULL), - m_resetAfterUnload(false), m_unloadWhenEmpty(false) + : Map(id, expiry, InstanceId, SpawnMode), + m_resetAfterUnload(false), m_unloadWhenEmpty(false), + i_data(NULL), i_script_id(0) { // 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 @@ -1834,10 +2311,10 @@ bool InstanceMap::CanEnter(Player *player) } // cannot enter if the instance is full (player cap), GMs don't count - InstanceTemplate const* iTemplate = objmgr.GetInstanceTemplate(GetId()); - if (!player->isGameMaster() && GetPlayersCountExceptGMs() >= iTemplate->maxPlayers) + uint32 maxPlayers = GetMaxPlayers(); + if (!player->isGameMaster() && GetPlayersCountExceptGMs() >= maxPlayers) { - sLog.outDetail("MAP: Instance '%u' of map '%s' cannot have more than '%u' players. Player '%s' rejected", GetInstanceId(), GetMapName(), iTemplate->maxPlayers, player->GetName()); + sLog.outDetail("MAP: Instance '%u' of map '%s' cannot have more than '%u' players. Player '%s' rejected", GetInstanceId(), GetMapName(), maxPlayers, player->GetName()); player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS); return false; } @@ -1867,79 +2344,81 @@ bool InstanceMap::Add(Player *player) if(!CanEnter(player)) return false; - // get or create an instance save for the map - InstanceSave *mapSave = sInstanceSaveManager.GetInstanceSave(GetInstanceId()); - if(!mapSave) + // Dungeon only code + if(IsDungeon()) { - sLog.outDetail("InstanceMap::Add: creating instance save for map %d spawnmode %d with instance id %d", GetId(), GetSpawnMode(), GetInstanceId()); - mapSave = sInstanceSaveManager.AddInstanceSave(GetId(), GetInstanceId(), GetSpawnMode(), 0, true); - } - - // check for existing instance binds - InstancePlayerBind *playerBind = player->GetBoundInstance(GetId(), GetSpawnMode()); - if(playerBind && playerBind->perm) - { - // cannot enter other instances if bound permanently - if(playerBind->save != mapSave) + // get or create an instance save for the map + InstanceSave *mapSave = sInstanceSaveManager.GetInstanceSave(GetInstanceId()); + if(!mapSave) { - sLog.outError("InstanceMap::Add: player %s(%d) is permanently bound to instance %d,%d,%d,%d,%d,%d but he is being put in instance %d,%d,%d,%d,%d,%d", player->GetName(), player->GetGUIDLow(), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset()); - assert(false); + sLog.outDetail("InstanceMap::Add: creating instance save for map %d spawnmode %d with instance id %d", GetId(), GetSpawnMode(), GetInstanceId()); + mapSave = sInstanceSaveManager.AddInstanceSave(GetId(), GetInstanceId(), GetSpawnMode(), 0, true); } - } - else - { - Group *pGroup = player->GetGroup(); - if(pGroup) + + // check for existing instance binds + InstancePlayerBind *playerBind = player->GetBoundInstance(GetId(), GetSpawnMode()); + if(playerBind && playerBind->perm) { - // solo saves should be reset when entering a group - InstanceGroupBind *groupBind = pGroup->GetBoundInstance(GetId(), GetSpawnMode()); - if(playerBind) + // cannot enter other instances if bound permanently + if(playerBind->save != mapSave) { - sLog.outError("InstanceMap::Add: player %s(%d) is being put in instance %d,%d,%d,%d,%d,%d but he is in group %d and is bound to instance %d,%d,%d,%d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset(), GUID_LOPART(pGroup->GetLeaderGUID()), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset()); - if(groupBind) sLog.outError("InstanceMap::Add: the group is bound to instance %d,%d,%d,%d,%d,%d", groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty(), groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount(), groupBind->save->CanReset()); - sLog.outError("InstanceMap::Add: do not let player %s enter instance otherwise crash will happen", player->GetName()); - return false; - //player->UnbindInstance(GetId(), GetSpawnMode()); - //assert(false); + sLog.outError("InstanceMap::Add: player %s(%d) is permanently bound to instance %d,%d,%d,%d,%d,%d but he is being put in instance %d,%d,%d,%d,%d,%d", player->GetName(), player->GetGUIDLow(), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset()); + assert(false); } - // bind to the group or keep using the group save - if(!groupBind) - pGroup->BindToInstance(mapSave, false); - else + } + else + { + Group *pGroup = player->GetGroup(); + if(pGroup) { - // cannot jump to a different instance without resetting it - if(groupBind->save != mapSave) + // solo saves should be reset when entering a group + InstanceGroupBind *groupBind = pGroup->GetBoundInstance(GetId(), GetSpawnMode()); + if(playerBind) { - sLog.outError("InstanceMap::Add: player %s(%d) is being put in instance %d,%d,%d but he is in group %d which is bound to instance %d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), GUID_LOPART(pGroup->GetLeaderGUID()), groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty()); - if(mapSave) - sLog.outError("MapSave players: %d, group count: %d", mapSave->GetPlayerCount(), mapSave->GetGroupCount()); - else - sLog.outError("MapSave NULL"); - if(groupBind->save) - sLog.outError("GroupBind save players: %d, group count: %d", groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount()); - else - sLog.outError("GroupBind save NULL"); - assert(false); + sLog.outError("InstanceMap::Add: player %s(%d) is being put in instance %d,%d,%d,%d,%d,%d but he is in group %d and is bound to instance %d,%d,%d,%d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset(), GUID_LOPART(pGroup->GetLeaderGUID()), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset()); + if(groupBind) sLog.outError("InstanceMap::Add: the group is bound to instance %d,%d,%d,%d,%d,%d", groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty(), groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount(), groupBind->save->CanReset()); + //assert(false); + return false; } - // if the group/leader is permanently bound to the instance - // players also become permanently bound when they enter - if(groupBind->perm) + // bind to the group or keep using the group save + if(!groupBind) + pGroup->BindToInstance(mapSave, false); + else { - WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4); - data << uint32(0); - player->GetSession()->SendPacket(&data); - player->BindToInstance(mapSave, true); + // cannot jump to a different instance without resetting it + if(groupBind->save != mapSave) + { + sLog.outError("InstanceMap::Add: player %s(%d) is being put in instance %d,%d,%d but he is in group %d which is bound to instance %d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), GUID_LOPART(pGroup->GetLeaderGUID()), groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty()); + if(mapSave) + sLog.outError("MapSave players: %d, group count: %d", mapSave->GetPlayerCount(), mapSave->GetGroupCount()); + else + sLog.outError("MapSave NULL"); + if(groupBind->save) + sLog.outError("GroupBind save players: %d, group count: %d", groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount()); + else + sLog.outError("GroupBind save NULL"); + assert(false); + } + // if the group/leader is permanently bound to the instance + // players also become permanently bound when they enter + if(groupBind->perm) + { + WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4); + data << uint32(0); + player->GetSession()->SendPacket(&data); + player->BindToInstance(mapSave, true); + } } } - } - else - { - // set up a solo bind or continue using it - if(!playerBind) - player->BindToInstance(mapSave, false); else - // cannot jump to a different instance without resetting it - assert(playerBind->save == mapSave); + { + // set up a solo bind or continue using it + if(!playerBind) + player->BindToInstance(mapSave, false); + else + // cannot jump to a different instance without resetting it + assert(playerBind->save == mapSave); + } } } @@ -1948,7 +2427,6 @@ bool InstanceMap::Add(Player *player) // first player enters (no players yet) SetResetSchedule(false); - player->SendInitWorldStates(); sLog.outDetail("MAP: Player '%s' entered the instance '%u' of map '%s'", player->GetName(), GetInstanceId(), GetMapName()); // initialize unload state m_unloadTimer = 0; @@ -2075,10 +2553,13 @@ bool InstanceMap::Reset(uint8 method) void InstanceMap::PermBindAllPlayers(Player *player) { + if(!IsDungeon()) + return; + InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId()); if(!save) { - sLog.outError("Cannot bind players, no instance save available for map!\n"); + sLog.outError("Cannot bind players, no instance save available for map!"); return; } @@ -2104,12 +2585,6 @@ void InstanceMap::PermBindAllPlayers(Player *player) } } -time_t InstanceMap::GetResetTime() -{ - InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId()); - return save ? save->GetDifficulty() : DIFFICULTY_NORMAL; -} - void InstanceMap::UnloadAll() { if(HavePlayers()) @@ -2139,7 +2614,7 @@ void InstanceMap::SetResetSchedule(bool on) // only for normal instances // the reset time is only scheduled when there are no payers inside // it is assumed that the reset time will rarely (if ever) change while the reset is scheduled - if(!HavePlayers() && !IsRaid() && !IsHeroic()) + if(IsDungeon() && !HavePlayers() && !IsRaid() && !IsHeroic()) { InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId()); if(!save) sLog.outError("InstanceMap::SetResetSchedule: cannot turn schedule %s, no save available for instance %d of %d", on ? "on" : "off", GetInstanceId(), GetId()); @@ -2147,6 +2622,14 @@ void InstanceMap::SetResetSchedule(bool on) } } +uint32 InstanceMap::GetMaxPlayers() const +{ + InstanceTemplate const* iTemplate = objmgr.GetInstanceTemplate(GetId()); + if(!iTemplate) + return 0; + return IsHeroic() ? iTemplate->maxPlayersHeroic : iTemplate->maxPlayers; +} + /* ******* Battleground Instance Maps ******* */ BattleGroundMap::BattleGroundMap(uint32 id, time_t expiry, uint32 InstanceId) @@ -2204,7 +2687,7 @@ void BattleGroundMap::UnloadAll() { if(Player * plr = m_mapRefManager.getFirst()->getSource()) { - plr->TeleportTo(plr->m_homebindMapId, plr->m_homebindX, plr->m_homebindY, plr->m_homebindZ, plr->GetOrientation()); + plr->TeleportTo(plr->GetBattleGroundEntryPoint()); // TeleportTo removes the player from this map (if the map exists) -> calls BattleGroundMap::Remove -> invalidates the iterator. // just in case, remove the player from the list explicitly here as well to prevent a possible infinite loop // note that this remove is not needed if the code works well in other places |