diff --git a/sql/updates/characters/4.3.4/custom_2018_11_18_00_characters.sql b/sql/updates/characters/4.3.4/custom_2018_11_18_00_characters.sql new file mode 100644 index 00000000000..748bcf22468 --- /dev/null +++ b/sql/updates/characters/4.3.4/custom_2018_11_18_00_characters.sql @@ -0,0 +1 @@ +ALTER TABLE `characters` ADD COLUMN `trans_spawn_id` INT(11) UNSIGNED DEFAULT 0 NOT NULL AFTER `transguid`; diff --git a/sql/updates/world/custom/custom_2018_11_20_00_world.sql b/sql/updates/world/custom/custom_2018_11_20_00_world.sql new file mode 100644 index 00000000000..727167c3c62 --- /dev/null +++ b/sql/updates/world/custom/custom_2018_11_20_00_world.sql @@ -0,0 +1 @@ +UPDATE `trinity_string` SET `content_default`='TransMapID: %i TransOffsetX: %f TransOffsetY: %f TransOffsetZ: %f TransOffsetO: %f (Transport ID: %u %s)' WHERE `entry`=186; diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 6bb7fc4c758..cf6407e6eee 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -64,8 +64,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHARACTER, "SELECT c.guid, account, name, race, class, gender, level, xp, money, skin, face, hairStyle, hairColor, facialStyle, bankSlots, restState, playerFlags, " "position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, " "resettalents_time, talentTree, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, instance_mode_mask, " - "totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, " - "health, power1, power2, power3, power4, power5, instance_id, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, knownTitles, actionBars, grantableLevels, fishingSteps " + "totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, health, power1, power2, power3, power4, power5, instance_id, talentGroupsCount, activeTalentGroup, " + "exploredZones, equipmentCache, knownTitles, actionBars, grantableLevels, fishingSteps, trans_spawn_id " "FROM characters c LEFT JOIN character_fishingsteps cfs ON c.guid = cfs.guid WHERE c.guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_GROUP_MEMBER, "SELECT guid FROM group_member WHERE memberGuid = ?", CONNECTION_BOTH); @@ -383,17 +383,17 @@ void CharacterDatabaseConnection::DoPrepareStatements() // Player saving PrepareStatement(CHAR_INS_CHARACTER, "INSERT INTO characters (guid, account, name, race, class, gender, level, xp, money, skin, face, hairStyle, hairColor, facialStyle, bankSlots, restState, playerFlags, " - "map, instance_id, instance_mode_mask, position_x, position_y, position_z, orientation, trans_x, trans_y, trans_z, trans_o, transguid, " + "map, instance_id, instance_mode_mask, position_x, position_y, position_z, orientation, trans_x, trans_y, trans_z, trans_o, transguid, trans_spawn_id, " "taximask, cinematic, " "totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, talentTree, " "extra_flags, stable_slots, at_login, zone, " "death_expire_time, taxi_path, totalKills, " "todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, health, power1, power2, power3, " "power4, power5, latency, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, knownTitles, actionBars, grantableLevels, achievementPoints) VALUES " - "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC); + "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHARACTER, "UPDATE characters SET name=?,race=?,class=?,gender=?,level=?,xp=?,money=?,skin=?,face=?,hairStyle=?,hairColor=?,facialStyle=?,bankSlots=?,restState=?,playerFlags=?," - "map=?,instance_id=?,instance_mode_mask=?,position_x=?,position_y=?,position_z=?,orientation=?,trans_x=?,trans_y=?,trans_z=?,trans_o=?,transguid=?,taximask=?,cinematic=?,totaltime=?,leveltime=?,rest_bonus=?," - "logout_time=?,is_logout_resting=?,resettalents_cost=?,resettalents_time=?,talentTree=?,extra_flags=?,stable_slots=?,at_login=?,zone=?,death_expire_time=?,taxi_path=?," + "map=?,instance_id=?,instance_mode_mask=?,position_x=?,position_y=?,position_z=?,orientation=?,trans_x=?,trans_y=?,trans_z=?,trans_o=?,transguid=?, trans_spawn_id=?,taximask=?,cinematic=?," + "totaltime=?,leveltime=?,rest_bonus=?,logout_time=?,is_logout_resting=?,resettalents_cost=?,resettalents_time=?,talentTree=?,extra_flags=?,stable_slots=?,at_login=?,zone=?,death_expire_time=?,taxi_path=?," "totalKills=?,todayKills=?,yesterdayKills=?,chosenTitle=?," "watchedFaction=?,drunk=?,health=?,power1=?,power2=?,power3=?,power4=?,power5=?,latency=?,talentGroupsCount=?,activeTalentGroup=?,exploredZones=?," "equipmentCache=?,knownTitles=?,actionBars=?,grantableLevels=?,achievementPoints=?,online=? WHERE guid=?", CONNECTION_ASYNC); diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp index e6ec98ef07d..46494e014bb 100644 --- a/src/server/game/Battlefield/Battlefield.cpp +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -30,6 +30,7 @@ #include "MapManager.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" +#include "Transport.h" #include "WorldPacket.h" #include "WorldSession.h" #include @@ -802,13 +803,28 @@ GameObject* Battlefield::SpawnGameObject(uint32 entry, Position const& pos, Quat return nullptr; // Create gameobject - GameObject* go = new GameObject; - if (!go->Create(map->GenerateLowGuid(), entry, map, PHASEMASK_NORMAL, pos, rot, 255, GO_STATE_READY)) + GameObject* go = nullptr; + if (sObjectMgr->GetGameObjectTypeByEntry(entry) == GAMEOBJECT_TYPE_TRANSPORT) { - TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Gameobject template %u could not be found in the database! Battlefield has not been created!", entry); - TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Could not create gameobject template %u! Battlefield has not been created!", entry); - delete go; - return nullptr; + go = new Transport(); + if (!go->Create(map->GenerateLowGuid(), entry, map, PHASEMASK_NORMAL, pos, rot, 255, GO_STATE_READY)) + { + TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Gameobject template %u could not be found in the database! Battlefield has not been created!", entry); + TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Could not create gameobject template %u! Battlefield has not been created!", entry); + delete go; + return nullptr; + } + } + else + { + go = new GameObject(); + if (!go->Create(map->GenerateLowGuid(), entry, map, PHASEMASK_NORMAL, pos, rot, 255, GO_STATE_READY)) + { + TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Gameobject template %u could not be found in the database! Battlefield has not been created!", entry); + TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Could not create gameobject template %u! Battlefield has not been created!", entry); + delete go; + return nullptr; + } } // Add to world diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 162b608a198..8ecace43e71 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -1470,13 +1470,28 @@ bool Battleground::AddObject(uint32 type, uint32 entry, float x, float y, float // Must be created this way, adding to godatamap would add it to the base map of the instance // and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created // So we must create it specific for this instance - GameObject* go = new GameObject; - if (!go->Create(GetBgMap()->GenerateLowGuid(), entry, GetBgMap(), PHASEMASK_NORMAL, Position(x, y, z, o), rot, 255, goState)) + GameObject* go = nullptr; + if (sObjectMgr->GetGameObjectTypeByEntry(entry) == GAMEOBJECT_TYPE_TRANSPORT) { - TC_LOG_ERROR("bg.battleground", "Battleground::AddObject: cannot create gameobject (entry: %u) for BG (map: %u, instance id: %u)!", + go = new Transport(); + if (!go->Create(GetBgMap()->GenerateLowGuid(), entry, GetBgMap(), PHASEMASK_NORMAL, Position(x, y, z, o), rot, 255, goState)) + { + TC_LOG_ERROR("bg.battleground", "Battleground::AddObject: cannot create transport (entry: %u) for BG (map: %u, instance id: %u)!", entry, m_MapId, m_InstanceID); - delete go; - return false; + delete go; + return false; + } + } + else + { + go = new GameObject(); + if (!go->Create(GetBgMap()->GenerateLowGuid(), entry, GetBgMap(), PHASEMASK_NORMAL, Position(x, y, z, o), rot, 255, goState)) + { + TC_LOG_ERROR("bg.battleground", "Battleground::AddObject: cannot create gameobject (entry: %u) for BG (map: %u, instance id: %u)!", + entry, m_MapId, m_InstanceID); + delete go; + return false; + } } /* @@ -1592,7 +1607,7 @@ void Battleground::SpawnBGObject(uint32 type, uint32 respawntime) } } -Creature* Battleground::AddCreature(uint32 entry, uint32 type, float x, float y, float z, float o, TeamId /*teamId = TEAM_NEUTRAL*/, uint32 respawntime /*= 0*/, Transport* transport) +Creature* Battleground::AddCreature(uint32 entry, uint32 type, float x, float y, float z, float o, TeamId /*teamId = TEAM_NEUTRAL*/, uint32 respawntime /*= 0*/, MapTransport* transport) { // If the assert is called, means that BgCreatures must be resized! ASSERT(type < BgCreatures.size()); @@ -1646,7 +1661,7 @@ Creature* Battleground::AddCreature(uint32 entry, uint32 type, float x, float y, return creature; } -Creature* Battleground::AddCreature(uint32 entry, uint32 type, Position const& pos, TeamId teamId /*= TEAM_NEUTRAL*/, uint32 respawntime /*= 0*/, Transport* transport) +Creature* Battleground::AddCreature(uint32 entry, uint32 type, Position const& pos, TeamId teamId /*= TEAM_NEUTRAL*/, uint32 respawntime /*= 0*/, MapTransport* transport) { return AddCreature(entry, type, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), teamId, respawntime, transport); } diff --git a/src/server/game/Battlegrounds/Battleground.h b/src/server/game/Battlegrounds/Battleground.h index dd57323c948..1844a3d074e 100644 --- a/src/server/game/Battlegrounds/Battleground.h +++ b/src/server/game/Battlegrounds/Battleground.h @@ -31,7 +31,7 @@ class Creature; class GameObject; class Group; class Player; -class Transport; +class MapTransport; class Unit; class WorldObject; class WorldPacket; @@ -457,8 +457,8 @@ class TC_GAME_API Battleground void SpawnBGObject(uint32 type, uint32 respawntime); virtual bool AddObject(uint32 type, uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime = 0, GOState goState = GO_STATE_READY); bool AddObject(uint32 type, uint32 entry, Position const& pos, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime = 0, GOState goState = GO_STATE_READY); - virtual Creature* AddCreature(uint32 entry, uint32 type, float x, float y, float z, float o, TeamId teamId = TEAM_NEUTRAL, uint32 respawntime = 0, Transport* transport = nullptr); - Creature* AddCreature(uint32 entry, uint32 type, Position const& pos, TeamId teamId = TEAM_NEUTRAL, uint32 respawntime = 0, Transport* transport = nullptr); + virtual Creature* AddCreature(uint32 entry, uint32 type, float x, float y, float z, float o, TeamId teamId = TEAM_NEUTRAL, uint32 respawntime = 0, MapTransport* transport = nullptr); + Creature* AddCreature(uint32 entry, uint32 type, Position const& pos, TeamId teamId = TEAM_NEUTRAL, uint32 respawntime = 0, MapTransport* transport = nullptr); bool DelCreature(uint32 type); bool DelObject(uint32 type); virtual bool AddSpiritGuide(uint32 type, float x, float y, float z, float o, TeamId teamId = TEAM_NEUTRAL); diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundIC.h b/src/server/game/Battlegrounds/Zones/BattlegroundIC.h index c7685e04c48..8e3bc0643a0 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundIC.h +++ b/src/server/game/Battlegrounds/Zones/BattlegroundIC.h @@ -985,8 +985,8 @@ class BattlegroundIC : public Battleground BG_IC_GateState GateStatus[6]; ICNodePoint nodePoint[7]; - Transport* gunshipAlliance; - Transport* gunshipHorde; + MapTransport* gunshipAlliance; + MapTransport* gunshipHorde; uint32 GetNextBanner(ICNodePoint* node, uint32 team, bool returnDefinitve); diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp index cdcde68a1e2..f57bb1996b6 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp @@ -26,6 +26,7 @@ #include "ObjectMgr.h" #include "Player.h" #include "ScriptedCreature.h" +#include "Transport.h" #include "UpdateData.h" #include "WorldPacket.h" @@ -281,10 +282,9 @@ bool BattlegroundSA::ResetObjs() UpdateWorldState(BG_SA_YELLOW_GATEWS, 1); UpdateWorldState(BG_SA_ANCIENT_GATEWS, 1); - for (int i = BG_SA_BOAT_ONE; i <= BG_SA_BOAT_TWO; i++) - for (BattlegroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) - if (Player* player = ObjectAccessor::FindPlayer(itr->first)) - SendTransportInit(player); + for (BattlegroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + if (Player* player = ObjectAccessor::FindPlayer(itr->first)) + SendTransportInit(player); // set status manually so preparation is cast correctly in 2nd round too SetStatus(STATUS_WAIT_JOIN); @@ -298,23 +298,27 @@ void BattlegroundSA::StartShips() if (ShipsStarted) return; - DoorOpen(BG_SA_BOAT_ONE); - DoorOpen(BG_SA_BOAT_TWO); + for (uint8 i = BG_SA_BOAT_ONE; i <= BG_SA_BOAT_TWO; ++i) + if (GameObject* obj = GetBGObject(i)) + if (Transport* transport = obj->ToTransport()) + transport->SetTransportState(GO_STATE_TRANSPORT_ACTIVE); - for (int i = BG_SA_BOAT_ONE; i <= BG_SA_BOAT_TWO; i++) + for (BattlegroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) { - for (BattlegroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + if (Player* p = ObjectAccessor::FindPlayer(itr->first)) { - if (Player* p = ObjectAccessor::FindPlayer(itr->first)) - { - UpdateData data(p->GetMapId()); - WorldPacket pkt; - GetBGObject(i)->BuildValuesUpdateBlockForPlayer(&data, p); - data.BuildPacket(&pkt); - p->SendDirectMessage(&pkt); - } + UpdateData data(GetMapId()); + + for (uint8 i = BG_SA_BOAT_ONE; i <= BG_SA_BOAT_TWO; ++i) + if (GameObject* obj = GetBGObject(i)) + obj->BuildValuesUpdateBlockForPlayer(&data, p); + + WorldPacket pkt; + data.BuildPacket(&pkt); + p->SendDirectMessage(&pkt); } } + ShipsStarted = true; } diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 0026afd844f..dfe64e72312 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -92,8 +92,6 @@ GameObject::~GameObject() { delete m_AI; delete m_model; - if (m_goInfo && m_goInfo->type == GAMEOBJECT_TYPE_TRANSPORT) - delete m_goValue.Transport.StopFrames; //if (m_uint32Values) // field array can be not exist if GameOBject not loaded // CleanupsBeforeDelete(); } @@ -165,7 +163,7 @@ void GameObject::AddToWorld() bool toggledState = GetGoType() == GAMEOBJECT_TYPE_CHEST ? getLootState() == GO_READY : (GetGoState() == GO_STATE_READY || IsTransport()); if (m_model) { - if (Transport* trans = ToTransport()) + if (MapTransport* trans = ToMapTransport()) trans->SetDelayedAddModelToMap(); else GetMap()->InsertGameObjectModel(*m_model); @@ -235,9 +233,6 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u return false; } - if (goinfo->type == GAMEOBJECT_TYPE_TRANSPORT) - m_updateFlag |= UPDATEFLAG_TRANSPORT; - Object::_Create(guidlow, goinfo->entry, HighGuid::GameObject); m_goInfo = goinfo; @@ -293,36 +288,6 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u SetGoAnimProgress(255); SetUInt32Value(GAMEOBJECT_PARENTROTATION, m_goInfo->building.destructibleData); break; - case GAMEOBJECT_TYPE_TRANSPORT: - { - m_goValue.Transport.AnimationInfo = sTransportMgr->GetTransportAnimInfo(goinfo->entry); - m_goValue.Transport.PathProgress = getMSTime(); - if (m_goValue.Transport.AnimationInfo) - m_goValue.Transport.PathProgress -= m_goValue.Transport.PathProgress % GetTransportPeriod(); // align to period - m_goValue.Transport.CurrentSeg = 0; - m_goValue.Transport.StateUpdateTimer = 0; - m_goValue.Transport.StopFrames = new std::vector(); - - if (goinfo->transport.Timeto2ndfloor > 0) - { - if (!goinfo->transport.startOpen) - m_goValue.Transport.StopFrames->push_back(0); - m_goValue.Transport.StopFrames->push_back(goinfo->transport.Timeto2ndfloor); - } - if (goinfo->transport.Timeto3rdfloor > 0) - m_goValue.Transport.StopFrames->push_back(goinfo->transport.Timeto3rdfloor); - if (goinfo->transport.Timeto4thfloor > 0) - m_goValue.Transport.StopFrames->push_back(goinfo->transport.Timeto4thfloor); - if (goinfo->transport.Timeto5thfloor > 0) - m_goValue.Transport.StopFrames->push_back(goinfo->transport.Timeto5thfloor); - if (goinfo->transport.startOpen) - SetTransportState(GO_STATE_TRANSPORT_STOPPED, goinfo->transport.startOpen - 1); - else - SetTransportState(GO_STATE_TRANSPORT_ACTIVE); - - SetGoAnimProgress(animprogress); - break; - } case GAMEOBJECT_TYPE_FISHINGNODE: SetGoAnimProgress(0); break; @@ -414,53 +379,6 @@ void GameObject::Update(uint32 diff) SetLootState(GO_READY); break; } - case GAMEOBJECT_TYPE_TRANSPORT: - { - if (!m_goValue.Transport.AnimationInfo) - break; - - if (GetGoState() == GO_STATE_TRANSPORT_ACTIVE) - { - m_goValue.Transport.PathProgress += diff; - /* TODO: Fix movement in unloaded grid - currently GO will just disappear - uint32 timer = m_goValue.Transport.PathProgress % GetTransportPeriod(); - TransportAnimationEntry const* node = m_goValue.Transport.AnimationInfo->GetAnimNode(timer); - if (node && m_goValue.Transport.CurrentSeg != node->TimeSeg) - { - m_goValue.Transport.CurrentSeg = node->TimeSeg; - - G3D::Quat rotation; - if (TransportRotationEntry const* rot = m_goValue.Transport.AnimationInfo->GetAnimRotation(timer)) - rotation = G3D::Quat(rot->X, rot->Y, rot->Z, rot->W); - - G3D::Vector3 pos = rotation.toRotationMatrix() - * G3D::Matrix3::fromEulerAnglesZYX(GetOrientation(), 0.0f, 0.0f) - * G3D::Vector3(node->X, node->Y, node->Z); - - pos += G3D::Vector3(GetStationaryX(), GetStationaryY(), GetStationaryZ()); - - G3D::Vector3 src(GetPositionX(), GetPositionY(), GetPositionZ()); - - TC_LOG_DEBUG("misc", "Src: %s Dest: %s", src.toString().c_str(), pos.toString().c_str()); - - GetMap()->GameObjectRelocation(this, pos.x, pos.y, pos.z, GetOrientation()); - } - */ - - if (!m_goValue.Transport.StopFrames->empty()) - { - uint32 visualStateBefore = (m_goValue.Transport.StateUpdateTimer / 20000) & 1; - m_goValue.Transport.StateUpdateTimer += diff; - uint32 visualStateAfter = (m_goValue.Transport.StateUpdateTimer / 20000) & 1; - if (visualStateBefore != visualStateAfter) - { - ForceValuesUpdateAtIndex(GAMEOBJECT_LEVEL); - ForceValuesUpdateAtIndex(GAMEOBJECT_BYTES_1); - } - } - } - break; - } case GAMEOBJECT_TYPE_FISHINGNODE: { // fishing code (bobber ready) @@ -2314,48 +2232,6 @@ void GameObject::SetGoState(GOState state) } } -uint32 GameObject::GetTransportPeriod() const -{ - ASSERT(GetGOInfo()->type == GAMEOBJECT_TYPE_TRANSPORT); - if (m_goValue.Transport.AnimationInfo) - return m_goValue.Transport.AnimationInfo->TotalTime; - - // return something that will nicely divide for GAMEOBJECT_DYNAMIC value calculation - return m_goValue.Transport.PathProgress; -} - -void GameObject::SetTransportState(GOState state, uint32 stopFrame /*= 0*/) -{ - if (GetGoState() == state) - return; - - ASSERT(GetGOInfo()->type == GAMEOBJECT_TYPE_TRANSPORT); - ASSERT(state >= GO_STATE_TRANSPORT_ACTIVE); - if (state == GO_STATE_TRANSPORT_ACTIVE) - { - m_goValue.Transport.StateUpdateTimer = 0; - m_goValue.Transport.PathProgress = getMSTime(); - if (GetGoState() >= GO_STATE_TRANSPORT_STOPPED) - m_goValue.Transport.PathProgress += m_goValue.Transport.StopFrames->at(GetGoState() - GO_STATE_TRANSPORT_STOPPED); - SetGoState(GO_STATE_TRANSPORT_ACTIVE); - } - else - { - ASSERT(state < GOState(GO_STATE_TRANSPORT_STOPPED + MAX_GO_STATE_TRANSPORT_STOP_FRAMES)); - ASSERT(stopFrame < m_goValue.Transport.StopFrames->size()); - - uint32 stopFrameTime = m_goValue.Transport.StopFrames->at(stopFrame); - if (!stopFrameTime) // returning to a previous stop frame that has no travel time (e.g. stop frame at 0ms) - stopFrameTime = m_goValue.Transport.StopFrames->at(stopFrame + 1); - - m_goValue.Transport.PathProgress = getMSTime() + stopFrameTime; - SetGoState(GOState(GO_STATE_TRANSPORT_STOPPED + stopFrame)); - } - - ForceValuesUpdateAtIndex(GAMEOBJECT_LEVEL); - ForceValuesUpdateAtIndex(GAMEOBJECT_BYTES_1); -} - void GameObject::SetDisplayId(uint32 displayid) { SetUInt32Value(GAMEOBJECT_DISPLAYID, displayid); @@ -2454,7 +2330,6 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t if (!target) return; - bool isStoppableTransport = GetGoType() == GAMEOBJECT_TYPE_TRANSPORT && !m_goValue.Transport.StopFrames->empty(); bool forcedFlags = GetGoType() == GAMEOBJECT_TYPE_CHEST && GetGOInfo()->chest.groupLootRules && HasLootRecipient(); bool targetIsGM = target->IsGameMaster(); @@ -2498,16 +2373,12 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t dynFlags |= GO_DYNFLAG_LO_SPARKLE; break; case GAMEOBJECT_TYPE_TRANSPORT: - { - float timer = float(m_goValue.Transport.PathProgress % GetTransportPeriod()); - pathProgress = int16(timer / float(GetTransportPeriod()) * 65535.0f); - break; - } case GAMEOBJECT_TYPE_MO_TRANSPORT: { - if (uint32 transportPeriod = GetTransportPeriod()) + Transport const* transport = ToTransport(); + if (uint32 transportPeriod = transport->GetTransportPeriod()) { - float timer = float(m_goValue.Transport.PathProgress % transportPeriod); + float timer = float(transport->GetPathProgress() % transportPeriod); pathProgress = int16(timer / float(transportPeriod) * 65535.0f); } break; @@ -2528,27 +2399,6 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t fieldBuffer << goFlags; } - else if (index == GAMEOBJECT_LEVEL) - { - if (isStoppableTransport) - fieldBuffer << uint32(m_goValue.Transport.PathProgress); - else - fieldBuffer << m_uint32Values[index]; - } - else if (index == GAMEOBJECT_BYTES_1) - { - uint32 bytes1 = m_uint32Values[index]; - if (isStoppableTransport && GetGoState() == GO_STATE_TRANSPORT_ACTIVE) - { - if ((m_goValue.Transport.StateUpdateTimer / 20000) & 1) - { - bytes1 &= 0xFFFFFF00; - bytes1 |= GO_STATE_TRANSPORT_STOPPED; - } - } - - fieldBuffer << bytes1; - } else fieldBuffer << m_uint32Values[index]; // other cases } @@ -2631,3 +2481,72 @@ GameObjectModel* GameObject::CreateModel() { return GameObjectModel::Create(Trinity::make_unique(this), sWorld->GetDataPath()); } + +float GameObject::GetStationaryX() const +{ + if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT) + return GetPositionX(); + + return m_stationaryPosition.GetPositionX(); +} + +float GameObject::GetStationaryY() const +{ + if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT) + return GetPositionY(); + + return m_stationaryPosition.GetPositionY(); +} + +float GameObject::GetStationaryZ() const +{ + if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT) + return GetPositionZ(); + + return m_stationaryPosition.GetPositionZ(); +} + +float GameObject::GetStationaryO() const +{ + if (IsTransport()) + return GetOrientation(); + + return m_stationaryPosition.GetOrientation(); +} + +void GameObject::RelocateStationaryPosition(float x, float y, float z, float o) +{ + m_stationaryPosition.Relocate(x, y, z, o); +} + +Transport* GameObject::ToTransport() +{ + if (IsTransport()) + return reinterpret_cast(this); + + return nullptr; +} + +Transport const* GameObject::ToTransport() const +{ + if (IsTransport()) + return reinterpret_cast(this); + + return nullptr; +} + +MapTransport* GameObject::ToMapTransport() +{ + if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT) + return reinterpret_cast(this); + + return nullptr; +} + +MapTransport const* GameObject::ToMapTransport() const +{ + if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT) + return reinterpret_cast(this); + + return nullptr; +} diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index ae1c9d091a1..c147e6ecacb 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -42,9 +42,7 @@ union GameObjectValue { uint32 PathProgress; TransportAnimation const* AnimationInfo; - uint32 CurrentSeg; std::vector* StopFrames; - uint32 StateUpdateTimer; } Transport; //25 GAMEOBJECT_TYPE_FISHINGHOLE struct @@ -91,7 +89,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject void RemoveFromWorld() override; void CleanupsBeforeDelete(bool finalCleanup = true) override; - bool Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, QuaternionData const& rotation, uint32 animprogress, GOState go_state, uint32 artKit = 0, bool dynamic = false, ObjectGuid::LowType spawnid = 0); + virtual bool Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, QuaternionData const& rotation, uint32 animprogress, GOState go_state, uint32 artKit = 0, bool dynamic = false, ObjectGuid::LowType spawnid = 0); void Update(uint32 p_time) override; GameObjectTemplate const* GetGOInfo() const { return m_goInfo; } GameObjectTemplateAddon const* GetTemplateAddon() const { return m_goTemplateAddon; } @@ -163,8 +161,6 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject void SetGoType(GameobjectTypes type) { SetByteValue(GAMEOBJECT_BYTES_1, 1, type); } GOState GetGoState() const { return GOState(GetByteValue(GAMEOBJECT_BYTES_1, 0)); } void SetGoState(GOState state); - virtual uint32 GetTransportPeriod() const; - void SetTransportState(GOState state, uint32 stopFrame = 0); uint8 GetGoArtKit() const { return GetByteValue(GAMEOBJECT_BYTES_1, 2); } void SetGoArtKit(uint8 artkit); uint8 GetGoAnimProgress() const { return GetByteValue(GAMEOBJECT_BYTES_1, 3); } @@ -274,14 +270,17 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject GameObjectModel* m_model; void GetRespawnPosition(float &x, float &y, float &z, float* ori = nullptr) const; - Transport* ToTransport() { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT) return reinterpret_cast(this); else return nullptr; } - Transport const* ToTransport() const { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT) return reinterpret_cast(this); else return nullptr; } + Transport* ToTransport(); + Transport const* ToTransport() const; - float GetStationaryX() const override { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetPositionX(); return GetPositionX(); } - float GetStationaryY() const override { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetPositionY(); return GetPositionY(); } - float GetStationaryZ() const override { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetPositionZ(); return GetPositionZ(); } - float GetStationaryO() const override { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetOrientation(); return GetOrientation(); } - void RelocateStationaryPosition(float x, float y, float z, float o) { m_stationaryPosition.Relocate(x, y, z, o); } + MapTransport* ToMapTransport(); + MapTransport const* ToMapTransport() const; + + float GetStationaryX() const override; + float GetStationaryY() const override; + float GetStationaryZ() const override; + float GetStationaryO() const override; + void RelocateStationaryPosition(float x, float y, float z, float o); float GetInteractionDistance() const; @@ -290,6 +289,8 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject void AIM_Destroy(); bool AIM_Initialize(); + void SetSpawnId(uint32 spawnId) { m_spawnId = spawnId; } + protected: GameObjectModel* CreateModel(); void UpdateModel(); // updates model in case displayId were changed diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 4faf5d82d0e..160da8d825c 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -164,41 +164,13 @@ void Object::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) c if (!target) return; - uint8 updateType = UPDATETYPE_CREATE_OBJECT; + uint8 updateType = m_isNewObject ? UPDATETYPE_CREATE_OBJECT2 : UPDATETYPE_CREATE_OBJECT; uint16 flags = m_updateFlag; /** lower flag1 **/ if (target == this) // building packet for yourself flags |= UPDATEFLAG_SELF; - if (m_isNewObject) - { - switch (GetGUID().GetHigh()) - { - case HighGuid::Player: - case HighGuid::Pet: - case HighGuid::Corpse: - case HighGuid::DynamicObject: - updateType = UPDATETYPE_CREATE_OBJECT2; - break; - case HighGuid::Unit: - case HighGuid::Vehicle: - { - if (ToUnit()->IsSummon()) - updateType = UPDATETYPE_CREATE_OBJECT2; - break; - } - case HighGuid::GameObject: - { - if (ToGameObject()->GetOwnerGUID().IsPlayer()) - updateType = UPDATETYPE_CREATE_OBJECT2; - break; - } - default: - break; - } - } - if (WorldObject const* worldObject = dynamic_cast(this)) { if (!(flags & UPDATEFLAG_LIVING)) @@ -207,29 +179,17 @@ void Object::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) c if (worldObject->GetAIAnimKitId() || worldObject->GetMovementAnimKitId() || worldObject->GetMeleeAnimKitId()) flags |= UPDATEFLAG_ANIMKITS; + + if (!worldObject->m_movementInfo.transport.guid.IsEmpty() && (ToGameObject() || ToDynObject())) + flags |= UPDATEFLAG_GO_TRANSPORT_POSITION; } if (flags & UPDATEFLAG_STATIONARY_POSITION) { - // UPDATETYPE_CREATE_OBJECT2 dynamic objects, corpses... - if (isType(TYPEMASK_DYNAMICOBJECT | TYPEMASK_CORPSE | TYPEMASK_PLAYER)) - updateType = UPDATETYPE_CREATE_OBJECT2; - - // UPDATETYPE_CREATE_OBJECT2 for pets... - if (target->GetPetGUID() == GetGUID()) - updateType = UPDATETYPE_CREATE_OBJECT2; - - // UPDATETYPE_CREATE_OBJECT2 for some gameobject types... if (isType(TYPEMASK_GAMEOBJECT)) { switch (ToGameObject()->GetGoType()) { - case GAMEOBJECT_TYPE_TRAP: - case GAMEOBJECT_TYPE_DUEL_ARBITER: - case GAMEOBJECT_TYPE_FLAGSTAND: - case GAMEOBJECT_TYPE_FLAGDROP: - updateType = UPDATETYPE_CREATE_OBJECT2; - break; case GAMEOBJECT_TYPE_TRANSPORT: flags |= UPDATEFLAG_TRANSPORT; break; @@ -596,7 +556,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const if (flags & UPDATEFLAG_VEHICLE) { - *data << float(self->GetOrientation()); + *data << float(self->GetTransport() ? self->GetTransOffsetO() : self->GetOrientation()); *data << uint32(self->GetVehicleKit()->GetVehicleInfo()->m_ID); } @@ -605,20 +565,20 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const WorldObject const* self = static_cast(this); ObjectGuid transGuid = self->m_movementInfo.transport.guid; - data->WriteBit(transGuid[0]); - data->WriteBit(transGuid[5]); + data->WriteByteSeq(transGuid[0]); + data->WriteByteSeq(transGuid[5]); if (hasVehicleId) *data << uint32(self->m_movementInfo.transport.vehicleId); - data->WriteBit(transGuid[3]); + data->WriteByteSeq(transGuid[3]); *data << float(self->GetTransOffsetX()); - data->WriteBit(transGuid[4]); - data->WriteBit(transGuid[6]); - data->WriteBit(transGuid[1]); + data->WriteByteSeq(transGuid[4]); + data->WriteByteSeq(transGuid[6]); + data->WriteByteSeq(transGuid[1]); *data << uint32(self->GetTransTime()); *data << float(self->GetTransOffsetY()); - data->WriteBit(transGuid[2]); - data->WriteBit(transGuid[7]); + data->WriteByteSeq(transGuid[2]); + data->WriteByteSeq(transGuid[7]); *data << float(self->GetTransOffsetZ()); *data << int8(self->GetTransSeat()); *data << float(self->GetTransOffsetO()); @@ -685,16 +645,13 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const if (flags & UPDATEFLAG_TRANSPORT) { - GameObject const* go = ToGameObject(); - /** @TODO Use IsTransport() to also handle type 11 (TRANSPORT) - Currently grid objects are not updated if there are no nearby players, - this causes clients to receive different PathProgress - resulting in players seeing the object in a different position - */ - if (go && go->ToTransport()) - *data << uint32(go->GetGOValue()->Transport.PathProgress); - else - *data << uint32(GameTime::GetGameTimeMS()); + if (GameObject const* go = ToGameObject()) + { + if (go->IsDynTransport()) + *data << uint32(go->ToTransport()->GetPathProgress()); + else + *data << uint32(getMSTime()); + } } } @@ -2140,6 +2097,19 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonPropert return nullptr; } + if (summon->GetTransGUID().IsEmpty() && summoner) + { + if (Transport* transport = summoner->GetTransport()) + { + float x, y, z, o; + pos.GetPosition(x, y, z, o); + transport->CalculatePassengerOffset(x, y, z, &o); + summon->m_movementInfo.transport.pos.Relocate(x, y, z, o); + + transport->AddPassenger(summon); + } + } + // Set the summon to the summoner's phase if (summoner) PhasingHandler::InheritPhaseShift(summon, summoner); @@ -2238,11 +2208,24 @@ GameObject* WorldObject::SummonGameObject(uint32 entry, Position const& pos, Qua } Map* map = GetMap(); - GameObject* go = new GameObject(); - if (!go->Create(map->GenerateLowGuid(), entry, map, GetPhaseMask(), pos, rot, 255, GO_STATE_READY)) + GameObject* go = nullptr; + if (goinfo->type == GAMEOBJECT_TYPE_TRANSPORT) { - delete go; - return nullptr; + go = new Transport(); + if (!go->Create(map->GenerateLowGuid(), entry, map, GetPhaseMask(), pos, rot, 255, GO_STATE_READY)) + { + delete go; + return nullptr; + } + } + else + { + go = new GameObject(); + if (!go->Create(map->GenerateLowGuid(), entry, map, GetPhaseMask(), pos, rot, 255, GO_STATE_READY)) + { + delete go; + return nullptr; + } } PhasingHandler::InheritPhaseShift(go, this); @@ -2253,6 +2236,17 @@ GameObject* WorldObject::SummonGameObject(uint32 entry, Position const& pos, Qua else go->SetSpawnedByDefault(false); + Transport* transport = GetTransGUID().IsEmpty() ? GetTransport() : nullptr; + if (transport) + { + float x, y, z, o; + go->GetPosition(x, y, z, o); + transport->CalculatePassengerOffset(x, y, z, &o); + go->m_movementInfo.transport.pos.Relocate(x, y, z, o); + + transport->AddPassenger(go); + } + map->AddToMap(go); return go; } @@ -2756,6 +2750,14 @@ ObjectGuid WorldObject::GetTransGUID() const return ObjectGuid::Empty; } +MapTransport* WorldObject::GetMapTransport() const +{ + if (GetTransport()) + return GetTransport()->ToMapTransport(); + + return nullptr; +} + float WorldObject::GetFloorZ() const { if (!IsInWorld()) diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 2fdbe42570c..795aa5bf816 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -47,6 +47,7 @@ class Map; class Player; class TempSummon; class Transport; +class MapTransport; class Unit; class UpdateData; class WorldObject; @@ -460,6 +461,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation virtual ObjectGuid GetTransGUID() const; void SetTransport(Transport* t) { m_transport = t; } + MapTransport* GetMapTransport() const; + MovementInfo m_movementInfo; virtual float GetStationaryX() const { return GetPositionX(); } diff --git a/src/server/game/Entities/Object/ObjectGuid.cpp b/src/server/game/Entities/Object/ObjectGuid.cpp index ea45a44c564..5a7b03efd66 100644 --- a/src/server/game/Entities/Object/ObjectGuid.cpp +++ b/src/server/game/Entities/Object/ObjectGuid.cpp @@ -53,7 +53,6 @@ char const* ObjectGuid::GetTypeName(HighGuid high) case HighGuid::Corpse: return "Corpse"; case HighGuid::AreaTrigger: return "AreaTrigger"; case HighGuid::BattleGround: return "Battleground"; - case HighGuid::Mo_Transport: return "MoTransport"; case HighGuid::Instance: return "InstanceID"; case HighGuid::Group: return "Group"; case HighGuid::Guild: return "Guild"; @@ -133,6 +132,6 @@ GUID_TRAIT_INSTANTIATE_GUID(HighGuid::Unit) GUID_TRAIT_INSTANTIATE_GUID(HighGuid::Pet) GUID_TRAIT_INSTANTIATE_GUID(HighGuid::Vehicle) GUID_TRAIT_INSTANTIATE_GUID(HighGuid::DynamicObject) -GUID_TRAIT_INSTANTIATE_GUID(HighGuid::Mo_Transport) +GUID_TRAIT_INSTANTIATE_GUID(HighGuid::Transport) GUID_TRAIT_INSTANTIATE_GUID(HighGuid::Instance) GUID_TRAIT_INSTANTIATE_GUID(HighGuid::Group) diff --git a/src/server/game/Entities/Object/ObjectGuid.h b/src/server/game/Entities/Object/ObjectGuid.h index 9022cf12722..5c0638e19d7 100644 --- a/src/server/game/Entities/Object/ObjectGuid.h +++ b/src/server/game/Entities/Object/ObjectGuid.h @@ -64,7 +64,6 @@ enum class HighGuid Container = 0x400, // blizz 4000 Player = 0x000, // blizz 0000 GameObject = 0xF11, // blizz F110 - Transport = 0xF12, // blizz F120 (for GAMEOBJECT_TYPE_TRANSPORT) Unit = 0xF13, // blizz F130 Pet = 0xF14, // blizz F140 Vehicle = 0xF15, // blizz F550 @@ -72,7 +71,7 @@ enum class HighGuid Corpse = 0xF101, // blizz F100 AreaTrigger = 0xF102, BattleGround = 0x1F1, - Mo_Transport = 0x1FC, // blizz 1FC0 (for GAMEOBJECT_TYPE_MO_TRANSPORT) + Transport = 0x1FC, // blizz 1FC0 Instance = 0x1F4, // blizz 1F40 Group = 0x1F5, Guild = 0x1FF @@ -99,13 +98,11 @@ struct ObjectGuidTraits static bool const MapSpecific = true; \ } -GUID_TRAIT_GLOBAL(HighGuid::Mo_Transport); GUID_TRAIT_GLOBAL(HighGuid::Group); GUID_TRAIT_GLOBAL(HighGuid::Instance); GUID_TRAIT_GLOBAL(HighGuid::BattleGround); GUID_TRAIT_GLOBAL(HighGuid::Player); GUID_TRAIT_GLOBAL(HighGuid::Item); -GUID_TRAIT_GLOBAL(HighGuid::Transport); GUID_TRAIT_GLOBAL(HighGuid::Guild); GUID_TRAIT_MAP_SPECIFIC(HighGuid::Unit); GUID_TRAIT_MAP_SPECIFIC(HighGuid::Vehicle); @@ -119,6 +116,18 @@ GUID_TRAIT_MAP_SPECIFIC(HighGuid::AreaTrigger); #undef GUID_TRAIT_REALM_SPECIFIC #undef GUID_TRAIT_MAP_SPECIFIC +// Special case +// Global transports are loaded from `transports` table, Global part is used for them. +// after worldserver finishes loading, no more global transports can be created, only the ones existing within instances that never change maps +// here is where MapSpecific comes into play - each map takes over the responsibility to generate transport guids +// on top of this, regular elevators (GAMEOBJECT_TYPE_TRANSPORT) must also use Transport highguid type, otherwise client will reject seeing other players on them +template<> +struct ObjectGuidTraits +{ + static bool const Global = true; + static bool const MapSpecific = true; +}; + class ObjectGuid; class PackedGuid; @@ -139,8 +148,7 @@ class TC_GAME_API ObjectGuid static typename std::enable_if::Global, ObjectGuid>::type Create(LowType counter) { return Global(type, counter); } template - static typename std::enable_if::MapSpecific, ObjectGuid>::type Create(uint32 entry, LowType counter) { return MapSpecific(type, entry, counter); } - + static typename std::enable_if::MapSpecific && type != HighGuid::Transport, ObjectGuid>::type Create(uint32 entry, LowType counter) { return MapSpecific(type, entry, counter); } ObjectGuid() { _data._guid = UI64LIT(0); } explicit ObjectGuid(uint64 guid) { _data._guid = guid; } @@ -198,8 +206,7 @@ class TC_GAME_API ObjectGuid bool IsAreaTrigger() const { return GetHigh() == HighGuid::AreaTrigger; } bool IsBattleground() const { return GetHigh() == HighGuid::BattleGround; } bool IsTransport() const { return GetHigh() == HighGuid::Transport; } - bool IsMOTransport() const { return GetHigh() == HighGuid::Mo_Transport; } - bool IsAnyTypeGameObject() const { return IsGameObject() || IsTransport() || IsMOTransport(); } + bool IsAnyTypeGameObject() const { return IsGameObject() || IsTransport(); } bool IsInstance() const { return GetHigh() == HighGuid::Instance; } bool IsGroup() const { return GetHigh() == HighGuid::Group; } bool IsGuild() const { return GetHigh() == HighGuid::Guild; } @@ -217,7 +224,7 @@ class TC_GAME_API ObjectGuid case HighGuid::DynamicObject: return TYPEID_DYNAMICOBJECT; case HighGuid::Corpse: return TYPEID_CORPSE; case HighGuid::AreaTrigger: return TYPEID_AREATRIGGER; - case HighGuid::Mo_Transport: return TYPEID_GAMEOBJECT; + case HighGuid::Transport: return TYPEID_GAMEOBJECT; case HighGuid::Vehicle: return TYPEID_UNIT; // unknown case HighGuid::Instance: @@ -248,12 +255,11 @@ class TC_GAME_API ObjectGuid case HighGuid::Player: case HighGuid::DynamicObject: case HighGuid::Corpse: - case HighGuid::Mo_Transport: + case HighGuid::Transport: case HighGuid::Instance: case HighGuid::Group: return false; case HighGuid::GameObject: - case HighGuid::Transport: case HighGuid::Unit: case HighGuid::Pet: case HighGuid::Vehicle: diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index a4cb07a35eb..10dc1d78646 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -28,6 +28,7 @@ #include "SpellHistory.h" #include "SpellAuras.h" #include "SpellAuraEffects.h" +#include "Transport.h" #include "Unit.h" #include "Util.h" #include "Group.h" @@ -185,6 +186,17 @@ bool Pet::LoadPetData(Player* owner, uint32 petEntry, uint32 petnumber, bool cur return false; } + Transport* transport = GetTransGUID().IsEmpty() ? owner->GetTransport() : nullptr; + if (transport) + { + float x, y, z, o; + GetPosition(x, y, z, o); + transport->CalculatePassengerOffset(x, y, z, &o); + m_movementInfo.transport.pos.Relocate(x, y, z, o); + + transport->AddPassenger(this); + } + map->AddToMap(this->ToCreature()); return true; } @@ -273,6 +285,17 @@ bool Pet::LoadPetData(Player* owner, uint32 petEntry, uint32 petnumber, bool cur owner->SendMessageToSet(&data, true); } + Transport* transport = GetTransGUID().IsEmpty() ? owner->GetTransport() : nullptr; + if (transport) + { + float x, y, z, o; + GetPosition(x, y, z, o); + transport->CalculatePassengerOffset(x, y, z, &o); + m_movementInfo.transport.pos.Relocate(x, y, z, o); (x, y, z, o); + + transport->AddPassenger(this); + } + owner->SetMinion(this, true); map->AddToMap(ToCreature()); @@ -748,6 +771,17 @@ bool Pet::CreateBaseAtCreature(Creature* creature) else SetName(creature->GetNameForLocaleIdx(sObjectMgr->GetDBCLocaleIndex())); + Transport* transport = GetTransGUID().IsEmpty() ? creature->GetTransport() : nullptr; + if (transport) + { + float x, y, z, o; + GetPosition(x, y, z, o); + transport->CalculatePassengerOffset(x, y, z, &o); + m_movementInfo.transport.pos.Relocate(x, y, z, o); + + transport->AddPassenger(this); + } + return true; } @@ -761,6 +795,17 @@ bool Pet::CreateBaseAtCreatureInfo(CreatureTemplate const* cinfo, Unit* owner) Relocate(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ(), owner->GetOrientation()); + Transport* transport = GetTransGUID().IsEmpty() ? owner->GetTransport() : nullptr; + if (transport) + { + float x, y, z, o; + GetPosition(x, y, z, o); + transport->CalculatePassengerOffset(x, y, z, &o); + m_movementInfo.transport.pos.Relocate(x, y, z, o); + + transport->AddPassenger(this); + } + return true; } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index f535a051fce..772daddc0d7 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -242,6 +242,7 @@ Player::Player(WorldSession* session): Unit(true) m_bCanDelayTeleport = false; m_bHasDelayedTeleport = false; m_teleport_options = 0; + m_teleport_transport = nullptr; m_trade = nullptr; @@ -414,6 +415,8 @@ Player::Player(WorldSession* session): Unit(true) _hasValidLFGLeavePoint = false; _archaeology = new Archaeology(this); m_petScalingSynchTimer.Reset(1000); + + _transportSpawnID = 0; } Player::~Player() @@ -1399,7 +1402,7 @@ void Player::Update(uint32 p_time) //we should execute delayed teleports only for alive(!) players //because we don't want player's ghost teleported from graveyard if (IsHasDelayedTeleport() && IsAlive()) - TeleportTo(m_teleport_dest, m_teleport_options); + TeleportTo(m_teleport_dest, m_teleport_options, m_teleport_transport); } @@ -1676,7 +1679,7 @@ uint8 Player::GetChatTag() const return tag; } -bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options) +bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options, Transport* transport) { if (!MapManager::IsValidMapCoord(mapid, x, y, z, orientation)) { @@ -1755,6 +1758,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati //lets save teleport destination for player m_teleport_dest = WorldLocation(mapid, x, y, z, orientation); m_teleport_options = options; + m_teleport_transport = transport; return true; } @@ -1771,10 +1775,12 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati // this will be used instead of the current location in SaveToDB m_teleport_dest = WorldLocation(mapid, x, y, z, orientation); SetFallInformation(0, GetPositionZ()); + m_teleport_transport = transport; // code for finish transfer called in WorldSession::HandleMovementOpcodes() // at client packet CMSG_MOVE_TELEPORT_ACK - SetSemaphoreTeleportNear(true); + if (!GetTransport()) + SetSemaphoreTeleportNear(true); // near teleport, triggering send CMSG_MOVE_TELEPORT_ACK from client at landing if (!GetSession()->PlayerLogout()) { @@ -1817,6 +1823,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati //lets save teleport destination for player m_teleport_dest = WorldLocation(mapid, x, y, z, orientation); m_teleport_options = options; + m_teleport_transport = transport; return true; } @@ -1883,6 +1890,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati oldmap->RemovePlayerFromMap(this, false); m_teleport_dest = WorldLocation(mapid, x, y, z, orientation); + m_teleport_transport = transport; SetFallInformation(0, GetPositionZ()); // if the player is saved before worldportack (at logout for example) // this will be used instead of the current location in SaveToDB @@ -1910,9 +1918,9 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati return true; } -bool Player::TeleportTo(WorldLocation const& loc, uint32 options /*= 0*/) +bool Player::TeleportTo(WorldLocation const& loc, uint32 options /*= 0*/, Transport* transport /*=nullptr*/) { - return TeleportTo(loc.GetMapId(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ(), loc.GetOrientation(), options); + return TeleportTo(loc.GetMapId(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ(), loc.GetOrientation(), options, transport); } bool Player::TeleportToBGEntryPoint() @@ -17336,8 +17344,8 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder* holder) //"totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, " // 51 52 53 54 55 56 57 58 59 60 61 //"health, power1, power2, power3, power4, power5, instance_id, speccount, activespec, exploredZones, equipmentCache, " - // 62 63 64 65 - //"knownTitles, actionBars, grantableLevels, fishing_steps FROM characters WHERE guid = '%u'", guid); + // 62 63 64 65 66 + //"knownTitles, actionBars, grantableLevels, fishing_steps, trans_spawn_id FROM characters WHERE guid = '%u'", guid); PreparedQueryResult result = holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_FROM); if (!result) @@ -17479,6 +17487,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder* holder) // init saved position, and fix it later if problematic ObjectGuid::LowType transLowGUID = fields[36].GetUInt32(); + uint32 transSpawnId = fields[66].GetUInt32(); Relocate(fields[17].GetFloat(), fields[18].GetFloat(), fields[19].GetFloat(), fields[21].GetFloat()); @@ -17593,45 +17602,119 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder* holder) // currently we do not support transport in bg else if (transLowGUID) { - ObjectGuid transGUID(HighGuid::Mo_Transport, transLowGUID); - - Transport* transport = nullptr; - if (Transport* go = HashMapHolder::Find(transGUID)) - transport = go; - - if (transport) + // if transSpawnId presents, then search for static transport (e.g. elevator) + // else - needs motiontransport + if (transSpawnId) { - float x = fields[32].GetFloat(), y = fields[33].GetFloat(), z = fields[34].GetFloat(), o = fields[35].GetFloat(); - m_movementInfo.transport.pos.Relocate(x, y, z, o); - transport->CalculatePassengerPosition(x, y, z, &o); - - if (!Trinity::IsValidMapCoord(x, y, z, o) || - // transport size limited - std::fabs(m_movementInfo.transport.pos.GetPositionX()) > 250.0f || - std::fabs(m_movementInfo.transport.pos.GetPositionY()) > 250.0f || - std::fabs(m_movementInfo.transport.pos.GetPositionZ()) > 250.0f) + if (map = sMapMgr->CreateMap(mapId, this, instanceId)) { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) has invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to bind location.", - guid.ToString().c_str(), x, y, z, o); + auto bounds = map->GetGameObjectBySpawnIdStore().equal_range(transSpawnId); + if (bounds.first != bounds.second) + m_transport = bounds.first->second->ToTransport(); + } - m_movementInfo.transport.Reset(); + if (m_transport) + { + float x = fields[32].GetFloat(), y = fields[33].GetFloat(), z = fields[34].GetFloat(), o = fields[35].GetFloat(); + m_movementInfo.transport.pos.Relocate(x, y, z, o); + m_transport->CalculatePassengerPosition(x, y, z, &o); - RelocateToHomebind(); + if (!Trinity::IsValidMapCoord(x, y, z, o) || + // transport size limited + std::fabs(m_movementInfo.transport.pos.GetPositionX()) > 250.0f || + std::fabs(m_movementInfo.transport.pos.GetPositionY()) > 250.0f || + std::fabs(m_movementInfo.transport.pos.GetPositionZ()) > 250.0f) + { + TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) has invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to bind location.", + guid.ToString().c_str(), x, y, z, o); + + m_transport = nullptr; + m_movementInfo.transport.Reset(); + + RelocateToHomebind(); + } + else + { + Relocate(x, y, z, o); + mapId = m_transport->GetMapId(); + m_transport->AddPassenger(this); + } } else { - Relocate(x, y, z, o); - mapId = transport->GetMapId(); + // transport not presents in world - just take go position from db + if (GameObjectData const* data = sObjectMgr->GetGameObjectData(transSpawnId)) + { + float dataX = data->spawnPoint.GetPositionX(); + float dataY = data->spawnPoint.GetPositionY(); + float dataZ = data->spawnPoint.GetPositionZ(); + float dataOrient = data->spawnPoint.GetOrientation(); - transport->AddPassenger(this); + float x = fields[32].GetFloat(), y = fields[33].GetFloat(), z = fields[34].GetFloat(), o = fields[35].GetFloat(); + m_movementInfo.transport.pos.Relocate(x, y, z, o); + TransportBase::CalculatePassengerPosition(x, y, z, &o, dataX, dataY, dataZ, dataOrient); + + if (!Trinity::IsValidMapCoord(x, y, z, o) || + // transport size limited + std::fabs(m_movementInfo.transport.pos.GetPositionX()) > 250.0f || + std::fabs(m_movementInfo.transport.pos.GetPositionX()) > 250.0f || + std::fabs(m_movementInfo.transport.pos.GetPositionX()) > 250.0f) + { + TC_LOG_ERROR("entities.player", "Player (%s) have invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to bind location.", + guid.ToString().c_str(), x, y, z, o); + + m_movementInfo.transport.Reset(); + RelocateToHomebind(); + } + else + { + Relocate(x, y, z, o); + SetTransportSpawnID(transSpawnId); + } + } } } else { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) has problems with transport guid (%u). Teleport to bind location.", - guid.ToString().c_str(), transLowGUID); + ObjectGuid transGUID = ObjectGuid::Create(transLowGUID); - RelocateToHomebind(); + if (MapTransport* go = ObjectAccessor::GetMapTransport(transGUID)) + m_transport = go; + + if (m_transport) + { + float x = fields[32].GetFloat(), y = fields[33].GetFloat(), z = fields[34].GetFloat(), o = fields[35].GetFloat(); + m_movementInfo.transport.pos.Relocate(x, y, z, o); + m_transport->CalculatePassengerPosition(x, y, z, &o); + + if (!Trinity::IsValidMapCoord(x, y, z, o) || + // transport size limited + std::fabs(m_movementInfo.transport.pos.GetPositionX()) > 250.0f || + std::fabs(m_movementInfo.transport.pos.GetPositionX()) > 250.0f || + std::fabs(m_movementInfo.transport.pos.GetPositionX()) > 250.0f) + { + TC_LOG_ERROR("entities.player", "Player (%s) have invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to bind location.", + guid.ToString().c_str(), x, y, z, o); + + m_transport = nullptr; + m_movementInfo.transport.Reset(); + + RelocateToHomebind(); + } + else + { + Relocate(x, y, z, o); + mapId = m_transport->GetMapId(); + m_transport->AddPassenger(this); + } + } + else + { + TC_LOG_ERROR("entities.player", "Player (%s) have problems with transport guid (%u). Teleport to bind location.", + guid.ToString().c_str(), transLowGUID); + + RelocateToHomebind(); + } } } // currently we do not support taxi in instance @@ -19830,9 +19913,14 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setFloat(index++, finiteAlways(GetTransOffsetZ())); stmt->setFloat(index++, finiteAlways(GetTransOffsetO())); ObjectGuid::LowType transLowGUID = 0; - if (GetTransport()) - transLowGUID = GetTransport()->GetGUID().GetCounter(); + uint32 transSpawnId = 0; + if (Transport* transport = GetTransport()) + { + transLowGUID = transport->GetGUID().GetCounter(); + transSpawnId = transport->GetSpawnId(); + } stmt->setUInt32(index++, transLowGUID); + stmt->setUInt32(index++, transSpawnId); std::ostringstream ss; ss << m_taxi; @@ -19965,9 +20053,14 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setFloat(index++, finiteAlways(GetTransOffsetZ())); stmt->setFloat(index++, finiteAlways(GetTransOffsetO())); ObjectGuid::LowType transLowGUID = 0; - if (GetTransport()) - transLowGUID = GetTransport()->GetGUID().GetCounter(); + uint32 transSpawnId = 0; + if (Transport* transport = GetTransport()) + { + transLowGUID = transport->GetGUID().GetCounter(); + transSpawnId = transport->GetSpawnId(); + } stmt->setUInt32(index++, transLowGUID); + stmt->setUInt32(index++, transSpawnId); std::ostringstream ss; ss << m_taxi; @@ -23344,17 +23437,6 @@ inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, T* target, std::set s64.insert(target->GetGUID()); } -template<> -inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, GameObject* target, std::set& /*v*/) -{ - // @HACK: This is to prevent objects like deeprun tram from disappearing when player moves far from its spawn point while riding it - // But exclude stoppable elevators from this hack - they would be teleporting from one end to another - // if affected transports move so far horizontally that it causes them to run out of visibility range then you are out of luck - // fix visibility instead of adding hacks here - if (!target->IsDynTransport()) - s64.insert(target->GetGUID()); -} - template<> inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, Creature* target, std::set& v) { @@ -28004,6 +28086,17 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy SetMinion(pet, true); + Transport* transport = GetTransGUID().IsEmpty() ? GetTransport() : nullptr; + if (transport) + { + float x, y, z, o; + pet->GetPosition(x, y, z, o); + transport->CalculatePassengerOffset(x, y, z, &o); + pet->m_movementInfo.transport.pos.Relocate(x, y, z, o); + + transport->AddPassenger(pet); + } + switch (petType) { case SUMMON_PET: diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 218a66d97c6..34a09545682 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1035,8 +1035,8 @@ class TC_GAME_API Player : public Unit, public GridObject void SetObjectScale(float scale) override; - bool TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options = 0); - bool TeleportTo(WorldLocation const& loc, uint32 options = 0); + bool TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options = 0, Transport* transport = nullptr); + bool TeleportTo(WorldLocation const& loc, uint32 options = 0, Transport* transport = nullptr); bool TeleportToBGEntryPoint(); bool HasSummonPending() const; @@ -1923,6 +1923,8 @@ class TC_GAME_API Player : public Unit, public GridObject void LearnSkillRewardedSpells(uint32 skillId, uint32 skillValue); WorldLocation& GetTeleportDest() { return m_teleport_dest; } + Transport* GetTeleportTransport() const { return m_teleport_transport; } + void ResetTeleportTransport() { m_teleport_transport = nullptr; } bool IsBeingTeleported() const { return mSemaphoreTeleport_Near || mSemaphoreTeleport_Far; } bool IsBeingTeleportedNear() const { return mSemaphoreTeleport_Near; } bool IsBeingTeleportedFar() const { return mSemaphoreTeleport_Far; } @@ -2378,6 +2380,9 @@ class TC_GAME_API Player : public Unit, public GridObject // Mount Capabilites void UpdateMountCapabilities(); + uint32 GetTransportSpawnID() const { return _transportSpawnID; } + void SetTransportSpawnID(uint32 spawnId) { _transportSpawnID = spawnId; } + protected: // Gamemaster whisper whitelist GuidList WhisperList; @@ -2717,6 +2722,7 @@ class TC_GAME_API Player : public Unit, public GridObject // Current teleport data WorldLocation m_teleport_dest; uint32 m_teleport_options; + Transport* m_teleport_transport; bool mSemaphoreTeleport_Near; bool mSemaphoreTeleport_Far; @@ -2758,6 +2764,8 @@ class TC_GAME_API Player : public Unit, public GridObject std::vector PlayerPetDataStore; TimeTrackerSmall m_petScalingSynchTimer; + + uint32 _transportSpawnID; }; TC_GAME_API void AddItemsSetItem(Player* player, Item* item); diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 43abdad1a41..acd8041580b 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -31,48 +31,541 @@ #include "Totem.h" #include "UpdateData.h" #include "Vehicle.h" +#include "ZoneScript.h" Transport::Transport() : GameObject(), - _transportInfo(nullptr), _isMoving(true), _pendingStop(false), - _triggeredArrivalEvent(false), _triggeredDepartureEvent(false), - _passengerTeleportItr(_passengers.begin()), _delayedAddModel(false), _delayedTeleport(false) + _passengerTeleportItr(_passengers.begin()), _currentTransportTime(0), _destinationStopFrameTime(0), + _alignmentTransportTime(0), _lastStopFrameTime(0), _isDynamicTransport(false), _initialRelocate(false) { - m_updateFlag = UPDATEFLAG_TRANSPORT | UPDATEFLAG_LOWGUID | UPDATEFLAG_STATIONARY_POSITION | UPDATEFLAG_ROTATION; + m_updateFlag |= UPDATEFLAG_TRANSPORT; } Transport::~Transport() { ASSERT(_passengers.empty()); - UnloadStaticPassengers(); + if (m_goInfo && m_goInfo->type == GAMEOBJECT_TYPE_TRANSPORT) + delete m_goValue.Transport.StopFrames; } -bool Transport::Create(ObjectGuid::LowType guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress) +void Transport::CleanupsBeforeDelete(bool finalCleanup /*= true*/) { - Relocate(x, y, z, ang); - - if (!IsPositionValid()) + while (!_passengers.empty()) { - TC_LOG_ERROR("entities.transport", "Transport (GUID: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)", - guidlow, x, y); + WorldObject* obj = *_passengers.begin(); + RemovePassenger(obj); + } + + GameObject::CleanupsBeforeDelete(finalCleanup); +} + +void Transport::AddPassenger(WorldObject* passenger) +{ + if (!IsInWorld()) + return; + + if (_passengers.insert(passenger).second) + { + passenger->SetTransport(this); + passenger->m_movementInfo.transport.guid = GetGUID(); + passenger->m_movementInfo.transport.time = GetPathProgress(); + TC_LOG_DEBUG("entities.transport", "Object %s boarded transport %s.", passenger->GetName().c_str(), GetName().c_str()); + + if (Unit* unit = passenger->ToUnit()) + { + if (Guardian* guardian = unit->GetGuardianPet()) + { + if (!guardian->IsPet() || guardian->GetCharmInfo()->GetCommandState() == COMMAND_FOLLOW) + { + if (Transport* oldTransport = guardian->GetTransport()) + oldTransport->RemovePassenger(guardian); + + AddPassenger(guardian); + /// @HACK - transport models are not added to map's dynamic LoS calculations + /// because the current GameObjectModel cannot be moved without recreating + guardian->AddUnitState(UNIT_STATE_IGNORE_PATHFINDING); + } + } + } + + if (Player* plr = passenger->ToPlayer()) + { + sScriptMgr->OnAddPassenger(this, plr); + + if (GetGoType() == GAMEOBJECT_TYPE_TRANSPORT) + plr->SetTransportSpawnID(GetSpawnId()); + } + } +} + +void Transport::RemovePassenger(WorldObject* passenger) +{ + bool erased = false; + if (_passengerTeleportItr != _passengers.end()) + { + PassengerSet::iterator itr = _passengers.find(passenger); + if (itr != _passengers.end()) + { + if (itr == _passengerTeleportItr) + ++_passengerTeleportItr; + + _passengers.erase(itr); + erased = true; + } + } + else + erased = _passengers.erase(passenger) > 0; + + if (erased) + { + passenger->SetTransport(nullptr); + passenger->m_movementInfo.transport.Reset(); + TC_LOG_DEBUG("entities.transport", "Object %s removed from transport %s.", passenger->GetName().c_str(), GetName().c_str()); + + if (Unit* unit = passenger->ToUnit()) + { + if (Guardian* guardian = unit->GetGuardianPet()) + if (!guardian->IsPet() || guardian->GetCharmInfo()->GetCommandState() == COMMAND_FOLLOW) + { + if (guardian->GetTransGUID() == GetGUID()) + { + RemovePassenger(guardian); + /// @HACK - transport models are not added to map's dynamic LoS calculations + /// because the current GameObjectModel cannot be moved without recreating + guardian->ClearUnitState(UNIT_STATE_IGNORE_PATHFINDING); + } + } + } + + if (Player* plr = passenger->ToPlayer()) + { + sScriptMgr->OnRemovePassenger(this, plr); + plr->SetTransportSpawnID(0); + } + } +} + +bool Transport::Create(ObjectGuid::LowType guidlow, uint32 entry, Map* map, uint32 /*phaseMask*/, Position const& pos, QuaternionData const& rotation, uint32 animprogress, GOState go_state, uint32 artKit /*= 0*/, bool dynamic, ObjectGuid::LowType spawnid) +{ + ASSERT(map); + SetMap(map); + + SetZoneScript(); + if (GetZoneScript()) + { + entry = GetZoneScript()->GetGameObjectEntry(m_spawnId, entry); + if (!entry) + return false; + } + + // Set if this object can handle dynamic spawns + if (!dynamic) + SetRespawnCompatibilityMode(); + + if (!Transport::CreateTransport(guidlow, entry, map->GetId(), pos, animprogress)) + return false; + + if (m_goInfo->type != GAMEOBJECT_TYPE_TRANSPORT) + { + TC_LOG_ERROR("entities.transport", "Transport (Spawn id: %u Entry: %u) not created.", GetSpawnId(), entry); return false; } - Object::_Create(guidlow, 0, HighGuid::Mo_Transport); + m_stationaryPosition.Relocate(pos); + + GameObjectAddon const* gameObjectAddon = sObjectMgr->GetGameObjectAddon(GetSpawnId()); + + SetWorldRotation(rotation.x, rotation.y, rotation.z, rotation.w); + + // For most of gameobjects is (0, 0, 0, 1) quaternion, there are only some transports with not standard rotation + QuaternionData parentRotation; + if (gameObjectAddon) + parentRotation = gameObjectAddon->ParentRotation; + + SetParentRotation(parentRotation); + + SetGoArtKit(artKit); + + m_goValue.Transport.AnimationInfo = sTransportMgr->GetTransportAnimInfo(entry); + m_goValue.Transport.StopFrames = new std::vector(); + + if (m_goInfo->transport.Timeto2ndfloor > 0) + { + // If we have a stop frame we also need an additional one at 0ms + m_goValue.Transport.StopFrames->push_back(0); + m_goValue.Transport.StopFrames->push_back(m_goInfo->transport.Timeto2ndfloor); + } + if (m_goInfo->transport.Timeto3rdfloor > 0) + m_goValue.Transport.StopFrames->push_back(m_goInfo->transport.Timeto3rdfloor); + if (m_goInfo->transport.Timeto4thfloor > 0) + m_goValue.Transport.StopFrames->push_back(m_goInfo->transport.Timeto4thfloor); + if (m_goInfo->transport.Timeto5thfloor > 0) + m_goValue.Transport.StopFrames->push_back(m_goInfo->transport.Timeto5thfloor); + + _isDynamicTransport = m_goValue.Transport.StopFrames->empty(); + + uint32 pathProgress = getMSTime(); + if (m_goValue.Transport.AnimationInfo) + pathProgress -= pathProgress % GetTransportPeriod(); + + uint32 stopTimer = 0; + if (m_goInfo->transport.startOpen) + { + SetGoState(GO_STATE_TRANSPORT_STOPPED); + stopTimer = m_goValue.Transport.StopFrames->at(m_goInfo->transport.startOpen - 1); + _initialRelocate = true; + } + else + SetGoState(GO_STATE_TRANSPORT_ACTIVE); + + SetPathProgress(pathProgress + stopTimer); + + SetDestinationStopFrameTime(stopTimer); + SetCurrentTransportTime(stopTimer); + + if (gameObjectAddon && gameObjectAddon->InvisibilityValue) + { + m_invisibility.AddFlag(gameObjectAddon->invisibilityType); + m_invisibility.AddValue(gameObjectAddon->invisibilityType, gameObjectAddon->InvisibilityValue); + } + + LastUsedScriptID = GetGOInfo()->ScriptId; + AIM_Initialize(); + + if (spawnid) + m_spawnId = spawnid; + + return true; +} + +bool Transport::CreateTransport(ObjectGuid::LowType guidlow, uint32 entry, uint32 mapid, Position const& pos, uint32 animprogress) +{ + Relocate(pos); + + if (!IsPositionValid()) + { + TC_LOG_ERROR("entities.transport", "Transport (GUID: %u) not created. Suggested coordinates isn't valid %s", + guidlow, pos.ToString().c_str()); + return false; + } + + Object::_Create(guidlow, 0, HighGuid::Transport); GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(entry); if (!goinfo) { - TC_LOG_ERROR("sql.sql", "Transport not created: entry in `gameobject_template` not found, guidlow: %u map: %u (X: %f Y: %f Z: %f) ang: %f", guidlow, mapid, x, y, z, ang); + TC_LOG_ERROR("sql.sql", "Transport not created: entry in `gameobject_template` not found, guidlow: %u map: %u (Position: %s) ang: %f", guidlow, mapid, pos.ToString().c_str()); return false; } m_goInfo = goinfo; m_goTemplateAddon = sObjectMgr->GetGameObjectTemplateAddon(entry); + if (m_goTemplateAddon) + { + SetFaction(m_goTemplateAddon->faction); + SetUInt32Value(GAMEOBJECT_FLAGS, m_goTemplateAddon->flags); + } + + SetObjectScale(goinfo->size); + SetEntry(goinfo->entry); + SetDisplayId(goinfo->displayId); + SetGoType(GameobjectTypes(goinfo->type)); + SetGoAnimProgress(animprogress); + SetName(goinfo->name); + + m_model = CreateModel(); + return true; +} + +void Transport::Update(uint32 diff) +{ + GameObject::Update(diff); + + if (!IsInWorld()) + return; + + if (!m_goValue.Transport.AnimationInfo) + return; + + if (_initialRelocate) + { + RelocateToProgress(GetCurrentTransportTime()); + _initialRelocate = false; + } + + if (IsDynamicTransport()) + { + SetPathProgress(GetPathProgress() + diff); + + SetCurrentTransportTime(GetCurrentTransportTime() + diff); + if (GetCurrentTransportTime() >= GetTransportPeriod()) + SetCurrentTransportTime(GetCurrentTransportTime() % GetTransportPeriod()); + } + else + { + if (GetGoState() == GO_STATE_TRANSPORT_ACTIVE) + { + // waiting at it's destination for state change, do nothing + if (GetCurrentTransportTime() == GetDestinationStopFrameTime()) + return; + + SetPathProgress(GetPathProgress() + diff); + + // GOState has changed before previous state was reached, move to new destination immediately + if (GetCurrentTransportTime() < GetDestinationStopFrameTime()) + SetCurrentTransportTime(0); + else if (GetCurrentTransportTime() + diff < GetTransportPeriod()) + SetCurrentTransportTime(GetCurrentTransportTime() + diff); + else + SetCurrentTransportTime(0); + } + else + { + // waiting at it's destination for state change, do nothing + if (GetCurrentTransportTime() == GetDestinationStopFrameTime()) + return; + + int32 currentTime = GetCurrentTransportTime(); + int32 destinationTime = GetDestinationStopFrameTime(); + bool backwardsMovement = destinationTime < currentTime; + + SetPathProgress(GetPathProgress() + std::max(0, int32(backwardsMovement ? (diff * -1) : diff))); + + if (!backwardsMovement) + { + // GOState has changed before previous state was reached, move to new destination immediately + if (currentTime > destinationTime) + SetCurrentTransportTime(destinationTime); + else if (int32(currentTime + diff) < destinationTime) + SetCurrentTransportTime(currentTime + diff); + else + { + SetCurrentTransportTime(destinationTime); + SetAlignmentTransportTime(0); + } + } + else + { + uint32 timeDiff = diff; + // Skipping multiple stop frames while moving to frame 0. Align the steps to the travel time. + if (uint32 alignmentTime = GetAlightmentTransportTime()) + { + float alignmentPercentage = (float)alignmentTime / GetLastStopFrameTime(); + timeDiff += diff * alignmentPercentage; + } + + // GOState has changed before previous state was reached, move to new destination immediately + if (currentTime < destinationTime) + currentTime = destinationTime; + else if (int32(currentTime - timeDiff) > destinationTime) + currentTime -= timeDiff; + else + { + currentTime = destinationTime; + SetAlignmentTransportTime(0); + } + + SetCurrentTransportTime(currentTime); + } + } + } + + RelocateToProgress(GetCurrentTransportTime()); +} + +void Transport::RelocateToProgress(uint32 progress) +{ + TransportAnimationEntry const* curr = nullptr; + TransportAnimationEntry const* next = nullptr; + float percPos = 0.0f; + + if (m_goValue.Transport.AnimationInfo->GetAnimNode(progress, curr, next, percPos)) + { + // curr node offset + G3D::Vector3 pos = G3D::Vector3(curr->X, curr->Y, curr->Z); + + // move by percentage of segment already passed + pos += G3D::Vector3(percPos * (next->X - curr->X), percPos * (next->Y - curr->Y), percPos * (next->Z - curr->Z)); + + // rotate path by PathRotation + // PathRotation in db is only simple orientation rotation, so don't use sophisticated and not working code + // reminder: WorldRotation only influences model rotation, not the path + float sign = GetFloatValue(GAMEOBJECT_PARENTROTATION + 2) >= 0.0f ? 1.0f : -1.0f; + float pathRotAngle = sign * 2.0f * acos(GetFloatValue(GAMEOBJECT_PARENTROTATION + 3)); + float cs = cos(pathRotAngle), sn = sin(pathRotAngle); + float nx = pos.x * cs - pos.y * sn; + float ny = pos.x * sn + pos.y * cs; + + pos.x = nx; + pos.y = ny; + + // add stationary position to the calculated offset + pos += G3D::Vector3(GetStationaryX(), GetStationaryY(), GetStationaryZ()); + + // rotate by AnimRotation at current segment + // AnimRotation in dbc is only simple orientation rotation, so don't use sophisticated and not working code + QuaternionData currRot = QuaternionData(); + QuaternionData nextRot = QuaternionData(); + float percRot = 0.0f; + + m_goValue.Transport.AnimationInfo->GetAnimRotation(progress, currRot, nextRot, percRot); + + float signCurr = currRot.z >= 0.0f ? 1.0f : -1.0f; + float oriRotAngleCurr = signCurr * 2.0f * acos(currRot.w); + float signNext = nextRot.z >= 0.0f ? 1.0f : -1.0f; + float oriRotAngleNext = signNext * 2.0f * acos(nextRot.w); + float oriRotAngle = oriRotAngleCurr + percRot * (oriRotAngleNext - oriRotAngleCurr); + + // check if position is valid + if (!Trinity::IsValidMapCoord(pos.x, pos.y, pos.z)) + return; + + GetMap()->GameObjectRelocation(this, pos.x, pos.y, pos.z, NormalizeOrientation(GetStationaryO() + oriRotAngle)); + + UpdatePassengerPositions(_passengers); + } +} + +void Transport::UpdatePassengerPositions(PassengerSet& passengers) +{ + for (PassengerSet::iterator itr = passengers.begin(); itr != passengers.end(); ++itr) + { + WorldObject* passenger = *itr; + // transport teleported but passenger not yet (can happen for players) + if (passenger->GetMap() != GetMap()) + continue; + + // if passenger is on vehicle we have to assume the vehicle is also on transport + // and its the vehicle that will be updating its passengers + if (Unit* unit = passenger->ToUnit()) + { + if (unit->GetVehicle()) + continue; + + // if spline enabled, position will be updated in Unit::UpdateSplinePosition + if (unit->IsSplineEnabled()) + continue; + } + + // Do not use Unit::UpdatePosition here, we don't want to remove auras + // as if regular movement occurred + float x, y, z, o; + passenger->m_movementInfo.transport.pos.GetPosition(x, y, z, o); + CalculatePassengerPosition(x, y, z, &o); + switch (passenger->GetTypeId()) + { + case TYPEID_UNIT: + { + Creature* creature = passenger->ToCreature(); + GetMap()->CreatureRelocation(creature, x, y, z, o, false); + creature->GetTransportHomePosition(x, y, z, o); + CalculatePassengerPosition(x, y, z, &o); + creature->SetHomePosition(x, y, z, o); + break; + } + case TYPEID_PLAYER: + GetMap()->PlayerRelocation(passenger->ToPlayer(), x, y, z, o); + break; + case TYPEID_GAMEOBJECT: + GetMap()->GameObjectRelocation(passenger->ToGameObject(), x, y, z, o, false); + passenger->ToGameObject()->RelocateStationaryPosition(x, y, z, o); + break; + case TYPEID_DYNAMICOBJECT: + GetMap()->DynamicObjectRelocation(passenger->ToDynObject(), x, y, z, o); + break; + default: + break; + } + + if (Unit* unit = passenger->ToUnit()) + if (Vehicle* vehicle = unit->GetVehicleKit()) + vehicle->RelocatePassengers(); + } +} + +uint32 Transport::GetTransportPeriod() const +{ + ASSERT(m_goInfo->type == GAMEOBJECT_TYPE_TRANSPORT); + if (m_goValue.Transport.AnimationInfo) + return m_goValue.Transport.AnimationInfo->TotalTime; + + return 0; +} + +void Transport::SetTransportState(GOState state, uint32 stopFrame /*= 0*/) +{ + // Do not change the transport state before reaching the last selected destination + if (GetCurrentTransportTime() != GetDestinationStopFrameTime()) + return; + + ASSERT(m_goInfo->type == GAMEOBJECT_TYPE_TRANSPORT); + + uint32 stopTimer = 0; + uint32 backwardsTimer = 0; + + if (state == GO_STATE_TRANSPORT_ACTIVE) + { + if (GetGoState() >= GO_STATE_TRANSPORT_STOPPED) + stopTimer = m_goValue.Transport.StopFrames->at(GetGoState() - GO_STATE_TRANSPORT_STOPPED); + + SetUInt32Value(GAMEOBJECT_LEVEL, getMSTime() + stopTimer); + } + else + { + ASSERT(state < GOState(GO_STATE_TRANSPORT_STOPPED + MAX_GO_STATE_TRANSPORT_STOP_FRAMES)); + ASSERT(stopFrame < m_goValue.Transport.StopFrames->size()); + + stopTimer = m_goValue.Transport.StopFrames->at(stopFrame); + + // Handling backwards movement. According to retail tests, a transport that returns to stop frame 0 is using the time between frame 0 and 1 + if (!stopTimer) + backwardsTimer = m_goValue.Transport.StopFrames->at(stopFrame + 1); + + // forward movement. target frame time - current frame time + if (GetCurrentTransportTime() < stopTimer) + SetUInt32Value(GAMEOBJECT_LEVEL, getMSTime() + stopTimer - GetCurrentTransportTime()); + else if (backwardsTimer) + SetUInt32Value(GAMEOBJECT_LEVEL, getMSTime() + backwardsTimer); + else + SetUInt32Value(GAMEOBJECT_LEVEL, getMSTime() + GetCurrentTransportTime() - stopTimer); + + state = GOState(GO_STATE_TRANSPORT_STOPPED + stopFrame); + } + + uint32 pathProgress = getMSTime(); + if (m_goValue.Transport.AnimationInfo) + pathProgress -= pathProgress % GetTransportPeriod(); + + SetPathProgress(pathProgress + stopTimer + backwardsTimer); + SetDestinationStopFrameTime(stopTimer); + SetLastStopFrameTime(GetCurrentTransportTime()); + if (GetCurrentTransportTime() > backwardsTimer) + SetAlignmentTransportTime(backwardsTimer); + + SetGoState(state); +} + +MapTransport::MapTransport() : Transport(), + _transportInfo(nullptr), _isMoving(true), _pendingStop(false), + _triggeredArrivalEvent(false), _triggeredDepartureEvent(false), + _delayedAddModel(false), _delayedTeleport(false) +{ +} + +MapTransport::~MapTransport() +{ + UnloadStaticPassengers(); +} + +bool MapTransport::CreateMapTransport(ObjectGuid::LowType guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress) +{ + if (!Transport::CreateTransport(guidlow, entry, mapid, Position(x, y, z, ang), animprogress)) + return false; + TransportTemplate const* tInfo = sTransportMgr->GetTransportTemplate(entry); if (!tInfo) { - TC_LOG_ERROR("sql.sql", "Transport %u (name: %s) will not be created, missing `transport_template` entry.", entry, goinfo->name.c_str()); + TC_LOG_ERROR("sql.sql", "Transport %u (name: %s) will not be created, missing `transport_template` entry.", entry, m_goInfo->name.c_str()); return false; } @@ -84,21 +577,9 @@ bool Transport::Create(ObjectGuid::LowType guidlow, uint32 entry, uint32 mapid, _triggeredArrivalEvent = false; _triggeredDepartureEvent = false; - if (m_goTemplateAddon) - { - SetFaction(m_goTemplateAddon->faction); - SetUInt32Value(GAMEOBJECT_FLAGS, m_goTemplateAddon->flags); - } - m_goValue.Transport.PathProgress = 0; - SetObjectScale(goinfo->size); SetPeriod(tInfo->pathTime); - SetEntry(goinfo->entry); - SetDisplayId(goinfo->displayId); - SetGoState(!goinfo->moTransport.canBeStopped ? GO_STATE_READY : GO_STATE_ACTIVE); - SetGoType(GAMEOBJECT_TYPE_MO_TRANSPORT); - SetGoAnimProgress(animprogress); - SetName(goinfo->name); + SetGoState(!m_goInfo->moTransport.canBeStopped ? GO_STATE_READY : GO_STATE_ACTIVE); SetWorldRotation(0.0f, 0.0f, 0.0f, 1.0f); SetParentRotation(QuaternionData()); @@ -106,19 +587,14 @@ bool Transport::Create(ObjectGuid::LowType guidlow, uint32 entry, uint32 mapid, return true; } -void Transport::CleanupsBeforeDelete(bool finalCleanup /*= true*/) +void MapTransport::CleanupsBeforeDelete(bool finalCleanup /*= true*/) { UnloadStaticPassengers(); - while (!_passengers.empty()) - { - WorldObject* obj = *_passengers.begin(); - RemovePassenger(obj); - } - GameObject::CleanupsBeforeDelete(finalCleanup); + Transport::CleanupsBeforeDelete(finalCleanup); } -void Transport::Update(uint32 diff) +void MapTransport::Update(uint32 diff) { uint32 const positionUpdateDelay = 200; @@ -238,7 +714,7 @@ void Transport::Update(uint32 diff) sScriptMgr->OnTransportUpdate(this, diff); } -void Transport::DelayedUpdate(uint32 /*diff*/) +void MapTransport::DelayedUpdate(uint32 /*diff*/) { if (GetKeyFrames().size() <= 1) return; @@ -246,23 +722,7 @@ void Transport::DelayedUpdate(uint32 /*diff*/) DelayedTeleportTransport(); } -void Transport::AddPassenger(WorldObject* passenger) -{ - if (!IsInWorld()) - return; - - if (_passengers.insert(passenger).second) - { - passenger->SetTransport(this); - passenger->m_movementInfo.transport.guid = GetGUID(); - TC_LOG_DEBUG("entities.transport", "Object %s boarded transport %s.", passenger->GetName().c_str(), GetName().c_str()); - - if (Player* plr = passenger->ToPlayer()) - sScriptMgr->OnAddPassenger(this, plr); - } -} - -void Transport::RemovePassenger(WorldObject* passenger) +void MapTransport::RemovePassenger(WorldObject* passenger) { bool erased = false; if (_passengerTeleportItr != _passengers.end()) @@ -286,6 +746,12 @@ void Transport::RemovePassenger(WorldObject* passenger) passenger->m_movementInfo.transport.Reset(); TC_LOG_DEBUG("entities.transport", "Object %s removed from transport %s.", passenger->GetName().c_str(), GetName().c_str()); + if (Unit* unit = passenger->ToUnit()) + if (Guardian* guardian = unit->GetGuardianPet()) + if (!guardian->IsPet() || guardian->GetCharmInfo()->GetCommandState() == COMMAND_FOLLOW) + if (guardian->GetTransGUID() == GetGUID()) + RemovePassenger(guardian); + if (Player* plr = passenger->ToPlayer()) { sScriptMgr->OnRemovePassenger(this, plr); @@ -294,7 +760,7 @@ void Transport::RemovePassenger(WorldObject* passenger) } } -Creature* Transport::CreateNPCPassenger(ObjectGuid::LowType guid, CreatureData const* data) +Creature* MapTransport::CreateNPCPassenger(ObjectGuid::LowType guid, CreatureData const* data) { Map* map = GetMap(); if (map->GetCreatureRespawnTime(guid)) @@ -313,6 +779,7 @@ Creature* Transport::CreateNPCPassenger(ObjectGuid::LowType guid, CreatureData c creature->SetTransport(this); creature->m_movementInfo.transport.guid = GetGUID(); + creature->m_movementInfo.transport.time = GetPathProgress(); creature->m_movementInfo.transport.pos.Relocate(x, y, z, o); CalculatePassengerPosition(x, y, z, &o); creature->Relocate(x, y, z, o); @@ -344,13 +811,19 @@ Creature* Transport::CreateNPCPassenger(ObjectGuid::LowType guid, CreatureData c return creature; } -GameObject* Transport::CreateGOPassenger(ObjectGuid::LowType guid, GameObjectData const* data) +GameObject* MapTransport::CreateGOPassenger(ObjectGuid::LowType guid, GameObjectData const* data) { + ASSERT(data); + Map* map = GetMap(); if (map->GetGORespawnTime(guid)) return nullptr; - GameObject* go = new GameObject(); + GameObject* go = nullptr; + if (sObjectMgr->GetGameObjectTypeByEntry(data->id) == GAMEOBJECT_TYPE_TRANSPORT) + go = new Transport(); + else + go = new GameObject(); if (!go->LoadFromDB(guid, map, false)) { @@ -365,6 +838,7 @@ GameObject* Transport::CreateGOPassenger(ObjectGuid::LowType guid, GameObjectDat go->SetTransport(this); go->m_movementInfo.transport.guid = GetGUID(); + go->m_movementInfo.transport.time = GetPathProgress(); go->m_movementInfo.transport.pos.Relocate(x, y, z, o); CalculatePassengerPosition(x, y, z, &o); go->Relocate(x, y, z, o); @@ -390,7 +864,7 @@ GameObject* Transport::CreateGOPassenger(ObjectGuid::LowType guid, GameObjectDat return go; } -TempSummon* Transport::SummonPassenger(uint32 entry, Position const& pos, TempSummonType summonType, SummonPropertiesEntry const* properties /*= nullptr*/, uint32 duration /*= 0*/, Unit* summoner /*= nullptr*/, uint32 spellId /*= 0*/, uint32 vehId /*= 0*/) +TempSummon* MapTransport::SummonPassenger(uint32 entry, Position const& pos, TempSummonType summonType, SummonPropertiesEntry const* properties /*= nullptr*/, uint32 duration /*= 0*/, Unit* summoner /*= nullptr*/, uint32 spellId /*= 0*/, uint32 vehId /*= 0*/) { Map* map = FindMap(); if (!map) @@ -479,6 +953,7 @@ TempSummon* Transport::SummonPassenger(uint32 entry, Position const& pos, TempSu summon->SetTransport(this); summon->m_movementInfo.transport.guid = GetGUID(); summon->m_movementInfo.transport.pos.Relocate(pos); + summon->m_movementInfo.transport.time = GetPathProgress(); summon->Relocate(x, y, z, o); summon->SetHomePosition(x, y, z, o); summon->SetTransportHomePosition(pos); @@ -503,7 +978,7 @@ TempSummon* Transport::SummonPassenger(uint32 entry, Position const& pos, TempSu return summon; } -void Transport::UpdatePosition(float x, float y, float z, float o) +void MapTransport::UpdatePosition(float x, float y, float z, float o) { bool newActive = GetMap()->IsGridLoaded(x, y); Cell oldCell(GetPositionX(), GetPositionY()); @@ -529,7 +1004,7 @@ void Transport::UpdatePosition(float x, float y, float z, float o) // 4. is handed by grid unload } -void Transport::LoadStaticPassengers() +void MapTransport::LoadStaticPassengers() { if (uint32 mapId = GetGOInfo()->moTransport.mapID) { @@ -550,7 +1025,7 @@ void Transport::LoadStaticPassengers() } } -void Transport::UnloadStaticPassengers() +void MapTransport::UnloadStaticPassengers() { while (!_staticPassengers.empty()) { @@ -559,7 +1034,7 @@ void Transport::UnloadStaticPassengers() } } -void Transport::EnableMovement(bool enabled) +void MapTransport::EnableMovement(bool enabled) { if (!GetGOInfo()->moTransport.canBeStopped) return; @@ -567,7 +1042,7 @@ void Transport::EnableMovement(bool enabled) _pendingStop = !enabled; } -void Transport::MoveToNextWaypoint() +void MapTransport::MoveToNextWaypoint() { // Clear events flagging _triggeredArrivalEvent = false; @@ -579,7 +1054,7 @@ void Transport::MoveToNextWaypoint() _nextFrame = GetKeyFrames().begin(); } -float Transport::CalculateSegmentPos(float now) +float MapTransport::CalculateSegmentPos(float now) { KeyFrame const& frame = *_currentFrame; const float speed = float(m_goInfo->moTransport.moveSpeed); @@ -610,7 +1085,7 @@ float Transport::CalculateSegmentPos(float now) return segmentPos / frame.NextDistFromPrev; } -bool Transport::TeleportTransport(uint32 newMapid, float x, float y, float z, float o) +bool MapTransport::TeleportTransport(uint32 newMapid, float x, float y, float z, float o) { Map const* oldMap = GetMap(); @@ -646,14 +1121,14 @@ bool Transport::TeleportTransport(uint32 newMapid, float x, float y, float z, fl } } -void Transport::DelayedTeleportTransport() +void MapTransport::DelayedTeleportTransport() { if (!_delayedTeleport) return; _delayedTeleport = false; Map* newMap = sMapMgr->CreateBaseMap(_nextFrame->Node->MapID); - GetMap()->RemoveFromMap(this, false); + GetMap()->RemoveFromMap(this, false); SetMap(newMap); float x = _nextFrame->Node->LocX, @@ -685,66 +1160,10 @@ void Transport::DelayedTeleportTransport() } Relocate(x, y, z, o); - GetMap()->AddToMap(this); + GetMap()->AddToMap(this); } -void Transport::UpdatePassengerPositions(PassengerSet& passengers) -{ - for (PassengerSet::iterator itr = passengers.begin(); itr != passengers.end(); ++itr) - { - WorldObject* passenger = *itr; - // transport teleported but passenger not yet (can happen for players) - if (passenger->GetMap() != GetMap()) - continue; - - // if passenger is on vehicle we have to assume the vehicle is also on transport - // and its the vehicle that will be updating its passengers - if (Unit* unit = passenger->ToUnit()) - if (unit->GetVehicle()) - continue; - - // Do not use Unit::UpdatePosition here, we don't want to remove auras - // as if regular movement occurred - float x, y, z, o; - passenger->m_movementInfo.transport.pos.GetPosition(x, y, z, o); - CalculatePassengerPosition(x, y, z, &o); - switch (passenger->GetTypeId()) - { - case TYPEID_UNIT: - { - Creature* creature = passenger->ToCreature(); - GetMap()->CreatureRelocation(creature, x, y, z, o, false); - creature->GetTransportHomePosition(x, y, z, o); - CalculatePassengerPosition(x, y, z, &o); - creature->SetHomePosition(x, y, z, o); - break; - } - case TYPEID_PLAYER: - //relocate only passengers in world and skip any player that might be still logging in/teleporting - if (passenger->IsInWorld() && !passenger->ToPlayer()->IsBeingTeleported()) - { - GetMap()->PlayerRelocation(passenger->ToPlayer(), x, y, z, o); - passenger->ToPlayer()->SetFallInformation(0, passenger->GetPositionZ()); - } - break; - case TYPEID_GAMEOBJECT: - GetMap()->GameObjectRelocation(passenger->ToGameObject(), x, y, z, o, false); - passenger->ToGameObject()->RelocateStationaryPosition(x, y, z, o); - break; - case TYPEID_DYNAMICOBJECT: - GetMap()->DynamicObjectRelocation(passenger->ToDynObject(), x, y, z, o); - break; - default: - break; - } - - if (Unit* unit = passenger->ToUnit()) - if (Vehicle* vehicle = unit->GetVehicleKit()) - vehicle->RelocatePassengers(); - } -} - -void Transport::DoEventIfAny(KeyFrame const& node, bool departure) +void MapTransport::DoEventIfAny(KeyFrame const& node, bool departure) { if (uint32 eventid = departure ? node.Node->DepartureEventID : node.Node->ArrivalEventID) { @@ -754,7 +1173,7 @@ void Transport::DoEventIfAny(KeyFrame const& node, bool departure) } } -void Transport::BuildUpdate(UpdateDataMapType& data_map) +void MapTransport::BuildUpdate(UpdateDataMapType& data_map) { Map::PlayerList const& players = GetMap()->GetPlayers(); if (players.isEmpty()) diff --git a/src/server/game/Entities/Transport/Transport.h b/src/server/game/Entities/Transport/Transport.h index 5a65148de6b..076eedadcf4 100644 --- a/src/server/game/Entities/Transport/Transport.h +++ b/src/server/game/Entities/Transport/Transport.h @@ -27,15 +27,80 @@ struct CreatureData; class TC_GAME_API Transport : public GameObject, public TransportBase { - friend Transport* TransportMgr::CreateTransport(uint32, ObjectGuid::LowType, Map*, uint8, uint32, uint32); - - Transport(); public: typedef std::set PassengerSet; + Transport(); ~Transport(); - bool Create(ObjectGuid::LowType guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress); + bool Create(ObjectGuid::LowType guidlow, uint32 entry, Map* map, uint32 /*phaseMask*/, Position const& pos, QuaternionData const& rotation, uint32 animprogress, GOState go_state, uint32 artKit = 0, bool dynamic = false, ObjectGuid::LowType spawnid = 0) override; + bool CreateTransport(ObjectGuid::LowType guidlow, uint32 entry, uint32 mapid, Position const& pos, uint32 animprogress); + + void CleanupsBeforeDelete(bool finalCleanup = true) override; + + void AddPassenger(WorldObject* passenger); + virtual void RemovePassenger(WorldObject* passenger); + PassengerSet const& GetPassengers() const { return _passengers; } + + /// This method transforms supplied transport offsets into global coordinates + void CalculatePassengerPosition(float& x, float& y, float& z, float* o = nullptr) const override + { + TransportBase::CalculatePassengerPosition(x, y, z, o, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); + } + + /// This method transforms supplied global coordinates into local offsets + void CalculatePassengerOffset(float& x, float& y, float& z, float* o = nullptr) const override + { + TransportBase::CalculatePassengerOffset(x, y, z, o, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); + } + + void Update(uint32 diff) override; + + bool IsDynamicTransport() const { return _isDynamicTransport; } + + uint32 GetPathProgress() const { return GetGOValue()->Transport.PathProgress; } + void SetPathProgress(uint32 val) { m_goValue.Transport.PathProgress = val; } + + virtual uint32 GetTransportPeriod() const; + void SetTransportState(GOState state, uint32 stopFrame = 0); + + void RelocateToProgress(uint32 progress); + + void SetDestinationStopFrameTime(uint32 time) { _destinationStopFrameTime = time; } + uint32 GetDestinationStopFrameTime() const { return _destinationStopFrameTime; } + + void SetCurrentTransportTime(uint32 time) { _currentTransportTime = time; } + uint32 GetCurrentTransportTime() const { return _currentTransportTime; } + + void SetAlignmentTransportTime(uint32 time) { _alignmentTransportTime = time; } + uint32 GetAlightmentTransportTime() const { return _alignmentTransportTime; } + + void SetLastStopFrameTime(uint32 time) { _lastStopFrameTime = time; } + uint32 GetLastStopFrameTime() const { return _lastStopFrameTime; } + + protected: + void UpdatePassengerPositions(PassengerSet& passengers); + + PassengerSet _passengers; + PassengerSet::iterator _passengerTeleportItr; + + uint32 _destinationStopFrameTime; + uint32 _currentTransportTime; + uint32 _alignmentTransportTime; + uint32 _lastStopFrameTime; + bool _isDynamicTransport; + bool _initialRelocate; +}; + +class TC_GAME_API MapTransport : public Transport +{ + friend MapTransport* TransportMgr::CreateTransport(uint32, ObjectGuid::LowType, Map*, uint8, uint32, uint32); + + MapTransport(); + public: + ~MapTransport(); + + bool CreateMapTransport(ObjectGuid::LowType guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress); void CleanupsBeforeDelete(bool finalCleanup = true) override; void Update(uint32 diff) override; @@ -43,15 +108,13 @@ class TC_GAME_API Transport : public GameObject, public TransportBase void BuildUpdate(UpdateDataMapType& data_map) override; - void AddPassenger(WorldObject* passenger); - void RemovePassenger(WorldObject* passenger); - PassengerSet const& GetPassengers() const { return _passengers; } + void RemovePassenger(WorldObject* passenger) override; Creature* CreateNPCPassenger(ObjectGuid::LowType guid, CreatureData const* data); GameObject* CreateGOPassenger(ObjectGuid::LowType guid, GameObjectData const* data); /** - * @fn bool Transport::SummonPassenger(uint64, Position const&, TempSummonType, SummonPropertiesEntry const*, uint32, Unit*, uint32, uint32) + * @fn bool Transport::SummonPassenger(uint32, Position const&, TempSummonType, SummonPropertiesEntry const*, uint32, Unit*, uint32, uint32) * * @brief Temporarily summons a creature as passenger on this transport. * @@ -68,21 +131,8 @@ class TC_GAME_API Transport : public GameObject, public TransportBase */ TempSummon* SummonPassenger(uint32 entry, Position const& pos, TempSummonType summonType, SummonPropertiesEntry const* properties = nullptr, uint32 duration = 0, Unit* summoner = nullptr, uint32 spellId = 0, uint32 vehId = 0); - /// This method transforms supplied transport offsets into global coordinates - void CalculatePassengerPosition(float& x, float& y, float& z, float* o = nullptr) const override - { - TransportBase::CalculatePassengerPosition(x, y, z, o, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); - } - - /// This method transforms supplied global coordinates into local offsets - void CalculatePassengerOffset(float& x, float& y, float& z, float* o = nullptr) const override - { - TransportBase::CalculatePassengerOffset(x, y, z, o, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); - } - uint32 GetTransportPeriod() const override { return GetUInt32Value(GAMEOBJECT_LEVEL); } void SetPeriod(uint32 period) { SetUInt32Value(GAMEOBJECT_LEVEL, period); } - uint32 GetTimer() const { return GetGOValue()->Transport.PathProgress; } KeyFrameVec const& GetKeyFrames() const { return _transportInfo->keyFrames; } @@ -105,7 +155,6 @@ class TC_GAME_API Transport : public GameObject, public TransportBase float CalculateSegmentPos(float perc); bool TeleportTransport(uint32 newMapid, float x, float y, float z, float o); void DelayedTeleportTransport(); - void UpdatePassengerPositions(PassengerSet& passengers); void DoEventIfAny(KeyFrame const& node, bool departure); //! Helpers to know if stop frame was reached @@ -124,8 +173,6 @@ class TC_GAME_API Transport : public GameObject, public TransportBase bool _triggeredArrivalEvent; bool _triggeredDepartureEvent; - PassengerSet _passengers; - PassengerSet::iterator _passengerTeleportItr; PassengerSet _staticPassengers; bool _delayedAddModel; diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index aa2e9a04647..2969675c400 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -538,7 +538,7 @@ Vehicle* Vehicle::RemovePassenger(Unit* unit) // Enable gravity for passenger when he did not have it active before entering the vehicle if (seat->second.SeatInfo->HasFlag(VEHICLE_SEAT_FLAG_DISABLE_GRAVITY) && !seat->second.Passenger.IsGravityDisabled) - unit->SetDisableGravity(false); + unit->SetDisableGravity(false, true); // Remove UNIT_FLAG_NOT_SELECTABLE if passenger did not have it before entering vehicle if (seat->second.SeatInfo->HasFlag(VEHICLE_SEAT_FLAG_PASSENGER_NOT_SELECTABLE) && !seat->second.Passenger.IsUnselectable) @@ -879,7 +879,7 @@ bool VehicleJoinEvent::Execute(uint64, uint32) } if (veSeat->HasFlag(VEHICLE_SEAT_FLAG_DISABLE_GRAVITY)) - Passenger->SetDisableGravity(true); + Passenger->SetDisableGravity(true, true); if (veSeat->HasFlag(VEHICLE_SEAT_FLAG_PASSENGER_NOT_SELECTABLE)) Passenger->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); diff --git a/src/server/game/Entities/Vehicle/VehicleDefines.h b/src/server/game/Entities/Vehicle/VehicleDefines.h index 452cd1f3ef7..59b42946192 100644 --- a/src/server/game/Entities/Vehicle/VehicleDefines.h +++ b/src/server/game/Entities/Vehicle/VehicleDefines.h @@ -131,7 +131,6 @@ public: /// This method transforms supplied global coordinates into local offsets virtual void CalculatePassengerOffset(float& x, float& y, float& z, float* o = nullptr) const = 0; -protected: static void CalculatePassengerPosition(float& x, float& y, float& z, float* o, float transX, float transY, float transZ, float transO) { float inx = x, iny = y, inz = z; diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp index 86ab636931a..5957cca31ee 100644 --- a/src/server/game/Events/GameEventMgr.cpp +++ b/src/server/game/Events/GameEventMgr.cpp @@ -29,6 +29,7 @@ #include "ObjectMgr.h" #include "Player.h" #include "PoolMgr.h" +#include "Transport.h" #include "UnitAI.h" #include "World.h" #include "WorldPacket.h" @@ -1287,7 +1288,11 @@ void GameEventMgr::GameEventSpawn(int16 event_id) // We use current coords to unspawn, not spawn coords since creature can have changed grid if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint)) { - GameObject* pGameobject = new GameObject; + GameObject* pGameobject = nullptr; + if (sObjectMgr->GetGameObjectTypeByEntry(data->id) == GAMEOBJECT_TYPE_TRANSPORT) + pGameobject = new Transport(); + else + pGameobject = new GameObject(); //TC_LOG_DEBUG("misc", "Spawning gameobject %u", *itr); /// @todo find out when it is add to map if (!pGameobject->LoadFromDB(*itr, map, false)) diff --git a/src/server/game/Globals/ObjectAccessor.cpp b/src/server/game/Globals/ObjectAccessor.cpp index 6a0addb4f83..8cdf0355798 100644 --- a/src/server/game/Globals/ObjectAccessor.cpp +++ b/src/server/game/Globals/ObjectAccessor.cpp @@ -38,8 +38,8 @@ template void HashMapHolder::Insert(T* o) { static_assert(std::is_same::value - || std::is_same::value, - "Only Player and Transport can be registered in global HashMapHolder"); + || std::is_same::value, + "Only Player and MapTransport can be registered in global HashMapHolder"); boost::unique_lock lock(*GetLock()); @@ -83,7 +83,7 @@ HashMapHolder::MapType const& ObjectAccessor::GetPlayers() } template class TC_GAME_API HashMapHolder; -template class TC_GAME_API HashMapHolder; +template class TC_GAME_API HashMapHolder; namespace PlayerNameMapHolder { @@ -117,7 +117,6 @@ WorldObject* ObjectAccessor::GetWorldObject(WorldObject const& p, ObjectGuid con { case HighGuid::Player: return GetPlayer(p, guid); case HighGuid::Transport: - case HighGuid::Mo_Transport: case HighGuid::GameObject: return GetGameObject(p, guid); case HighGuid::Vehicle: case HighGuid::Unit: return GetCreature(p, guid); @@ -142,7 +141,6 @@ Object* ObjectAccessor::GetObjectByTypeMask(WorldObject const& p, ObjectGuid con return GetPlayer(p, guid); break; case HighGuid::Transport: - case HighGuid::Mo_Transport: case HighGuid::GameObject: if (typemask & TYPEMASK_GAMEOBJECT) return GetGameObject(p, guid); @@ -182,11 +180,21 @@ GameObject* ObjectAccessor::GetGameObject(WorldObject const& u, ObjectGuid const return u.GetMap()->GetGameObject(guid); } -Transport* ObjectAccessor::GetTransport(WorldObject const& u, ObjectGuid const& guid) +Transport* ObjectAccessor::GetTransportOnMap(WorldObject const& u, ObjectGuid const& guid) { return u.GetMap()->GetTransport(guid); } +MapTransport* ObjectAccessor::GetMapTransportOnMap(WorldObject const& u, ObjectGuid const& guid) +{ + return u.GetMap()->GetMapTransport(guid); +} + +MapTransport* ObjectAccessor::GetMapTransport(ObjectGuid const& guid) +{ + return HashMapHolder::Find(guid); +} + DynamicObject* ObjectAccessor::GetDynamicObject(WorldObject const& u, ObjectGuid const& guid) { return u.GetMap()->GetDynamicObject(guid); diff --git a/src/server/game/Globals/ObjectAccessor.h b/src/server/game/Globals/ObjectAccessor.h index 074ac6f981e..89b086b515e 100644 --- a/src/server/game/Globals/ObjectAccessor.h +++ b/src/server/game/Globals/ObjectAccessor.h @@ -67,7 +67,9 @@ namespace ObjectAccessor TC_GAME_API Object* GetObjectByTypeMask(WorldObject const&, ObjectGuid const&, uint32 typemask); TC_GAME_API Corpse* GetCorpse(WorldObject const& u, ObjectGuid const& guid); TC_GAME_API GameObject* GetGameObject(WorldObject const& u, ObjectGuid const& guid); - TC_GAME_API Transport* GetTransport(WorldObject const& u, ObjectGuid const& guid); + TC_GAME_API Transport* GetTransportOnMap(WorldObject const& u, ObjectGuid const& guid); + TC_GAME_API MapTransport* GetMapTransportOnMap(WorldObject const& u, ObjectGuid const& guid); + TC_GAME_API MapTransport* GetMapTransport(ObjectGuid const& guid); TC_GAME_API DynamicObject* GetDynamicObject(WorldObject const& u, ObjectGuid const& guid); TC_GAME_API AreaTrigger* GetAreaTrigger(WorldObject const& u, ObjectGuid const& guid); TC_GAME_API Unit* GetUnit(WorldObject const&, ObjectGuid const& guid); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 5c8bc51ca71..58b99530b61 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -53,6 +53,7 @@ #include "SpellMgr.h" #include "SpellScript.h" #include "TemporarySummon.h" +#include "Transport.h" #include "UpdateMask.h" #include "Util.h" #include "Vehicle.h" @@ -2158,7 +2159,12 @@ ObjectGuid::LowType ObjectMgr::AddGameObjectData(uint32 entry, uint32 mapId, Pos // We use spawn coords to spawn if (!map->Instanceable() && map->IsGridLoaded(data.spawnPoint)) { - GameObject* go = new GameObject; + GameObject* go = nullptr; + if (goinfo->type == GAMEOBJECT_TYPE_TRANSPORT) + go = new Transport(); + else + go = new GameObject(); + if (!go->LoadFromDB(spawnId, map, true)) { TC_LOG_ERROR("misc", "AddGameObjectData: cannot add gameobject entry %u to map", entry); @@ -7076,7 +7082,7 @@ void ObjectMgr::SetHighestGuids() result = WorldDatabase.Query("SELECT MAX(guid) FROM transports"); if (result) - GetGuidSequenceGenerator().Set((*result)[0].GetUInt32() + 1); + GetGuidSequenceGenerator().Set((*result)[0].GetUInt32() + 1); result = CharacterDatabase.Query("SELECT MAX(id) FROM auctionhouse"); if (result) @@ -10272,3 +10278,11 @@ void ObjectMgr::LoadCreatureQuestItems() TC_LOG_INFO("server.loading", ">> Loaded %u creature quest items in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } + +uint32 ObjectMgr::GetGameObjectTypeByEntry(uint32 entry) const +{ + if (GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(entry)) + return goinfo->type; + + return MAX_GAMEOBJECT_TYPE; +} diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 69c41ee5be9..38ad42da038 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -1553,6 +1553,8 @@ class TC_GAME_API ObjectMgr void LoadMissingKeyChains(); + uint32 GetGameObjectTypeByEntry(uint32 entry) const; + private: // first free id for selected id type uint32 _auctionId; diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index 0a8028994df..2d14b480d76 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -31,7 +31,7 @@ void VisibleNotifier::SendToSelf() { // at this moment i_clientGUIDs have guids that not iterate at grid level checks // but exist one case when this possible and object not out of range: transports - if (Transport* transport = i_player.GetTransport()) + if (MapTransport* transport = i_player.GetMapTransport()) { for (Transport::PassengerSet::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end(); ++itr) { diff --git a/src/server/game/Grids/ObjectGridLoader.cpp b/src/server/game/Grids/ObjectGridLoader.cpp index 8a2db63f3a7..987b274caf9 100644 --- a/src/server/game/Grids/ObjectGridLoader.cpp +++ b/src/server/game/Grids/ObjectGridLoader.cpp @@ -27,6 +27,7 @@ #include "GameObject.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" +#include "Transport.h" #include "World.h" #include "ScriptMgr.h" @@ -166,6 +167,48 @@ void LoadHelper(CellGuidSet const& guid_set, CellCoord &cell, GridRefManager } } +template <> +void LoadHelper(CellGuidSet const& guid_set, CellCoord &cell, GridRefManager &m, uint32 &count, Map* map) +{ + for (CellGuidSet::const_iterator i_guid = guid_set.begin(); i_guid != guid_set.end(); ++i_guid) + { + uint32 guid = *i_guid; + GameObjectData const* data = sObjectMgr->GetGameObjectData(guid); + + GameObject* obj = nullptr; + if (data && sObjectMgr->GetGameObjectTypeByEntry(data->id) == GAMEOBJECT_TYPE_TRANSPORT) + obj = new Transport(); + else + obj = new GameObject(); + + // Don't spawn at all if there's a respawn time + if (obj->GetTypeId() == TYPEID_GAMEOBJECT && !map->GetGORespawnTime(*i_guid)) + { + ObjectGuid::LowType guid = *i_guid; + if (obj->GetTypeId() == TYPEID_GAMEOBJECT) + { + // If gameobject in manual spawn group, don't spawn here, unless group is already active. + GameObjectData const* godata = sObjectMgr->GetGameObjectData(guid); + ASSERT(godata, "Tried to load gameobject with spawnId %u, but no such object exists.", guid); + if (!(godata->spawnGroupData->flags & SPAWNGROUP_FLAG_SYSTEM)) + if (!map->IsSpawnGroupActive(godata->spawnGroupData->groupId)) + { + delete obj; + continue; + } + } + } + + if (!obj->LoadFromDB(guid, map, false, false)) + { + delete obj; + continue; + } + + AddObjectHelper(cell, m, count, map, obj); + } +} + void ObjectGridLoader::Visit(GameObjectMapType &m) { CellCoord cellCoord = i_cell.GetCellCoord(); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index b7ae6ae2287..b2e573dd376 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -51,6 +51,7 @@ #include "ScriptMgr.h" #include "SharedDefines.h" #include "SocialMgr.h" +#include "Transport.h" #include "World.h" #include "WorldPacket.h" @@ -962,6 +963,26 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder) } } + // check if player is on transport and not added earlier to transport + // e.g. in case of loading transport on grid load + if (pCurrChar->GetTransportSpawnID() && !pCurrChar->GetTransport()) + { + Transport* transport = nullptr; + + auto bounds = pCurrChar->GetMap()->GetGameObjectBySpawnIdStore().equal_range(pCurrChar->GetTransportSpawnID()); + if (bounds.first != bounds.second) + transport = bounds.first->second->ToTransport(); + + if (transport) + transport->AddPassenger(pCurrChar); + else + { + pCurrChar->SetTransport(nullptr); + pCurrChar->m_movementInfo.transport.Reset(); + pCurrChar->SetTransportSpawnID(0); + } + } + pCurrChar->SendInitialPacketsAfterAddToMap(); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_ONLINE); diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 3b459d37529..453dc261a1c 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -88,10 +88,14 @@ void WorldSession::HandleMoveWorldportAckOpcode() return; } + float x = loc.GetPositionX(); + float y = loc.GetPositionY(); float z = loc.GetPositionZ(); + float o = loc.GetOrientation(); + if (GetPlayer()->HasUnitMovementFlag(MOVEMENTFLAG_HOVER)) z += GetPlayer()->GetFloatValue(UNIT_FIELD_HOVERHEIGHT); - GetPlayer()->Relocate(loc.GetPositionX(), loc.GetPositionY(), z, loc.GetOrientation()); + GetPlayer()->Relocate(x, y, z, o); GetPlayer()->SetFallInformation(0, GetPlayer()->GetPositionZ()); GetPlayer()->ResetMap(); @@ -108,6 +112,16 @@ void WorldSession::HandleMoveWorldportAckOpcode() return; } + if (Transport* transport = _player->GetTeleportTransport()) + { + transport->CalculatePassengerOffset(x, y, z, &o); + _player->m_movementInfo.transport.pos.Relocate(x, y, z, o); + + transport->AddPassenger(_player); + + _player->ResetTeleportTransport(); + } + // battleground state prepare (in case join to BG), at relogin/tele player not invited // only add to bg group and object, if the player was invited (else he entered through command) if (_player->InBattleground()) @@ -354,11 +368,7 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvPacket) } if (!mover->GetTransport() && !mover->GetVehicle()) - { - GameObject* go = mover->GetMap()->GetGameObject(movementInfo.transport.guid); - if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT) - movementInfo.transport.guid.Clear(); - } + movementInfo.transport.Reset(); } else if (plrMover && plrMover->GetTransport()) // if we were on a transport, leave plrMover->m_transport->RemovePassenger(plrMover); diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 8da28e43cc9..22c19d7b90e 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -313,6 +313,8 @@ i_scriptLock(false), _respawnCheckTimer(0), _defaultLight(GetDefaultMapLight(id) //lets initialize visibility distance for map Map::InitVisibilityDistance(); + GetGuidSequenceGenerator().Set(sObjectMgr->GetGenerator().GetNextAfterMaxUsed()); + _weatherUpdateTimer.SetInterval(time_t(1 * IN_MILLISECONDS)); MMAP::MMapFactory::createOrGetMMapManager()->loadMapInstance(sWorld->GetDataPath(), GetId(), i_InstanceId); @@ -493,7 +495,7 @@ void Map::DeleteFromWorld(Player* player) } template<> -void Map::DeleteFromWorld(Transport* transport) +void Map::DeleteFromWorld(MapTransport* transport) { ObjectAccessor::RemoveObject(transport); delete transport; @@ -692,7 +694,7 @@ bool Map::AddToMap(T* obj) } template<> -bool Map::AddToMap(Transport* obj) +bool Map::AddToMap(MapTransport* obj) { //TODO: Needs clean up. An object should not be added to map twice. if (obj->IsInWorld()) @@ -713,7 +715,7 @@ bool Map::AddToMap(Transport* obj) { for (Map::PlayerList::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) { - if (itr->GetSource()->GetTransport() != obj) + if (itr->GetSource()->GetMapTransport() != obj) { UpdateData data(GetId()); obj->BuildCreateUpdateBlockForPlayer(&data, itr->GetSource()); @@ -1064,7 +1066,7 @@ void Map::RemoveFromMap(T *obj, bool remove) } template<> -void Map::RemoveFromMap(Transport* obj, bool remove) +void Map::RemoveFromMap(MapTransport* obj, bool remove) { obj->RemoveFromWorld(); @@ -1786,10 +1788,10 @@ void Map::UnloadAll() for (TransportsContainer::iterator itr = _transports.begin(); itr != _transports.end();) { - Transport* transport = *itr; + MapTransport* transport = *itr; ++itr; - RemoveFromMap(transport, true); + RemoveFromMap(transport, true); } for (auto& cellCorpsePair : _corpsesByCell) @@ -2864,7 +2866,7 @@ void Map::SendInitSelf(Player* player) player->BuildCreateUpdateBlockForPlayer(&data, player); // build other passengers at transport also (they always visible and marked as visible and will not send at visibility update at add to map - if (Transport* transport = player->GetTransport()) + if (MapTransport* transport = player->GetMapTransport()) for (WorldObject* passenger : transport->GetPassengers()) if (player != passenger && player->HaveAtClient(passenger)) passenger->BuildCreateUpdateBlockForPlayer(&data, player); @@ -2880,7 +2882,7 @@ void Map::SendInitTransports(Player* player) UpdateData transData(player->GetMapId()); for (Transport* transport : _transports) { - if (transport != player->GetTransport() && player->IsInPhase(transport)) + if (transport != player->GetMapTransport() && player->IsInPhase(transport)) { transport->BuildCreateUpdateBlockForPlayer(&transData, player); player->m_visibleTransports.insert(transport->GetGUID()); @@ -2898,7 +2900,7 @@ void Map::SendRemoveTransports(Player* player) UpdateData transData(player->GetMapId()); for (Transport* transport : _transports) { - if (transport != player->GetTransport()) + if (transport != player->GetMapTransport()) { transport->BuildOutOfRangeUpdateBlock(&transData); player->m_visibleTransports.erase(transport->GetGUID()); @@ -3447,7 +3449,7 @@ void Map::DelayedUpdate(uint32 t_diff) { for (_transportsUpdateIter = _transports.begin(); _transportsUpdateIter != _transports.end();) { - Transport* transport = *_transportsUpdateIter; + MapTransport* transport = *_transportsUpdateIter; ++_transportsUpdateIter; if (!transport->IsInWorld()) @@ -3551,7 +3553,7 @@ void Map::RemoveAllObjectsInRemoveList() case TYPEID_GAMEOBJECT: { GameObject* go = obj->ToGameObject(); - if (Transport* transport = go->ToTransport()) + if (MapTransport* transport = go->ToMapTransport()) RemoveFromMap(transport, true); else RemoveFromMap(go, true); @@ -4327,13 +4329,22 @@ Pet* Map::GetPet(ObjectGuid const& guid) Transport* Map::GetTransport(ObjectGuid const& guid) { - if (!guid.IsMOTransport()) + if (!guid.IsTransport()) return nullptr; GameObject* go = GetGameObject(guid); return go ? go->ToTransport() : nullptr; } +MapTransport* Map::GetMapTransport(ObjectGuid const& guid) +{ + if (!guid.IsTransport()) + return nullptr; + + GameObject* go = GetGameObject(guid); + return go ? go->ToMapTransport() : nullptr; +} + void Map::UpdateIteratorBack(Player* player) { if (m_mapRefIter == player->GetMapRef()) diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 4fcb3e31c86..fe69303168d 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -47,6 +47,7 @@ class InstanceMap; class InstanceSave; class InstanceScript; class MapInstanced; +class MapTransport; class Object; class PhaseShift; class Player; @@ -523,6 +524,7 @@ class TC_GAME_API Map : public GridRefManager WorldObject* GetWorldObjectBySpawnId(SpawnObjectType type, ObjectGuid::LowType spawnId) const { return (type == SPAWN_TYPE_GAMEOBJECT) ? reinterpret_cast(GetGameObjectBySpawnId(spawnId)) : reinterpret_cast(GetCreatureBySpawnId(spawnId)); } Pet* GetPet(ObjectGuid const& guid); Transport* GetTransport(ObjectGuid const& guid); + MapTransport* GetMapTransport(ObjectGuid const& guid); MapStoredObjectTypesContainer& GetObjectsStore() { return _objectsStore; } @@ -731,7 +733,7 @@ class TC_GAME_API Map : public GridRefManager ActiveNonPlayers::iterator m_activeNonPlayersIter; // Objects that must update even in inactive grids without activating them - typedef std::set TransportsContainer; + typedef std::set TransportsContainer; TransportsContainer _transports; TransportsContainer::iterator _transportsUpdateIter; diff --git a/src/server/game/Maps/MapScripts.cpp b/src/server/game/Maps/MapScripts.cpp index 0a7b933a0b6..a9862ae1e7d 100644 --- a/src/server/game/Maps/MapScripts.cpp +++ b/src/server/game/Maps/MapScripts.cpp @@ -316,9 +316,6 @@ void Map::ScriptsProcess() case HighGuid::Corpse: source = GetCorpse(step.sourceGUID); break; - case HighGuid::Mo_Transport: - source = GetTransport(step.sourceGUID); - break; default: TC_LOG_ERROR("scripts", "%s source with unsupported high guid %s.", step.script->GetDebugInfo().c_str(), step.sourceGUID.ToString().c_str()); @@ -348,9 +345,6 @@ void Map::ScriptsProcess() case HighGuid::Corpse: target = GetCorpse(step.targetGUID); break; - case HighGuid::Mo_Transport: - target = GetTransport(step.targetGUID); - break; default: TC_LOG_ERROR("scripts", "%s target with unsupported high guid %s.", step.script->GetDebugInfo().c_str(), step.targetGUID.ToString().c_str()); diff --git a/src/server/game/Maps/TransportMgr.cpp b/src/server/game/Maps/TransportMgr.cpp index 90ff79e03d2..1bac0156eb2 100644 --- a/src/server/game/Maps/TransportMgr.cpp +++ b/src/server/game/Maps/TransportMgr.cpp @@ -362,7 +362,7 @@ void TransportMgr::AddPathNodeToTransport(uint32 transportEntry, uint32 timeSeg, animNode.Path[timeSeg] = node; } -Transport* TransportMgr::CreateTransport(uint32 entry, ObjectGuid::LowType guid /*= 0*/, Map* map /*= nullptr*/, uint8 phaseUseFlags /*= 0*/, uint32 phaseId /*= 0*/, uint32 phaseGroupId /*= 0*/) +MapTransport* TransportMgr::CreateTransport(uint32 entry, ObjectGuid::LowType guid /*= 0*/, Map* map /*= nullptr*/, uint8 phaseUseFlags /*= 0*/, uint32 phaseId /*= 0*/, uint32 phaseGroupId /*= 0*/) { // instance case, execute GetGameObjectEntry hook if (map) @@ -384,7 +384,7 @@ Transport* TransportMgr::CreateTransport(uint32 entry, ObjectGuid::LowType guid } // create transport... - Transport* trans = new Transport(); + MapTransport* trans = new MapTransport(); // ...at first waypoint TaxiPathNodeEntry const* startNode = tInfo->keyFrames.begin()->Node; @@ -395,9 +395,9 @@ Transport* TransportMgr::CreateTransport(uint32 entry, ObjectGuid::LowType guid float o = tInfo->keyFrames.begin()->InitialOrientation; // initialize the gameobject base - ObjectGuid::LowType guidLow = guid ? guid : sObjectMgr->GetGenerator().Generate(); + ObjectGuid::LowType guidLow = guid ? guid : ASSERT_NOTNULL(map)->GenerateLowGuid(); - if (!trans->Create(guidLow, entry, mapId, x, y, z, o, 255)) + if (!trans->CreateMapTransport(guidLow, entry, mapId, x, y, z, o, 255)) { delete trans; return nullptr; @@ -418,11 +418,11 @@ Transport* TransportMgr::CreateTransport(uint32 entry, ObjectGuid::LowType guid // use preset map for instances (need to know which instance) trans->SetMap(map ? map : sMapMgr->CreateMap(mapId, nullptr)); if (map && map->IsDungeon()) - trans->m_zoneScript = map->ToInstanceMap()->GetInstanceScript(); + trans->SetZoneScript(); // Passengers will be loaded once a player is near - HashMapHolder::Insert(trans); - trans->GetMap()->AddToMap(trans); + HashMapHolder::Insert(trans); + trans->GetMap()->AddToMap(trans); return trans; } @@ -508,20 +508,63 @@ void TransportMgr::CreateInstanceTransports(Map* map) CreateTransport(*itr, 0, map); } -TransportAnimationEntry const* TransportAnimation::GetAnimNode(uint32 time) const +bool TransportAnimation::GetAnimNode(uint32 time, TransportAnimationEntry const*& curr, TransportAnimationEntry const*& next, float& percPos) const { - auto itr = Path.lower_bound(time); - if (itr != Path.end()) - return itr->second; + if (Path.empty()) + return false; - return nullptr; + for (TransportPathContainer::const_reverse_iterator itr = Path.rbegin(); itr != Path.rend(); ++itr) + if (time >= itr->first) + { + curr = itr->second; + ASSERT(itr != Path.rbegin()); + + --itr; + next = itr->second; + + percPos = float(time - curr->TimeSeg) / float(next->TimeSeg - curr->TimeSeg); + + return true; + } + + return false; } -TransportRotationEntry const* TransportAnimation::GetAnimRotation(uint32 time) const +void TransportAnimation::GetAnimRotation(uint32 time, QuaternionData& curr, QuaternionData& next, float& percRot) const { - auto itr = Rotations.lower_bound(time); - if (itr != Rotations.end()) - return itr->second; + if (Rotations.empty()) + { + curr = QuaternionData(0.0f, 0.0f, 0.0f, 1.0f); + next = QuaternionData(0.0f, 0.0f, 0.0f, 1.0f); + percRot = 0.0f; + return; + } - return nullptr; + for (TransportPathRotationContainer::const_reverse_iterator itr = Rotations.rbegin(); itr != Rotations.rend(); ++itr) + if (time >= itr->first) + { + uint32 currSeg = itr->second->TimeSeg; + uint32 nextSeg = 0; + + curr = QuaternionData(itr->second->X, itr->second->Y, itr->second->Z, itr->second->W); + if (itr != Rotations.rbegin()) + { + --itr; + next = QuaternionData(itr->second->X, itr->second->Y, itr->second->Z, itr->second->W); + nextSeg = itr->second->TimeSeg; + } + else + { + next = QuaternionData(Rotations.begin()->second->X, Rotations.begin()->second->Y, Rotations.begin()->second->Z, Rotations.begin()->second->W); + nextSeg = this->TotalTime; + } + + percRot = float(time - currSeg) / float(nextSeg - currSeg); + + return; + } + + curr = QuaternionData(0.0f, 0.0f, 0.0f, 1.0f); + next = QuaternionData(0.0f, 0.0f, 0.0f, 1.0f); + percRot = 0.0f; } diff --git a/src/server/game/Maps/TransportMgr.h b/src/server/game/Maps/TransportMgr.h index 96f71ea659d..2989eb38212 100644 --- a/src/server/game/Maps/TransportMgr.h +++ b/src/server/game/Maps/TransportMgr.h @@ -25,7 +25,7 @@ struct KeyFrame; struct GameObjectTemplate; struct TransportTemplate; -class Transport; +class MapTransport; class Map; namespace Movement @@ -94,8 +94,8 @@ struct TC_GAME_API TransportAnimation TransportPathRotationContainer Rotations; uint32 TotalTime; - TransportAnimationEntry const* GetAnimNode(uint32 time) const; - TransportRotationEntry const* GetAnimRotation(uint32 time) const; + bool GetAnimNode(uint32 time, TransportAnimationEntry const*& curr, TransportAnimationEntry const*& next, float& percPos) const; + void GetAnimRotation(uint32 time, QuaternionData& curr, QuaternionData& next, float& percRot) const; }; typedef std::map TransportAnimationContainer; @@ -112,7 +112,7 @@ class TC_GAME_API TransportMgr void LoadTransportAnimationAndRotation(); // Creates a transport using given GameObject template entry - Transport* CreateTransport(uint32 entry, ObjectGuid::LowType guid = 0, Map* map = nullptr, uint8 phaseUseFlags = 0, uint32 phaseId = 0, uint32 phaseGroupId = 0); + MapTransport* CreateTransport(uint32 entry, ObjectGuid::LowType guid = 0, Map* map = nullptr, uint8 phaseUseFlags = 0, uint32 phaseId = 0, uint32 phaseGroupId = 0); // Spawns all continent transports, used at core startup void SpawnContinentTransports(); diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 26aba208b73..b4e079f60ff 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -1815,11 +1815,11 @@ enum GOState : uint8 GO_STATE_ACTIVE = 0, // show in world as used and not reset (closed door open) GO_STATE_READY = 1, // show in world as ready (closed door close) GO_STATE_ACTIVE_ALTERNATIVE = 2, // show in world as used in alt way and not reset (closed door open by cannon fire) - GO_STATE_TRANSPORT_ACTIVE = 24, - GO_STATE_TRANSPORT_STOPPED = 25 + GO_STATE_TRANSPORT_ACTIVE = 24, // transport exclusive: transport may always move + GO_STATE_TRANSPORT_STOPPED = 25 // transport exclusive: transport is going to stop at provided stop frame }; -#define MAX_GO_STATE 3 +#define MAX_GO_STATE 3 #define MAX_GO_STATE_TRANSPORT_STOP_FRAMES 9 enum GameObjectDestructibleState diff --git a/src/server/game/Pools/PoolMgr.cpp b/src/server/game/Pools/PoolMgr.cpp index 4f6d9e602b7..3a63cc8d061 100644 --- a/src/server/game/Pools/PoolMgr.cpp +++ b/src/server/game/Pools/PoolMgr.cpp @@ -22,6 +22,7 @@ #include "Log.h" #include "MapManager.h" #include "ObjectMgr.h" +#include "Transport.h" //////////////////////////////////////////////////////////// // template class ActivePoolData @@ -410,7 +411,11 @@ void PoolGroup::Spawn1Object(PoolObject* obj) // We use current coords to unspawn, not spawn coords since creature can have changed grid if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint)) { - GameObject* pGameobject = new GameObject; + GameObject* pGameobject = nullptr; + if (sObjectMgr->GetGameObjectTypeByEntry(data->id) == GAMEOBJECT_TYPE_TRANSPORT) + pGameobject = new Transport(); + else + pGameobject = new GameObject(); //TC_LOG_DEBUG("pool", "Spawning gameobject %u", guid); if (!pGameobject->LoadFromDB(obj->guid, map, false)) { diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 3b8967ed334..4dc4fcc2842 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -58,6 +58,7 @@ #include "SpellMgr.h" #include "TemporarySummon.h" #include "Totem.h" +#include "Transport.h" #include "Unit.h" #include "UpdateMask.h" #include "Util.h" @@ -3166,7 +3167,11 @@ void Spell::EffectSummonObjectWild(SpellEffIndex effIndex) uint32 gameobject_id = m_spellInfo->Effects[effIndex].MiscValue; - GameObject* pGameObj = new GameObject; + GameObject* pGameObj = nullptr; + if (sObjectMgr->GetGameObjectTypeByEntry(gameobject_id) == GAMEOBJECT_TYPE_TRANSPORT) + pGameObj = new Transport(); + else + pGameObj = new GameObject(); WorldObject* target = focusObject; if (!target) @@ -4144,7 +4149,11 @@ void Spell::EffectSummonObject(SpellEffIndex effIndex) m_caster->m_ObjectSlot[slot].Clear(); } - GameObject* go = new GameObject(); + GameObject* go = nullptr; + if (sObjectMgr->GetGameObjectTypeByEntry(go_id) == GAMEOBJECT_TYPE_TRANSPORT) + go = new Transport(); + else + go = new GameObject(); float x, y, z; // If dest location if present @@ -4870,12 +4879,24 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) QuaternionData rot = QuaternionData::fromEulerAnglesZYX(fo, 0.f, 0.f); - GameObject* pGameObj = new GameObject; - - if (!pGameObj->Create(cMap->GenerateLowGuid(), name_id, cMap, 0, Position(fx, fy, fz, m_caster->GetOrientation()), rot, 255, GO_STATE_READY)) + GameObject* pGameObj = nullptr; + if (goinfo->type == GAMEOBJECT_TYPE_TRANSPORT) { - delete pGameObj; - return; + pGameObj = new Transport(); + if (!pGameObj->Create(cMap->GenerateLowGuid(), name_id, cMap, 0, Position(fx, fy, fz, m_caster->GetOrientation()), rot, 255, GO_STATE_READY)) + { + delete pGameObj; + return; + } + } + else + { + pGameObj = new GameObject(); + if (!pGameObj->Create(cMap->GenerateLowGuid(), name_id, cMap, 0, Position(fx, fy, fz, m_caster->GetOrientation()), rot, 255, GO_STATE_READY)) + { + delete pGameObj; + return; + } } PhasingHandler::InheritPhaseShift(pGameObj, m_caster); diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index e30f481cc4d..b31e4f31947 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -1425,7 +1425,7 @@ public: static bool HandleDebugTransportCommand(ChatHandler* handler, char const* args) { - Transport* transport = handler->GetSession()->GetPlayer()->GetTransport(); + MapTransport* transport = handler->GetSession()->GetPlayer()->GetMapTransport(); if (!transport) return false; diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp index f4354e6b741..8b0f00f5dae 100644 --- a/src/server/scripts/Commands/cs_gobject.cpp +++ b/src/server/scripts/Commands/cs_gobject.cpp @@ -37,6 +37,7 @@ EndScriptData */ #include "Player.h" #include "PoolMgr.h" #include "RBAC.h" +#include "Transport.h" #include "WorldSession.h" // definitions are over in cs_npc.cpp @@ -151,8 +152,18 @@ public: Player* player = handler->GetSession()->GetPlayer(); Map* map = player->GetMap(); - GameObject* object = new GameObject; - ObjectGuid::LowType guidLow = map->GenerateLowGuid(); + GameObject* object = nullptr; + ObjectGuid::LowType guidLow = ObjectGuid::Empty; + if (objectInfo->type == GAMEOBJECT_TYPE_TRANSPORT) + { + object = new Transport(); + ObjectGuid::LowType guidLow = map->GenerateLowGuid(); + } + else + { + object = new GameObject(); + ObjectGuid::LowType guidLow = map->GenerateLowGuid(); + } if (!object->Create(guidLow, objectInfo->entry, map, 0, *player, QuaternionData(), 255, GO_STATE_READY)) { @@ -176,7 +187,10 @@ public: // this is required to avoid weird behavior and memory leaks delete object; - object = new GameObject(); + if (objectInfo->type == GAMEOBJECT_TYPE_TRANSPORT) + object = new Transport(); + else + object = new GameObject(); // this will generate a new guid if the object is in an instance if (!object->LoadFromDB(guidLow, map, true)) { diff --git a/src/server/scripts/Commands/cs_group.cpp b/src/server/scripts/Commands/cs_group.cpp index 83b5885437c..2372bfcfcf9 100644 --- a/src/server/scripts/Commands/cs_group.cpp +++ b/src/server/scripts/Commands/cs_group.cpp @@ -154,8 +154,8 @@ public: // before GM float x, y, z; - gmPlayer->GetClosePoint(x, y, z, player->GetCombatReach()); - player->TeleportTo(gmPlayer->GetMapId(), x, y, z, player->GetOrientation()); + gmPlayer->GetPosition(x, y, z); + player->TeleportTo(gmPlayer->GetMapId(), x, y, z, player->GetOrientation(), 0, gmPlayer->GetTransport()); } return true; diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index d64159209ac..2f41affcc33 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -281,13 +281,18 @@ public: areaId, (areaEntry ? areaEntry->area_name : unknown), object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), object->GetOrientation()); if (Transport* transport = object->GetTransport()) - handler->PSendSysMessage(LANG_TRANSPORT_POSITION, - transport->GetGOInfo()->moTransport.mapID, object->GetTransOffsetX(), object->GetTransOffsetY(), object->GetTransOffsetZ(), object->GetTransOffsetO(), - transport->GetEntry(), transport->GetName().c_str()); - handler->PSendSysMessage(LANG_GRID_POSITION, - cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), object->GetInstanceId(), - zoneX, zoneY, groundZ, floorZ, haveMap, haveVMap, haveMMap); + { + int32 transMapID = -1; + if (transport->GetGoType() == GAMEOBJECT_TYPE_MO_TRANSPORT) + transMapID = transport->GetGOInfo()->moTransport.mapID; + handler->PSendSysMessage(LANG_TRANSPORT_POSITION, + transMapID, object->GetTransOffsetX(), object->GetTransOffsetY(), object->GetTransOffsetZ(), object->GetTransOffsetO(), + transport->GetEntry(), transport->GetName().c_str()); + handler->PSendSysMessage(LANG_GRID_POSITION, + cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), object->GetInstanceId(), + zoneX, zoneY, groundZ, floorZ, haveMap, haveVMap, haveMMap); + } LiquidData liquidStatus; ZLiquidStatus status = map->GetLiquidStatus(object->GetPhaseShift(), object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), MAP_ALL_LIQUIDS, &liquidStatus); @@ -449,9 +454,9 @@ public: // to point to see at target with same orientation float x, y, z; - target->GetContactPoint(_player, x, y, z); + target->GetPosition(x, y, z); - _player->TeleportTo(target->GetMapId(), x, y, z, _player->GetAngle(target), TELE_TO_GM_MODE); + _player->TeleportTo(target->GetMapId(), x, y, z, _player->GetAngle(target), TELE_TO_GM_MODE, target->GetTransport()); PhasingHandler::InheritPhaseShift(_player, target); _player->UpdateObjectVisibility(); } @@ -581,8 +586,8 @@ public: // before GM float x, y, z; - _player->GetClosePoint(x, y, z, target->GetCombatReach()); - target->TeleportTo(_player->GetMapId(), x, y, z, target->GetOrientation()); + _player->GetPosition(x, y, z); + target->TeleportTo(_player->GetMapId(), x, y, z, target->GetOrientation(), 0, _player->GetTransport()); PhasingHandler::InheritPhaseShift(target, handler->GetSession()->GetPlayer()); target->UpdateObjectVisibility(); } diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index 97d69d1b16c..71b93c7dfec 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -347,7 +347,7 @@ public: Player* chr = handler->GetSession()->GetPlayer(); Map* map = chr->GetMap(); - if (Transport* trans = chr->GetTransport()) + if (MapTransport* trans = chr->GetMapTransport()) { ObjectGuid::LowType guid = map->GenerateLowGuid(); CreatureData& data = sObjectMgr->NewOrExistCreatureData(guid); diff --git a/src/server/scripts/Kalimdor/HallsOfOrigination/halls_of_origination.cpp b/src/server/scripts/Kalimdor/HallsOfOrigination/halls_of_origination.cpp index f6dce99fed7..8b9dec9560a 100644 --- a/src/server/scripts/Kalimdor/HallsOfOrigination/halls_of_origination.cpp +++ b/src/server/scripts/Kalimdor/HallsOfOrigination/halls_of_origination.cpp @@ -26,6 +26,7 @@ #include "SpellAuraEffects.h" #include "SpellScript.h" #include "TemporarySummon.h" +#include "Transport.h" #include "halls_of_origination.h" enum Spells @@ -103,11 +104,10 @@ struct go_hoo_the_makers_lift_controller : public GameObjectAI return true; // Handle elevator: gossip item index => stopFrame (floor index). - GameObject* elevator = instance->GetGameObject(DATA_LIFT_OF_THE_MAKERS); + Transport* elevator = ObjectAccessor::GetTransportOnMap(*me, instance->GetGuidData(DATA_LIFT_OF_THE_MAKERS)); if (!elevator) return true; - elevator->SetTransportState(GO_STATE_TRANSPORT_ACTIVE, action); elevator->SetTransportState(GO_STATE_TRANSPORT_STOPPED, action); return true; diff --git a/src/server/scripts/Kalimdor/HallsOfOrigination/instance_halls_of_origination.cpp b/src/server/scripts/Kalimdor/HallsOfOrigination/instance_halls_of_origination.cpp index 2c9ddc4b77e..36279d6d13a 100644 --- a/src/server/scripts/Kalimdor/HallsOfOrigination/instance_halls_of_origination.cpp +++ b/src/server/scripts/Kalimdor/HallsOfOrigination/instance_halls_of_origination.cpp @@ -126,7 +126,7 @@ class instance_halls_of_origination : public InstanceMapScript UpdateTransitDevice(go); break; case GO_LIFT_OF_THE_MAKERS: - go->SetTransportState(GO_STATE_TRANSPORT_STOPPED, 0); + //go->SetTransportState(GO_STATE_TRANSPORT_STOPPED, 0); break; case GO_VAULT_OF_LIGHTS_DOOR: if (_vaultOfLightState != NOT_STARTED) diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp index 231580b3edd..886f1b35a3a 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp @@ -1126,7 +1126,7 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript me->RemoveAurasDueToSpell(SPELL_HARVEST_SOUL); if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) Talk(SAY_JAINA_ESCAPE_9); - if (Transport* gunship = ObjectAccessor::GetTransport(*me, _instance->GetGuidData(DATA_GUNSHIP))) + if (MapTransport* gunship = ObjectAccessor::GetMapTransportOnMap(*me, _instance->GetGuidData(DATA_GUNSHIP))) gunship->EnableMovement(true); _instance->SetBossState(DATA_THE_LICH_KING_ESCAPE, DONE); break; @@ -1198,7 +1198,7 @@ class npc_the_lich_king_escape_hor : public CreatureScript if (Creature* target = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_ESCAPE_LEADER))) DoCast(target, SPELL_HARVEST_SOUL); - if (Transport* gunship = ObjectAccessor::GetTransport(*me, _instance->GetGuidData(DATA_GUNSHIP))) + if (MapTransport* gunship = ObjectAccessor::GetMapTransportOnMap(*me, _instance->GetGuidData(DATA_GUNSHIP))) gunship->EnableMovement(true); break; default: diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp index d62e7703f0f..f3dd6cbf787 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp @@ -391,7 +391,7 @@ class instance_halls_of_reflection : public InstanceMapScript _teamInInstance = player->GetTeam(); } - if (Transport* gunship = sTransportMgr->CreateTransport(_teamInInstance == HORDE ? GO_ORGRIMS_HAMMER : GO_THE_SKYBREAKER, 0, instance)) + if (MapTransport* gunship = sTransportMgr->CreateTransport(_teamInInstance == HORDE ? GO_ORGRIMS_HAMMER : GO_THE_SKYBREAKER, 0, instance)) gunship->EnableMovement(GetBossState(DATA_THE_LICH_KING_ESCAPE) == DONE); } @@ -637,7 +637,7 @@ class instance_halls_of_reflection : public InstanceMapScript if (GetBossState(DATA_THE_LICH_KING_ESCAPE) == DONE) break; - if (Transport* gunship = instance->GetTransport(GunshipGUID)) + if (MapTransport* gunship = instance->GetMapTransport(GunshipGUID)) gunship->EnableMovement(false); if (Creature* jainaOrSylvanas = instance->GetCreature(JainaOrSylvanasEscapeGUID)) @@ -655,7 +655,7 @@ class instance_halls_of_reflection : public InstanceMapScript } break; case EVENT_GUNSHIP_ARRIVAL_2: - if (Transport* gunship = instance->GetTransport(GunshipGUID)) + if (MapTransport* gunship = instance->GetMapTransport(GunshipGUID)) gunship->EnableMovement(false); for (ObjectGuid guid : GunshipStairGUIDs) diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp index fe5f7c1570d..70015eb2e2f 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp @@ -384,7 +384,7 @@ public: ResetSlots(HORDE); } - void SetTransport(Transport* transport) { _transport = transport; } + void SetTransport(MapTransport* transport) { _transport = transport; } void ResetSlots(uint32 team) { @@ -455,7 +455,7 @@ private: return newPos; } - Transport* _transport; + MapTransport* _transport; ObjectGuid _controlledSlots[MAX_SLOTS]; time_t _respawnCooldowns[MAX_SLOTS]; Position const* _spawnPoint; @@ -497,7 +497,7 @@ public: _caster->CastSpell(_caster, _spellId, true); _caster->GetTransport()->AddObjectToRemoveList(); - if (Transport* go = HashMapHolder::Find(_otherTransport)) + if (MapTransport* go = ObjectAccessor::GetMapTransport(_otherTransport)) go->AddObjectToRemoveList(); return true; @@ -776,10 +776,10 @@ class npc_gunship : public CreatureScript if (isVictory) { - if (Transport* otherTransport = HashMapHolder::Find(instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE))) - otherTransport->EnableMovement(true); + if (MapTransport* trans = ObjectAccessor::GetMapTransport(instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE))) + trans->EnableMovement(true); - me->GetTransport()->EnableMovement(true); + me->GetMapTransport()->EnableMovement(true); if (Creature* ship = me->FindNearestCreature(_teamInInstance == HORDE ? NPC_ORGRIMS_HAMMER : NPC_THE_SKYBREAKER, 200.0f)) { @@ -855,7 +855,7 @@ class npc_high_overlord_saurfang_igb : public CreatureScript _instance(creature->GetInstanceScript()) { _controller.ResetSlots(HORDE); - _controller.SetTransport(creature->GetTransport()); + _controller.SetTransport(creature->GetMapTransport()); me->setRegeneratingHealth(false); me->m_CombatDistance = 70.0f; _firstMageCooldown = time(nullptr) + 60; @@ -970,7 +970,7 @@ class npc_high_overlord_saurfang_igb : public CreatureScript bool GossipSelect(Player* /*player*/, uint32 /*menuId*/, uint32 /*gossipListId*/) override { me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - me->GetTransport()->EnableMovement(true); + me->GetMapTransport()->EnableMovement(true); _events.SetPhase(PHASE_INTRO); _events.ScheduleEvent(EVENT_INTRO_H_1, 5000, 0, PHASE_INTRO); _events.ScheduleEvent(EVENT_INTRO_H_2, 16000, 0, PHASE_INTRO); @@ -1038,10 +1038,10 @@ class npc_high_overlord_saurfang_igb : public CreatureScript _controller.SummonCreatures(SLOT_MAGE_1, SLOT_MAGE_2); _controller.SummonCreatures(SLOT_MARINE_1, Is25ManRaid() ? SLOT_MARINE_4 : SLOT_MARINE_2); _controller.SummonCreatures(SLOT_SERGEANT_1, Is25ManRaid() ? SLOT_SERGEANT_2 : SLOT_SERGEANT_1); - if (Transport* orgrimsHammer = me->GetTransport()) + if (MapTransport* orgrimsHammer = me->GetMapTransport()) orgrimsHammer->SummonPassenger(NPC_TELEPORT_PORTAL, OrgrimsHammerTeleportPortal, TEMPSUMMON_TIMED_DESPAWN, nullptr, 21000); - if (Transport* skybreaker = HashMapHolder::Find(_instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE))) + if (MapTransport* skybreaker = ObjectAccessor::GetMapTransport(_instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE))) skybreaker->SummonPassenger(NPC_TELEPORT_EXIT, SkybreakerTeleportExit, TEMPSUMMON_TIMED_DESPAWN, nullptr, 23000); _events.ScheduleEvent(EVENT_ADDS_BOARD_YELL, 6000); @@ -1124,7 +1124,7 @@ class npc_muradin_bronzebeard_igb : public CreatureScript _instance(creature->GetInstanceScript()) { _controller.ResetSlots(ALLIANCE); - _controller.SetTransport(creature->GetTransport()); + _controller.SetTransport(creature->GetMapTransport()); me->setRegeneratingHealth(false); me->m_CombatDistance = 70.0f; _firstMageCooldown = time(nullptr) + 60; @@ -1239,7 +1239,7 @@ class npc_muradin_bronzebeard_igb : public CreatureScript bool GossipSelect(Player* /*player*/, uint32 /*menuId*/, uint32 /*gossipListId*/) override { me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - me->GetTransport()->EnableMovement(true); + me->GetMapTransport()->EnableMovement(true); _events.SetPhase(PHASE_INTRO); _events.ScheduleEvent(EVENT_INTRO_A_1, 5000); _events.ScheduleEvent(EVENT_INTRO_A_2, 10000, 0, PHASE_INTRO); @@ -1311,10 +1311,10 @@ class npc_muradin_bronzebeard_igb : public CreatureScript _controller.SummonCreatures(SLOT_MAGE_1, SLOT_MAGE_2); _controller.SummonCreatures(SLOT_MARINE_1, Is25ManRaid() ? SLOT_MARINE_4 : SLOT_MARINE_2); _controller.SummonCreatures(SLOT_SERGEANT_1, Is25ManRaid() ? SLOT_SERGEANT_2 : SLOT_SERGEANT_1); - if (Transport* skybreaker = me->GetTransport()) + if (MapTransport* skybreaker = me->GetMapTransport()) skybreaker->SummonPassenger(NPC_TELEPORT_PORTAL, SkybreakerTeleportPortal, TEMPSUMMON_TIMED_DESPAWN, nullptr, 21000); - if (Transport* orgrimsHammer = HashMapHolder::Find(_instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE))) + if (MapTransport* orgrimsHammer = ObjectAccessor::GetMapTransport(_instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE))) orgrimsHammer->SummonPassenger(NPC_TELEPORT_EXIT, OrgrimsHammerTeleportExit, TEMPSUMMON_TIMED_DESPAWN, nullptr, 23000); _events.ScheduleEvent(EVENT_ADDS_BOARD_YELL, 6000); @@ -1460,7 +1460,7 @@ struct npc_gunship_boarding_addAI : public gunship_npc_AI if (!myTransport) return; - if (Transport* destTransport = HashMapHolder::Find(Instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE))) + if (MapTransport* destTransport = ObjectAccessor::GetMapTransport(Instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE))) destTransport->CalculatePassengerPosition(x, y, z, &o); float angle = frand(0, float(M_PI) * 2.0f); @@ -2336,7 +2336,7 @@ class spell_igb_gunship_fall_teleport : public SpellScriptLoader void SelectTransport(WorldObject*& target) { if (InstanceScript* instance = target->GetInstanceScript()) - target = HashMapHolder::Find(instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE)); + target = ObjectAccessor::GetMapTransport(instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE)); } void RelocateDest(SpellEffIndex /*effIndex*/) diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp index f3d44f6658e..b7c6360e107 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp @@ -508,7 +508,8 @@ class instance_icecrown_citadel : public InstanceMapScript case GO_LADY_DEATHWHISPER_ELEVATOR: LadyDeathwisperElevatorGUID = go->GetGUID(); if (GetBossState(DATA_LADY_DEATHWHISPER) == DONE) - go->SetTransportState(GO_STATE_TRANSPORT_ACTIVE); + if (Transport* transport = go->ToTransport()) + transport->SetTransportState(GO_STATE_TRANSPORT_ACTIVE); break; case GO_THE_SKYBREAKER_H: case GO_ORGRIMS_HAMMER_A: @@ -824,7 +825,7 @@ class instance_icecrown_citadel : public InstanceMapScript if (GameObject* teleporter = instance->GetGameObject(TeleporterRampartsGUID)) SetTeleporterState(teleporter, true); - if (GameObject* elevator = instance->GetGameObject(LadyDeathwisperElevatorGUID)) + if (Transport* elevator = instance->GetTransport(LadyDeathwisperElevatorGUID)) elevator->SetTransportState(GO_STATE_TRANSPORT_ACTIVE); SpawnGunship(); @@ -1018,7 +1019,7 @@ class instance_icecrown_citadel : public InstanceMapScript { SetBossState(DATA_ICECROWN_GUNSHIP_BATTLE, NOT_STARTED); uint32 gunshipEntry = TeamInInstance == HORDE ? GO_ORGRIMS_HAMMER_H : GO_THE_SKYBREAKER_A; - if (Transport* gunship = sTransportMgr->CreateTransport(gunshipEntry, 0, instance)) + if (MapTransport* gunship = sTransportMgr->CreateTransport(gunshipEntry, 0, instance)) GunshipGUID = gunship->GetGUID(); } } @@ -1403,7 +1404,7 @@ class instance_icecrown_citadel : public InstanceMapScript case EVENT_PLAYERS_GUNSHIP_SPAWN: case EVENT_PLAYERS_GUNSHIP_COMBAT: if (GameObject* go = source->ToGameObject()) - if (Transport* transport = go->ToTransport()) + if (MapTransport* transport = go->ToMapTransport()) transport->EnableMovement(false); break; case EVENT_PLAYERS_GUNSHIP_SAURFANG: @@ -1411,7 +1412,7 @@ class instance_icecrown_citadel : public InstanceMapScript if (Creature* captain = source->FindNearestCreature(TeamInInstance == HORDE ? NPC_IGB_HIGH_OVERLORD_SAURFANG : NPC_IGB_MURADIN_BRONZEBEARD, 100.0f)) captain->AI()->DoAction(ACTION_EXIT_SHIP); if (GameObject* go = source->ToGameObject()) - if (Transport* transport = go->ToTransport()) + if (MapTransport* transport = go->ToMapTransport()) transport->EnableMovement(false); break; }