diff options
| author | Shauren <shauren.trinity@gmail.com> | 2013-10-16 18:37:29 +0200 |
|---|---|---|
| committer | Shauren <shauren.trinity@gmail.com> | 2013-10-16 18:37:29 +0200 |
| commit | ce55647c415b710c6b440d96c2f26ebbc06c1d6e (patch) | |
| tree | 515af245894c37aa76c590b6e602eb4e398d68be /src/server/game/Entities | |
| parent | 53cc37bceca9ce7de1b9839251ccc809ebf768b6 (diff) | |
Core/Transports
* Rewritten path generation, now uses splines - timers are a lot more accurate now
* Implemented stopping transports
* Implemented spawning transports in instances
* Implemented spawning gameobjects as transport passengers
* Transport passengers are now stored in creature/gameobject table using gameobject_template.data6 from transport's template as map id
Diffstat (limited to 'src/server/game/Entities')
| -rw-r--r-- | src/server/game/Entities/Creature/Creature.cpp | 26 | ||||
| -rw-r--r-- | src/server/game/Entities/Creature/Creature.h | 31 | ||||
| -rw-r--r-- | src/server/game/Entities/GameObject/GameObject.cpp | 94 | ||||
| -rw-r--r-- | src/server/game/Entities/GameObject/GameObject.h | 25 | ||||
| -rw-r--r-- | src/server/game/Entities/Object/Object.cpp | 20 | ||||
| -rw-r--r-- | src/server/game/Entities/Object/Object.h | 48 | ||||
| -rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 24 | ||||
| -rw-r--r-- | src/server/game/Entities/Transport/Transport.cpp | 931 | ||||
| -rw-r--r-- | src/server/game/Entities/Transport/Transport.h | 112 | ||||
| -rw-r--r-- | src/server/game/Entities/Unit/Unit.cpp | 7 | ||||
| -rw-r--r-- | src/server/game/Entities/Unit/Unit.h | 6 |
11 files changed, 627 insertions, 697 deletions
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 4efd78d5932..f68d87de2f4 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -52,7 +52,7 @@ #include "World.h" #include "WorldPacket.h" -// apply implementation of the singletons +#include "Transport.h" TrainerSpell const* TrainerSpellData::Find(uint32 spell_id) const { @@ -141,7 +141,7 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) return true; } -Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapCreature(), +Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), m_lootRecipient(0), m_lootRecipientGroup(0), m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_reactState(REACT_AGGRESSIVE), @@ -934,7 +934,8 @@ void Creature::SaveToDB() return; } - SaveToDB(GetMapId(), data->spawnMask, GetPhaseMask()); + uint32 mapId = GetTransport() ? GetTransport()->GetGOInfo()->moTransport.mapID : GetMapId(); + SaveToDB(mapId, data->spawnMask, GetPhaseMask()); } void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) @@ -973,10 +974,21 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) data.phaseMask = phaseMask; data.displayid = displayId; data.equipmentId = GetCurrentEquipmentId(); - data.posX = GetPositionX(); - data.posY = GetPositionY(); - data.posZ = GetPositionZMinusOffset(); - data.orientation = GetOrientation(); + if (!GetTransport()) + { + data.posX = GetPositionX(); + data.posY = GetPositionY(); + data.posZ = GetPositionZMinusOffset(); + data.orientation = GetOrientation(); + } + else + { + data.posX = GetTransOffsetX(); + data.posY = GetTransOffsetY(); + data.posZ = GetTransOffsetZ(); + data.orientation = GetTransOffsetO(); + } + data.spawntimesecs = m_respawnDelay; // prevent add data integrity problems data.spawndist = GetDefaultMovementType() == IDLE_MOTION_TYPE ? 0.0f : m_respawnradius; diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 3a07d9c101b..a555469da63 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -415,36 +415,7 @@ typedef std::map<uint32, time_t> CreatureSpellCooldowns; #define MAX_VENDOR_ITEMS 150 // Limitation in 3.x.x item count in SMSG_LIST_INVENTORY -enum CreatureCellMoveState -{ - CREATURE_CELL_MOVE_NONE, // not in move list - CREATURE_CELL_MOVE_ACTIVE, // in move list - CREATURE_CELL_MOVE_INACTIVE // in move list but should not move -}; - -class MapCreature -{ - friend class Map; // map for moving creatures - friend class ObjectGridLoader; // grid loader for loading creatures - -protected: - MapCreature() : _moveState(CREATURE_CELL_MOVE_NONE) {} - -private: - Cell _currentCell; - Cell const& GetCurrentCell() const { return _currentCell; } - void SetCurrentCell(Cell const& cell) { _currentCell = cell; } - - CreatureCellMoveState _moveState; - Position _newPosition; - void SetNewCellPosition(float x, float y, float z, float o) - { - _moveState = CREATURE_CELL_MOVE_ACTIVE; - _newPosition.Relocate(x, y, z, o); - } -}; - -class Creature : public Unit, public GridObject<Creature>, public MapCreature +class Creature : public Unit, public GridObject<Creature>, public MapObject { public: diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index eef416b339d..9829b6019de 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -16,6 +16,7 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <G3D/Quat.h> #include "GameObjectAI.h" #include "Battleground.h" #include "CellImpl.h" @@ -32,8 +33,10 @@ #include "SpellMgr.h" #include "UpdateFieldFlags.h" #include "World.h" +#include "Transport.h" -GameObject::GameObject(): WorldObject(false), m_model(NULL), m_goValue(), m_AI(NULL) +GameObject::GameObject() : WorldObject(false), MapObject(), + m_model(NULL), m_goValue(), m_AI(NULL) { m_objectType |= TYPEMASK_GAMEOBJECT; m_objectTypeId = TYPEID_GAMEOBJECT; @@ -100,6 +103,9 @@ void GameObject::CleanupsBeforeDelete(bool /*finalCleanup*/) if (m_uint32Values) // field array can be not exist if GameOBject not loaded RemoveFromOwner(); + + if (GetTransport() && !ToTransport()) + GetTransport()->RemovePassenger(this); } void GameObject::RemoveFromOwner() @@ -169,6 +175,7 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map* map, uint32 phaseMa SetMap(map); Relocate(x, y, z, ang); + m_stationaryPosition.Relocate(x, y, z, ang); if (!IsPositionValid()) { TC_LOG_ERROR(LOG_FILTER_GENERAL, "Gameobject (GUID: %u Entry: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)", guidlow, name_id, x, y); @@ -192,6 +199,9 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map* map, uint32 phaseMa return false; } + if (goinfo->type == GAMEOBJECT_TYPE_TRANSPORT) + m_updateFlag = (m_updateFlag | UPDATEFLAG_TRANSPORT) & ~UPDATEFLAG_POSITION; + Object::_Create(guidlow, goinfo->entry, HIGHGUID_GAMEOBJECT); m_goInfo = goinfo; @@ -238,9 +248,11 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map* map, uint32 phaseMa break; case GAMEOBJECT_TYPE_TRANSPORT: SetUInt32Value(GAMEOBJECT_LEVEL, goinfo->transport.pause); - if (goinfo->transport.startOpen) - SetGoState(GO_STATE_ACTIVE); + SetGoState(goinfo->transport.startOpen ? GO_STATE_ACTIVE : GO_STATE_READY); SetGoAnimProgress(animprogress); + m_goValue.Transport.PathProgress = 0; + m_goValue.Transport.AnimationInfo = sTransportMgr->GetTransportAnimInfo(goinfo->entry); + m_goValue.Transport.CurrentSeg = 0; break; case GAMEOBJECT_TYPE_FISHINGNODE: SetGoAnimProgress(0); @@ -274,18 +286,10 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map* map, uint32 phaseMa void GameObject::Update(uint32 diff) { - if (!AI()) - { - if (!AIM_Initialize()) - TC_LOG_ERROR(LOG_FILTER_GENERAL, "Could not initialize GameObjectAI"); - } else + if (AI()) AI()->UpdateAI(diff); - - if (IS_MO_TRANSPORT_GUID(GetGUID())) - { - //((Transport*)this)->Update(p_time); - return; - } + else if (!AIM_Initialize()) + TC_LOG_ERROR(LOG_FILTER_GENERAL, "Could not initialize GameObjectAI"); switch (m_lootState) { @@ -308,6 +312,38 @@ void GameObject::Update(uint32 diff) m_lootState = GO_READY; break; } + /* TODO: Fix movement in unloaded grid - currently GO will just disappear + case GAMEOBJECT_TYPE_TRANSPORT: + { + if (!m_goValue.Transport.AnimationInfo) + break; + + if (GetGoState() == GO_STATE_READY) + { + m_goValue.Transport.PathProgress += diff; + uint32 timer = m_goValue.Transport.PathProgress % m_goValue.Transport.AnimationInfo->TotalTime; + 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 = m_goValue.Transport.AnimationInfo->GetAnimRotation(timer); + 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()); + + sLog->outInfo(LOG_FILTER_GENERAL, "Src: %s Dest: %s", src.toString().c_str(), pos.toString().c_str()); + + GetMap()->GameObjectRelocation(this, pos.x, pos.y, pos.z, GetOrientation()); + } + } + break; + } + */ case GAMEOBJECT_TYPE_FISHINGNODE: { // fishing code (bobber ready) @@ -1182,10 +1218,12 @@ void GameObject::Use(Unit* user) if (itr->second) { if (Player* ChairUser = ObjectAccessor::FindPlayer(itr->second)) + { if (ChairUser->IsSitState() && ChairUser->getStandState() != UNIT_STAND_STATE_SIT && ChairUser->GetExactDist2d(x_i, y_i) < 0.1f) continue; // This seat is already occupied by ChairUser. NOTE: Not sure if the ChairUser->getStandState() != UNIT_STAND_STATE_SIT check is required. else itr->second = 0; // This seat is unoccupied. + } else itr->second = 0; // The seat may of had an occupant, but they're offline. } @@ -2102,6 +2140,7 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t if (index == GAMEOBJECT_DYNAMIC) { uint16 dynFlags = 0; + int16 pathProgress = -1; switch (GetGoType()) { case GAMEOBJECT_TYPE_CHEST: @@ -2115,12 +2154,13 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t if (ActivateToQuest(target)) dynFlags |= GO_DYNFLAG_LO_SPARKLE; break; - default: + case GAMEOBJECT_TYPE_MO_TRANSPORT: + pathProgress = int16(float(m_goValue.Transport.PathProgress) / float(GetUInt32Value(GAMEOBJECT_LEVEL)) * 65535.0f); break; } fieldBuffer << uint16(dynFlags); - fieldBuffer << uint16(-1); + fieldBuffer << int16(pathProgress); } else if (index == GAMEOBJECT_FLAGS) { @@ -2140,3 +2180,25 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t updateMask.AppendToPacket(data); data->append(fieldBuffer); } + +void GameObject::GetRespawnPosition(float &x, float &y, float &z, float* ori /* = NULL*/) const +{ + if (m_DBTableGuid) + { + if (GameObjectData const* data = sObjectMgr->GetGOData(GetDBTableGUIDLow())) + { + x = data->posX; + y = data->posY; + z = data->posZ; + if (ori) + *ori = data->orientation; + return; + } + } + + x = GetPositionX(); + y = GetPositionY(); + z = GetPositionZ(); + if (ori) + *ori = GetOrientation(); +} diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 3bddac81ee9..de92257893b 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -28,6 +28,7 @@ class GameObjectAI; class Group; +class Transport; #define MAX_GAMEOBJECT_QUEST_ITEMS 6 @@ -229,6 +230,7 @@ struct GameObjectTemplate uint32 transportPhysics; //5 uint32 mapID; //6 uint32 worldState1; //7 + uint32 canBeStopped; //8 } moTransport; //16 GAMEOBJECT_TYPE_DUELFLAG - empty //17 GAMEOBJECT_TYPE_FISHINGNODE - empty @@ -539,9 +541,17 @@ struct GameObjectTemplate typedef UNORDERED_MAP<uint32, GameObjectTemplate> GameObjectTemplateContainer; class OPvPCapturePoint; +struct TransportAnimation; union GameObjectValue { + //11 GAMEOBJECT_TYPE_TRANSPORT + struct + { + uint32 PathProgress; + TransportAnimation const* AnimationInfo; + uint32 CurrentSeg; + } Transport; //25 GAMEOBJECT_TYPE_FISHINGHOLE struct { @@ -617,7 +627,7 @@ class GameObjectModel; // 5 sec for bobber catch #define FISHING_BOBBER_READY_TIME 5 -class GameObject : public WorldObject, public GridObject<GameObject> +class GameObject : public WorldObject, public GridObject<GameObject>, public MapObject { public: explicit GameObject(); @@ -811,8 +821,19 @@ class GameObject : public WorldObject, public GridObject<GameObject> uint32 GetDisplayId() const { return GetUInt32Value(GAMEOBJECT_DISPLAYID); } GameObjectModel* m_model; + void GetRespawnPosition(float &x, float &y, float &z, float* ori = NULL) const; + + Transport* ToTransport() { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT) return reinterpret_cast<Transport*>(this); else return NULL; } + Transport const* ToTransport() const { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT) return reinterpret_cast<Transport const*>(this); else return NULL; } + + float GetStationaryX() const { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetPositionX(); return GetPositionX(); } + float GetStationaryY() const { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetPositionY(); return GetPositionY(); } + float GetStationaryZ() const { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetPositionZ(); return GetPositionZ(); } + float GetStationaryO() const { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetOrientation(); return GetOrientation(); } + protected: bool AIM_Initialize(); + void UpdateModel(); // updates model in case displayId were changed uint32 m_spellId; time_t m_respawnTime; // (secs) time of next respawn (or despawn if GO have owner()), uint32 m_respawnDelayTime; // (secs) if 0 then current GO state no dependent from timer @@ -835,6 +856,7 @@ class GameObject : public WorldObject, public GridObject<GameObject> GameObjectValue m_goValue; uint64 m_rotation; + Position m_stationaryPosition; uint64 m_lootRecipient; uint32 m_lootRecipientGroup; @@ -842,7 +864,6 @@ class GameObject : public WorldObject, public GridObject<GameObject> private: void RemoveFromOwner(); void SwitchDoorOrButton(bool activate, bool alternative = false); - void UpdateModel(); // updates model in case displayId were changed //! Object distance/size - overridden from Object::_IsWithinDist. Needs to take in account proper GO size. bool _IsWithinDist(WorldObject const* obj, float dist2compare, bool /*is3D*/) const diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 9d77e144c0b..2eb0fd65149 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -219,9 +219,6 @@ void Object::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) c case GAMEOBJECT_TYPE_FLAGDROP: updateType = UPDATETYPE_CREATE_OBJECT2; break; - case GAMEOBJECT_TYPE_TRANSPORT: - flags |= UPDATEFLAG_TRANSPORT; - break; default: break; } @@ -414,13 +411,10 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const // 0x40 if (flags & UPDATEFLAG_STATIONARY_POSITION) { - *data << object->GetPositionX(); - *data << object->GetPositionY(); - if (isType(TYPEMASK_UNIT)) - *data << unit->GetPositionZMinusOffset(); - else - *data << object->GetPositionZ(); - *data << object->GetOrientation(); + *data << object->GetStationaryX(); + *data << object->GetStationaryY(); + *data << object->GetStationaryZ(); + *data << object->GetStationaryO(); } } } @@ -473,7 +467,11 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const // 0x2 if (flags & UPDATEFLAG_TRANSPORT) { - *data << uint32(getMSTime()); // Unknown - getMSTime is wrong. + GameObject const* go = ToGameObject(); + if (go && go->IsTransport()) + *data << uint32(go->GetGOValue()->Transport.PathProgress); + else + *data << uint32(getMSTime()); } // 0x80 diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 66b3a737e0c..3e010fa98a2 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -523,6 +523,35 @@ class FlaggedValuesArray32 T_FLAGS m_flags; }; +enum MapObjectCellMoveState +{ + MAP_OBJECT_CELL_MOVE_NONE, //not in move list + MAP_OBJECT_CELL_MOVE_ACTIVE, //in move list + MAP_OBJECT_CELL_MOVE_INACTIVE, //in move list but should not move +}; + +class MapObject +{ + friend class Map; //map for moving creatures + friend class ObjectGridLoader; //grid loader for loading creatures + + protected: + MapObject() : _moveState(MAP_OBJECT_CELL_MOVE_NONE) {} + + private: + Cell _currentCell; + Cell const& GetCurrentCell() const { return _currentCell; } + void SetCurrentCell(Cell const& cell) { _currentCell = cell; } + + MapObjectCellMoveState _moveState; + Position _newPosition; + void SetNewCellPosition(float x, float y, float z, float o) + { + _moveState = MAP_OBJECT_CELL_MOVE_ACTIVE; + _newPosition.Relocate(x, y, z, o); + } +}; + class WorldObject : public Object, public WorldLocation { protected: @@ -700,16 +729,22 @@ class WorldObject : public Object, public WorldLocation // Transports Transport* GetTransport() const { return m_transport; } - virtual float GetTransOffsetX() const { return 0; } - virtual float GetTransOffsetY() const { return 0; } - virtual float GetTransOffsetZ() const { return 0; } - virtual float GetTransOffsetO() const { return 0; } - virtual uint32 GetTransTime() const { return 0; } - virtual int8 GetTransSeat() const { return -1; } + float GetTransOffsetX() const { return m_movementInfo.transport.pos.GetPositionX(); } + float GetTransOffsetY() const { return m_movementInfo.transport.pos.GetPositionY(); } + float GetTransOffsetZ() const { return m_movementInfo.transport.pos.GetPositionZ(); } + float GetTransOffsetO() const { return m_movementInfo.transport.pos.GetOrientation(); } + uint32 GetTransTime() const { return m_movementInfo.transport.time; } + int8 GetTransSeat() const { return m_movementInfo.transport.seat; } virtual uint64 GetTransGUID() const; void SetTransport(Transport* t) { m_transport = t; } MovementInfo m_movementInfo; + + virtual float GetStationaryX() const { return GetPositionX(); } + virtual float GetStationaryY() const { return GetPositionY(); } + virtual float GetStationaryZ() const { return GetPositionZ(); } + virtual float GetStationaryO() const { return GetOrientation(); } + protected: std::string m_name; bool m_isActive; @@ -739,7 +774,6 @@ class WorldObject : public Object, public WorldLocation uint16 m_notifyflags; uint16 m_executed_notifies; - virtual bool _IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D) const; bool CanNeverSee(WorldObject const* obj) const; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 9ada25b1c81..40dbe6c2fe7 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -17182,17 +17182,15 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) } else { - for (MapManager::TransportSet::iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter) + if (GameObject* go = HashMapHolder<GameObject>::Find(m_movementInfo.transport.guid)) + m_transport = go->ToTransport(); + + if (m_transport) { - if ((*iter)->GetGUIDLow() == transGUID) - { - m_transport = *iter; - m_transport->AddPassenger(this); - mapId = (m_transport->GetMapId()); - break; - } + m_transport->AddPassenger(this); + mapId = m_transport->GetMapId(); } - if (!m_transport) + else { TC_LOG_ERROR(LOG_FILTER_PLAYER, "Player (guidlow %d) have problems with transport guid (%u). Teleport to bind location.", guid, transGUID); @@ -22165,7 +22163,7 @@ inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, T* target, std::set template<> inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, GameObject* target, std::set<Unit*>& /*v*/) { - // Don't update only GAMEOBJECT_TYPE_TRANSPORT (or all transports and destructible buildings?) + // @HACK: This is to prevent objects like deeprun tram from disappearing when player moves far from its spawn point while riding it if ((target->GetGOInfo()->type != GAMEOBJECT_TYPE_TRANSPORT)) s64.insert(target->GetGUID()); } @@ -22217,9 +22215,6 @@ void Player::UpdateVisibilityOf(WorldObject* target) { if (CanSeeOrDetect(target, false, true)) { - //if (target->isType(TYPEMASK_UNIT) && ((Unit*)target)->m_Vehicle) - // UpdateVisibilityOf(((Unit*)target)->m_Vehicle); - target->SendUpdateToPlayer(this); m_clientGUIDs.insert(target->GetGUID()); @@ -22308,9 +22303,6 @@ void Player::UpdateVisibilityOf(T* target, UpdateData& data, std::set<Unit*>& vi { if (CanSeeOrDetect(target, false, true)) { - //if (target->isType(TYPEMASK_UNIT) && ((Unit*)target)->m_Vehicle) - // UpdateVisibilityOf(((Unit*)target)->m_Vehicle, data, visibleNow); - target->BuildCreateUpdateBlockForPlayer(&data, this); UpdateVisibilityOf_helper(m_clientGUIDs, target, visibleNow); diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 23197b40ad0..e6ca93307e7 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -26,166 +26,23 @@ #include "DBCStores.h" #include "World.h" #include "GameObjectAI.h" +#include "Vehicle.h" +#include "MapReference.h" #include "Player.h" -void MapManager::LoadTransports() +Transport::Transport() : GameObject(), + _transportInfo(NULL), _isMoving(true), _pendingStop(false) { - uint32 oldMSTime = getMSTime(); - - QueryResult result = WorldDatabase.Query("SELECT guid, entry, name, period, ScriptName FROM transports"); - - if (!result) - { - TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 transports. DB table `transports` is empty!"); - return; - } - - uint32 count = 0; - - do - { - - Field* fields = result->Fetch(); - uint32 lowguid = fields[0].GetUInt32(); - uint32 entry = fields[1].GetUInt32(); - std::string name = fields[2].GetString(); - uint32 period = fields[3].GetUInt32(); - uint32 scriptId = sObjectMgr->GetScriptId(fields[4].GetCString()); - - GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(entry); - - if (!goinfo) - { - TC_LOG_ERROR(LOG_FILTER_SQL, "Transport ID:%u, Name: %s, will not be loaded, gameobject_template missing", entry, name.c_str()); - continue; - } - - if (goinfo->type != GAMEOBJECT_TYPE_MO_TRANSPORT) - { - TC_LOG_ERROR(LOG_FILTER_SQL, "Transport ID:%u, Name: %s, will not be loaded, gameobject_template type wrong", entry, name.c_str()); - continue; - } - - // TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, "Loading transport %d between %s, %s", entry, name.c_str(), goinfo->name); - - std::set<uint32> mapsUsed; - - Transport* t = new Transport(period, scriptId); - if (!t->GenerateWaypoints(goinfo->moTransport.taxiPathId, mapsUsed)) - // skip transports with empty waypoints list - { - TC_LOG_ERROR(LOG_FILTER_SQL, "Transport (path id %u) path size = 0. Transport ignored, check DBC files or transport GO data0 field.", goinfo->moTransport.taxiPathId); - delete t; - continue; - } - - float x = t->m_WayPoints[0].x; - float y = t->m_WayPoints[0].y; - float z = t->m_WayPoints[0].z; - uint32 mapid = t->m_WayPoints[0].mapid; - float o = 1.0f; - - // creates the Gameobject - if (!t->Create(lowguid, entry, mapid, x, y, z, o, 255, 0)) - { - delete t; - continue; - } - - m_Transports.insert(t); - - for (std::set<uint32>::const_iterator i = mapsUsed.begin(); i != mapsUsed.end(); ++i) - m_TransportsByMap[*i].insert(t); - - //If we someday decide to use the grid to track transports, here: - t->SetMap(sMapMgr->CreateBaseMap(mapid)); - t->AddToWorld(); - - ++count; - } - while (result->NextRow()); - - // check transport data DB integrity - result = WorldDatabase.Query("SELECT gameobject.guid, gameobject.id, transports.name FROM gameobject, transports WHERE gameobject.id = transports.entry"); - if (result) // wrong data found - { - do - { - Field* fields = result->Fetch(); - - uint32 guid = fields[0].GetUInt32(); - uint32 entry = fields[1].GetUInt32(); - std::string name = fields[2].GetString(); - TC_LOG_ERROR(LOG_FILTER_SQL, "Transport %u '%s' have record (GUID: %u) in `gameobject`. Transports must not have any records in `gameobject` or its behavior will be unpredictable/bugged.", entry, name.c_str(), guid); - } - while (result->NextRow()); - } - - TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, ">> Loaded %u transports in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); -} - -void MapManager::LoadTransportNPCs() -{ - uint32 oldMSTime = getMSTime(); - - // 0 1 2 3 4 5 6 7 - QueryResult result = WorldDatabase.Query("SELECT guid, npc_entry, transport_entry, TransOffsetX, TransOffsetY, TransOffsetZ, TransOffsetO, emote FROM creature_transport"); - - if (!result) - { - TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 transport NPCs. DB table `creature_transport` is empty!"); - return; - } - - uint32 count = 0; - - do - { - Field* fields = result->Fetch(); - uint32 guid = fields[0].GetInt32(); - uint32 entry = fields[1].GetInt32(); - uint32 transportEntry = fields[2].GetInt32(); - float tX = fields[3].GetFloat(); - float tY = fields[4].GetFloat(); - float tZ = fields[5].GetFloat(); - float tO = fields[6].GetFloat(); - uint32 anim = fields[7].GetInt32(); - - for (MapManager::TransportSet::iterator itr = m_Transports.begin(); itr != m_Transports.end(); ++itr) - { - if ((*itr)->GetEntry() == transportEntry) - { - (*itr)->AddNPCPassenger(guid, entry, tX, tY, tZ, tO, anim); - break; - } - } - - ++count; - } - while (result->NextRow()); - - TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, ">> Loaded %u transport npcs in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); -} - -Transport::Transport(uint32 period, uint32 script) : GameObject(), m_pathTime(0), m_timer(0), -currenttguid(0), m_period(period), ScriptId(script), m_nextNodeTime(0) -{ - m_updateFlag = (UPDATEFLAG_TRANSPORT | UPDATEFLAG_LOWGUID | UPDATEFLAG_STATIONARY_POSITION | UPDATEFLAG_ROTATION); + m_updateFlag = UPDATEFLAG_TRANSPORT | UPDATEFLAG_LOWGUID | UPDATEFLAG_STATIONARY_POSITION | UPDATEFLAG_ROTATION; } Transport::~Transport() { - for (CreatureSet::iterator itr = m_NPCPassengerSet.begin(); itr != m_NPCPassengerSet.end(); ++itr) - { - (*itr)->SetTransport(NULL); - GetMap()->AddObjectToRemoveList(*itr); - } } -bool Transport::Create(uint32 guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress, uint32 dynflags) +bool Transport::Create(uint32 guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress) { Relocate(x, y, z, ang); - // instance id and phaseMask isn't set to values different from std. if (!IsPositionValid()) { @@ -206,520 +63,510 @@ bool Transport::Create(uint32 guidlow, uint32 entry, uint32 mapid, float x, floa m_goInfo = goinfo; - SetObjectScale(goinfo->size); + TransportTemplate const* tInfo = sTransportMgr->GetTransportTemplate(entry); + if (!tInfo) + { + TC_LOG_ERROR(LOG_FILTER_SQL, "Transport %u (name: %s) will not be created, missing `transport_template` entry.", entry, goinfo->name); + return false; + } + + _transportInfo = tInfo; + // initialize waypoints + _nextFrame = tInfo->keyFrames.begin(); + _currentFrame = _nextFrame++; + _triggeredArrivalEvent = false; + _triggeredDepartureEvent = false; + + m_goValue.Transport.PathProgress = 0; + SetFloatValue(OBJECT_FIELD_SCALE_X, goinfo->size); SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction); SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags); - SetUInt32Value(GAMEOBJECT_LEVEL, m_period); + SetPeriod(tInfo->pathTime); SetEntry(goinfo->entry); - SetDisplayId(goinfo->displayId); - SetGoState(GO_STATE_READY); - SetGoType(GameobjectTypes(goinfo->type)); - + _pendingStop = goinfo->moTransport.canBeStopped != 0; + SetGoType(GAMEOBJECT_TYPE_MO_TRANSPORT); SetGoAnimProgress(animprogress); - if (dynflags) - SetUInt32Value(GAMEOBJECT_DYNAMIC, MAKE_PAIR32(0, dynflags)); - SetName(goinfo->name); - - SetZoneScript(); - + UpdateRotationFields(0.0f, 1.0f); return true; } -struct keyFrame +void Transport::Update(uint32 diff) { - explicit keyFrame(TaxiPathNodeEntry const& _node) : node(&_node), - distSinceStop(-1.0f), distUntilStop(-1.0f), distFromPrev(-1.0f), tFrom(0.0f), tTo(0.0f) - { - } + uint32 const positionUpdateDelay = 200; - TaxiPathNodeEntry const* node; + if (AI()) + AI()->UpdateAI(diff); + else if (!AIM_Initialize()) + TC_LOG_ERROR(LOG_FILTER_TRANSPORTS, "Could not initialize GameObjectAI for Transport"); - float distSinceStop; - float distUntilStop; - float distFromPrev; - float tFrom, tTo; -}; + if (GetKeyFrames().size() <= 1) + return; -bool Transport::GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids) -{ - if (pathid >= sTaxiPathNodesByPath.size()) - return false; + m_goValue.Transport.PathProgress += diff; - TaxiPathNodeList const& path = sTaxiPathNodesByPath[pathid]; + uint32 timer = m_goValue.Transport.PathProgress % GetPeriod(); - std::vector<keyFrame> keyFrames; - int mapChange = 0; - mapids.clear(); - for (size_t i = 1; i < path.size() - 1; ++i) + // Set current waypoint + // Desired outcome: _currentFrame->DepartureTime < timer < _nextFrame->ArriveTime + // ... arrive | ... delay ... | departure + // event / event / + for (;;) { - if (mapChange == 0) + if (timer >= _currentFrame->ArriveTime) { - TaxiPathNodeEntry const& node_i = path[i]; - if (node_i.mapid == path[i+1].mapid) + if (!_triggeredArrivalEvent) { - keyFrame k(node_i); - keyFrames.push_back(k); - mapids.insert(k.node->mapid); + DoEventIfAny(*_currentFrame, false); + _triggeredArrivalEvent = true; } - else + + if (timer < _currentFrame->DepartureTime) { - mapChange = 1; + SetMoving(false); + if (_pendingStop) + SetGoState(GO_STATE_READY); + break; // its a stop frame and we are waiting } } - else - { - --mapChange; - } - } - - int lastStop = -1; - int firstStop = -1; - // first cell is arrived at by teleportation :S - keyFrames[0].distFromPrev = 0; - if (keyFrames[0].node->actionFlag == 2) - { - lastStop = 0; - } - - // find the rest of the distances between key points - for (size_t i = 1; i < keyFrames.size(); ++i) - { - if ((keyFrames[i].node->actionFlag == 1) || (keyFrames[i].node->mapid != keyFrames[i-1].node->mapid)) + if (_pendingStop && timer >= _currentFrame->DepartureTime && GetGoState() == GO_STATE_READY) { - keyFrames[i].distFromPrev = 0; + m_goValue.Transport.PathProgress = (m_goValue.Transport.PathProgress / GetPeriod()); + m_goValue.Transport.PathProgress *= GetPeriod(); + m_goValue.Transport.PathProgress += _currentFrame->ArriveTime; + break; } - else - { - keyFrames[i].distFromPrev = - sqrt(pow(keyFrames[i].node->x - keyFrames[i - 1].node->x, 2) + - pow(keyFrames[i].node->y - keyFrames[i - 1].node->y, 2) + - pow(keyFrames[i].node->z - keyFrames[i - 1].node->z, 2)); - } - if (keyFrames[i].node->actionFlag == 2) + + if (timer >= _currentFrame->DepartureTime && !_triggeredDepartureEvent) { - // remember first stop frame - if (firstStop == -1) - firstStop = i; - lastStop = i; + DoEventIfAny(*_currentFrame, true); // departure event + _triggeredDepartureEvent = true; } - } - - float tmpDist = 0; - for (size_t i = 0; i < keyFrames.size(); ++i) - { - int j = (i + lastStop) % keyFrames.size(); - if (keyFrames[j].node->actionFlag == 2) - tmpDist = 0; - else - tmpDist += keyFrames[j].distFromPrev; - keyFrames[j].distSinceStop = tmpDist; - } - for (int i = int(keyFrames.size()) - 1; i >= 0; i--) - { - int j = (i + (firstStop+1)) % keyFrames.size(); - tmpDist += keyFrames[(j + 1) % keyFrames.size()].distFromPrev; - keyFrames[j].distUntilStop = tmpDist; - if (keyFrames[j].node->actionFlag == 2) - tmpDist = 0; - } + if (timer >= _currentFrame->DepartureTime && timer < _currentFrame->NextArriveTime) + break; // found current waypoint - for (size_t i = 0; i < keyFrames.size(); ++i) - { - if (keyFrames[i].distSinceStop < (30 * 30 * 0.5f)) - keyFrames[i].tFrom = sqrt(2 * keyFrames[i].distSinceStop); - else - keyFrames[i].tFrom = ((keyFrames[i].distSinceStop - (30 * 30 * 0.5f)) / 30) + 30; + MoveToNextWaypoint(); - if (keyFrames[i].distUntilStop < (30 * 30 * 0.5f)) - keyFrames[i].tTo = sqrt(2 * keyFrames[i].distUntilStop); - else - keyFrames[i].tTo = ((keyFrames[i].distUntilStop - (30 * 30 * 0.5f)) / 30) + 30; + // not waiting anymore + SetMoving(true); - keyFrames[i].tFrom *= 1000; - keyFrames[i].tTo *= 1000; - } - - // for (int i = 0; i < keyFrames.size(); ++i) { - // TC_LOG_INFO(LOG_FILTER_TRANSPORTS, "%f, %f, %f, %f, %f, %f, %f", keyFrames[i].x, keyFrames[i].y, keyFrames[i].distUntilStop, keyFrames[i].distSinceStop, keyFrames[i].distFromPrev, keyFrames[i].tFrom, keyFrames[i].tTo); - // } + // Enable movement + if (GetGOInfo()->moTransport.canBeStopped) + SetGoState(GO_STATE_ACTIVE); - // Now we're completely set up; we can move along the length of each waypoint at 100 ms intervals - // speed = max(30, t) (remember x = 0.5s^2, and when accelerating, a = 1 unit/s^2 - int t = 0; - bool teleport = false; - if (keyFrames[keyFrames.size() - 1].node->mapid != keyFrames[0].node->mapid) - teleport = true; + // Departure event + if (_currentFrame->IsTeleportFrame()) + TeleportTransport(_nextFrame->Node->mapid, _nextFrame->Node->x, _nextFrame->Node->y, _nextFrame->Node->z); - m_WayPoints[0] = WayPoint(keyFrames[0].node->mapid, keyFrames[0].node->x, keyFrames[0].node->y, keyFrames[0].node->z, teleport, 0, - keyFrames[0].node->arrivalEventID, keyFrames[0].node->departureEventID); + sScriptMgr->OnRelocate(this, _currentFrame->Node->index, _currentFrame->Node->mapid, _currentFrame->Node->x, _currentFrame->Node->y, _currentFrame->Node->z); - t += keyFrames[0].node->delay * 1000; + TC_LOG_DEBUG(LOG_FILTER_TRANSPORTS, "Transport %u (%s) moved to node %u %u %f %f %f", GetEntry(), GetName(), _currentFrame->Node->index, _currentFrame->Node->mapid, _currentFrame->Node->x, _currentFrame->Node->y, _currentFrame->Node->z); + } - uint32 cM = keyFrames[0].node->mapid; - for (size_t i = 0; i < keyFrames.size() - 1; ++i) + // Set position + _positionChangeTimer.Update(diff); + if (_positionChangeTimer.Passed()) { - float d = 0; - float tFrom = keyFrames[i].tFrom; - float tTo = keyFrames[i].tTo; - - // keep the generation of all these points; we use only a few now, but may need the others later - if (((d < keyFrames[i + 1].distFromPrev) && (tTo > 0))) + _positionChangeTimer.Reset(positionUpdateDelay); + if (IsMoving()) { - while ((d < keyFrames[i + 1].distFromPrev) && (tTo > 0)) - { - tFrom += 100; - tTo -= 100; - - if (d > 0) - { - float newX = keyFrames[i].node->x + (keyFrames[i + 1].node->x - keyFrames[i].node->x) * d / keyFrames[i + 1].distFromPrev; - float newY = keyFrames[i].node->y + (keyFrames[i + 1].node->y - keyFrames[i].node->y) * d / keyFrames[i + 1].distFromPrev; - float newZ = keyFrames[i].node->z + (keyFrames[i + 1].node->z - keyFrames[i].node->z) * d / keyFrames[i + 1].distFromPrev; - - teleport = false; - if (keyFrames[i].node->mapid != cM) - { - teleport = true; - cM = keyFrames[i].node->mapid; - } - - // TC_LOG_INFO(LOG_FILTER_TRANSPORTS, "T: %d, D: %f, x: %f, y: %f, z: %f", t, d, newX, newY, newZ); - if (teleport) - m_WayPoints[t] = WayPoint(keyFrames[i].node->mapid, newX, newY, newZ, teleport, 0); - } - - if (tFrom < tTo) // caught in tFrom dock's "gravitational pull" - { - if (tFrom <= 30000) - { - d = 0.5f * (tFrom / 1000) * (tFrom / 1000); - } - else - { - d = 0.5f * 30 * 30 + 30 * ((tFrom - 30000) / 1000); - } - d = d - keyFrames[i].distSinceStop; - } - else - { - if (tTo <= 30000) - { - d = 0.5f * (tTo / 1000) * (tTo / 1000); - } - else - { - d = 0.5f * 30 * 30 + 30 * ((tTo - 30000) / 1000); - } - d = keyFrames[i].distUntilStop - d; - } - t += 100; - } - t -= 100; + float t = CalculateSegmentPos(float(timer) * 0.001f); + G3D::Vector3 pos, dir; + _currentFrame->Spline->evaluate_percent(_currentFrame->Index, t, pos); + //_currentFrame->Spline->evaluate_derivative(_currentFrame->Index, t, dir); + UpdatePosition(pos.x, pos.y, pos.z, 0.0f/*atan2(dir.x, dir.y)*/); } - - if (keyFrames[i + 1].tFrom > keyFrames[i + 1].tTo) - t += 100 - ((long)keyFrames[i + 1].tTo % 100); - else - t += (long)keyFrames[i + 1].tTo % 100; - - teleport = false; - if ((keyFrames[i + 1].node->actionFlag == 1) || (keyFrames[i + 1].node->mapid != keyFrames[i].node->mapid)) - { - teleport = true; - cM = keyFrames[i + 1].node->mapid; - } - - m_WayPoints[t] = WayPoint(keyFrames[i + 1].node->mapid, keyFrames[i + 1].node->x, keyFrames[i + 1].node->y, keyFrames[i + 1].node->z, teleport, - 0, keyFrames[i + 1].node->arrivalEventID, keyFrames[i + 1].node->departureEventID); - // TC_LOG_INFO(LOG_FILTER_TRANSPORTS, "T: %d, x: %f, y: %f, z: %f, t:%d", t, pos.x, pos.y, pos.z, teleport); - - t += keyFrames[i + 1].node->delay * 1000; } - uint32 timer = t; - - // TC_LOG_INFO(LOG_FILTER_TRANSPORTS, " Generated %lu waypoints, total time %u.", (unsigned long)m_WayPoints.size(), timer); - - m_curr = m_WayPoints.begin(); - m_next = GetNextWayPoint(); - m_pathTime = timer; + sScriptMgr->OnTransportUpdate(this, diff); +} - m_nextNodeTime = m_curr->first; +void Transport::AddPassenger(WorldObject* passenger) +{ + if (_passengers.insert(passenger).second) + TC_LOG_DEBUG(LOG_FILTER_TRANSPORTS, "Object %s boarded transport %s.", passenger->GetName(), GetName()); - return true; + if (Player* plr = passenger->ToPlayer()) + sScriptMgr->OnAddPassenger(this, plr); } -Transport::WayPointMap::const_iterator Transport::GetNextWayPoint() +void Transport::RemovePassenger(WorldObject* passenger) { - WayPointMap::const_iterator iter = m_curr; - ++iter; - if (iter == m_WayPoints.end()) - iter = m_WayPoints.begin(); - return iter; + if (_passengers.erase(passenger) || _staticPassengers.erase(passenger)) // static passenger can remove itself in case of grid unload + TC_LOG_DEBUG(LOG_FILTER_TRANSPORTS, "Object %s removed from transport %s.", passenger->GetName(), GetName()); + + + if (Player* plr = passenger->ToPlayer()) + sScriptMgr->OnRemovePassenger(this, plr); } -void Transport::TeleportTransport(uint32 newMapid, float x, float y, float z) +Creature* Transport::CreateNPCPassenger(uint32 guid, CreatureData const* data) { - Map const* oldMap = GetMap(); - Relocate(x, y, z); + Map* map = GetMap(); + Creature* creature = new Creature(); - for (PlayerSet::const_iterator itr = m_passengers.begin(); itr != m_passengers.end();) + if (!creature->LoadCreatureFromDB(guid, map, false)) { - Player* player = *itr; - ++itr; - - if (player->isDead() && !player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) - player->ResurrectPlayer(1.0f); - - player->TeleportTo(newMapid, x, y, z, GetOrientation(), TELE_TO_NOT_LEAVE_TRANSPORT); + delete creature; + return NULL; } - //we need to create and save new Map object with 'newMapid' because if not done -> lead to invalid Map object reference... - //player far teleport would try to create same instance, but we need it NOW for transport... + float x = data->posX; + float y = data->posY; + float z = data->posZ; + float o = data->orientation; - RemoveFromWorld(); - ResetMap(); - Map* newMap = sMapMgr->CreateBaseMap(newMapid); - SetMap(newMap); - ASSERT(GetMap()); - AddToWorld(); + creature->SetTransport(this); + creature->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); + creature->m_movementInfo.transport.guid = GetGUID(); + creature->m_movementInfo.transport.pos.Relocate(x, y, z, o); + CalculatePassengerPosition(x, y, z, &o); + creature->Relocate(x, y, z, o); + creature->SetHomePosition(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetOrientation()); + creature->SetTransportHomePosition(creature->m_movementInfo.transport.pos); - if (oldMap != newMap) + if (!creature->IsPositionValid()) { - UpdateForMap(oldMap); - UpdateForMap(newMap); + TC_LOG_ERROR(LOG_FILTER_TRANSPORTS, "Creature (guidlow %d, entry %d) not created. Suggested coordinates aren't valid (X: %f Y: %f)",creature->GetGUIDLow(),creature->GetEntry(),creature->GetPositionX(),creature->GetPositionY()); + delete creature; + return NULL; } - for (CreatureSet::iterator itr = m_NPCPassengerSet.begin(); itr != m_NPCPassengerSet.end(); ++itr) - (*itr)->FarTeleportTo(newMap, x, y, z, (*itr)->GetOrientation()); -} - -bool Transport::AddPassenger(Player* passenger) -{ - if (m_passengers.insert(passenger).second) - TC_LOG_INFO(LOG_FILTER_TRANSPORTS, "Player %s boarded transport %s.", passenger->GetName().c_str(), GetName().c_str()); + map->AddToMap(creature); + _staticPassengers.insert(creature); - sScriptMgr->OnAddPassenger(this, passenger); - return true; + sScriptMgr->OnAddCreaturePassenger(this, creature); + return creature; } -bool Transport::RemovePassenger(Player* passenger) +GameObject* Transport::CreateGOPassenger(uint32 guid, GameObjectData const* data) { - if (m_passengers.erase(passenger)) - TC_LOG_INFO(LOG_FILTER_TRANSPORTS, "Player %s removed from transport %s.", passenger->GetName().c_str(), GetName().c_str()); - - sScriptMgr->OnRemovePassenger(this, passenger); - return true; -} + Map* map = GetMap(); + GameObject* go = new GameObject(); -void Transport::Update(uint32 p_diff) -{ - if (!AI()) + if (!go->LoadGameObjectFromDB(guid, map, false)) { - if (!AIM_Initialize()) - TC_LOG_ERROR(LOG_FILTER_TRANSPORTS, "Could not initialize GameObjectAI for Transport"); - } else - AI()->UpdateAI(p_diff); - - if (m_WayPoints.size() <= 1) - return; + delete go; + return NULL; + } - m_timer = getMSTime() % m_period; - while (((m_timer - m_curr->first) % m_pathTime) > ((m_next->first - m_curr->first) % m_pathTime)) - { - DoEventIfAny(*m_curr, true); + float x = data->posX; + float y = data->posY; + float z = data->posZ; + float o = data->orientation; - m_curr = GetNextWayPoint(); - m_next = GetNextWayPoint(); + go->SetTransport(this); + go->m_movementInfo.transport.guid = GetGUID(); + go->m_movementInfo.transport.pos.Relocate(x, y, z, o); + CalculatePassengerPosition(x, y, z, &o); + go->Relocate(x, y, z, o); - DoEventIfAny(*m_curr, false); + if (!go->IsPositionValid()) + { + TC_LOG_ERROR(LOG_FILTER_TRANSPORTS, "GameObject (guidlow %d, entry %d) not created. Suggested coordinates aren't valid (X: %f Y: %f)", go->GetGUIDLow(), go->GetEntry(), go->GetPositionX(), go->GetPositionY()); + delete go; + return NULL; + } - // first check help in case client-server transport coordinates de-synchronization - if (m_curr->second.mapid != GetMapId() || m_curr->second.teleport) - { - TeleportTransport(m_curr->second.mapid, m_curr->second.x, m_curr->second.y, m_curr->second.z); - } - else - { - Relocate(m_curr->second.x, m_curr->second.y, m_curr->second.z, GetAngle(m_next->second.x, m_next->second.y) + float(M_PI)); - UpdatePassengerPositions(); // COME BACK MARKER - } + map->AddToMap(go); + _staticPassengers.insert(go); - sScriptMgr->OnRelocate(this, m_curr->first, m_curr->second.mapid, m_curr->second.x, m_curr->second.y, m_curr->second.z); + //sScriptMgr->OnAddCreaturePassenger(this, go); + return go; +} - m_nextNodeTime = m_curr->first; +void Transport::CalculatePassengerPosition(float& x, float& y, float& z, float* o /*= NULL*/) const +{ + float inx = x, iny = y, inz = z; + if (o) + *o = Position::NormalizeOrientation(GetOrientation() + *o); - if (m_curr == m_WayPoints.begin()) - TC_LOG_DEBUG(LOG_FILTER_TRANSPORTS, " ************ BEGIN ************** %s", m_name.c_str()); + x = GetPositionX() + inx * std::cos(GetOrientation()) - iny * std::sin(GetOrientation()); + y = GetPositionY() + iny * std::cos(GetOrientation()) + inx * std::sin(GetOrientation()); + z = GetPositionZ() + inz; +} - TC_LOG_DEBUG(LOG_FILTER_TRANSPORTS, "%s moved to %d %f %f %f %d", m_name.c_str(), m_curr->second.id, m_curr->second.x, m_curr->second.y, m_curr->second.z, m_curr->second.mapid); - } +void Transport::CalculatePassengerOffset(float& x, float& y, float& z, float* o /*= NULL*/) const +{ + if (o) + *o = Position::NormalizeOrientation(*o - GetOrientation()); - sScriptMgr->OnTransportUpdate(this, p_diff); + z -= GetPositionZ(); + y -= GetPositionY(); // y = searchedY * std::cos(o) + searchedX * std::sin(o) + x -= GetPositionX(); // x = searchedX * std::cos(o) + searchedY * std::sin(o + pi) + float inx = x, iny = y; + y = (iny - inx * std::tan(GetOrientation())) / (std::cos(GetOrientation()) + std::sin(GetOrientation()) * std::tan(GetOrientation())); + x = (inx + iny * std::tan(GetOrientation())) / (std::cos(GetOrientation()) + std::sin(GetOrientation()) * std::tan(GetOrientation())); } -void Transport::UpdateForMap(Map const* targetMap) +void Transport::UpdatePosition(float x, float y, float z, float o) { - Map::PlayerList const& player = targetMap->GetPlayers(); - if (player.isEmpty()) - return; + bool newActive = GetMap()->IsGridLoaded(x, y); + + Relocate(x, y, z, o); + + UpdatePassengerPositions(_passengers); + + /* There are four possible scenarios that trigger loading/unloading passengers: + 1. transport moves from inactive to active grid + 2. the grid that transport is currently in becomes active + 3. transport moves from active to inactive grid + 4. the grid that transport is currently in unloads + */ + if (_staticPassengers.empty() && newActive) // 1. and 2. + LoadStaticPassengers(); + else if (!_staticPassengers.empty() && !newActive && Cell(x, y).DiffGrid(Cell(GetPositionX(), GetPositionY()))) // 3. + UnloadStaticPassengers(); + else + UpdatePassengerPositions(_staticPassengers); + // 4. is handed by grid unload +} - if (GetMapId() == targetMap->GetId()) +void Transport::LoadStaticPassengers() +{ + if (uint32 mapId = GetGOInfo()->moTransport.mapID) { - for (Map::PlayerList::const_iterator itr = player.begin(); itr != player.end(); ++itr) + CellObjectGuidsMap const& cells = sObjectMgr->GetMapObjectGuids(mapId, GetMap()->GetSpawnMode()); + CellGuidSet::const_iterator guidEnd; + for (CellObjectGuidsMap::const_iterator cellItr = cells.begin(); cellItr != cells.end(); ++cellItr) { - if (this != itr->GetSource()->GetTransport()) - { - UpdateData transData; - BuildCreateUpdateBlockForPlayer(&transData, itr->GetSource()); - WorldPacket packet; - transData.BuildPacket(&packet); - itr->GetSource()->SendDirectMessage(&packet); - } + // Creatures on transport + guidEnd = cellItr->second.creatures.end(); + for (CellGuidSet::const_iterator guidItr = cellItr->second.creatures.begin(); guidItr != guidEnd; ++guidItr) + CreateNPCPassenger(*guidItr, sObjectMgr->GetCreatureData(*guidItr)); + + // GameObjects on transport + guidEnd = cellItr->second.gameobjects.end(); + for (CellGuidSet::const_iterator guidItr = cellItr->second.gameobjects.begin(); guidItr != guidEnd; ++guidItr) + CreateGOPassenger(*guidItr, sObjectMgr->GetGOData(*guidItr)); } } - else - { - UpdateData transData; - BuildOutOfRangeUpdateBlock(&transData); - WorldPacket out_packet; - transData.BuildPacket(&out_packet); - - for (Map::PlayerList::const_iterator itr = player.begin(); itr != player.end(); ++itr) - if (this != itr->GetSource()->GetTransport()) - itr->GetSource()->SendDirectMessage(&out_packet); - } } -void Transport::DoEventIfAny(WayPointMap::value_type const& node, bool departure) +void Transport::UnloadStaticPassengers() { - if (uint32 eventid = departure ? node.second.departureEventID : node.second.arrivalEventID) + while (!_staticPassengers.empty()) { - TC_LOG_DEBUG(LOG_FILTER_MAPSCRIPTS, "Taxi %s event %u of node %u of %s path", departure ? "departure" : "arrival", eventid, node.first, GetName().c_str()); - GetMap()->ScriptsStart(sEventScripts, eventid, this, this); - EventInform(eventid); + WorldObject* obj = *_staticPassengers.begin(); + obj->AddObjectToRemoveList(); // also removes from _staticPassengers } } -void Transport::BuildStartMovePacket(Map const* targetMap) +void Transport::EnableMovement(bool enabled) { - SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); - SetGoState(GO_STATE_ACTIVE); - UpdateForMap(targetMap); + if (!GetGOInfo()->moTransport.canBeStopped) + return; + + _pendingStop = !enabled; } -void Transport::BuildStopMovePacket(Map const* targetMap) +void Transport::MoveToNextWaypoint() { - RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); - SetGoState(GO_STATE_READY); - UpdateForMap(targetMap); + // Clear events flagging + _triggeredArrivalEvent = false; + _triggeredDepartureEvent = false; + + // Set frames + _currentFrame = _nextFrame++; + if (_nextFrame == GetKeyFrames().end()) + _nextFrame = GetKeyFrames().begin(); } -uint32 Transport::AddNPCPassenger(uint32 tguid, uint32 entry, float x, float y, float z, float o, uint32 anim) +float Transport::CalculateSegmentPos(float now) { - Map* map = GetMap(); - //make it world object so it will not be unloaded with grid - Creature* creature = new Creature(true); - - if (!creature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, GetPhaseMask(), entry, 0, GetGOInfo()->faction, 0, 0, 0, 0)) + KeyFrame const& frame = *_currentFrame; + const float speed = float(m_goInfo->moTransport.moveSpeed); + const float accel = float(m_goInfo->moTransport.accelRate); + float timeSinceStop = frame.TimeFrom + (now - (1.0f/IN_MILLISECONDS) * frame.DepartureTime); + float timeUntilStop = frame.TimeTo - (now - (1.0f/IN_MILLISECONDS) * frame.DepartureTime); + float segmentPos, dist; + float accelTime = _transportInfo->accelTime; + float accelDist = _transportInfo->accelDist; + // calculate from nearest stop, less confusing calculation... + if (timeSinceStop < timeUntilStop) { - delete creature; - return 0; + if (timeSinceStop < accelTime) + dist = 0.5f * accel * timeSinceStop * timeSinceStop; + else + dist = accelDist + (timeSinceStop - accelTime) * speed; + segmentPos = dist - frame.DistSinceStop; + } + else + { + if (timeUntilStop < _transportInfo->accelTime) + dist = 0.5f * accel * timeUntilStop * timeUntilStop; + else + dist = accelDist + (timeUntilStop - accelTime) * speed; + segmentPos = frame.DistUntilStop - dist; } - creature->SetTransport(this); - creature->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); - creature->m_movementInfo.guid = GetGUID(); - creature->m_movementInfo.transport.pos.Relocate(x, y, z, o); + return segmentPos / frame.NextDistFromPrev; +} + +void Transport::TeleportTransport(uint32 newMapid, float x, float y, float z) +{ + Map const* oldMap = GetMap(); - if (anim) - creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, anim); + if (oldMap->GetId() != newMapid) + { + Map* newMap = sMapMgr->CreateBaseMap(newMapid); + Map::PlayerList const& oldPlayers = GetMap()->GetPlayers(); + if (!oldPlayers.isEmpty()) + { + UpdateData data; + BuildOutOfRangeUpdateBlock(&data); + WorldPacket packet; + data.BuildPacket(&packet); + for (Map::PlayerList::const_iterator itr = oldPlayers.begin(); itr != oldPlayers.end(); ++itr) + if (itr->GetSource()->GetTransport() != this) + itr->GetSource()->SendDirectMessage(&packet); + } - creature->Relocate( - GetPositionX() + (x * std::cos(GetOrientation()) + y * std::sin(GetOrientation() + float(M_PI))), - GetPositionY() + (y * std::cos(GetOrientation()) + x * std::sin(GetOrientation())), - z + GetPositionZ(), - o + GetOrientation()); + UnloadStaticPassengers(); + GetMap()->RemoveFromMap<Transport>(this, false); + SetMap(newMap); - creature->SetHomePosition(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetOrientation()); - creature->SetTransportHomePosition(creature->m_movementInfo.transport.pos); + Map::PlayerList const& newPlayers = GetMap()->GetPlayers(); + if (!newPlayers.isEmpty()) + { + for (Map::PlayerList::const_iterator itr = newPlayers.begin(); itr != newPlayers.end(); ++itr) + { + if (itr->GetSource()->GetTransport() != this) + { + UpdateData data; + BuildCreateUpdateBlockForPlayer(&data, itr->GetSource()); + WorldPacket packet; + data.BuildPacket(&packet); + itr->GetSource()->SendDirectMessage(&packet); + } + } + } - if (!creature->IsPositionValid()) - { - TC_LOG_ERROR(LOG_FILTER_TRANSPORTS, "Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)", creature->GetGUIDLow(), creature->GetEntry(), creature->GetPositionX(), creature->GetPositionY()); - delete creature; - return 0; - } + // Teleport passengers after everyone on destination map are sent create packet + // but before transport itself is registered there and begins updating + for (std::set<WorldObject*>::iterator itr = _staticPassengers.begin(); itr != _staticPassengers.end(); ++itr) + { + switch ((*itr)->GetTypeId()) + { + case TYPEID_UNIT: + (*itr)->ToCreature()->FarTeleportTo(newMap, x, y, z, (*itr)->GetOrientation()); + break; + case TYPEID_GAMEOBJECT: + { + GameObject* go = (*itr)->ToGameObject(); + go->GetMap()->RemoveFromMap(go, false); + Relocate(x, y, z, go->GetOrientation()); + SetMap(newMap); + newMap->AddToMap(go); + break; + } + } + } - map->AddToMap(creature); - m_NPCPassengerSet.insert(creature); + for (std::set<WorldObject*>::iterator itr = _passengers.begin(); itr != _passengers.end(); ++itr) + { + switch ((*itr)->GetTypeId()) + { + case TYPEID_UNIT: + if (!IS_PLAYER_GUID((*itr)->ToUnit()->GetOwnerGUID())) // pets should be teleported with player + (*itr)->ToCreature()->FarTeleportTo(newMap, x, y, z, (*itr)->GetOrientation()); + break; + case TYPEID_GAMEOBJECT: + { + GameObject* go = (*itr)->ToGameObject(); + go->GetMap()->RemoveFromMap(go, false); + Relocate(x, y, z, go->GetOrientation()); + SetMap(newMap); + newMap->AddToMap(go); + break; + } + case TYPEID_PLAYER: + (*itr)->ToPlayer()->TeleportTo(newMapid, x, y, z, (*itr)->GetOrientation(), TELE_TO_NOT_LEAVE_TRANSPORT); + break; + } + } - if (tguid == 0) - { - ++currenttguid; - tguid = currenttguid; + GetMap()->AddToMap<Transport>(this); } else - currenttguid = std::max(tguid, currenttguid); - - creature->SetGUIDTransport(tguid); - sScriptMgr->OnAddCreaturePassenger(this, creature); - return tguid; -} - -void Transport::UpdatePosition(MovementInfo* mi) -{ - float transport_o = mi->pos.GetOrientation() - mi->transport.pos.GetOrientation(); - float transport_x = mi->pos.m_positionX - (mi->transport.pos.m_positionX * std::cos(transport_o) - mi->transport.pos.m_positionY * std::sin(transport_o)); - float transport_y = mi->pos.m_positionY - (mi->transport.pos.m_positionY * std::cos(transport_o) + mi->transport.pos.m_positionX * std::sin(transport_o)); - float transport_z = mi->pos.m_positionZ - mi->transport.pos.m_positionZ; + { + // Teleport players, they need to know it + for (std::set<WorldObject*>::iterator itr = _passengers.begin(); itr != _passengers.end(); ++itr) + if ((*itr)->GetTypeId() == TYPEID_PLAYER) + (*itr)->ToUnit()->NearTeleportTo(x, y, z, GetOrientation()); + } - Relocate(transport_x, transport_y, transport_z, transport_o); - UpdatePassengerPositions(); + UpdatePosition(x, y, z, GetOrientation()); } -void Transport::UpdatePassengerPositions() +void Transport::UpdatePassengerPositions(std::set<WorldObject*>& passengers) { - for (CreatureSet::iterator itr = m_NPCPassengerSet.begin(); itr != m_NPCPassengerSet.end(); ++itr) + for (std::set<WorldObject*>::iterator itr = passengers.begin(); itr != passengers.end(); ++itr) { - Creature* npc = *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; - npc->m_movementInfo.transport.pos.GetPosition(x, y, z, o); - CalculatePassengerPosition(x, y, z, &o); - GetMap()->CreatureRelocation(npc, x, y, z, o, false); - npc->GetTransportHomePosition(x, y, z, o); + passenger->m_movementInfo.transport.pos.GetPosition(x, y, z, o); CalculatePassengerPosition(x, y, z, &o); - npc->SetHomePosition(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); + break; + } + + if (Unit* unit = passenger->ToUnit()) + if (Vehicle* vehicle = unit->GetVehicleKit()) + vehicle->RelocatePassengers(); } } -void Transport::CalculatePassengerPosition(float& x, float& y, float& z, float* o /*= NULL*/) const +void Transport::DoEventIfAny(KeyFrame const& node, bool departure) { - float inx = x, iny = y, inz = z; - if (o) - *o = Position::NormalizeOrientation(GetOrientation() + *o); - - x = GetPositionX() + inx * std::cos(GetOrientation()) - iny * std::sin(GetOrientation()); - y = GetPositionY() + iny * std::cos(GetOrientation()) + inx * std::sin(GetOrientation()); - z = GetPositionZ() + inz; + if (uint32 eventid = departure ? node.Node->departureEventID : node.Node->arrivalEventID) + { + TC_LOG_DEBUG(LOG_FILTER_MAPSCRIPTS, "Taxi %s event %u of node %u of %s path", departure ? "departure" : "arrival", eventid, node.Node->index, GetName()); + GetMap()->ScriptsStart(sEventScripts, eventid, this, this); + EventInform(eventid); + } } -void Transport::CalculatePassengerOffset(float& x, float& y, float& z, float* o /*= NULL*/) const +void Transport::BuildUpdate(UpdateDataMapType& data_map) { - if (o) - *o = Position::NormalizeOrientation(*o - GetOrientation()); + Map::PlayerList const& players = GetMap()->GetPlayers(); + if (players.isEmpty()) + return; - z -= GetPositionZ(); - y -= GetPositionY(); // y = searchedY * std::cos(o) + searchedX * std::sin(o) - x -= GetPositionX(); // x = searchedX * std::cos(o) + searchedY * std::sin(o + pi) - float inx = x, iny = y; - y = (iny - inx * std::tan(GetOrientation())) / (std::cos(GetOrientation()) + std::sin(GetOrientation()) * std::tan(GetOrientation())); - x = (inx + iny * std::tan(GetOrientation())) / (std::cos(GetOrientation()) + std::sin(GetOrientation()) * std::tan(GetOrientation())); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + BuildFieldsUpdate(itr->GetSource(), data_map); + + ClearUpdateMask(true); } diff --git a/src/server/game/Entities/Transport/Transport.h b/src/server/game/Entities/Transport/Transport.h index 445bec456fd..22a54e8428d 100644 --- a/src/server/game/Entities/Transport/Transport.h +++ b/src/server/game/Entities/Transport/Transport.h @@ -20,34 +20,30 @@ #define TRANSPORTS_H #include "GameObject.h" +#include "TransportMgr.h" #include "VehicleDefines.h" -#include <map> -#include <set> -#include <string> +struct CreatureData; class Transport : public GameObject, public TransportBase { + friend Transport* TransportMgr::CreateTransport(uint32, uint32, Map*); + + Transport(); public: - Transport(uint32 period, uint32 script); ~Transport(); - bool Create(uint32 guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress, uint32 dynflags); - bool GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids); - void Update(uint32 p_time); - bool AddPassenger(Player* passenger); - bool RemovePassenger(Player* passenger); + bool Create(uint32 guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress); + void Update(uint32 diff); - void RemovePassenger(Creature* passenger) { m_NPCPassengerSet.erase(passenger); } + void BuildUpdate(UpdateDataMapType& data_map); - typedef std::set<Player*> PlayerSet; - PlayerSet const& GetPassengers() const { return m_passengers; } + void AddPassenger(WorldObject* passenger); + void RemovePassenger(WorldObject* passenger); + std::set<WorldObject*> const& GetPassengers() const { return _passengers; } - typedef std::set<Creature*> CreatureSet; - CreatureSet m_NPCPassengerSet; - uint32 AddNPCPassenger(uint32 tguid, uint32 entry, float x, float y, float z, float o, uint32 anim=0); - void UpdatePosition(MovementInfo* mi); - void UpdatePassengerPositions(); + Creature* CreateNPCPassenger(uint32 guid, CreatureData const* data); + GameObject* CreateGOPassenger(uint32 guid, GameObjectData const* data); /// This method transforms supplied transport offsets into global coordinates void CalculatePassengerPosition(float& x, float& y, float& z, float* o = NULL) const; @@ -55,50 +51,48 @@ class Transport : public GameObject, public TransportBase /// This method transforms supplied global coordinates into local offsets void CalculatePassengerOffset(float& x, float& y, float& z, float* o = NULL) const; - void BuildStartMovePacket(Map const* targetMap); - void BuildStopMovePacket(Map const* targetMap); - uint32 GetScriptId() const { return ScriptId; } - private: - struct WayPoint - { - WayPoint() : mapid(0), x(0), y(0), z(0), teleport(false), id(0) {} - WayPoint(uint32 _mapid, float _x, float _y, float _z, bool _teleport, uint32 _id = 0, - uint32 _arrivalEventID = 0, uint32 _departureEventID = 0) - : mapid(_mapid), x(_x), y(_y), z(_z), teleport(_teleport), id(_id), - arrivalEventID(_arrivalEventID), departureEventID(_departureEventID) - { - } - uint32 mapid; - float x; - float y; - float z; - bool teleport; - uint32 id; - uint32 arrivalEventID; - uint32 departureEventID; - }; - - typedef std::map<uint32, WayPoint> WayPointMap; - - WayPointMap::const_iterator m_curr; - WayPointMap::const_iterator m_next; - uint32 m_pathTime; - uint32 m_timer; - - PlayerSet m_passengers; - - uint32 currenttguid; - uint32 m_period; - uint32 ScriptId; - public: - WayPointMap m_WayPoints; - uint32 m_nextNodeTime; + uint32 GetPeriod() const { 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; } + + void UpdatePosition(float x, float y, float z, float o); + + //! Needed when transport moves from inactive to active grid + void LoadStaticPassengers(); + + //! Needed when transport enters inactive grid + void UnloadStaticPassengers(); + + void EnableMovement(bool enabled); private: + void MoveToNextWaypoint(); + float CalculateSegmentPos(float perc); void TeleportTransport(uint32 newMapid, float x, float y, float z); - void UpdateForMap(Map const* map); - void DoEventIfAny(WayPointMap::value_type const& node, bool departure); - WayPointMap::const_iterator GetNextWayPoint(); + void UpdatePassengerPositions(std::set<WorldObject*>& passengers); + void DoEventIfAny(KeyFrame const& node, bool departure); + + //! Helpers to know if stop frame was reached + bool IsMoving() const { return _isMoving; } + void SetMoving(bool val) { _isMoving = val; } + + TransportTemplate const* _transportInfo; + + KeyFrameVec::const_iterator _currentFrame; + KeyFrameVec::const_iterator _nextFrame; + uint32 _moveTimer; + TimeTrackerSmall _positionChangeTimer; + bool _isMoving; + bool _pendingStop; + + //! These are needed to properly control events triggering only once for each frame + bool _triggeredArrivalEvent; + bool _triggeredDepartureEvent; + + std::set<WorldObject*> _passengers; + std::set<WorldObject*> _staticPassengers; }; -#endif +#endif diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 74c50145311..2fd1c500305 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -17329,6 +17329,8 @@ void Unit::SetFacingTo(float ori) { Movement::MoveSplineInit init(this); init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZMinusOffset(), false); + if (HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && GetTransGUID()) + init.DisableTransportPathTransformations(); // It makes no sense to target global orientation init.SetFacing(ori); init.Launch(); } @@ -17340,7 +17342,10 @@ void Unit::SetFacingToObject(WorldObject* object) return; /// @todo figure out under what conditions creature will move towards object instead of facing it where it currently is. - SetFacingTo(GetAngle(object)); + Movement::MoveSplineInit init(this); + init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZMinusOffset()); + init.SetFacing(GetAngle(object)); // when on transport, GetAngle will still return global coordinates (and angle) that needs transforming + init.Launch(); } bool Unit::SetWalk(bool enable) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 13f8c8e781f..25352a924ef 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -2078,12 +2078,6 @@ class Unit : public WorldObject bool IsOnVehicle(const Unit* vehicle) const; Unit* GetVehicleBase() const; Creature* GetVehicleCreatureBase() const; - float GetTransOffsetX() const { return m_movementInfo.transport.pos.GetPositionX(); } - float GetTransOffsetY() const { return m_movementInfo.transport.pos.GetPositionY(); } - float GetTransOffsetZ() const { return m_movementInfo.transport.pos.GetPositionZ(); } - float GetTransOffsetO() const { return m_movementInfo.transport.pos.GetOrientation(); } - uint32 GetTransTime() const { return m_movementInfo.transport.time; } - int8 GetTransSeat() const { return m_movementInfo.transport.seat; } uint64 GetTransGUID() const; /// Returns the transport this unit is on directly (if on vehicle and transport, return vehicle) TransportBase* GetDirectTransport() const; |
