root/trunk/src/game/Map.cpp @ 141

Revision 141, 63.4 kB (checked in by yumileroy, 17 years ago)

[svn] Improve TargetedMovement? (TODO: let mob find "near angle" rather than "random angle").
Delete a repeated check in instance canenter().
Fix some spell targets.
Add some sunwell spell sql.
Fix Magtheridons earthquake. (TODO: need to find out why soul transfer has no effect when casted by mobs)
Let Brutallus dual wield. Enable burn (still no script effect).
Quick fix for shadowmoon valley illidan quest crash (wait for author's fix).

Original author: megamage
Date: 2008-10-31 21:42:00-05:00

Line 
1/*
2 * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
3 *
4 * Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include "MapManager.h"
22#include "Player.h"
23#include "GridNotifiers.h"
24#include "WorldSession.h"
25#include "Log.h"
26#include "GridStates.h"
27#include "CellImpl.h"
28#include "InstanceData.h"
29#include "Map.h"
30#include "GridNotifiersImpl.h"
31#include "Config/ConfigEnv.h"
32#include "Transports.h"
33#include "ObjectAccessor.h"
34#include "ObjectMgr.h"
35#include "World.h"
36#include "ScriptCalls.h"
37#include "Group.h"
38
39#include "MapInstanced.h"
40#include "InstanceSaveMgr.h"
41#include "VMapFactory.h"
42
43#define DEFAULT_GRID_EXPIRY     300
44#define MAX_GRID_LOAD_TIME      50
45
46// magic *.map header
47const char MAP_MAGIC[] = "MAP_2.00";
48
49GridState* si_GridStates[MAX_GRID_STATE];
50
51Map::~Map()
52{
53    UnloadAll(true);
54}
55
56bool Map::ExistMap(uint32 mapid,int x,int y)
57{
58    int len = sWorld.GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1;
59    char* tmp = new char[len];
60    snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),mapid,x,y);
61
62    FILE *pf=fopen(tmp,"rb");
63
64    if(!pf)
65    {
66        sLog.outError("Check existing of map file '%s': not exist!",tmp);
67        delete[] tmp;
68        return false;
69    }
70
71    char magic[8];
72    fread(magic,1,8,pf);
73    if(strncmp(MAP_MAGIC,magic,8))
74    {
75        sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.",tmp);
76        delete [] tmp;
77        fclose(pf);                                         //close file before return
78        return false;
79    }
80
81    delete [] tmp;
82    fclose(pf);
83
84    return true;
85}
86
87bool Map::ExistVMap(uint32 mapid,int x,int y)
88{
89    if(VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager())
90    {
91        if(vmgr->isMapLoadingEnabled())
92        {
93                                                            // x and y are swaped !! => fixed now
94            bool exists = vmgr->existsMap((sWorld.GetDataPath()+ "vmaps").c_str(),  mapid, x,y);
95            if(!exists)
96            {
97                std::string name = vmgr->getDirFileName(mapid,x,y);
98                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());
99                return false;
100            }
101        }
102    }
103
104    return true;
105}
106
107void Map::LoadVMap(int x,int y)
108{
109                                                            // x and y are swaped !!
110    int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapManager()->loadMap((sWorld.GetDataPath()+ "vmaps").c_str(),  GetId(), x,y);
111    switch(vmapLoadResult)
112    {
113        case VMAP::VMAP_LOAD_RESULT_OK:
114            sLog.outDetail("VMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), x,y, x,y);
115            break;
116        case VMAP::VMAP_LOAD_RESULT_ERROR:
117            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);
118            break;
119        case VMAP::VMAP_LOAD_RESULT_IGNORED:
120            DEBUG_LOG("Ignored VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), x,y, x,y);
121            break;
122    }
123}
124
125void Map::LoadMap(uint32 mapid, uint32 instanceid, int x,int y)
126{
127    if( instanceid != 0 )
128    {
129        if(GridMaps[x][y])
130            return;
131
132        Map* baseMap = const_cast<Map*>(MapManager::Instance().GetBaseMap(mapid));
133
134        // load gridmap for base map
135        if (!baseMap->GridMaps[x][y])
136            baseMap->EnsureGridCreated(GridPair(63-x,63-y));
137
138//+++        if (!baseMap->GridMaps[x][y])  don't check for GridMaps[gx][gy], we need the management for vmaps
139//            return;
140
141        ((MapInstanced*)(baseMap))->AddGridMapReference(GridPair(x,y));
142        baseMap->SetUnloadFlag(GridPair(63-x,63-y), false);
143        GridMaps[x][y] = baseMap->GridMaps[x][y];
144        return;
145    }
146
147    //map already load, delete it before reloading (Is it neccessary? Do we really need the abilty the reload maps during runtime?)
148    if(GridMaps[x][y])
149    {
150        sLog.outDetail("Unloading already loaded map %u before reloading.",mapid);
151        delete (GridMaps[x][y]);
152        GridMaps[x][y]=NULL;
153    }
154
155    // map file name
156    char *tmp=NULL;
157    // Pihhan: dataPath length + "maps/" + 3+2+2+ ".map" length may be > 32 !
158    int len = sWorld.GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1;
159    tmp = new char[len];
160    snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),mapid,x,y);
161    sLog.outDetail("Loading map %s",tmp);
162    // loading data
163    FILE *pf=fopen(tmp,"rb");
164    if(!pf)
165    {
166        delete [] tmp;
167        return;
168    }
169
170    char magic[8];
171    fread(magic,1,8,pf);
172    if(strncmp(MAP_MAGIC,magic,8))
173    {
174        sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.",tmp);
175        delete [] tmp;
176        fclose(pf);                                         //close file before return
177        return;
178    }
179    delete []  tmp;
180
181    GridMap * buf= new GridMap;
182    fread(buf,1,sizeof(GridMap),pf);
183    fclose(pf);
184
185    GridMaps[x][y] = buf;
186}
187
188void Map::LoadMapAndVMap(uint32 mapid, uint32 instanceid, int x,int y)
189{
190    LoadMap(mapid,instanceid,x,y);
191    if(instanceid == 0)
192        LoadVMap(x, y);                                     // Only load the data for the base map
193}
194
195void Map::InitStateMachine()
196{
197    si_GridStates[GRID_STATE_INVALID] = new InvalidState;
198    si_GridStates[GRID_STATE_ACTIVE] = new ActiveState;
199    si_GridStates[GRID_STATE_IDLE] = new IdleState;
200    si_GridStates[GRID_STATE_REMOVAL] = new RemovalState;
201}
202
203void Map::DeleteStateMachine()
204{
205    delete si_GridStates[GRID_STATE_INVALID];
206    delete si_GridStates[GRID_STATE_ACTIVE];
207    delete si_GridStates[GRID_STATE_IDLE];
208    delete si_GridStates[GRID_STATE_REMOVAL];
209}
210
211Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode)
212  : i_id(id), i_gridExpiry(expiry), i_mapEntry (sMapStore.LookupEntry(id)),
213 i_InstanceId(InstanceId), i_spawnMode(SpawnMode), m_unloadTimer(0)
214{
215    for(unsigned int idx=0; idx < MAX_NUMBER_OF_GRIDS; ++idx)
216    {
217        for(unsigned int j=0; j < MAX_NUMBER_OF_GRIDS; ++j)
218        {
219            //z code
220            GridMaps[idx][j] =NULL;
221            setNGrid(NULL, idx, j);
222        }
223    }
224}
225
226// Template specialization of utility methods
227template<class T>
228void Map::AddToGrid(T* obj, NGridType *grid, Cell const& cell)
229{
230    (*grid)(cell.CellX(), cell.CellY()).template AddGridObject<T>(obj, obj->GetGUID());
231}
232
233template<>
234void Map::AddToGrid(Player* obj, NGridType *grid, Cell const& cell)
235{
236    (*grid)(cell.CellX(), cell.CellY()).AddWorldObject(obj, obj->GetGUID());
237}
238
239template<>
240void Map::AddToGrid(Corpse *obj, NGridType *grid, Cell const& cell)
241{
242    // add to world object registry in grid
243    if(obj->GetType()!=CORPSE_BONES)
244    {
245        (*grid)(cell.CellX(), cell.CellY()).AddWorldObject(obj, obj->GetGUID());
246    }
247    // add to grid object store
248    else
249    {
250        (*grid)(cell.CellX(), cell.CellY()).AddGridObject(obj, obj->GetGUID());
251    }
252}
253
254template<>
255void Map::AddToGrid(Creature* obj, NGridType *grid, Cell const& cell)
256{
257    // add to world object registry in grid
258    if(obj->isPet())
259    {
260        (*grid)(cell.CellX(), cell.CellY()).AddWorldObject<Creature>(obj, obj->GetGUID());
261        obj->SetCurrentCell(cell);
262    }
263    // add to grid object store
264    else
265    {
266        (*grid)(cell.CellX(), cell.CellY()).AddGridObject<Creature>(obj, obj->GetGUID());
267        obj->SetCurrentCell(cell);
268    }
269}
270
271template<class T>
272void Map::RemoveFromGrid(T* obj, NGridType *grid, Cell const& cell)
273{
274    (*grid)(cell.CellX(), cell.CellY()).template RemoveGridObject<T>(obj, obj->GetGUID());
275}
276
277template<>
278void Map::RemoveFromGrid(Player* obj, NGridType *grid, Cell const& cell)
279{
280    (*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject(obj, obj->GetGUID());
281}
282
283template<>
284void Map::RemoveFromGrid(Corpse *obj, NGridType *grid, Cell const& cell)
285{
286    // remove from world object registry in grid
287    if(obj->GetType()!=CORPSE_BONES)
288    {
289        (*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject(obj, obj->GetGUID());
290    }
291    // remove from grid object store
292    else
293    {
294        (*grid)(cell.CellX(), cell.CellY()).RemoveGridObject(obj, obj->GetGUID());
295    }
296}
297
298template<>
299void Map::RemoveFromGrid(Creature* obj, NGridType *grid, Cell const& cell)
300{
301    // remove from world object registry in grid
302    if(obj->isPet())
303    {
304        (*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject<Creature>(obj, obj->GetGUID());
305    }
306    // remove from grid object store
307    else
308    {
309        (*grid)(cell.CellX(), cell.CellY()).RemoveGridObject<Creature>(obj, obj->GetGUID());
310    }
311}
312
313template<class T>
314void Map::DeleteFromWorld(T* obj)
315{
316    // Note: In case resurrectable corpse and pet its removed from gloabal lists in own destructors
317    delete obj;
318}
319
320template<class T>
321void Map::AddNotifier(T* , Cell const& , CellPair const& )
322{
323}
324
325template<>
326void Map::AddNotifier(Player* obj, Cell const& cell, CellPair const& cellpair)
327{
328    PlayerRelocationNotify(obj,cell,cellpair);
329}
330
331template<>
332void Map::AddNotifier(Creature* obj, Cell const& cell, CellPair const& cellpair)
333{
334    CreatureRelocationNotify(obj,cell,cellpair);
335}
336
337void
338Map::EnsureGridCreated(const GridPair &p)
339{
340    if(!getNGrid(p.x_coord, p.y_coord))
341    {
342        Guard guard(*this);
343        if(!getNGrid(p.x_coord, p.y_coord))
344        {
345            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)),
346                p.x_coord, p.y_coord);
347
348            // build a linkage between this map and NGridType
349            buildNGridLinkage(getNGrid(p.x_coord, p.y_coord));
350
351            getNGrid(p.x_coord, p.y_coord)->SetGridState(GRID_STATE_IDLE);
352
353            //z coord
354            int gx=63-p.x_coord;
355            int gy=63-p.y_coord;
356
357            if(!GridMaps[gx][gy])
358                Map::LoadMapAndVMap(i_id,i_InstanceId,gx,gy);
359        }
360    }
361}
362
363void
364Map::EnsureGridLoadedForPlayer(const Cell &cell, Player *player, bool add_player)
365{
366    EnsureGridCreated(GridPair(cell.GridX(), cell.GridY()));
367    NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
368
369    assert(grid != NULL);
370    if( !isGridObjectDataLoaded(cell.GridX(), cell.GridY()) )
371    {
372        if( player != NULL )
373        {
374            player->SendDelayResponse(MAX_GRID_LOAD_TIME);
375            DEBUG_LOG("Player %s enter cell[%u,%u] triggers of loading grid[%u,%u] on map %u", player->GetName(), cell.CellX(), cell.CellY(), cell.GridX(), cell.GridY(), i_id);
376        }
377        else
378        {
379            DEBUG_LOG("Player nearby triggers of loading grid [%u,%u] on map %u", cell.GridX(), cell.GridY(), i_id);
380        }
381
382        ObjectGridLoader loader(*grid, this, cell);
383        loader.LoadN();
384        setGridObjectDataLoaded(true, cell.GridX(), cell.GridY());
385
386        // Add resurrectable corpses to world object list in grid
387        ObjectAccessor::Instance().AddCorpsesToGrid(GridPair(cell.GridX(),cell.GridY()),(*grid)(cell.CellX(), cell.CellY()), this);
388
389        ResetGridExpiry(*getNGrid(cell.GridX(), cell.GridY()), 0.1f);
390        grid->SetGridState(GRID_STATE_ACTIVE);
391
392        if( add_player && player != NULL )
393            (*grid)(cell.CellX(), cell.CellY()).AddWorldObject(player, player->GetGUID());
394    }
395    else if( player && add_player )
396        AddToGrid(player,grid,cell);
397}
398
399void
400Map::LoadGrid(const Cell& cell, bool no_unload)
401{
402    EnsureGridCreated(GridPair(cell.GridX(), cell.GridY()));
403    NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
404
405    assert(grid != NULL);
406    if( !isGridObjectDataLoaded(cell.GridX(), cell.GridY()) )
407    {
408        ObjectGridLoader loader(*grid, this, cell);
409        loader.LoadN();
410
411        // Add resurrectable corpses to world object list in grid
412        ObjectAccessor::Instance().AddCorpsesToGrid(GridPair(cell.GridX(),cell.GridY()),(*grid)(cell.CellX(), cell.CellY()), this);
413
414        setGridObjectDataLoaded(true,cell.GridX(), cell.GridY());
415        if(no_unload)
416            getNGrid(cell.GridX(), cell.GridY())->setUnloadFlag(false);
417    }
418    LoadVMap(63-cell.GridX(),63-cell.GridY());
419}
420
421bool Map::Add(Player *player)
422{
423    player->SetInstanceId(this->GetInstanceId());
424
425    // update player state for other player and visa-versa
426    CellPair p = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
427    Cell cell(p);
428    EnsureGridLoadedForPlayer(cell, player, true);
429    player->AddToWorld();
430
431    SendInitSelf(player);
432    SendInitTransports(player);
433
434    UpdatePlayerVisibility(player,cell,p);
435    UpdateObjectsVisibilityFor(player,cell,p);
436
437    AddNotifier(player,cell,p);
438    return true;
439}
440
441template<class T>
442void
443Map::Add(T *obj)
444{
445    CellPair p = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
446
447    assert(obj);
448
449    if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
450    {
451        sLog.outError("Map::Add: Object " I64FMTD " have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
452        return;
453    }
454
455    Cell cell(p);
456    EnsureGridCreated(GridPair(cell.GridX(), cell.GridY()));
457    NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
458    assert( grid != NULL );
459
460    AddToGrid(obj,grid,cell);
461    obj->AddToWorld();
462
463    DEBUG_LOG("Object %u enters grid[%u,%u]", GUID_LOPART(obj->GetGUID()), cell.GridX(), cell.GridY());
464
465    UpdateObjectVisibility(obj,cell,p);
466
467    AddNotifier(obj,cell,p);
468}
469
470void Map::MessageBroadcast(Player *player, WorldPacket *msg, bool to_self)
471{
472    CellPair p = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
473
474    if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
475    {
476        sLog.outError("Map::MessageBroadcast: Player (GUID: %u) have invalid coordinates X:%f Y:%f grid cell [%u:%u]", player->GetGUIDLow(), player->GetPositionX(), player->GetPositionY(), p.x_coord, p.y_coord);
477        return;
478    }
479
480    Cell cell(p);
481    cell.data.Part.reserved = ALL_DISTRICT;
482
483    if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
484        return;
485
486    Trinity::MessageDeliverer post_man(*player, msg, to_self);
487    TypeContainerVisitor<Trinity::MessageDeliverer, WorldTypeMapContainer > message(post_man);
488    CellLock<ReadGuard> cell_lock(cell, p);
489    cell_lock->Visit(cell_lock, message, *this);
490}
491
492void Map::MessageBroadcast(WorldObject *obj, WorldPacket *msg)
493{
494    CellPair p = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
495
496    if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
497    {
498        sLog.outError("Map::MessageBroadcast: Object " I64FMTD " have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
499        return;
500    }
501
502    Cell cell(p);
503    cell.data.Part.reserved = ALL_DISTRICT;
504    cell.SetNoCreate();
505
506    if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
507        return;
508
509    Trinity::ObjectMessageDeliverer post_man(msg);
510    TypeContainerVisitor<Trinity::ObjectMessageDeliverer, WorldTypeMapContainer > message(post_man);
511    CellLock<ReadGuard> cell_lock(cell, p);
512    cell_lock->Visit(cell_lock, message, *this);
513}
514
515void Map::MessageDistBroadcast(Player *player, WorldPacket *msg, float dist, bool to_self, bool own_team_only)
516{
517    CellPair p = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
518
519    if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
520    {
521        sLog.outError("Map::MessageBroadcast: Player (GUID: %u) have invalid coordinates X:%f Y:%f grid cell [%u:%u]", player->GetGUIDLow(), player->GetPositionX(), player->GetPositionY(), p.x_coord, p.y_coord);
522        return;
523    }
524
525    Cell cell(p);
526    cell.data.Part.reserved = ALL_DISTRICT;
527
528    if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
529        return;
530
531    Trinity::MessageDistDeliverer post_man(*player, msg, dist, to_self, own_team_only);
532    TypeContainerVisitor<Trinity::MessageDistDeliverer , WorldTypeMapContainer > message(post_man);
533    CellLock<ReadGuard> cell_lock(cell, p);
534    cell_lock->Visit(cell_lock, message, *this);
535}
536
537void Map::MessageDistBroadcast(WorldObject *obj, WorldPacket *msg, float dist)
538{
539    CellPair p = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
540
541    if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
542    {
543        sLog.outError("Map::MessageBroadcast: Object " I64FMTD " have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
544        return;
545    }
546
547    Cell cell(p);
548    cell.data.Part.reserved = ALL_DISTRICT;
549    cell.SetNoCreate();
550
551    if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
552        return;
553
554    Trinity::ObjectMessageDistDeliverer post_man(*obj, msg,dist);
555    TypeContainerVisitor<Trinity::ObjectMessageDistDeliverer, WorldTypeMapContainer > message(post_man);
556    CellLock<ReadGuard> cell_lock(cell, p);
557    cell_lock->Visit(cell_lock, message, *this);
558}
559
560bool Map::loaded(const GridPair &p) const
561{
562    return ( getNGrid(p.x_coord, p.y_coord) && isGridObjectDataLoaded(p.x_coord, p.y_coord) );
563}
564
565void Map::Update(const uint32 &t_diff)
566{
567    // 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 !
568    // This isn't really bother us, since as soon as we have instanced BG-s, the whole map unloads as the BG gets ended
569    if (IsBattleGroundOrArena())
570        return;
571
572    for (GridRefManager<NGridType>::iterator i = GridRefManager<NGridType>::begin(); i != GridRefManager<NGridType>::end(); )
573    {
574        NGridType *grid = i->getSource();
575        GridInfo *info = i->getSource()->getGridInfoRef();
576        ++i;                                                // The update might delete the map and we need the next map before the iterator gets invalid
577        assert(grid->GetGridState() >= 0 && grid->GetGridState() < MAX_GRID_STATE);
578        si_GridStates[grid->GetGridState()]->Update(*this, *grid, *info, grid->getX(), grid->getY(), t_diff);
579    }
580}
581
582void InstanceMap::Update(const uint32& t_diff)
583{
584    Map::Update(t_diff);
585
586    if(i_data)
587        i_data->Update(t_diff);
588}
589
590
591void Map::Remove(Player *player, bool remove)
592{
593    CellPair p = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
594    if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP)
595    {
596        // invalid coordinates
597        player->RemoveFromWorld();
598
599        if( remove )
600            DeleteFromWorld(player);
601
602        return;
603    }
604
605    Cell cell(p);
606
607    if( !getNGrid(cell.data.Part.grid_x, cell.data.Part.grid_y) )
608    {
609        sLog.outError("Map::Remove() i_grids was NULL x:%d, y:%d",cell.data.Part.grid_x,cell.data.Part.grid_y);
610        return;
611    }
612
613    DEBUG_LOG("Remove player %s from grid[%u,%u]", player->GetName(), cell.GridX(), cell.GridY());
614    NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
615    assert(grid != NULL);
616
617    player->RemoveFromWorld();
618    RemoveFromGrid(player,grid,cell);
619
620    SendRemoveTransports(player);
621
622    UpdateObjectsVisibilityFor(player,cell,p);
623
624    if( remove )
625        DeleteFromWorld(player);
626}
627
628bool Map::RemoveBones(uint64 guid, float x, float y)
629{
630    if (IsRemovalGrid(x, y))
631    {
632        Corpse * corpse = ObjectAccessor::Instance().GetObjectInWorld(GetId(), x, y, guid, (Corpse*)NULL);
633        if(corpse && corpse->GetTypeId() == TYPEID_CORPSE && corpse->GetType() == CORPSE_BONES)
634            corpse->DeleteBonesFromWorld();
635        else
636            return false;
637    }
638    return true;
639}
640
641template<class T>
642void
643Map::Remove(T *obj, bool remove)
644{
645    CellPair p = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
646    if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
647    {
648        sLog.outError("Map::Remove: Object " I64FMTD " have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
649        return;
650    }
651
652    Cell cell(p);
653    if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
654        return;
655
656    DEBUG_LOG("Remove object " I64FMTD " from grid[%u,%u]", obj->GetGUID(), cell.data.Part.grid_x, cell.data.Part.grid_y);
657    NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
658    assert( grid != NULL );
659
660    obj->RemoveFromWorld();
661    RemoveFromGrid(obj,grid,cell);
662
663    UpdateObjectVisibility(obj,cell,p);
664
665    if( remove )
666    {
667        // if option set then object already saved at this moment
668        if(!sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY))
669            obj->SaveRespawnTime();
670        DeleteFromWorld(obj);
671    }
672}
673
674void
675Map::PlayerRelocation(Player *player, float x, float y, float z, float orientation)
676{
677    assert(player);
678
679    CellPair old_val = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
680    CellPair new_val = Trinity::ComputeCellPair(x, y);
681
682    Cell old_cell(old_val);
683    Cell new_cell(new_val);
684    new_cell |= old_cell;
685    bool same_cell = (new_cell == old_cell);
686
687    player->Relocate(x, y, z, orientation);
688
689    if( old_cell.DiffGrid(new_cell) || old_cell.DiffCell(new_cell) )
690    {
691        DEBUG_LOG("Player %s relocation grid[%u,%u]cell[%u,%u]->grid[%u,%u]cell[%u,%u]", 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());
692
693        // update player position for group at taxi flight
694        if(player->GetGroup() && player->isInFlight())
695            player->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION);
696
697        NGridType* oldGrid = getNGrid(old_cell.GridX(), old_cell.GridY());
698        RemoveFromGrid(player, oldGrid,old_cell);
699        if( !old_cell.DiffGrid(new_cell) )
700            AddToGrid(player, oldGrid,new_cell);
701
702        if( old_cell.DiffGrid(new_cell) )
703            EnsureGridLoadedForPlayer(new_cell, player, true);
704    }
705
706    // if move then update what player see and who seen
707    UpdatePlayerVisibility(player,new_cell,new_val);
708    UpdateObjectsVisibilityFor(player,new_cell,new_val);
709    PlayerRelocationNotify(player,new_cell,new_val);
710    NGridType* newGrid = getNGrid(new_cell.GridX(), new_cell.GridY());
711    if( !same_cell && newGrid->GetGridState()!= GRID_STATE_ACTIVE )
712    {
713        ResetGridExpiry(*newGrid, 0.1f);
714        newGrid->SetGridState(GRID_STATE_ACTIVE);
715    }
716}
717
718void
719Map::CreatureRelocation(Creature *creature, float x, float y, float z, float ang)
720{
721    assert(CheckGridIntegrity(creature,false));
722
723    Cell old_cell = creature->GetCurrentCell();
724
725    CellPair new_val = Trinity::ComputeCellPair(x, y);
726    Cell new_cell(new_val);
727
728    // delay creature move for grid/cell to grid/cell moves
729    if( old_cell.DiffCell(new_cell) || old_cell.DiffGrid(new_cell) )
730    {
731        #ifdef TRINITY_DEBUG
732        if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
733            sLog.outDebug("Creature (GUID: %u Entry: %u) added to moving list from grid[%u,%u]cell[%u,%u] to grid[%u,%u]cell[%u,%u].", creature->GetGUIDLow(), creature->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
734        #endif
735        AddCreatureToMoveList(creature,x,y,z,ang);
736        // in diffcell/diffgrid case notifiers called at finishing move creature in Map::MoveAllCreaturesInMoveList
737    }
738    else
739    {
740        creature->Relocate(x, y, z, ang);
741        CreatureRelocationNotify(creature,new_cell,new_val);
742    }
743    assert(CheckGridIntegrity(creature,true));
744}
745
746void Map::AddCreatureToMoveList(Creature *c, float x, float y, float z, float ang)
747{
748    if(!c)
749        return;
750
751    i_creaturesToMove[c] = CreatureMover(x,y,z,ang);
752}
753
754void Map::MoveAllCreaturesInMoveList()
755{
756    while(!i_creaturesToMove.empty())
757    {
758        // get data and remove element;
759        CreatureMoveList::iterator iter = i_creaturesToMove.begin();
760        Creature* c = iter->first;
761        CreatureMover cm = iter->second;
762        i_creaturesToMove.erase(iter);
763
764        // calculate cells
765        CellPair new_val = Trinity::ComputeCellPair(cm.x, cm.y);
766        Cell new_cell(new_val);
767
768        // do move or do move to respawn or remove creature if previous all fail
769        if(CreatureCellRelocation(c,new_cell))
770        {
771            // update pos
772            c->Relocate(cm.x, cm.y, cm.z, cm.ang);
773            CreatureRelocationNotify(c,new_cell,new_cell.cellPair());
774        }
775        else
776        {
777            // if creature can't be move in new cell/grid (not loaded) move it to repawn cell/grid
778            // creature coordinates will be updated and notifiers send
779            if(!CreatureRespawnRelocation(c))
780            {
781                // ... or unload (if respawn grid also not loaded)
782                #ifdef TRINITY_DEBUG
783                if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
784                    sLog.outDebug("Creature (GUID: %u Entry: %u ) can't be move to unloaded respawn grid.",c->GetGUIDLow(),c->GetEntry());
785                #endif
786                c->CleanupsBeforeDelete();
787                AddObjectToRemoveList(c);
788            }
789        }
790    }
791}
792
793bool Map::CreatureCellRelocation(Creature *c, Cell new_cell)
794{
795    Cell const& old_cell = c->GetCurrentCell();
796    if(!old_cell.DiffGrid(new_cell) )                       // in same grid
797    {
798        // if in same cell then none do
799        if(old_cell.DiffCell(new_cell))
800        {
801            #ifdef TRINITY_DEBUG
802            if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
803                sLog.outDebug("Creature (GUID: %u Entry: %u) moved in grid[%u,%u] from cell[%u,%u] to cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.CellX(), new_cell.CellY());
804            #endif
805
806            if( !old_cell.DiffGrid(new_cell) )
807            {
808                RemoveFromGrid(c,getNGrid(old_cell.GridX(), old_cell.GridY()),old_cell);
809                AddToGrid(c,getNGrid(new_cell.GridX(), new_cell.GridY()),new_cell);
810                c->SetCurrentCell(new_cell);
811            }
812        }
813        else
814        {
815            #ifdef TRINITY_DEBUG
816            if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
817                sLog.outDebug("Creature (GUID: %u Entry: %u) move in same grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY());
818            #endif
819        }
820    }
821    else                                                    // in diff. grids
822    if(loaded(GridPair(new_cell.GridX(), new_cell.GridY())))
823    {
824        #ifdef TRINITY_DEBUG
825        if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
826            sLog.outDebug("Creature (GUID: %u Entry: %u) moved from grid[%u,%u]cell[%u,%u] to grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
827        #endif
828
829        RemoveFromGrid(c,getNGrid(old_cell.GridX(), old_cell.GridY()),old_cell);
830        {
831            EnsureGridCreated(GridPair(new_cell.GridX(), new_cell.GridY()));
832            AddToGrid(c,getNGrid(new_cell.GridX(), new_cell.GridY()),new_cell);
833        }
834    }
835    else
836    {
837        #ifdef TRINITY_DEBUG
838        if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
839            sLog.outDebug("Creature (GUID: %u Entry: %u) attempt move from grid[%u,%u]cell[%u,%u] to unloaded grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
840        #endif
841        return false;
842    }
843
844    return true;
845}
846
847bool Map::CreatureRespawnRelocation(Creature *c)
848{
849    float resp_x, resp_y, resp_z, resp_o;
850    c->GetRespawnCoord(resp_x, resp_y, resp_z, &resp_o);
851
852    CellPair resp_val = Trinity::ComputeCellPair(resp_x, resp_y);
853    Cell resp_cell(resp_val);
854
855    c->CombatStop();
856    c->GetMotionMaster()->Clear();
857
858    #ifdef TRINITY_DEBUG
859    if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
860        sLog.outDebug("Creature (GUID: %u Entry: %u) will moved from grid[%u,%u]cell[%u,%u] to respawn grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), c->GetCurrentCell().GridX(), c->GetCurrentCell().GridY(), c->GetCurrentCell().CellX(), c->GetCurrentCell().CellY(), resp_cell.GridX(), resp_cell.GridY(), resp_cell.CellX(), resp_cell.CellY());
861    #endif
862
863    // teleport it to respawn point (like normal respawn if player see)
864    if(CreatureCellRelocation(c,resp_cell))
865    {
866        c->Relocate(resp_x, resp_y, resp_z, resp_o);
867        c->GetMotionMaster()->Initialize();                 // prevent possible problems with default move generators
868        CreatureRelocationNotify(c,resp_cell,resp_cell.cellPair());
869        return true;
870    }
871    else
872        return false;
873}
874
875bool Map::UnloadGrid(const uint32 &x, const uint32 &y, bool pForce)
876{
877    NGridType *grid = getNGrid(x, y);
878    assert( grid != NULL);
879
880    {
881        if(!pForce && ObjectAccessor::Instance().ActiveObjectsNearGrid(x, y, i_id, i_InstanceId) )
882            return false;
883
884        DEBUG_LOG("Unloading grid[%u,%u] for map %u", x,y, i_id);
885        ObjectGridUnloader unloader(*grid);
886
887        // Finish creature moves, remove and delete all creatures with delayed remove before moving to respawn grids
888        // Must know real mob position before move
889        DoDelayedMovesAndRemoves();
890
891        // move creatures to respawn grids if this is diff.grid or to remove list
892        unloader.MoveToRespawnN();
893
894        // Finish creature moves, remove and delete all creatures with delayed remove before unload
895        DoDelayedMovesAndRemoves();
896
897        unloader.UnloadN();
898        delete getNGrid(x, y);
899        setNGrid(NULL, x, y);
900    }
901    int gx=63-x;
902    int gy=63-y;
903
904    // delete grid map, but don't delete if it is from parent map (and thus only reference)
905    //+++if (GridMaps[gx][gy]) don't check for GridMaps[gx][gy], we might have to unload vmaps
906    {
907        if (i_InstanceId == 0)
908        {
909            if(GridMaps[gx][gy]) delete (GridMaps[gx][gy]);
910            // x and y are swaped
911            VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId(), gy, gx);
912        }
913        else
914            ((MapInstanced*)(MapManager::Instance().GetBaseMap(i_id)))->RemoveGridMapReference(GridPair(gx, gy));
915        GridMaps[gx][gy] = NULL;
916    }
917    DEBUG_LOG("Unloading grid[%u,%u] for map %u finished", x,y, i_id);
918    return true;
919}
920
921void Map::UnloadAll(bool pForce)
922{
923    // clear all delayed moves, useless anyway do this moves before map unload.
924    i_creaturesToMove.clear();
925
926    for (GridRefManager<NGridType>::iterator i = GridRefManager<NGridType>::begin(); i != GridRefManager<NGridType>::end(); )
927    {
928        NGridType &grid(*i->getSource());
929        ++i;
930        UnloadGrid(grid.getX(), grid.getY(), pForce);       // deletes the grid and removes it from the GridRefManager
931    }
932}
933
934float Map::GetHeight(float x, float y, float z, bool pUseVmaps) const
935{
936    GridPair p = Trinity::ComputeGridPair(x, y);
937
938    // half opt method
939    int gx=(int)(32-x/SIZE_OF_GRIDS);                       //grid x
940    int gy=(int)(32-y/SIZE_OF_GRIDS);                       //grid y
941
942    float lx=MAP_RESOLUTION*(32 -x/SIZE_OF_GRIDS - gx);
943    float ly=MAP_RESOLUTION*(32 -y/SIZE_OF_GRIDS - gy);
944
945    // ensure GridMap is loaded
946    const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy));
947
948    // find raw .map surface under Z coordinates
949    float mapHeight;
950    if(GridMap* gmap = GridMaps[gx][gy])
951    {
952        int lx_int = (int)lx;
953        int ly_int = (int)ly;
954
955        float zi[4];
956        // Probe 4 nearest points (except border cases)
957        zi[0] = gmap->Z[lx_int][ly_int];
958        zi[1] = lx < MAP_RESOLUTION-1 ? gmap->Z[lx_int+1][ly_int] : zi[0];
959        zi[2] = ly < MAP_RESOLUTION-1 ? gmap->Z[lx_int][ly_int+1] : zi[0];
960        zi[3] = lx < MAP_RESOLUTION-1 && ly < MAP_RESOLUTION-1 ? gmap->Z[lx_int+1][ly_int+1] : zi[0];
961        // Recalculate them like if their x,y positions were in the range 0,1
962        float b[4];
963        b[0] = zi[0];
964        b[1] = zi[1]-zi[0];
965        b[2] = zi[2]-zi[0];
966        b[3] = zi[0]-zi[1]-zi[2]+zi[3];
967        // Normalize the dx and dy to be in range 0..1
968        float fact_x = lx - lx_int;
969        float fact_y = ly - ly_int;
970        // Use the simplified bilinear equation, as described in [url="http://en.wikipedia.org/wiki/Bilinear_interpolation"]http://en.wikipedia.org/wiki/Bilinear_interpolation[/url]
971        float _mapheight = b[0] + (b[1]*fact_x) + (b[2]*fact_y) + (b[3]*fact_x*fact_y);
972
973        // look from a bit higher pos to find the floor, ignore under surface case
974        if(z + 2.0f > _mapheight)
975            mapHeight = _mapheight;
976        else
977            mapHeight = VMAP_INVALID_HEIGHT_VALUE;
978    }
979    else
980        mapHeight = VMAP_INVALID_HEIGHT_VALUE;
981
982    float vmapHeight;
983    if(pUseVmaps)
984    {
985        VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager();
986        if(vmgr->isHeightCalcEnabled())
987        {
988            // look from a bit higher pos to find the floor
989            vmapHeight = vmgr->getHeight(GetId(), x, y, z + 2.0f);
990        }
991        else
992            vmapHeight = VMAP_INVALID_HEIGHT_VALUE;
993    }
994    else
995        vmapHeight = VMAP_INVALID_HEIGHT_VALUE;
996
997    // mapHeight set for any above raw ground Z or <= INVALID_HEIGHT
998    // vmapheight set for any under Z value or <= INVALID_HEIGHT
999
1000    if( vmapHeight > INVALID_HEIGHT )
1001    {
1002        if( mapHeight > INVALID_HEIGHT )
1003        {
1004            // we have mapheight and vmapheight and must select more appropriate
1005
1006            // we are already under the surface or vmap height above map heigt
1007            // or if the distance of the vmap height is less the land height distance
1008            if( z < mapHeight || vmapHeight > mapHeight || fabs(mapHeight-z) > fabs(vmapHeight-z) )
1009                return vmapHeight;
1010            else
1011                return mapHeight;                           // better use .map surface height
1012
1013        }
1014        else
1015            return vmapHeight;                              // we have only vmapHeight (if have)
1016    }
1017    else
1018    {
1019        if(!pUseVmaps)
1020            return mapHeight;                               // explicitly use map data (if have)
1021        else if(mapHeight > INVALID_HEIGHT && (z < mapHeight + 2 || z == MAX_HEIGHT))
1022            return mapHeight;                               // explicitly use map data if original z < mapHeight but map found (z+2 > mapHeight)
1023        else
1024            return VMAP_INVALID_HEIGHT_VALUE;               // we not have any height
1025    }
1026}
1027
1028uint16 Map::GetAreaFlag(float x, float y ) const
1029{
1030    //local x,y coords
1031    float lx,ly;
1032    int gx,gy;
1033    GridPair p = Trinity::ComputeGridPair(x, y);
1034
1035    // half opt method
1036    gx=(int)(32-x/SIZE_OF_GRIDS) ;                          //grid x
1037    gy=(int)(32-y/SIZE_OF_GRIDS);                           //grid y
1038
1039    lx=16*(32 -x/SIZE_OF_GRIDS - gx);
1040    ly=16*(32 -y/SIZE_OF_GRIDS - gy);
1041    //DEBUG_LOG("my %d %d si %d %d",gx,gy,p.x_coord,p.y_coord);
1042
1043    // ensure GridMap is loaded
1044    const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy));
1045
1046    if(GridMaps[gx][gy])
1047        return GridMaps[gx][gy]->area_flag[(int)(lx)][(int)(ly)];
1048    // this used while not all *.map files generated (instances)
1049    else
1050        return GetAreaFlagByMapId(i_id);
1051}
1052
1053uint8 Map::GetTerrainType(float x, float y ) const
1054{
1055    //local x,y coords
1056    float lx,ly;
1057    int gx,gy;
1058
1059    // half opt method
1060    gx=(int)(32-x/SIZE_OF_GRIDS) ;                          //grid x
1061    gy=(int)(32-y/SIZE_OF_GRIDS);                           //grid y
1062
1063    lx=16*(32 -x/SIZE_OF_GRIDS - gx);
1064    ly=16*(32 -y/SIZE_OF_GRIDS - gy);
1065
1066    // ensure GridMap is loaded
1067    const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy));
1068
1069    if(GridMaps[gx][gy])
1070        return GridMaps[gx][gy]->terrain_type[(int)(lx)][(int)(ly)];
1071    else
1072        return 0;
1073
1074}
1075
1076float Map::GetWaterLevel(float x, float y ) const
1077{
1078    //local x,y coords
1079    float lx,ly;
1080    int gx,gy;
1081
1082    // half opt method
1083    gx=(int)(32-x/SIZE_OF_GRIDS) ;                          //grid x
1084    gy=(int)(32-y/SIZE_OF_GRIDS);                           //grid y
1085
1086    lx=128*(32 -x/SIZE_OF_GRIDS - gx);
1087    ly=128*(32 -y/SIZE_OF_GRIDS - gy);
1088
1089    // ensure GridMap is loaded
1090    const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy));
1091
1092    if(GridMaps[gx][gy])
1093        return GridMaps[gx][gy]->liquid_level[(int)(lx)][(int)(ly)];
1094    else
1095        return 0;
1096}
1097
1098uint32 Map::GetAreaId(uint16 areaflag,uint32 map_id)
1099{
1100    AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id);
1101
1102    if (entry)
1103        return entry->ID;
1104    else
1105        return 0;
1106}
1107
1108uint32 Map::GetZoneId(uint16 areaflag,uint32 map_id)
1109{
1110    AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id);
1111
1112    if( entry )
1113        return ( entry->zone != 0 ) ? entry->zone : entry->ID;
1114    else
1115        return 0;
1116}
1117
1118bool Map::IsInWater(float x, float y, float pZ) const
1119{
1120    // This method is called too often to use vamps for that (4. parameter = false).
1121    // The pZ pos is taken anyway for future use
1122    float z = GetHeight(x,y,pZ,false);                      // use .map base surface height
1123
1124    // underground or instance without vmap
1125    if(z <= INVALID_HEIGHT)
1126        return false;
1127
1128    float water_z = GetWaterLevel(x,y);
1129    uint8 flag = GetTerrainType(x,y);
1130    return (z < (water_z-2)) && (flag & 0x01);
1131}
1132
1133bool Map::IsUnderWater(float x, float y, float z) const
1134{
1135    float water_z = GetWaterLevel(x,y);
1136    uint8 flag = GetTerrainType(x,y);
1137    return (z < (water_z-2)) && (flag & 0x01);
1138}
1139
1140bool Map::CheckGridIntegrity(Creature* c, bool moved) const
1141{
1142    Cell const& cur_cell = c->GetCurrentCell();
1143
1144    CellPair xy_val = Trinity::ComputeCellPair(c->GetPositionX(), c->GetPositionY());
1145    Cell xy_cell(xy_val);
1146    if(xy_cell != cur_cell)
1147    {
1148        sLog.outError("ERROR: %s (GUID: %u) X: %f Y: %f (%s) in grid[%u,%u]cell[%u,%u] instead grid[%u,%u]cell[%u,%u]",
1149            (c->GetTypeId()==TYPEID_PLAYER ? "Player" : "Creature"),c->GetGUIDLow(),
1150            c->GetPositionX(),c->GetPositionY(),(moved ? "final" : "original"),
1151            cur_cell.GridX(), cur_cell.GridY(), cur_cell.CellX(), cur_cell.CellY(),
1152            xy_cell.GridX(),  xy_cell.GridY(),  xy_cell.CellX(),  xy_cell.CellY());
1153        return true;                                        // not crash at error, just output error in debug mode
1154    }
1155
1156    return true;
1157}
1158
1159const char* Map::GetMapName() const
1160{
1161    return i_mapEntry ? i_mapEntry->name[sWorld.GetDefaultDbcLocale()] : "UNNAMEDMAP\x0";
1162}
1163
1164void Map::UpdateObjectVisibility( WorldObject* obj, Cell cell, CellPair cellpair)
1165{
1166    cell.data.Part.reserved = ALL_DISTRICT;
1167    cell.SetNoCreate();
1168    Trinity::VisibleChangesNotifier notifier(*obj);
1169    TypeContainerVisitor<Trinity::VisibleChangesNotifier, WorldTypeMapContainer > player_notifier(notifier);
1170    CellLock<GridReadGuard> cell_lock(cell, cellpair);
1171    cell_lock->Visit(cell_lock, player_notifier, *this);
1172}
1173
1174void Map::UpdatePlayerVisibility( Player* player, Cell cell, CellPair cellpair )
1175{
1176    cell.data.Part.reserved = ALL_DISTRICT;
1177
1178    Trinity::PlayerNotifier pl_notifier(*player);
1179    TypeContainerVisitor<Trinity::PlayerNotifier, WorldTypeMapContainer > player_notifier(pl_notifier);
1180
1181    CellLock<ReadGuard> cell_lock(cell, cellpair);
1182    cell_lock->Visit(cell_lock, player_notifier, *this);
1183}
1184
1185void Map::UpdateObjectsVisibilityFor( Player* player, Cell cell, CellPair cellpair )
1186{
1187    Trinity::VisibleNotifier notifier(*player);
1188
1189    cell.data.Part.reserved = ALL_DISTRICT;
1190    cell.SetNoCreate();
1191    TypeContainerVisitor<Trinity::VisibleNotifier, WorldTypeMapContainer > world_notifier(notifier);
1192    TypeContainerVisitor<Trinity::VisibleNotifier, GridTypeMapContainer  > grid_notifier(notifier);
1193    CellLock<GridReadGuard> cell_lock(cell, cellpair);
1194    cell_lock->Visit(cell_lock, world_notifier, *this);
1195    cell_lock->Visit(cell_lock, grid_notifier,  *this);
1196
1197    // send data
1198    notifier.Notify();
1199}
1200
1201void Map::PlayerRelocationNotify( Player* player, Cell cell, CellPair cellpair )
1202{
1203    CellLock<ReadGuard> cell_lock(cell, cellpair);
1204    Trinity::PlayerRelocationNotifier relocationNotifier(*player);
1205    cell.data.Part.reserved = ALL_DISTRICT;
1206
1207    TypeContainerVisitor<Trinity::PlayerRelocationNotifier, GridTypeMapContainer >  p2grid_relocation(relocationNotifier);
1208    TypeContainerVisitor<Trinity::PlayerRelocationNotifier, WorldTypeMapContainer > p2world_relocation(relocationNotifier);
1209
1210    cell_lock->Visit(cell_lock, p2grid_relocation, *this);
1211    cell_lock->Visit(cell_lock, p2world_relocation, *this);
1212}
1213
1214void Map::CreatureRelocationNotify(Creature *creature, Cell cell, CellPair cellpair)
1215{
1216    CellLock<ReadGuard> cell_lock(cell, cellpair);
1217    Trinity::CreatureRelocationNotifier relocationNotifier(*creature);
1218    cell.data.Part.reserved = ALL_DISTRICT;
1219    cell.SetNoCreate();                                     // not trigger load unloaded grids at notifier call
1220
1221    TypeContainerVisitor<Trinity::CreatureRelocationNotifier, WorldTypeMapContainer > c2world_relocation(relocationNotifier);
1222    TypeContainerVisitor<Trinity::CreatureRelocationNotifier, GridTypeMapContainer >  c2grid_relocation(relocationNotifier);
1223
1224    cell_lock->Visit(cell_lock, c2world_relocation, *this);
1225    cell_lock->Visit(cell_lock, c2grid_relocation, *this);
1226}
1227
1228void Map::SendInitSelf( Player * player )
1229{
1230    sLog.outDetail("Creating player data for himself %u", player->GetGUIDLow());
1231
1232    UpdateData data;
1233
1234    bool hasTransport = false;
1235
1236    // attach to player data current transport data
1237    if(Transport* transport = player->GetTransport())
1238    {
1239        hasTransport = true;
1240        transport->BuildCreateUpdateBlockForPlayer(&data, player);
1241    }
1242
1243    // build data for self presence in world at own client (one time for map)
1244    player->BuildCreateUpdateBlockForPlayer(&data, player);
1245
1246    // build other passengers at transport also (they always visible and marked as visible and will not send at visibility update at add to map
1247    if(Transport* transport = player->GetTransport())
1248    {
1249        for(Transport::PlayerSet::const_iterator itr = transport->GetPassengers().begin();itr!=transport->GetPassengers().end();++itr)
1250        {
1251            if(player!=(*itr) && player->HaveAtClient(*itr))
1252            {
1253                hasTransport = true;
1254                (*itr)->BuildCreateUpdateBlockForPlayer(&data, player);
1255            }
1256        }
1257    }
1258
1259    WorldPacket packet;
1260    data.BuildPacket(&packet, hasTransport);
1261    player->GetSession()->SendPacket(&packet);
1262}
1263
1264void Map::SendInitTransports( Player * player )
1265{
1266    // Hack to send out transports
1267    MapManager::TransportMap& tmap = MapManager::Instance().m_TransportsByMap;
1268
1269    // no transports at map
1270    if (tmap.find(player->GetMapId()) == tmap.end())
1271        return;
1272
1273    UpdateData transData;
1274
1275    MapManager::TransportSet& tset = tmap[player->GetMapId()];
1276
1277    bool hasTransport = false;
1278
1279    for (MapManager::TransportSet::iterator i = tset.begin(); i != tset.end(); ++i)
1280    {
1281        if((*i) != player->GetTransport())                  // send data for current transport in other place
1282        {
1283            hasTransport = true;
1284            (*i)->BuildCreateUpdateBlockForPlayer(&transData, player);
1285        }
1286    }
1287
1288    WorldPacket packet;
1289    transData.BuildPacket(&packet, hasTransport);
1290    player->GetSession()->SendPacket(&packet);
1291}
1292
1293void Map::SendRemoveTransports( Player * player )
1294{
1295    // Hack to send out transports
1296    MapManager::TransportMap& tmap = MapManager::Instance().m_TransportsByMap;
1297
1298    // no transports at map
1299    if (tmap.find(player->GetMapId()) == tmap.end())
1300        return;
1301
1302    UpdateData transData;
1303
1304    MapManager::TransportSet& tset = tmap[player->GetMapId()];
1305
1306    // except used transport
1307    for (MapManager::TransportSet::iterator i = tset.begin(); i != tset.end(); ++i)
1308        if(player->GetTransport() != (*i))
1309            (*i)->BuildOutOfRangeUpdateBlock(&transData);
1310
1311    WorldPacket packet;
1312    transData.BuildPacket(&packet);
1313    player->GetSession()->SendPacket(&packet);
1314}
1315
1316inline void Map::setNGrid(NGridType *grid, uint32 x, uint32 y)
1317{
1318    if(x >= MAX_NUMBER_OF_GRIDS || y >= MAX_NUMBER_OF_GRIDS)
1319    {
1320        sLog.outError("map::setNGrid() Invalid grid coordinates found: %d, %d!",x,y);
1321        assert(false);
1322    }
1323    i_grids[x][y] = grid;
1324}
1325
1326void Map::DoDelayedMovesAndRemoves()
1327{
1328    MoveAllCreaturesInMoveList();
1329    RemoveAllObjectsInRemoveList();
1330}
1331
1332void Map::AddObjectToRemoveList(WorldObject *obj)
1333{
1334    assert(obj->GetMapId()==GetId() && obj->GetInstanceId()==GetInstanceId());
1335
1336    i_objectsToRemove.insert(obj);
1337    //sLog.outDebug("Object (GUID: %u TypeId: %u ) added to removing list.",obj->GetGUIDLow(),obj->GetTypeId());
1338}
1339
1340void Map::RemoveAllObjectsInRemoveList()
1341{
1342    if(i_objectsToRemove.empty())
1343        return;
1344
1345    //sLog.outDebug("Object remover 1 check.");
1346    while(!i_objectsToRemove.empty())
1347    {
1348        WorldObject* obj = *i_objectsToRemove.begin();
1349        i_objectsToRemove.erase(i_objectsToRemove.begin());
1350
1351        switch(obj->GetTypeId())
1352        {
1353            case TYPEID_CORPSE:
1354            {
1355                Corpse* corpse = ObjectAccessor::Instance().GetCorpse(*obj, obj->GetGUID());
1356                if (!corpse)
1357                    sLog.outError("ERROR: Try delete corpse/bones %u that not in map", obj->GetGUIDLow());
1358                else
1359                    Remove(corpse,true);
1360                break;
1361            }
1362        case TYPEID_DYNAMICOBJECT:
1363            Remove((DynamicObject*)obj,true);
1364            break;
1365        case TYPEID_GAMEOBJECT:
1366            Remove((GameObject*)obj,true);
1367            break;
1368        case TYPEID_UNIT:
1369            Remove((Creature*)obj,true);
1370            break;
1371        default:
1372            sLog.outError("Non-grid object (TypeId: %u) in grid object removing list, ignored.",obj->GetTypeId());
1373            break;
1374        }
1375    }
1376    //sLog.outDebug("Object remover 2 check.");
1377}
1378
1379bool Map::CanUnload(const uint32 &diff)
1380{
1381    if(!m_unloadTimer) return false;
1382    if(m_unloadTimer < diff) return true;
1383    m_unloadTimer -= diff;
1384    return false;
1385}
1386
1387template void Map::Add(Corpse *);
1388template void Map::Add(Creature *);
1389template void Map::Add(GameObject *);
1390template void Map::Add(DynamicObject *);
1391
1392template void Map::Remove(Corpse *,bool);
1393template void Map::Remove(Creature *,bool);
1394template void Map::Remove(GameObject *, bool);
1395template void Map::Remove(DynamicObject *, bool);
1396
1397/* ******* Dungeon Instance Maps ******* */
1398
1399InstanceMap::InstanceMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode)
1400  : Map(id, expiry, InstanceId, SpawnMode), i_data(NULL),
1401    m_resetAfterUnload(false), m_unloadWhenEmpty(false)
1402{
1403    // the timer is started by default, and stopped when the first player joins
1404    // this make sure it gets unloaded if for some reason no player joins
1405    m_unloadTimer = std::max(sWorld.getConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY);
1406}
1407
1408InstanceMap::~InstanceMap()
1409{
1410    if(i_data)
1411    {
1412        delete i_data;
1413        i_data = NULL;
1414    }
1415}
1416
1417/*
1418    Do map specific checks to see if the player can enter
1419*/
1420bool InstanceMap::CanEnter(Player *player)
1421{
1422    if(std::find(i_Players.begin(),i_Players.end(),player)!=i_Players.end())
1423    {
1424        sLog.outError("InstanceMap::CanEnter - player %s(%u) already in map %d,%d,%d!", player->GetName(), player->GetGUIDLow(), GetId(), GetInstanceId(), GetSpawnMode());
1425        assert(false);
1426        return false;
1427    }
1428
1429    // cannot enter if the instance is full (player cap), GMs don't count
1430    InstanceTemplate const* iTemplate = objmgr.GetInstanceTemplate(GetId());
1431    if (!player->isGameMaster() && GetPlayersCountExceptGMs() >= iTemplate->maxPlayers)
1432    {
1433        sLog.outDetail("MAP: Instance '%u' of map '%s' cannot have more than '%u' players. Player '%s' rejected", GetInstanceId(), GetMapName(), iTemplate->maxPlayers, player->GetName());
1434        player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS);
1435        return false;
1436    }
1437
1438    // cannot enter while players in the instance are in combat
1439    Group *pGroup = player->GetGroup();
1440    if(!player->isGameMaster() && pGroup && pGroup->InCombatToInstance(GetInstanceId()) && player->isAlive() && player->GetMapId() != GetId())
1441    {
1442        player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT);
1443        return false;
1444    }
1445
1446    /*if(!player->isGameMaster() && i_data && i_data->IsEncounterInProgress())
1447    {
1448        sLog.outDebug("InstanceMap::CanEnter - Player '%s' can't enter instance '%s' while an encounter is in progress.", player->GetName(), GetMapName());
1449        player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT);
1450        return false;
1451    }*/
1452
1453    return Map::CanEnter(player);
1454}
1455
1456/*
1457    Do map specific checks and add the player to the map if successful.
1458*/
1459bool InstanceMap::Add(Player *player)
1460{
1461    // TODO: Not sure about checking player level: already done in HandleAreaTriggerOpcode
1462    // GMs still can teleport player in instance.
1463    // Is it needed?
1464
1465    {
1466        Guard guard(*this);
1467        if(!CanEnter(player))
1468            return false;
1469
1470        // get or create an instance save for the map
1471        InstanceSave *mapSave = sInstanceSaveManager.GetInstanceSave(GetInstanceId());
1472        if(!mapSave)
1473        {
1474            sLog.outDetail("InstanceMap::Add: creating instance save for map %d spawnmode %d with instance id %d", GetId(), GetSpawnMode(), GetInstanceId());
1475            mapSave = sInstanceSaveManager.AddInstanceSave(GetId(), GetInstanceId(), GetSpawnMode(), 0, true);
1476        }
1477
1478        // check for existing instance binds
1479        InstancePlayerBind *playerBind = player->GetBoundInstance(GetId(), GetSpawnMode());
1480        if(playerBind && playerBind->perm)
1481        {
1482            // cannot enter other instances if bound permanently
1483            if(playerBind->save != mapSave)
1484            {
1485                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());
1486                assert(false);
1487            }
1488        }
1489        else
1490        {
1491            Group *pGroup = player->GetGroup();
1492            if(pGroup)
1493            {
1494                // solo saves should be reset when entering a group
1495                InstanceGroupBind *groupBind = pGroup->GetBoundInstance(GetId(), GetSpawnMode());
1496                if(playerBind)
1497                {
1498                    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());
1499                    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());
1500                    assert(false);
1501                }
1502                // bind to the group or keep using the group save
1503                if(!groupBind)
1504                    pGroup->BindToInstance(mapSave, false);
1505                else
1506                {
1507                    // cannot jump to a different instance without resetting it
1508                    if(groupBind->save != mapSave)
1509                    {
1510                        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());
1511                        if(mapSave)
1512                            sLog.outError("MapSave players: %d, group count: %d", mapSave->GetPlayerCount(), mapSave->GetGroupCount());
1513                        else
1514                            sLog.outError("MapSave NULL");
1515                        if(groupBind->save)
1516                            sLog.outError("GroupBind save players: %d, group count: %d", groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount());
1517                        else
1518                            sLog.outError("GroupBind save NULL");
1519                        assert(false);
1520                    }
1521                    // if the group/leader is permanently bound to the instance
1522                    // players also become permanently bound when they enter
1523                    if(groupBind->perm)
1524                    {
1525                        WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
1526                        data << uint32(0);
1527                        player->GetSession()->SendPacket(&data);
1528                        player->BindToInstance(mapSave, true);
1529                    }
1530                }
1531            }
1532            else
1533            {
1534                // set up a solo bind or continue using it
1535                if(!playerBind)
1536                    player->BindToInstance(mapSave, false);
1537                else
1538                    // cannot jump to a different instance without resetting it
1539                    assert(playerBind->save == mapSave);
1540            }
1541        }
1542
1543        if(i_data) i_data->OnPlayerEnter(player);
1544        SetResetSchedule(false);
1545
1546        i_Players.push_back(player);
1547        player->SendInitWorldStates();
1548        sLog.outDetail("MAP: Player '%s' entered the instance '%u' of map '%s'", player->GetName(), GetInstanceId(), GetMapName());
1549        // initialize unload state
1550        m_unloadTimer = 0;
1551        m_resetAfterUnload = false;
1552        m_unloadWhenEmpty = false;
1553    }
1554
1555    // this will acquire the same mutex so it cannot be in the previous block
1556    Map::Add(player);
1557    return true;
1558}
1559
1560void InstanceMap::Remove(Player *player, bool remove)
1561{
1562    sLog.outDetail("MAP: Removing player '%s' from instance '%u' of map '%s' before relocating to other map", player->GetName(), GetInstanceId(), GetMapName());
1563    i_Players.remove(player);
1564    SetResetSchedule(true);
1565    if(!m_unloadTimer && i_Players.empty())
1566        m_unloadTimer = m_unloadWhenEmpty ? MIN_UNLOAD_DELAY : std::max(sWorld.getConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY);
1567    Map::Remove(player, remove);
1568}
1569
1570Creature * Map::GetCreatureInMap(uint64 guid)
1571{
1572    Creature * obj = HashMapHolder<Creature>::Find(guid);
1573    if(obj && obj->GetInstanceId() != GetInstanceId()) obj = NULL;
1574    return obj;
1575}
1576
1577GameObject * Map::GetGameObjectInMap(uint64 guid)
1578{
1579    GameObject * obj = HashMapHolder<GameObject>::Find(guid);
1580    if(obj && obj->GetInstanceId() != GetInstanceId()) obj = NULL;
1581    return obj;
1582}
1583
1584void InstanceMap::CreateInstanceData(bool load)
1585{
1586    if(i_data != NULL)
1587        return;
1588
1589    InstanceTemplate const* mInstance = objmgr.GetInstanceTemplate(GetId());
1590    if (mInstance)
1591    {
1592        i_script = mInstance->script;
1593        i_data = Script->CreateInstanceData(this);
1594    }
1595
1596    if(!i_data)
1597        return;
1598
1599    if(load)
1600    {
1601        // TODO: make a global storage for this
1602        QueryResult* result = CharacterDatabase.PQuery("SELECT data FROM instance WHERE map = '%u' AND id = '%u'", GetId(), i_InstanceId);
1603        if (result)
1604        {
1605            Field* fields = result->Fetch();
1606            const char* data = fields[0].GetString();
1607            if(data)
1608            {
1609                sLog.outDebug("Loading instance data for `%s` with id %u", i_script.c_str(), i_InstanceId);
1610                i_data->Load(data);
1611            }
1612            delete result;
1613        }
1614    }
1615    else
1616    {
1617        sLog.outDebug("New instance data, \"%s\" ,initialized!",i_script.c_str());
1618        i_data->Initialize();
1619    }
1620}
1621
1622/*
1623    Returns true if there are no players in the instance
1624*/
1625bool InstanceMap::Reset(uint8 method)
1626{
1627    // note: since the map may not be loaded when the instance needs to be reset
1628    // the instance must be deleted from the DB by InstanceSaveManager
1629
1630    if(!i_Players.empty())
1631    {
1632        if(method == INSTANCE_RESET_ALL)
1633        {
1634            // notify the players to leave the instance so it can be reset
1635            for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
1636                (*itr)->SendResetFailedNotify(GetId());
1637        }
1638        else
1639        {
1640            if(method == INSTANCE_RESET_GLOBAL)
1641            {
1642                // set the homebind timer for players inside (1 minute)
1643                for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
1644                    (*itr)->m_InstanceValid = false;
1645            }
1646
1647            // the unload timer is not started
1648            // instead the map will unload immediately after the players have left
1649            m_unloadWhenEmpty = true;
1650            m_resetAfterUnload = true;
1651        }
1652    }
1653    else
1654    {
1655        // unloaded at next update
1656        m_unloadTimer = MIN_UNLOAD_DELAY;
1657        m_resetAfterUnload = true;
1658    }
1659
1660    return i_Players.empty();
1661}
1662
1663uint32 InstanceMap::GetPlayersCountExceptGMs() const
1664{
1665    uint32 count = 0;
1666    for(PlayerList::const_iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
1667        if(!(*itr)->isGameMaster())
1668            ++count;
1669    return count;
1670}
1671
1672void InstanceMap::PermBindAllPlayers(Player *player)
1673{
1674    InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId());
1675    if(!save)
1676    {
1677        sLog.outError("Cannot bind players, no instance save available for map!\n");
1678        return;
1679    }
1680
1681    Group *group = player->GetGroup();
1682    // group members outside the instance group don't get bound
1683    for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
1684    {
1685        if(*itr)
1686        {
1687            // players inside an instance cannot be bound to other instances
1688            // some players may already be permanently bound, in this case nothing happens
1689            InstancePlayerBind *bind = (*itr)->GetBoundInstance(save->GetMapId(), save->GetDifficulty());
1690            if(!bind || !bind->perm)
1691            {
1692                (*itr)->BindToInstance(save, true);
1693                WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
1694                data << uint32(0);
1695                (*itr)->GetSession()->SendPacket(&data);
1696            }
1697
1698            // if the leader is not in the instance the group will not get a perm bind
1699            if(group && group->GetLeaderGUID() == (*itr)->GetGUID())
1700                group->BindToInstance(save, true);
1701        }
1702    }
1703}
1704
1705time_t InstanceMap::GetResetTime()
1706{
1707    InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId());
1708    return save ? save->GetDifficulty() : DIFFICULTY_NORMAL;
1709}
1710
1711void InstanceMap::UnloadAll(bool pForce)
1712{
1713    if(!i_Players.empty())
1714    {
1715        sLog.outError("InstanceMap::UnloadAll: there are still players in the instance at unload, should not happen!");
1716        for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
1717            if(*itr) (*itr)->TeleportTo((*itr)->m_homebindMapId, (*itr)->m_homebindX, (*itr)->m_homebindY, (*itr)->m_homebindZ, (*itr)->GetOrientation());
1718    }
1719
1720    if(m_resetAfterUnload == true)
1721        objmgr.DeleteRespawnTimeForInstance(GetInstanceId());
1722
1723    Map::UnloadAll(pForce);
1724}
1725
1726void InstanceMap::SendResetWarnings(uint32 timeLeft)
1727{
1728    for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
1729        (*itr)->SendInstanceResetWarning(GetId(), timeLeft);
1730}
1731
1732void InstanceMap::SetResetSchedule(bool on)
1733{
1734    // only for normal instances
1735    // the reset time is only scheduled when there are no payers inside
1736    // it is assumed that the reset time will rarely (if ever) change while the reset is scheduled
1737    if(i_Players.empty() && !IsRaid() && !IsHeroic())
1738    {
1739        InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId());
1740        if(!save) sLog.outError("InstanceMap::SetResetSchedule: cannot turn schedule %s, no save available for instance %d of %d", on ? "on" : "off", GetInstanceId(), GetId());
1741        else sInstanceSaveManager.ScheduleReset(on, save->GetResetTime(), InstanceSaveManager::InstResetEvent(0, GetId(), GetInstanceId()));
1742    }
1743}
1744
1745void InstanceMap::SendToPlayers(WorldPacket const* data) const
1746{
1747    for(PlayerList::const_iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
1748        (*itr)->GetSession()->SendPacket(data);
1749}
1750
1751/* ******* Battleground Instance Maps ******* */
1752
1753BattleGroundMap::BattleGroundMap(uint32 id, time_t expiry, uint32 InstanceId)
1754  : Map(id, expiry, InstanceId, DIFFICULTY_NORMAL)
1755{
1756}
1757
1758BattleGroundMap::~BattleGroundMap()
1759{
1760}
1761
1762bool BattleGroundMap::CanEnter(Player * player)
1763{
1764    if(std::find(i_Players.begin(),i_Players.end(),player)!=i_Players.end())
1765    {
1766        sLog.outError("BGMap::CanEnter - player %u already in map!", player->GetGUIDLow());
1767        assert(false);
1768        return false;
1769    }
1770
1771    if(player->GetBattleGroundId() != GetInstanceId())
1772        return false;
1773
1774    // player number limit is checked in bgmgr, no need to do it here
1775
1776    return Map::CanEnter(player);
1777}
1778
1779bool BattleGroundMap::Add(Player * player)
1780{
1781    {
1782        Guard guard(*this);
1783        if(!CanEnter(player))
1784            return false;
1785        i_Players.push_back(player);
1786        // reset instance validity, battleground maps do not homebind
1787        player->m_InstanceValid = true;
1788    }
1789    return Map::Add(player);
1790}
1791
1792void BattleGroundMap::Remove(Player *player, bool remove)
1793{
1794    sLog.outDetail("MAP: Removing player '%s' from bg '%u' of map '%s' before relocating to other map", player->GetName(), GetInstanceId(), GetMapName());
1795    i_Players.remove(player);
1796    Map::Remove(player, remove);
1797}
1798
1799void BattleGroundMap::SetUnload()
1800{
1801    m_unloadTimer = MIN_UNLOAD_DELAY;
1802}
1803
1804void BattleGroundMap::UnloadAll(bool pForce)
1805{
1806    while(!i_Players.empty())
1807    {
1808        PlayerList::iterator itr = i_Players.begin();
1809        Player * plr = *itr;
1810        if(plr) (plr)->TeleportTo((*itr)->m_homebindMapId, (*itr)->m_homebindX, (*itr)->m_homebindY, (*itr)->m_homebindZ, (*itr)->GetOrientation());
1811        // TeleportTo removes the player from this map (if the map exists) -> calls BattleGroundMap::Remove -> invalidates the iterator.
1812        // just in case, remove the player from the list explicitly here as well to prevent a possible infinite loop
1813        // note that this remove is not needed if the code works well in other places
1814        i_Players.remove(plr);
1815    }
1816
1817    Map::UnloadAll(pForce);
1818}
Note: See TracBrowser for help on using the browser.