void MapMgr::RemoveObject(Object *obj) { ASSERT(obj); ASSERT(obj->GetMapId() == _mapId); ASSERT(obj->GetPositionX() > _minX && obj->GetPositionX() < _maxX); ASSERT(obj->GetPositionY() > _minY && obj->GetPositionY() < _maxY); ASSERT(_cells); sLog.outDetail("Removing object "I64FMT" with type %i from the world.", obj->GetGUID(), obj->GetTypeId()); // That object types are not map objects. TODO: add AI groups here? if(obj->GetTypeId() == TYPEID_ITEM || obj->GetTypeId() == TYPEID_CONTAINER) { // remove updatable flag and exit obj->RemoveFromWorld(); return; } obj->RemoveFromWorld(); ObjectMap::iterator itr = _objects.find(obj->GetGUID()); _objects.erase(itr); // remove us from updated objects list ObjectSet::iterator updi = _updatedObjects.find(obj); if(updi != _updatedObjects.end()) _updatedObjects.erase(updi); MapCell *objCell = obj->GetMapCell(); obj->SetMapCell(0); objCell->RemoveObject(obj); for (Object::InRangeSet::iterator iter = obj->GetInRangeSetBegin(); iter != obj->GetInRangeSetEnd(); iter++) { (*iter)->RemoveInRangeObject(obj); if((*iter)->GetTypeId() == TYPEID_PLAYER) obj->DestroyForPlayer( (Player*)*iter ); } obj->ClearInRangeSet(); }
void MapMgr::ChangeObjectLocation(Object* obj) { /* if ( !obj ) return; // crashfix */ ARCEMU_ASSERT(obj != NULL); // Items and containers are of no interest for us if(obj->IsItem() || obj->IsContainer() || obj->GetMapMgr() != this) { return; } Player* plObj = NULL; ByteBuffer* buf = 0; if(obj->IsPlayer()) { plObj = TO< Player* >(obj); } Object* curObj; float fRange = 0.0f; /////////////////////////////////////// // Update in-range data for old objects /////////////////////////////////////// if(obj->HasInRangeObjects()) { for(Object::InRangeSet::iterator iter = obj->GetInRangeSetBegin(); iter != obj->GetInRangeSetEnd();) { curObj = *iter; ++iter; if(curObj->IsPlayer() && plObj != NULL && plObj->transporter_info.guid && plObj->transporter_info.guid == TO< Player* >(curObj)->transporter_info.guid) fRange = 0.0f; // unlimited distance for people on same boat else if(curObj->GetTypeFromGUID() == HIGHGUID_TYPE_TRANSPORTER) fRange = 0.0f; // unlimited distance for transporters (only up to 2 cells +/- anyway.) //If the object announcing its position is a transport, or other special object, then deleting it from visible objects should be avoided. - By: VLack else if(obj->IsGameObject() && (TO< GameObject* >(obj)->GetOverrides() & GAMEOBJECT_INFVIS) && obj->GetMapId() == curObj->GetMapId()) fRange = 0.0f; //If the object we're checking for possible removal is a transport or other special object, and we are players on the same map, don't remove it... else if(plObj && curObj->IsGameObject() && (TO< GameObject* >(curObj)->GetOverrides() & GAMEOBJECT_INFVIS) && obj->GetMapId() == curObj->GetMapId()) fRange = 0.0f; else if(curObj->IsPlayer() && TO< Player* >(curObj)->GetFarsightTarget() == obj->GetGUID()) fRange = 0.0f;//Mind Vision, Eye of Kilrogg else fRange = m_UpdateDistance; // normal distance if(fRange > 0.0f && (curObj->GetDistance2dSq(obj) > fRange)) { if(plObj != NULL) plObj->RemoveIfVisible(curObj->GetGUID()); if(curObj->IsPlayer()) TO< Player* >(curObj)->RemoveIfVisible(obj->GetGUID()); curObj->RemoveInRangeObject(obj); if(obj->GetMapMgr() != this) { /* Something removed us. */ return; } obj->RemoveInRangeObject(curObj); } } } /////////////////////////// // Get new cell coordinates /////////////////////////// if(obj->GetMapMgr() != this) { /* Something removed us. */ return; } if(obj->GetPositionX() >= _maxX || obj->GetPositionX() <= _minX || obj->GetPositionY() >= _maxY || obj->GetPositionY() <= _minY) { if(plObj != NULL) { if(plObj->GetBindMapId() != GetMapId()) { plObj->SafeTeleport(plObj->GetBindMapId(), 0, plObj->GetBindPositionX(), plObj->GetBindPositionY(), plObj->GetBindPositionZ(), 0); plObj->GetSession()->SystemMessage("Teleported you to your hearthstone location as you were out of the map boundaries."); return; } else { obj->GetPositionV()->ChangeCoords(plObj->GetBindPositionX(), plObj->GetBindPositionY(), plObj->GetBindPositionZ(), 0); plObj->GetSession()->SystemMessage("Teleported you to your hearthstone location as you were out of the map boundaries."); plObj->SendTeleportAckMsg(plObj->GetPosition()); } } else { obj->GetPositionV()->ChangeCoords(0, 0, 0, 0); } } uint32 cellX = GetPosX(obj->GetPositionX()); uint32 cellY = GetPosY(obj->GetPositionY()); if(cellX >= _sizeX || cellY >= _sizeY) { return; } MapCell* objCell = GetCell(cellX, cellY); MapCell* pOldCell = obj->GetMapCell(); if(objCell == NULL) { objCell = Create(cellX, cellY); objCell->Init(cellX, cellY, this); } ARCEMU_ASSERT(objCell != NULL); // If object moved cell if(objCell != pOldCell) { // THIS IS A HACK! // Current code, if a creature on a long waypoint path moves from an active // cell into an inactive one, it will disable itself and will never return. // This is to prevent cpu leaks. I will think of a better solution very soon :P if(!objCell->IsActive() && !plObj && obj->IsActive()) obj->Deactivate(this); if(pOldCell != NULL) pOldCell->RemoveObject(obj); objCell->AddObject(obj); obj->SetMapCell(objCell); // if player we need to update cell activity // radius = 2 is used in order to update both // old and new cells if(obj->IsPlayer()) { // have to unlock/lock here to avoid a deadlock situation. UpdateCellActivity(cellX, cellY, 2); if(pOldCell != NULL) { // only do the second check if there's -/+ 2 difference if(abs((int)cellX - (int)pOldCell->_x) > 2 || abs((int)cellY - (int)pOldCell->_y) > 2) { UpdateCellActivity(pOldCell->_x, pOldCell->_y, 2); } } } } ////////////////////////////////////// // Update in-range set for new objects ////////////////////////////////////// uint32 endX = cellX <= _sizeX ? cellX + 1 : (_sizeX - 1); uint32 endY = cellY <= _sizeY ? cellY + 1 : (_sizeY - 1); uint32 startX = cellX > 0 ? cellX - 1 : 0; uint32 startY = cellY > 0 ? cellY - 1 : 0; uint32 posX, posY; MapCell* cell; //If the object announcing it's position is a special one, then it should do so in a much wider area - like the distance between the two transport towers in Orgrimmar, or more. - By: VLack if(obj->IsGameObject() && (TO< GameObject* >(obj)->GetOverrides() & GAMEOBJECT_ONMOVEWIDE)) { endX = cellX + 5 <= _sizeX ? cellX + 6 : (_sizeX - 1); endY = cellY + 5 <= _sizeY ? cellY + 6 : (_sizeY - 1); startX = cellX > 5 ? cellX - 6 : 0; startY = cellY > 5 ? cellY - 6 : 0; } for(posX = startX; posX <= endX; ++posX) { for(posY = startY; posY <= endY; ++posY) { cell = GetCell(posX, posY); if(cell) UpdateInRangeSet(obj, plObj, cell, &buf); } } if(buf) delete buf; }
void MapMgr::RemoveObject(Object* obj, bool free_guid) { ///////////// // Assertions ///////////// ARCEMU_ASSERT(obj != NULL); ARCEMU_ASSERT(obj->GetMapId() == _mapId); //ARCEMU_ASSERT( obj->GetPositionX() > _minX && obj->GetPositionX() < _maxX); //ARCEMU_ASSERT( obj->GetPositionY() > _minY && obj->GetPositionY() < _maxY); ARCEMU_ASSERT(_cells != NULL); if(obj->IsActive()) obj->Deactivate(this); //there is a very small chance that on double player ports on same update player is added to multiple insertpools but not removed //one clear example was the double port proc when exploiting double resurrect m_objectinsertlock.Acquire(); m_objectinsertpool.erase(obj); m_objectinsertlock.Release(); _updates.erase(obj); obj->ClearUpdateMask(); /////////////////////////////////////// // Remove object from all needed places /////////////////////////////////////// switch(obj->GetTypeFromGUID()) { case HIGHGUID_TYPE_UNIT: case HIGHGUID_TYPE_VEHICLE: ARCEMU_ASSERT(obj->GetUIdFromGUID() <= m_CreatureHighGuid); CreatureStorage[ obj->GetUIdFromGUID() ] = NULL; if(TO_CREATURE(obj)->m_spawn != NULL) { _sqlids_creatures.erase(TO_CREATURE(obj)->m_spawn->id); } if(free_guid) _reusable_guids_creature.push_back(obj->GetUIdFromGUID()); break; case HIGHGUID_TYPE_PET: if(pet_iterator != m_PetStorage.end() && pet_iterator->second->GetGUID() == obj->GetGUID()) ++pet_iterator; m_PetStorage.erase(obj->GetUIdFromGUID()); break; case HIGHGUID_TYPE_DYNAMICOBJECT: m_DynamicObjectStorage.erase(obj->GetLowGUID()); break; case HIGHGUID_TYPE_GAMEOBJECT: ARCEMU_ASSERT(obj->GetUIdFromGUID() <= m_GOHighGuid); GOStorage[ obj->GetUIdFromGUID() ] = NULL; if(TO_GAMEOBJECT(obj)->m_spawn != NULL) { _sqlids_gameobjects.erase(TO_GAMEOBJECT(obj)->m_spawn->id); } if(free_guid) _reusable_guids_gameobject.push_back(obj->GetUIdFromGUID()); break; } // That object types are not map objects. TODO: add AI groups here? if(obj->IsItem() || obj->IsContainer()) { return; } if(obj->IsCorpse()) { m_corpses.erase(TO< Corpse* >(obj)); } MapCell* cell = GetCell(obj->GetMapCellX(), obj->GetMapCellY()); if(cell == NULL) { /* set the map cell correctly */ if(obj->GetPositionX() >= _maxX || obj->GetPositionX() <= _minY || obj->GetPositionY() >= _maxY || obj->GetPositionY() <= _minY) { // do nothing } else { cell = this->GetCellByCoords(obj->GetPositionX(), obj->GetPositionY()); obj->SetMapCell(cell); } } if(cell != NULL) { // Remove object from cell cell->RemoveObject(obj); // Unset object's cell obj->SetMapCell(NULL); } Player* plObj = NULL; // Clear any updates pending if(obj->IsPlayer()) { plObj = TO_PLAYER(obj); _processQueue.erase(plObj); plObj->ClearAllPendingUpdates(); } obj->RemoveSelfFromInrangeSets(); // Clear object's in-range set obj->ClearInRangeSet(); // If it's a player - update his nearby cells if(!_shutdown && obj->IsPlayer()) { // get x/y if(obj->GetPositionX() >= _maxX || obj->GetPositionX() <= _minY || obj->GetPositionY() >= _maxY || obj->GetPositionY() <= _minY) { // do nothing } else { uint32 x = GetPosX(obj->GetPositionX()); uint32 y = GetPosY(obj->GetPositionY()); UpdateCellActivity(x, y, 2); } m_PlayerStorage.erase(TO< Player* >(obj)->GetLowGUID()); } // Remove the session from our set if it is a player. if(obj->IsPlayer()) { for(set<Object*>::iterator itr = _mapWideStaticObjects.begin(); itr != _mapWideStaticObjects.end(); ++itr) { plObj->PushOutOfRange((*itr)->GetNewGUID()); } // Setting an instance ID here will trigger the session to be removed // by MapMgr::run(). :) plObj->GetSession()->SetInstance(0); // Add it to the global session set. // Don't "re-add" to session if it is being deleted. if(!plObj->GetSession()->bDeleted) sWorld.AddGlobalSession(plObj->GetSession()); } if(!HasPlayers()) { if(this->pInstance != NULL && this->pInstance->m_persistent) this->pInstance->m_creatorGroup = 0; if(!InactiveMoveTime && !forced_expire && GetMapInfo()->type != INSTANCE_NULL) { InactiveMoveTime = UNIXTIME + (MAPMGR_INACTIVE_MOVE_TIME * 60); Log.Debug("MapMgr", "Instance %u is now idle. (%s)", m_instanceID, GetBaseMap()->GetName()); } } }