Add basic support for NPCs on transports; DB data required.

Currently no support for combat while the transport is in motion.
Based on a patch by Socolin.
Fixes issue #168

--HG--
branch : trunk
This commit is contained in:
silinoron
2010-07-30 21:54:46 -07:00
parent b3bca2743e
commit 71b1c781e3
15 changed files with 320 additions and 88 deletions
+24 -1
View File
@@ -42,7 +42,7 @@
#include <map>
#include "GlobalEvents.h"
#include "OutdoorPvPMgr.h"
#include "Transport.h"
#include "TargetedMovementGenerator.h" // for HandleNpcUnFollowCommand
#include "CreatureGroups.h"
@@ -1005,6 +1005,29 @@ bool ChatHandler::HandleNpcAddCommand(const char* args)
float o = chr->GetOrientation();
Map *map = chr->GetMap();
if (chr->GetTransport())
{
uint32 tguid = chr->GetTransport()->AddNPCPassenger(0, id, chr->GetTransOffsetX(), chr->GetTransOffsetY(), chr->GetTransOffsetZ(), chr->GetTransOffsetO());
if (tguid > 0)
{
WorldDatabase.PQuery("INSERT INTO creature_transport (guid, npc_entry, transport_entry, TransOffsetX, TransOffsetY, TransOffsetZ, TransOffsetO) values (%u, %u, %f, %f, %f, %f, %u)", tguid, id, chr->GetTransport()->GetEntry(), chr->GetTransOffsetX(), chr->GetTransOffsetY(), chr->GetTransOffsetZ(), chr->GetTransOffsetO());
TransportCreatureProto *transportCreatureProto = new TransportCreatureProto;
transportCreatureProto->guid = tguid;
transportCreatureProto->npc_entry = id;
uint32 transportEntry = chr->GetTransport()->GetEntry();
transportCreatureProto->TransOffsetX = chr->GetTransOffsetX();
transportCreatureProto->TransOffsetY = chr->GetTransOffsetY();
transportCreatureProto->TransOffsetZ = chr->GetTransOffsetZ();
transportCreatureProto->TransOffsetO = chr->GetTransOffsetO();
transportCreatureProto->emote = 0;
sMapMgr.m_TransportNPCMap[transportEntry].insert(transportCreatureProto);
sMapMgr.m_TransportNPCs.insert(transportCreatureProto);
}
return true;
}
Creature* pCreature = new Creature;
if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), id, 0, (uint32)teamval, x, y, z, o))
{
+5
View File
@@ -57,6 +57,7 @@
#include "DBCEnums.h"
#include "ConditionMgr.h"
#include "DisableMgr.h"
#include "Transport.h"
bool ChatHandler::HandleAHBotOptionsCommand(const char *args)
{
@@ -4801,6 +4802,10 @@ bool ChatHandler::HandleNpcPlayEmoteCommand(const char *args)
return false;
}
if (target->GetTransport())
if (target->GetGUIDTransport())
WorldDatabase.PQuery("UPDATE creature_transport SET emote=%u WHERE transport_entry=%u AND guid=%u", emote, target->GetTransport()->GetEntry(), target->GetGUIDTransport());
target->SetUInt32Value(UNIT_NPC_EMOTESTATE,emote);
return true;
+3 -1
View File
@@ -667,6 +667,8 @@ class Creature : public Unit, public GridObject<Creature>
float m_SightDistance, m_CombatDistance;
void SetGUIDTransport(uint32 guid) { guid_transport=guid; }
uint32 GetGUIDTransport() { return guid_transport; }
protected:
bool CreateFromProto(uint32 guidlow, uint32 Entry, uint32 vehId, uint32 team, const CreatureData *data = NULL);
bool InitEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL);
@@ -714,7 +716,7 @@ class Creature : public Unit, public GridObject<Creature>
CreatureData const* m_creatureData;
uint16 m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable
uint32 guid_transport;
private:
//WaypointMovementGenerator vars
uint32 m_waypointID;
+17 -2
View File
@@ -1151,6 +1151,7 @@ WorldObject::WorldObject()
, m_name("")
, m_notifyflags(0), m_executed_notifies(0)
{
m_transport = NULL;
}
void WorldObject::SetWorldObject(bool on)
@@ -1235,6 +1236,22 @@ float WorldObject::GetDistanceZ(const WorldObject* obj) const
bool WorldObject::_IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D) const
{
float sizefactor = GetObjectSize() + obj->GetObjectSize();
float maxdist = dist2compare + sizefactor;
if (m_transport && obj->GetTransport() && obj->GetTransport()->GetGUIDLow() == m_transport->GetGUIDLow())
{
float dtx = m_movementInfo.t_x - obj->m_movementInfo.t_x;
float dty = m_movementInfo.t_y - obj->m_movementInfo.t_y;
float disttsq = dtx * dtx + dty * dty;
if (is3D)
{
float dtz = m_movementInfo.t_z - obj->m_movementInfo.t_z;
disttsq += dtz * dtz;
}
return disttsq < (maxdist * maxdist);
}
float dx = GetPositionX() - obj->GetPositionX();
float dy = GetPositionY() - obj->GetPositionY();
float distsq = dx*dx + dy*dy;
@@ -1243,8 +1260,6 @@ bool WorldObject::_IsWithinDist(WorldObject const* obj, float dist2compare, bool
float dz = GetPositionZ() - obj->GetPositionZ();
distsq += dz*dz;
}
float sizefactor = GetObjectSize() + obj->GetObjectSize();
float maxdist = dist2compare + sizefactor;
return distsq < maxdist * maxdist;
}
+46
View File
@@ -115,9 +115,46 @@ class Vehicle;
class CreatureAI;
class ZoneScript;
class Unit;
class Transport;
typedef UNORDERED_MAP<Player*, UpdateData> UpdateDataMapType;
struct MovementInfo
{
// common
uint64 guid;
uint32 flags;
uint16 unk1;
uint32 time;
float x, y, z, o;
// transport
uint64 t_guid;
float t_x, t_y, t_z, t_o;
uint32 t_time;
int8 t_seat;
// swimming and unknown
float s_pitch;
// last fall time
uint32 fallTime;
// jumping
float j_zspeed, j_sinAngle, j_cosAngle, j_xyspeed;
// spline
float u_unk1;
MovementInfo()
{
flags = 0;
time = t_time = fallTime = 0;
unk1 = 0;
x = y = z = o = t_x = t_y = t_z = t_o = s_pitch = j_zspeed = j_sinAngle = j_cosAngle = j_xyspeed = u_unk1 = 0.0f;
t_guid = 0;
}
uint32 GetMovementFlags() { return flags; }
void AddMovementFlag(uint32 flag) { flags |= flag; }
bool HasMovementFlag(uint32 flag) const { return flags & flag; }
};
class Object
{
public:
@@ -696,12 +733,21 @@ class WorldObject : public Object, public WorldLocation
bool m_isWorldObject;
uint32 LastUsedScriptID;
// Transports
Transport *GetTransport() const { return m_transport; }
void SetTransport(Transport *t) { m_transport = t; }
MovementInfo m_movementInfo;
protected:
explicit WorldObject();
std::string m_name;
bool m_isActive;
ZoneScript *m_zoneScript;
// transports
Transport *m_transport;
//these functions are used mostly for Relocate() and Corpse/Player specific stuff...
//use them ONLY in LoadFromDB()/Create() funcs and nowhere else!
//mapId/instanceId should be set in SetMap() function!
+1 -1
View File
@@ -16093,7 +16093,7 @@ bool Player::LoadFromDB(uint32 guid, SqlQueryHolder *holder)
GetPositionX()+m_movementInfo.t_x,GetPositionY()+m_movementInfo.t_y,
GetPositionZ()+m_movementInfo.t_z,GetOrientation()+m_movementInfo.t_o) ||
// transport size limited
m_movementInfo.t_x > 50 || m_movementInfo.t_y > 50 || m_movementInfo.t_z > 50)
m_movementInfo.t_x > 250 || m_movementInfo.t_y > 250 || m_movementInfo.t_z > 250)
{
sLog.outError("Player (guidlow %d) have invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",
guid,GetPositionX()+m_movementInfo.t_x,GetPositionY()+m_movementInfo.t_y,
+174 -37
View File
@@ -108,7 +108,12 @@ void MapManager::LoadTransports()
//If we someday decide to use the grid to track transports, here:
t->SetMap(sMapMgr.CreateMap(mapid, t, 0));
//t->GetMap()->Add<GameObject>((GameObject *)t);
for (TransportNPCSet::const_iterator i = m_TransportNPCMap[entry].begin(); i != m_TransportNPCMap[entry].end(); ++i)
{
TransportCreatureProto *proto = (*i);
t->AddNPCPassenger(proto->guid, proto->npc_entry, proto->TransOffsetX, proto->TransOffsetY, proto->TransOffsetZ, proto->TransOffsetO, proto->emote);
}
++count;
} while (result->NextRow());
@@ -132,6 +137,48 @@ void MapManager::LoadTransports()
}
}
void MapManager::LoadTransportNPCs()
{
// Spawn NPCs linked to the transport
// 0 1 2 3 4 5 6 7
QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT guid, npc_entry, transport_entry, TransOffsetX, TransOffsetY, TransOffsetZ, TransOffsetO, emote FROM creature_transport");
uint32 count = 0;
if (!result)
{
barGoLink bar(1);
bar.step();
sLog.outString();
sLog.outString(">> Loaded %u transport NPCs.", count);
return;
}
barGoLink bar(result->GetRowCount());
do
{
bar.step();
Field *fields = result->Fetch();
TransportCreatureProto *transportCreatureProto = new TransportCreatureProto;
transportCreatureProto->guid = fields[0].GetUInt32();
transportCreatureProto->npc_entry = fields[1].GetUInt32();
uint32 transportEntry = fields[2].GetUInt32();
transportCreatureProto->TransOffsetX = fields[3].GetFloat();
transportCreatureProto->TransOffsetY = fields[4].GetFloat();
transportCreatureProto->TransOffsetZ = fields[5].GetFloat();
transportCreatureProto->TransOffsetO = fields[6].GetFloat();
transportCreatureProto->emote = fields[7].GetUInt32();
m_TransportNPCs.insert(transportCreatureProto);
m_TransportNPCMap[transportEntry].insert(transportCreatureProto);
count++;
} while (result->NextRow());
sLog.outString();
sLog.outString(">> Loaded %u transport npcs", count);
}
Transport::Transport() : GameObject()
{
m_updateFlag = (UPDATEFLAG_TRANSPORT | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HAS_POSITION | UPDATEFLAG_ROTATION);
@@ -443,16 +490,22 @@ void Transport::TeleportTransport(uint32 newMapid, float x, float y, float z)
plr->ResurrectPlayer(1.0);
}
plr->TeleportTo(newMapid, x, y, z, GetOrientation(), TELE_TO_NOT_LEAVE_TRANSPORT);
//WorldPacket data(SMSG_811, 4);
//data << uint32(0);
//plr->GetSession()->SendPacket(&data);
}
//we need to create and save new Map object with 'newMapid' because if not done -> lead to invalid Map object reference...
//player far teleport would try to create same instance, but we need it NOW for transport...
//correct me if I'm wrong O.o
//yes, you're right
// Clear all NPCs on the transport
for (std::set<uint64>::iterator itr = m_NPCPassengerSet.begin(); itr != m_NPCPassengerSet.end();)
{
std::set<uint64>::iterator it2 = itr;
++itr;
uint64 guid = (*it2);
if (Creature *npc = Creature::GetCreature(*this, guid))
npc->AddObjectToRemoveList();
}
m_NPCPassengerSet.clear();
ResetMap();
Map * newMap = sMapMgr.CreateMap(newMapid, this, 0);
@@ -464,6 +517,12 @@ void Transport::TeleportTransport(uint32 newMapid, float x, float y, float z)
UpdateForMap(oldMap);
UpdateForMap(newMap);
}
for (std::set<TransportCreatureProto *>::const_iterator i = sMapMgr.m_TransportNPCMap[GetEntry()].begin(); i != sMapMgr.m_TransportNPCMap[GetEntry()].end(); ++i)
{
TransportCreatureProto *proto = (*i);
AddNPCPassenger(proto->guid, proto->npc_entry, proto->TransOffsetX, proto->TransOffsetY, proto->TransOffsetZ, proto->TransOffsetO, proto->emote);
}
}
bool Transport::AddPassenger(Player* passenger)
@@ -502,37 +561,9 @@ void Transport::Update(uint32 /*p_time*/)
}
else
{
Relocate(m_curr->second.x, m_curr->second.y, m_curr->second.z);
Relocate(m_curr->second.x, m_curr->second.y, m_curr->second.z, GetAngle(m_next->second.x,m_next->second.y) + 3.1415926f);
UpdateNPCPositions(); // COME BACK MARKER
}
/*
if (m_curr->second.delayed)
{
switch (GetEntry())
{
case 176495:
case 164871:
case 175080:
SendPlaySound(11804, false); break; // ZeppelinDocked
case 20808:
case 181646:
case 176231:
case 176244:
case 176310:
case 177233:
SendPlaySound(5495, false);break; // BoatDockingWarning
default:
SendPlaySound(5154, false); break; // ShipDocked
}
}
*/
/*
for (PlayerSet::const_iterator itr = m_passengers.begin(); itr != m_passengers.end();)
{
PlayerSet::const_iterator it2 = itr;
++itr;
//(*it2)->SetPosition(m_curr->second.x + (*it2)->GetTransOffsetX(), m_curr->second.y + (*it2)->GetTransOffsetY(), m_curr->second.z + (*it2)->GetTransOffsetZ(), (*it2)->GetTransOffsetO());
}
*/
m_nextNodeTime = m_curr->first;
@@ -585,3 +616,109 @@ void Transport::DoEventIfAny(WayPointMap::value_type const& node, bool departure
GetMap()->ScriptsStart(sEventScripts, eventid, this, this);
}
}
void Transport::BuildStartMovePacket(Map const* targetMap)
{
SetByteValue(GAMEOBJECT_FLAGS ,0,41);
SetByteValue(GAMEOBJECT_BYTES_1, 0, GO_STATE_ACTIVE);
UpdateForMap(targetMap);
}
void Transport::BuildStopMovePacket(Map const* targetMap)
{
SetByteValue(GAMEOBJECT_FLAGS ,0,40);
UpdateForMap(targetMap);
}
uint32 Transport::AddNPCPassenger(uint32 tguid, uint32 entry, float x, float y, float z, float o, uint32 anim)
{
Map* map = GetMap();
Creature* pCreature = new Creature;
if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), GetMap(), this->GetPhaseMask(), entry, 0, (uint32)(this->GetGOInfo()->faction), 0, 0, 0, 0))
{
delete pCreature;
return 0;
}
pCreature->SetTransport(this);
pCreature->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT);
pCreature->m_movementInfo.guid = GetGUID();
pCreature->m_movementInfo.t_x = x;
pCreature->m_movementInfo.t_y = y;
pCreature->m_movementInfo.t_z = z;
pCreature->m_movementInfo.t_o = o;
pCreature->setActive(true);
if (anim > 0)
pCreature->SetUInt32Value(UNIT_NPC_EMOTESTATE,anim);
pCreature->Relocate(
GetPositionX() + (x * cos(GetOrientation()) + y * sin(GetOrientation() + 3.14159f)),
GetPositionY() + (y * cos(GetOrientation()) + x * sin(GetOrientation())),
z + GetPositionZ() ,
o + GetOrientation());
if(!pCreature->IsPositionValid())
{
sLog.outError("Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY());
delete pCreature;
return 0;
}
pCreature->AIM_Initialize();
map->Add(pCreature);
m_NPCPassengerSet.insert(pCreature->GetGUID());
if (tguid == 0)
{
currenttguid++;
tguid = currenttguid;
}
else
currenttguid = std::max(tguid,currenttguid);
pCreature->SetGUIDTransport(tguid);
return tguid;
}
void Transport::UpdatePosition(MovementInfo* mi)
{
float transport_o = mi->o - mi->t_o;
float transport_x = mi->x - (mi->t_x*cos(transport_o) - mi->t_y*sin(transport_o));
float transport_y = mi->y - (mi->t_y*cos(transport_o) + mi->t_x*sin(transport_o));
float transport_z = mi->z - mi->t_z;
Relocate(transport_x,transport_y,transport_z,transport_o);
UpdateNPCPositions();
}
void Transport::UpdateNPCPositions()
{
if (m_NPCPassengerSet.size() > 0)
{
// We update the positions of all NPCs
for(std::set<uint64>::iterator itr = m_NPCPassengerSet.begin(); itr != m_NPCPassengerSet.end();)
{
std::set<uint64>::iterator it2 = itr;
++itr;
uint64 guid = (*it2);
if (Creature* npc = Creature::GetCreature(*this, guid))
{
float x, y, z, o;
o = GetOrientation() + npc->m_movementInfo.t_o;
x = GetPositionX() + (npc->m_movementInfo.t_x * cos(GetOrientation()) + npc->m_movementInfo.t_y * sin(GetOrientation() + 3.14159f));
y = GetPositionY() + (npc->m_movementInfo.t_y * cos(GetOrientation()) + npc->m_movementInfo.t_x * sin(GetOrientation()));
z = GetPositionZ() + npc->m_movementInfo.t_z;
npc->SetPosition(x, y, z,o);
npc->SetHomePosition(x,y,z,o);
}
else
{
m_NPCPassengerSet.erase(guid);
continue;
}
}
}
}
@@ -27,6 +27,18 @@
#include <set>
#include <string>
struct TransportCreatureProto
{
uint32 guid;
uint32 transport_entry;
uint32 npc_entry;
float TransOffsetX;
float TransOffsetY;
float TransOffsetZ;
float TransOffsetO;
uint32 emote;
};
class Transport : public GameObject
{
public:
@@ -41,6 +53,12 @@ class Transport : public GameObject
typedef std::set<Player*> PlayerSet;
PlayerSet const& GetPassengers() const { return m_passengers; }
mutable std::set<uint64> m_NPCPassengerSet;
uint32 AddNPCPassenger(uint32 tguid, uint32 entry, float x, float y, float z, float o, uint32 anim=0);
void UpdatePosition(MovementInfo *mi);
void UpdateNPCPositions();
void BuildStartMovePacket(Map const *targetMap);
void BuildStopMovePacket(Map const *targetMap);
private:
struct WayPoint
{
@@ -70,6 +88,7 @@ class Transport : public GameObject
PlayerSet m_passengers;
uint32 currenttguid;
public:
WayPointMap m_WayPoints;
uint32 m_nextNodeTime;
+1 -1
View File
@@ -96,7 +96,7 @@ static bool procPrepared = InitTriggerAuraData();
Unit::Unit()
: WorldObject(), i_motionMaster(this), m_ThreatManager(this), m_HostileRefManager(this)
, IsAIEnabled(false), NeedChangeAI(false)
, i_AI(NULL), i_disabledAI(NULL), m_removedAurasCount(0), m_vehicle(NULL), m_transport(NULL)
, i_AI(NULL), i_disabledAI(NULL), m_removedAurasCount(0), m_vehicle(NULL)
, m_ControlledByPlayer(false), m_procDeep(0), m_unitTypeMask(UNIT_MASK_NONE), m_vehicleKit(NULL)
, m_movedPlayer(NULL)
{
-44
View File
@@ -722,42 +722,6 @@ enum MonsterMovementFlags
};
*/
struct MovementInfo
{
// common
uint64 guid;
uint32 flags;
uint16 unk1;
uint32 time;
float x, y, z, o;
// transport
uint64 t_guid;
float t_x, t_y, t_z, t_o;
uint32 t_time;
int8 t_seat;
// swimming and unknown
float s_pitch;
// last fall time
uint32 fallTime;
// jumping
float j_zspeed, j_sinAngle, j_cosAngle, j_xyspeed;
// spline
float u_unk1;
MovementInfo()
{
flags = 0;
time = t_time = fallTime = 0;
unk1 = 0;
x = y = z = o = t_x = t_y = t_z = t_o = s_pitch = j_zspeed = j_sinAngle = j_cosAngle = j_xyspeed = u_unk1 = 0.0f;
t_guid = 0;
}
uint32 GetMovementFlags() { return flags; }
void AddMovementFlag(uint32 flag) { flags |= flag; }
bool HasMovementFlag(uint32 flag) const { return flags & flag; }
};
enum UnitTypeMask
{
UNIT_MASK_NONE = 0x00000000,
@@ -1936,7 +1900,6 @@ class Unit : public WorldObject
Unit *GetMisdirectionTarget() { return m_misdirectionTargetGUID ? GetUnit(*this, m_misdirectionTargetGUID) : NULL; }
bool IsAIEnabled, NeedChangeAI;
MovementInfo m_movementInfo;
bool CreateVehicleKit(uint32 id);
void RemoveVehicleKit();
Vehicle *GetVehicleKit()const { return m_vehicleKit; }
@@ -1959,10 +1922,6 @@ class Unit : public WorldObject
void ExitVehicle();
void ChangeSeat(int8 seatId, bool next = true);
// Transports
Transport * GetTransport() const { return m_transport; }
void SetTransport(Transport * t) { m_transport = t; }
void BuildMovementPacket(ByteBuffer *data) const;
bool isMoving() const { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_MOVING); }
@@ -1988,9 +1947,6 @@ class Unit : public WorldObject
UnitAI *i_AI, *i_disabledAI;
// Transports
Transport * m_transport;
void _UpdateSpells(uint32 time);
void _DeleteRemovedAuras();
+3
View File
@@ -52,6 +52,9 @@ MapManager::~MapManager()
for (TransportSet::iterator i = m_Transports.begin(); i != m_Transports.end(); ++i)
delete *i;
for (TransportNPCSet::iterator i = m_TransportNPCs.begin(); i != m_TransportNPCs.end(); ++i)
delete *i;
Map::DeleteStateMachine();
}
+7
View File
@@ -30,6 +30,7 @@
#include "MapUpdater.h"
class Transport;
struct TransportCreatureProto;
class MapManager
{
@@ -125,6 +126,7 @@ class MapManager
void DoDelayedMovesAndRemoves();
void LoadTransports();
void LoadTransportNPCs();
typedef std::set<Transport *> TransportSet;
TransportSet m_Transports;
@@ -132,6 +134,11 @@ class MapManager
typedef std::map<uint32, TransportSet> TransportMap;
TransportMap m_TransportsByMap;
typedef std::set<TransportCreatureProto *> TransportNPCSet;
TransportNPCSet m_TransportNPCs;
typedef std::map<uint32, TransportNPCSet> TransportNPCMap;
TransportNPCMap m_TransportNPCMap;
bool CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck = false);
void RemoveBonesFromMap(uint32 mapid, uint64 guid, float x, float y);
uint32 GenerateInstanceId() { return ++i_MaxInstanceId; }
+5
View File
@@ -43,6 +43,7 @@
#include "zlib.h"
#include "ScriptMgr.h"
#include "LFGMgr.h"
#include "Transport.h"
/// WorldSession constructor
WorldSession::WorldSession(uint32 id, WorldSocket *sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale) :
@@ -729,6 +730,10 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo *mi)
data >> mi->t_o;
data >> mi->t_time;
data >> mi->t_seat;
if(mi->x != mi->t_x)
if(GetPlayer()->GetTransport())
GetPlayer()->GetTransport()->UpdatePosition(mi);
}
if ((mi->flags & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING)) || (mi->unk1 & 0x20))
+3 -1
View File
@@ -1704,7 +1704,9 @@ void World::SetInitialWorldSettings()
sLog.outString("Starting Outdoor PvP System");
sOutdoorPvPMgr.InitOutdoorPvP();
//Not sure if this can be moved up in the sequence (with static data loading) as it uses MapManager
sLog.outString("Loading Transport NPCs...");
sMapMgr.LoadTransportNPCs();
sLog.outString("Loading Transports...");
sMapMgr.LoadTransports();