diff --git a/Source/Framework/Constants/GameObjectConst.cs b/Source/Framework/Constants/GameObjectConst.cs index 924f50cc8..b873107af 100644 --- a/Source/Framework/Constants/GameObjectConst.cs +++ b/Source/Framework/Constants/GameObjectConst.cs @@ -93,7 +93,7 @@ namespace Framework.Constants Max = 3 } - public enum GameObjectDynamicLowFlags + public enum GameObjectDynamicLowFlags : ushort { HideModel = 0x02, Activate = 0x04, diff --git a/Source/Game/AI/SmartScripts/SmartScript.cs b/Source/Game/AI/SmartScripts/SmartScript.cs index 5caf8695b..6f31d1d09 100644 --- a/Source/Game/AI/SmartScripts/SmartScript.cs +++ b/Source/Game/AI/SmartScripts/SmartScript.cs @@ -1360,7 +1360,7 @@ namespace Game.AI break; if (e.GetTargetType() == SmartTargets.Self) - _me.SetFacingTo((_me.GetTransport() ? _me.GetTransportHomePosition() : _me.GetHomePosition()).GetOrientation()); + _me.SetFacingTo((_me.GetTransport() != null ? _me.GetTransportHomePosition() : _me.GetHomePosition()).GetOrientation()); else if (e.GetTargetType() == SmartTargets.Position) _me.SetFacingTo(e.Target.o); else if (!targets.Empty()) diff --git a/Source/Game/Chat/Commands/DebugCommands.cs b/Source/Game/Chat/Commands/DebugCommands.cs index dc6848016..5efc63378 100644 --- a/Source/Game/Chat/Commands/DebugCommands.cs +++ b/Source/Game/Chat/Commands/DebugCommands.cs @@ -1079,7 +1079,7 @@ namespace Game.Chat [Command("transport", RBACPermissions.CommandDebugTransport)] static bool HandleDebugTransportCommand(CommandHandler handler, string operation) { - Transport transport = handler.GetSession().GetPlayer().GetTransport(); + Transport transport = handler.GetSession().GetPlayer().GetTransport(); if (!transport) return false; diff --git a/Source/Game/Chat/Commands/MiscCommands.cs b/Source/Game/Chat/Commands/MiscCommands.cs index e94030dc9..ac5164b8b 100644 --- a/Source/Game/Chat/Commands/MiscCommands.cs +++ b/Source/Game/Chat/Commands/MiscCommands.cs @@ -959,7 +959,7 @@ namespace Game.Chat areaId, (areaEntry != null ? areaEntry.AreaName[handler.GetSessionDbcLocale()] : unknown), obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ(), obj.GetOrientation()); - Transport transport = obj.GetTransport(); + Transport transport = obj.GetTransport(); if (transport) { handler.SendSysMessage(CypherStrings.TransportPosition, transport.GetGoInfo().MoTransport.SpawnMap, obj.GetTransOffsetX(), obj.GetTransOffsetY(), obj.GetTransOffsetZ(), obj.GetTransOffsetO(), diff --git a/Source/Game/Chat/Commands/NPCCommands.cs b/Source/Game/Chat/Commands/NPCCommands.cs index a76f45161..4bb53ea2c 100644 --- a/Source/Game/Chat/Commands/NPCCommands.cs +++ b/Source/Game/Chat/Commands/NPCCommands.cs @@ -654,7 +654,7 @@ namespace Game.Chat Player chr = handler.GetSession().GetPlayer(); Map map = chr.GetMap(); - Transport trans = chr.GetTransport(); + Transport trans = chr.GetTransport(); if (trans) { ulong guid = Global.ObjectMgr.GenerateCreatureSpawnId(); diff --git a/Source/Game/Entities/AreaTrigger/AreaTrigger.cs b/Source/Game/Entities/AreaTrigger/AreaTrigger.cs index 3f17e98fe..e23c13914 100644 --- a/Source/Game/Entities/AreaTrigger/AreaTrigger.cs +++ b/Source/Game/Entities/AreaTrigger/AreaTrigger.cs @@ -191,8 +191,8 @@ namespace Game.Entities } // movement on transport of areatriggers on unit is handled by themself - Transport transport = m_movementInfo.transport.guid.IsEmpty() ? caster.GetTransport() : null; - if (transport) + ITransport transport = m_movementInfo.transport.guid.IsEmpty() ? caster.GetTransport() : null; + if (transport != null) { float x, y, z, o; pos.GetPosition(out x, out y, out z, out o); @@ -211,7 +211,7 @@ namespace Game.Entities if (!GetMap().AddToMap(this)) { // Returning false will cause the object to be deleted - remove from transport - if (transport) + if (transport != null) transport.RemovePassenger(this); return false; } diff --git a/Source/Game/Entities/Corpse.cs b/Source/Game/Entities/Corpse.cs index 29810c71c..f8a36a072 100644 --- a/Source/Game/Entities/Corpse.cs +++ b/Source/Game/Entities/Corpse.cs @@ -286,6 +286,7 @@ namespace Game.Entities } public CorpseDynFlags GetCorpseDynamicFlags() { return (CorpseDynFlags)(uint)m_corpseData.DynamicFlags; } + public bool HasCorpseDynamicFlag(CorpseDynFlags flag) { return (m_corpseData.DynamicFlags & (uint)flag) != 0; } public void SetCorpseDynamicFlag(CorpseDynFlags dynamicFlags) { SetUpdateFieldFlagValue(m_values.ModifyValue(m_corpseData).ModifyValue(m_corpseData.DynamicFlags), (uint)dynamicFlags); } public void RemoveCorpseDynamicFlag(CorpseDynFlags dynamicFlags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(m_corpseData).ModifyValue(m_corpseData.DynamicFlags), (uint)dynamicFlags); } public void ReplaceAllCorpseDynamicFlags(CorpseDynFlags dynamicFlags) { SetUpdateFieldValue(m_values.ModifyValue(m_corpseData).ModifyValue(m_corpseData.DynamicFlags), (uint)dynamicFlags); } diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index 86e3471e6..452b52dd1 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -1254,7 +1254,12 @@ namespace Game.Entities return; } - uint mapId = GetTransport() ? (uint)GetTransport().GetGoInfo().MoTransport.SpawnMap : GetMapId(); + uint mapId = GetMapId(); + ITransport transport = GetTransport(); + if (transport != null) + if (transport.GetMapIdForSpawning() >= 0) + mapId = (uint)transport.GetMapIdForSpawning(); + SaveToDB(mapId, data.SpawnDifficulties); } diff --git a/Source/Game/Entities/DynamicObject.cs b/Source/Game/Entities/DynamicObject.cs index 3777884e9..5ff0e685c 100644 --- a/Source/Game/Entities/DynamicObject.cs +++ b/Source/Game/Entities/DynamicObject.cs @@ -111,8 +111,8 @@ namespace Game.Entities if (IsWorldObject()) SetActive(true); //must before add to map to be put in world container - Transport transport = caster.GetTransport(); - if (transport) + ITransport transport = caster.GetTransport(); + if (transport != null) { float x, y, z, o; pos.GetPosition(out x, out y, out z, out o); @@ -126,7 +126,7 @@ namespace Game.Entities if (!GetMap().AddToMap(this)) { // Returning false will cause the object to be deleted - remove from transport - if (transport) + if (transport != null) transport.RemovePassenger(this); return false; } diff --git a/Source/Game/Entities/GameObject/GameObject.cs b/Source/Game/Entities/GameObject/GameObject.cs index 60ee47a6b..b1f2dfb71 100644 --- a/Source/Game/Entities/GameObject/GameObject.cs +++ b/Source/Game/Entities/GameObject/GameObject.cs @@ -60,8 +60,6 @@ namespace Game.Entities { m_AI = null; m_model = null; - if (m_goInfo != null && m_goInfo.type == GameObjectTypes.Transport) - m_goValue.Transport.StopFrames.Clear(); base.Dispose(); } @@ -311,38 +309,15 @@ namespace Game.Entities SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.ParentRotation), new Quaternion(goInfo.DestructibleBuilding.DestructibleModelRec, 0f, 0f, 0f)); break; case GameObjectTypes.Transport: - m_goValue.Transport.AnimationInfo = Global.TransportMgr.GetTransportAnimInfo(goInfo.entry); - m_goValue.Transport.PathProgress = Time.GetMSTime(); - if (!m_goValue.Transport.AnimationInfo.Path.Empty()) - 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 List(); - if (goInfo.Transport.Timeto2ndfloor > 0) - m_goValue.Transport.StopFrames.Add(goInfo.Transport.Timeto2ndfloor); - if (goInfo.Transport.Timeto3rdfloor > 0) - m_goValue.Transport.StopFrames.Add(goInfo.Transport.Timeto3rdfloor); - if (goInfo.Transport.Timeto4thfloor > 0) - m_goValue.Transport.StopFrames.Add(goInfo.Transport.Timeto4thfloor); - if (goInfo.Transport.Timeto5thfloor > 0) - m_goValue.Transport.StopFrames.Add(goInfo.Transport.Timeto5thfloor); - if (goInfo.Transport.Timeto6thfloor > 0) - m_goValue.Transport.StopFrames.Add(goInfo.Transport.Timeto6thfloor); - if (goInfo.Transport.Timeto7thfloor > 0) - m_goValue.Transport.StopFrames.Add(goInfo.Transport.Timeto7thfloor); - if (goInfo.Transport.Timeto8thfloor > 0) - m_goValue.Transport.StopFrames.Add(goInfo.Transport.Timeto8thfloor); - if (goInfo.Transport.Timeto9thfloor > 0) - m_goValue.Transport.StopFrames.Add(goInfo.Transport.Timeto9thfloor); - if (goInfo.Transport.Timeto10thfloor > 0) - m_goValue.Transport.StopFrames.Add(goInfo.Transport.Timeto10thfloor); + m_goTypeImpl = new GameObjectType.Transport(this); if (goInfo.Transport.startOpen != 0) - SetTransportState(GameObjectState.TransportStopped, goInfo.Transport.startOpen - 1); + SetGoState(GameObjectState.TransportStopped); else - SetTransportState(GameObjectState.TransportActive); + SetGoState(GameObjectState.TransportActive); SetGoAnimProgress(animProgress); + SetActive(true); break; case GameObjectTypes.FishingNode: SetLevel(1); @@ -452,6 +427,9 @@ namespace Game.Entities } } + if (m_goTypeImpl != null) + m_goTypeImpl.Update(diff); + switch (m_lootState) { case LootState.NotReady: @@ -475,50 +453,6 @@ namespace Game.Entities m_lootState = LootState.Ready; break; } - case GameObjectTypes.Transport: - if (m_goValue.Transport.AnimationInfo.Path.Empty()) - break; - - m_goValue.Transport.PathProgress += diff; - - if (GetGoState() == GameObjectState.TransportActive) - { - m_goValue.Transport.PathProgress += diff; - /* TODO: Fix movement in unloaded grid - currently GO will just disappear - public uint 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 = 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()); - - TC_LOG_DEBUG("misc", "Src: {0} Dest: {1}", src.toString().c_str(), pos.toString().c_str()); - - GetMap().GameObjectRelocation(this, pos.x, pos.y, pos.z, GetOrientation()); - } - */ - if (!m_goValue.Transport.StopFrames.Empty()) - { - uint visualStateBefore = (m_goValue.Transport.StateUpdateTimer / 20000) & 1; - m_goValue.Transport.StateUpdateTimer += diff; - uint visualStateAfter = (m_goValue.Transport.StateUpdateTimer / 20000) & 1; - if (visualStateBefore != visualStateAfter) - { - m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.Level); - m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.State); - ForceUpdateFieldChange(); - } - } - } - break; case GameObjectTypes.FishingNode: { // fishing code (bobber ready) @@ -1072,7 +1006,13 @@ namespace Game.Entities return; } - SaveToDB(GetMapId(), data.SpawnDifficulties); + uint mapId = GetMapId(); + ITransport transport = GetTransport(); + if (transport != null) + if (transport.GetMapIdForSpawning() >= 0) + mapId = (uint)transport.GetMapIdForSpawning(); + + SaveToDB(mapId, data.SpawnDifficulties); } public void SaveToDB(uint mapid, List spawnDifficulties) @@ -1293,7 +1233,7 @@ namespace Game.Entities if (gInfo == null) return false; - return gInfo.type == GameObjectTypes.MapObjTransport || (gInfo.type == GameObjectTypes.Transport && m_goValue.Transport.StopFrames.Empty()); + return gInfo.type == GameObjectTypes.MapObjTransport || gInfo.type == GameObjectTypes.Transport; } public bool IsDestructibleBuilding() @@ -1581,7 +1521,7 @@ namespace Game.Entities case GameObjectActions.GoTo9thFloor: case GameObjectActions.GoTo10thFloor: if (GetGoType() == GameObjectTypes.Transport) - SetTransportState(GameObjectState.TransportStopped, (uint)(action - GameObjectActions.GoTo1stFloor)); + SetGoState((GameObjectState)action); else Log.outError(LogFilter.Spells, $"Spell {spellId} targeted non-transport gameobject for transport only action \"Go to Floor\" {action} in effect {effectIndex}"); break; @@ -2453,7 +2393,7 @@ namespace Game.Entities { Quaternion localRotation = GetLocalRotation(); - Transport transport = GetTransport(); + Transport transport = GetTransport(); if (transport != null) { Quaternion worldRotation = transport.GetWorldRotation(); @@ -2473,7 +2413,7 @@ namespace Game.Entities { return $"{base.GetDebugInfo()}\nSpawnId: {GetSpawnId()} GoState: {GetGoState()} ScriptId: {GetScriptId()} AIName: {GetAIName()}"; } - + public bool IsAtInteractDistance(Player player, SpellInfo spell = null) { if (spell != null || (spell = GetSpellForLock(player)) != null) @@ -2518,7 +2458,7 @@ namespace Game.Entities { return IsInMap(player) && IsInPhase(player) && IsAtInteractDistance(player); } - + public SpellInfo GetSpellForLock(Player player) { if (!player) @@ -2563,7 +2503,6 @@ namespace Game.Entities return null; } - public void ModifyHealth(int change, WorldObject attackerOrHealer = null, uint spellId = 0) { if (m_goValue.Building.MaxHealth == 0 || change == 0) @@ -2742,9 +2681,14 @@ namespace Game.Entities public void SetGoState(GameObjectState state) { + GameObjectState oldState = GetGoState(); SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.State), (sbyte)state); if (GetAI() != null) GetAI().OnStateChanged(state); + + if (m_goTypeImpl != null) + m_goTypeImpl.OnStateChanged(oldState, state); + if (m_model != null && !IsTransport()) { if (!IsInWorld) @@ -2759,38 +2703,6 @@ namespace Game.Entities } } - public virtual uint GetTransportPeriod() - { - Cypher.Assert(GetGoInfo().type == GameObjectTypes.Transport); - if (!m_goValue.Transport.AnimationInfo.Path.Empty()) - return m_goValue.Transport.AnimationInfo.TotalTime; - - return 0; - } - - public void SetTransportState(GameObjectState state, uint stopFrame = 0) - { - if (GetGoState() == state) - return; - - Cypher.Assert(GetGoInfo().type == GameObjectTypes.Transport); - Cypher.Assert(state >= GameObjectState.TransportActive); - if (state == GameObjectState.TransportActive) - { - m_goValue.Transport.StateUpdateTimer = 0; - m_goValue.Transport.PathProgress = Time.GetMSTime(); - if (GetGoState() >= GameObjectState.TransportStopped) - m_goValue.Transport.PathProgress += m_goValue.Transport.StopFrames.LookupByIndex(GetGoState() - GameObjectState.TransportStopped); - SetGoState(GameObjectState.TransportActive); - } - else - { - Cypher.Assert(stopFrame < m_goValue.Transport.StopFrames.Count); - m_goValue.Transport.PathProgress = Time.GetMSTime() + m_goValue.Transport.StopFrames[(int)stopFrame]; - SetGoState((GameObjectState)((int)GameObjectState.TransportStopped + stopFrame)); - } - } - public void SetDisplayId(uint displayid) { SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.DisplayID), displayid); @@ -2985,6 +2897,33 @@ namespace Game.Entities base.ClearUpdateMask(remove); } + public List GetPauseTimes() + { + GameObjectType.Transport transport = m_goTypeImpl as GameObjectType.Transport; + if (transport != null) + return transport.GetPauseTimes(); + + return null; + } + + public void SetPathProgressForClient(float progress) + { + DoWithSuppressingObjectUpdates(() => + { + ObjectFieldData dynflagMask = new(); + dynflagMask.MarkChanged(m_objectData.DynamicFlags); + bool marked = (m_objectData.GetUpdateMask() & dynflagMask.GetUpdateMask()).IsAnySet(); + + uint dynamicFlags = (uint)GetDynamicFlags(); + dynamicFlags &= 0xFFFF; // remove high bits + dynamicFlags |= (uint)(progress * 65535.0f) << 16; + ReplaceAllDynamicFlags((GameObjectDynamicLowFlags)dynamicFlags); + + if (!marked) + m_objectData.ClearChanged(m_objectData.DynamicFlags); + }); + } + public void GetRespawnPosition(out float x, out float y, out float z, out float ori) { if (m_goData != null) @@ -2993,6 +2932,31 @@ namespace Game.Entities GetPosition(out x, out y, out z, out ori); } + public ITransport ToTransportBase() + { + switch (GetGoType()) + { + case GameObjectTypes.Transport: + return (GameObjectType.Transport)m_goTypeImpl; + case GameObjectTypes.MapObjTransport: + return (Transport)this; + default: + break; + } + + return null; + } + + public void AfterRelocation() + { + UpdateModelPosition(); + UpdatePositionData(); + if (m_goTypeImpl != null) + m_goTypeImpl.OnRelocated(); + + UpdateObjectVisibility(false); + } + public float GetInteractionDistance() { switch (GetGoType()) @@ -3239,7 +3203,7 @@ namespace Game.Entities return m_goValue.CapturePoint.State == BattlegroundCapturePointState.ContestedHorde || m_goValue.CapturePoint.State == BattlegroundCapturePointState.HordeCaptured; } - + public override ushort GetAIAnimKitId() { return _animKitId; } public uint GetWorldEffectID() { return _worldEffectID; } @@ -3308,6 +3272,12 @@ namespace Game.Entities public void ReplaceAllFlags(GameObjectFlags flags) { SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.Flags), (uint)flags); } public void SetLevel(uint level) { SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.Level), level); } + public GameObjectDynamicLowFlags GetDynamicFlags() { return (GameObjectDynamicLowFlags)(uint)m_objectData.DynamicFlags; } + public bool HasDynamicFlag(GameObjectDynamicLowFlags flag) { return (m_objectData.DynamicFlags & (uint)flag) != 0; } + public void SetDynamicFlag(GameObjectDynamicLowFlags flag) { SetUpdateFieldFlagValue(m_values.ModifyValue(m_objectData).ModifyValue(m_objectData.DynamicFlags), (uint)flag); } + public void RemoveDynamicFlag(GameObjectDynamicLowFlags flag) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(m_objectData).ModifyValue(m_objectData.DynamicFlags), (uint)flag); } + public void ReplaceAllDynamicFlags(GameObjectDynamicLowFlags flag) { SetUpdateFieldValue(m_values.ModifyValue(m_objectData).ModifyValue(m_objectData.DynamicFlags), (uint)flag); } + public GameObjectTypes GetGoType() { return (GameObjectTypes)(sbyte)m_gameObjectData.TypeID; } public void SetGoType(GameObjectTypes type) { SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.TypeID), (sbyte)type); } public GameObjectState GetGoState() { return (GameObjectState)(sbyte)m_gameObjectData.State; } @@ -3389,6 +3359,7 @@ namespace Game.Entities public override float GetStationaryY() { return StationaryPosition.GetPositionY(); } public override float GetStationaryZ() { return StationaryPosition.GetPositionZ(); } public override float GetStationaryO() { return StationaryPosition.GetOrientation(); } + public Position GetStationaryPosition() { return StationaryPosition; } public void RelocateStationaryPosition(float x, float y, float z, float o) { StationaryPosition.Relocate(x, y, z, o); } @@ -3407,6 +3378,12 @@ namespace Game.Entities Cell.VisitWorldObjects(this, deliverer, GetVisibilityRange()); } + void HandleCustomTypeCommand(GameObjectTypeBase.CustomCommand command) + { + if (m_goTypeImpl != null) + command.Execute(m_goTypeImpl); + } + public void CreateModel() { m_model = GameObjectModel.Create(new GameObjectModelOwnerImpl(this)); @@ -3419,8 +3396,9 @@ namespace Game.Entities public bool GetRespawnCompatibilityMode() { return m_respawnCompatibilityMode; } #region Fields - protected GameObjectFieldData m_gameObjectData; - protected GameObjectValue m_goValue; + public GameObjectFieldData m_gameObjectData; + GameObjectTypeBase m_goTypeImpl; + protected GameObjectValue m_goValue; // TODO: replace with m_goTypeImpl protected GameObjectTemplate m_goInfo; protected GameObjectTemplateAddon m_goTemplateAddon; GameObjectData m_goData; @@ -3509,6 +3487,26 @@ namespace Game.Entities GameObject _owner; } + // Base class for GameObject type specific implementations + class GameObjectTypeBase + { + protected GameObject _owner; + + public GameObjectTypeBase(GameObject owner) + { + _owner = owner; + } + + public virtual void Update(uint diff) { } + public virtual void OnStateChanged(GameObjectState oldState, GameObjectState newState) { } + public virtual void OnRelocated() { } + + public class CustomCommand + { + public virtual void Execute(GameObjectTypeBase type) { } + } + } + public struct GameObjectValue { public transport Transport; @@ -3549,4 +3547,387 @@ namespace Game.Entities public uint AssaultTimer; } } + + namespace GameObjectType + { + //11 GAMEOBJECT_TYPE_TRANSPORT + class Transport : GameObjectTypeBase, ITransport + { + TransportAnimation _animationInfo; + uint _pathProgress; + uint _stateChangeTime; + uint _stateChangeProgress; + List _stopFrames = new(); + bool _autoCycleBetweenStopFrames; + TimeTracker _positionUpdateTimer; + List _passengers = new(); + + static TimeSpan PositionUpdateInterval = TimeSpan.FromMilliseconds(50); + + public Transport(GameObject owner) : base(owner) + { + _animationInfo = Global.TransportMgr.GetTransportAnimInfo(owner.GetGoInfo().entry); + _pathProgress = GameTime.GetGameTimeMS() % GetTransportPeriod(); + _stateChangeTime = GameTime.GetGameTimeMS(); + _stateChangeProgress = _pathProgress; + + GameObjectTemplate goInfo = _owner.GetGoInfo(); + if (goInfo.Transport.Timeto2ndfloor > 0) + { + _stopFrames.Add(goInfo.Transport.Timeto2ndfloor); + if (goInfo.Transport.Timeto3rdfloor > 0) + { + _stopFrames.Add(goInfo.Transport.Timeto3rdfloor); + if (goInfo.Transport.Timeto4thfloor > 0) + { + _stopFrames.Add(goInfo.Transport.Timeto4thfloor); + if (goInfo.Transport.Timeto5thfloor > 0) + { + _stopFrames.Add(goInfo.Transport.Timeto5thfloor); + if (goInfo.Transport.Timeto6thfloor > 0) + { + _stopFrames.Add(goInfo.Transport.Timeto6thfloor); + if (goInfo.Transport.Timeto7thfloor > 0) + { + _stopFrames.Add(goInfo.Transport.Timeto7thfloor); + if (goInfo.Transport.Timeto8thfloor > 0) + { + _stopFrames.Add(goInfo.Transport.Timeto8thfloor); + if (goInfo.Transport.Timeto9thfloor > 0) + { + _stopFrames.Add(goInfo.Transport.Timeto9thfloor); + if (goInfo.Transport.Timeto10thfloor > 0) + _stopFrames.Add(goInfo.Transport.Timeto10thfloor); + } + } + } + } + } + } + } + } + + _positionUpdateTimer.Reset(PositionUpdateInterval); + } + + public override void Update(uint diff) + { + if (_animationInfo == null) + return; + + _positionUpdateTimer.Update(diff); + if (!_positionUpdateTimer.Passed()) + return; + + _positionUpdateTimer.Reset(PositionUpdateInterval); + + uint now = GameTime.GetGameTimeMS(); + uint period = GetTransportPeriod(); + uint newProgress = 0; + if (_stopFrames.Empty()) + newProgress = now % period; + else + { + int stopTargetTime = 0; + if (_owner.GetGoState() == GameObjectState.TransportActive) + stopTargetTime = 0; + else + stopTargetTime = (int)(_stopFrames[_owner.GetGoState() - GameObjectState.TransportStopped]); + + if (now < _owner.m_gameObjectData.Level) + { + int timeToStop = (int)(_owner.m_gameObjectData.Level - _stateChangeTime); + float stopSourcePathPct = (float)_stateChangeProgress / (float)period; + float stopTargetPathPct = (float)stopTargetTime / (float)period; + float timeSinceStopProgressPct = (float)(now - _stateChangeTime) / (float)timeToStop; + + float progressPct; + if (!_owner.HasDynamicFlag(GameObjectDynamicLowFlags.InvertedMovement)) + { + if (_owner.GetGoState() == GameObjectState.TransportActive) + stopTargetPathPct = 1.0f; + + float pathPctBetweenStops = stopTargetPathPct - stopSourcePathPct; + if (pathPctBetweenStops < 0.0f) + pathPctBetweenStops += 1.0f; + + progressPct = pathPctBetweenStops * timeSinceStopProgressPct + stopSourcePathPct; + if (progressPct > 1.0f) + progressPct = progressPct - 1.0f; + } + else + { + float pathPctBetweenStops = stopSourcePathPct - stopTargetPathPct; + if (pathPctBetweenStops < 0.0f) + pathPctBetweenStops += 1.0f; + + progressPct = stopSourcePathPct - pathPctBetweenStops * timeSinceStopProgressPct; + if (progressPct < 0.0f) + progressPct += 1.0f; + } + + newProgress = (uint)((float)period * progressPct) % period; + } + else + newProgress = (uint)stopTargetTime; + + if (newProgress == stopTargetTime && newProgress != _pathProgress) + { + uint eventId; + switch (_owner.GetGoState() - GameObjectState.TransportActive) + { + case 0: + eventId = _owner.GetGoInfo().Transport.Reached1stfloor; + break; + case 1: + eventId = _owner.GetGoInfo().Transport.Reached2ndfloor; + break; + case 2: + eventId = _owner.GetGoInfo().Transport.Reached3rdfloor; + break; + case 3: + eventId = _owner.GetGoInfo().Transport.Reached4thfloor; + break; + case 4: + eventId = _owner.GetGoInfo().Transport.Reached5thfloor; + break; + case 5: + eventId = _owner.GetGoInfo().Transport.Reached6thfloor; + break; + case 6: + eventId = _owner.GetGoInfo().Transport.Reached7thfloor; + break; + case 7: + eventId = _owner.GetGoInfo().Transport.Reached8thfloor; + break; + case 8: + eventId = _owner.GetGoInfo().Transport.Reached9thfloor; + break; + case 9: + eventId = _owner.GetGoInfo().Transport.Reached10thfloor; + break; + default: + eventId = 0u; + break; + } + + if (eventId != 0) + GameEvents.Trigger(eventId, _owner, null); + + if (_autoCycleBetweenStopFrames) + { + GameObjectState currentState = _owner.GetGoState(); + GameObjectState newState; + if (currentState == GameObjectState.TransportActive) + newState = GameObjectState.TransportStopped; + else if (currentState - GameObjectState.TransportActive == _stopFrames.Count) + newState = currentState - 1; + else if (_owner.HasDynamicFlag(GameObjectDynamicLowFlags.InvertedMovement)) + newState = currentState - 1; + else + newState = currentState + 1; + + _owner.SetGoState(newState); + } + } + } + + if (_pathProgress == newProgress) + return; + + _pathProgress = newProgress; + + TransportAnimationRecord oldAnimation = _animationInfo.GetPrevAnimNode(newProgress); + TransportAnimationRecord newAnimation = _animationInfo.GetNextAnimNode(newProgress); + if (oldAnimation != null && newAnimation != null) + { + Matrix4x4 pathRotation = Matrix4x4.CreateFromQuaternion(new Quaternion(_owner.m_gameObjectData.ParentRotation.GetValue().X, _owner.m_gameObjectData.ParentRotation.GetValue().Y, + _owner.m_gameObjectData.ParentRotation.GetValue().Z, _owner.m_gameObjectData.ParentRotation.GetValue().W)); + + Vector3 prev = new(oldAnimation.Pos.X, oldAnimation.Pos.Y, oldAnimation.Pos.Z); + Vector3 next = new(newAnimation.Pos.X, newAnimation.Pos.Y, newAnimation.Pos.Z); + + float animProgress = (float)(newProgress - oldAnimation.TimeIndex) / (float)(newAnimation.TimeIndex - oldAnimation.TimeIndex); + + Vector3 dst = Vector3.Transform(Vector3.Lerp(prev, next, animProgress), pathRotation);//todo check this + + dst += _owner.GetStationaryPosition(); + + _owner.GetMap().GameObjectRelocation(_owner, dst.X, dst.Y, dst.Z, _owner.GetOrientation()); + } + + TransportRotationRecord oldRotation = _animationInfo.GetPrevAnimRotation(newProgress); + TransportRotationRecord newRotation = _animationInfo.GetNextAnimRotation(newProgress); + if (oldRotation != null && newRotation != null) + { + Quaternion prev = new(oldRotation.Rot[0], oldRotation.Rot[1], oldRotation.Rot[2], oldRotation.Rot[3]); + Quaternion next = new(newRotation.Rot[0], newRotation.Rot[1], newRotation.Rot[2], newRotation.Rot[3]); + + float animProgress = (float)(newProgress - oldRotation.TimeIndex) / (float)(newRotation.TimeIndex - oldRotation.TimeIndex); + + Quaternion rotation = Quaternion.Lerp(prev, next, animProgress); + + _owner.SetLocalRotation(rotation.X, rotation.Y, rotation.Z, rotation.W); + _owner.UpdateModelPosition(); + } + + // update progress marker for client + _owner.SetPathProgressForClient((float)_pathProgress / (float)period); + } + + public override void OnStateChanged(GameObjectState oldState, GameObjectState newState) + { + Cypher.Assert(newState >= GameObjectState.TransportActive); + + // transports without stop frames just keep animating in state 24 + if (_stopFrames.Empty()) + { + if (newState != GameObjectState.TransportActive) + _owner.SetGoState(GameObjectState.TransportActive); + return; + } + + uint stopPathProgress = 0; + + if (newState != GameObjectState.TransportActive) + { + Cypher.Assert(newState < (GameObjectState)(GameObjectState.TransportStopped + 9)); + int stopFrame = (int)(newState - GameObjectState.TransportStopped); + Cypher.Assert(stopFrame < _stopFrames.Count); + stopPathProgress = _stopFrames[stopFrame]; + } + + _stateChangeTime = GameTime.GetGameTimeMS(); + _stateChangeProgress = _pathProgress; + uint timeToStop = (uint)Math.Abs(_pathProgress - stopPathProgress); + _owner.SetLevel(GameTime.GetGameTimeMS() + timeToStop); + _owner.SetPathProgressForClient((float)_pathProgress / (float)GetTransportPeriod()); + + if (oldState == GameObjectState.Active || oldState == newState) + { + // initialization + if (_pathProgress > stopPathProgress) + _owner.SetDynamicFlag(GameObjectDynamicLowFlags.InvertedMovement); + else + _owner.RemoveDynamicFlag(GameObjectDynamicLowFlags.InvertedMovement); + + return; + } + + int pauseTimesCount = _stopFrames.Count; + int newToOldStateDelta = newState - oldState; + if (newToOldStateDelta < 0) + newToOldStateDelta += pauseTimesCount + 1; + + int oldToNewStateDelta = oldState - newState; + if (oldToNewStateDelta < 0) + oldToNewStateDelta += pauseTimesCount + 1; + + if (oldToNewStateDelta < newToOldStateDelta) + _owner.SetDynamicFlag(GameObjectDynamicLowFlags.InvertedMovement); + else + _owner.RemoveDynamicFlag(GameObjectDynamicLowFlags.InvertedMovement); + } + + public override void OnRelocated() + { + UpdatePassengerPositions(); + } + + public void UpdatePassengerPositions() + { + foreach (WorldObject passenger in _passengers) + { + float x, y, z, o; + passenger.m_movementInfo.transport.pos.GetPosition(out x, out y, out z, out o); + CalculatePassengerPosition(ref x, ref y, ref z, ref o); + ITransport.UpdatePassengerPosition(this, _owner.GetMap(), passenger, x, y, z, o, true); + } + } + + public uint GetTransportPeriod() + { + if (_animationInfo != null) + return _animationInfo.TotalTime; + + return 1; + } + + public List GetPauseTimes() + { + return _stopFrames; + } + + public ObjectGuid GetTransportGUID() { return _owner.GetGUID(); } + + public float GetTransportOrientation() { return _owner.GetOrientation(); } + + public void AddPassenger(WorldObject passenger) + { + if (!_owner.IsInWorld) + return; + + if (!_passengers.Contains(passenger)) + { + _passengers.Add(passenger); + passenger.SetTransport(this); + passenger.m_movementInfo.transport.guid = GetTransportGUID(); + Log.outDebug(LogFilter.Transport, $"Object {passenger.GetName()} boarded transport {_owner.GetName()}."); + } + } + + public ITransport RemovePassenger(WorldObject passenger) + { + if (_passengers.Remove(passenger)) + { + passenger.SetTransport(null); + passenger.m_movementInfo.transport.Reset(); + Log.outDebug(LogFilter.Transport, $"Object {passenger.GetName()} removed from transport {_owner.GetName()}."); + + Player plr = passenger.ToPlayer(); + if (plr != null) + plr.SetFallInformation(0, plr.GetPositionZ()); + } + + return this; + } + + public void CalculatePassengerPosition(ref float x, ref float y, ref float z, ref float o) + { + ITransport.CalculatePassengerPosition(ref x, ref y, ref z, ref o, _owner.GetPositionX(), _owner.GetPositionY(), _owner.GetPositionZ(), _owner.GetOrientation()); + } + + public void CalculatePassengerOffset(ref float x, ref float y, ref float z, ref float o) + { + ITransport.CalculatePassengerOffset(ref x, ref y, ref z, ref o, _owner.GetPositionX(), _owner.GetPositionY(), _owner.GetPositionZ(), _owner.GetOrientation()); + } + + public int GetMapIdForSpawning() + { + return _owner.GetGoInfo().Transport.SpawnMap; + } + + public void SetAutoCycleBetweenStopFrames(bool on) + { + _autoCycleBetweenStopFrames = on; + } + } + + class SetTransportAutoCycleBetweenStopFrames : GameObjectTypeBase.CustomCommand + { + bool _on; + + public SetTransportAutoCycleBetweenStopFrames(bool on) + { + _on = on; + } + + public override void Execute(GameObjectTypeBase type) + { + Transport transport = (Transport)type; + if (transport != null) + transport.SetAutoCycleBetweenStopFrames(_on); + } + } + } } \ No newline at end of file diff --git a/Source/Game/Entities/GameObject/GameObjectData.cs b/Source/Game/Entities/GameObject/GameObjectData.cs index 1ebc8db51..56728736c 100644 --- a/Source/Game/Entities/GameObject/GameObjectData.cs +++ b/Source/Game/Entities/GameObject/GameObjectData.cs @@ -544,6 +544,8 @@ namespace Game.Entities return SpellFocus.GiganticAOI != 0; case GameObjectTypes.Goober: return Goober.GiganticAOI != 0; + case GameObjectTypes.Transport: + return true; case GameObjectTypes.SpellCaster: return SpellCaster.GiganticAOI != 0; case GameObjectTypes.FlagStand: diff --git a/Source/Game/Entities/Object/Update/UpdateFields.cs b/Source/Game/Entities/Object/Update/UpdateFields.cs index fc2ea4374..0e34dd608 100644 --- a/Source/Game/Entities/Object/Update/UpdateFields.cs +++ b/Source/Game/Entities/Object/Update/UpdateFields.cs @@ -79,27 +79,27 @@ namespace Game.Entities uint GetViewerDependentDynamicFlags(ObjectFieldData objectData, WorldObject obj, Player receiver) { - UnitDynFlags unitDynFlags = (UnitDynFlags)(uint)objectData.DynamicFlags; + uint unitDynFlags = objectData.DynamicFlags; Unit unit = obj.ToUnit(); if (unit != null) { - unitDynFlags &= ~UnitDynFlags.Tapped; + unitDynFlags &= ~(uint)UnitDynFlags.Tapped; Creature creature = obj.ToCreature(); if (creature != null) { if (creature.HasLootRecipient() && !creature.IsTappedBy(receiver)) - unitDynFlags |= UnitDynFlags.Tapped; + unitDynFlags |= (uint)UnitDynFlags.Tapped; if (!receiver.IsAllowedToLoot(creature)) - unitDynFlags &= ~UnitDynFlags.Lootable; + unitDynFlags &= ~(uint)UnitDynFlags.Lootable; } // unit UNIT_DYNFLAG_TRACK_UNIT should only be sent to caster of SPELL_AURA_MOD_STALKED auras - if (unitDynFlags.HasAnyFlag(UnitDynFlags.TrackUnit)) + if (unitDynFlags.HasAnyFlag((uint)UnitDynFlags.TrackUnit)) if (!unit.HasAuraTypeWithCaster(AuraType.ModStalked, receiver.GetGUID())) - unitDynFlags &= ~UnitDynFlags.TrackUnit; + unitDynFlags &= ~(uint)UnitDynFlags.TrackUnit; } else { @@ -126,12 +126,18 @@ namespace Game.Entities dynFlags |= GameObjectDynamicLowFlags.Sparkle | GameObjectDynamicLowFlags.Highlight; break; case GameObjectTypes.Transport: + { + dynFlags = (GameObjectDynamicLowFlags)((int)unitDynFlags & 0xFFFF); + pathProgress = (ushort)((int)unitDynFlags >> 16); + break; + } case GameObjectTypes.MapObjTransport: { - uint transportPeriod = gameObject.GetTransportPeriod(); + Transport transport = gameObject.ToTransport(); + uint transportPeriod = transport.GetTransportPeriod(); if (transportPeriod != 0) { - float timer = (float)(gameObject.GetGoValue().Transport.PathProgress % transportPeriod); + float timer = (float)(transport.GetTimer() % transportPeriod); pathProgress = (ushort)(timer / (float)transportPeriod * 65535.0f); } break; @@ -146,11 +152,11 @@ namespace Game.Entities break; } - unitDynFlags = (UnitDynFlags)((pathProgress << 16) | (ushort)dynFlags); + unitDynFlags = (uint)((pathProgress << 16) | (ushort)dynFlags); } } - return (uint)unitDynFlags; + return unitDynFlags; } } @@ -4946,13 +4952,13 @@ namespace Game.Entities data.WriteFloat(rotation.Z); data.WriteFloat(rotation.W); data.WriteUInt32(FactionTemplate); - data.WriteInt8(GetViewerGameObjectState(this, owner, receiver)); + data.WriteInt8(State); data.WriteInt8(TypeID); data.WriteUInt8(PercentHealth); data.WriteUInt32(ArtKit); data.WriteInt32(EnableDoodadSets.Size()); data.WriteUInt32(CustomParam); - data.WriteUInt32(GetViewerGameObjectLevel(this, owner, receiver)); + data.WriteUInt32(Level); data.WriteUInt32(AnimGroupInstance); for (int i = 0; i < EnableDoodadSets.Size(); ++i) { @@ -5053,7 +5059,7 @@ namespace Game.Entities } if (changesMask[14]) { - data.WriteInt8(GetViewerGameObjectState(this, owner, receiver)); + data.WriteInt8(State); } if (changesMask[15]) { @@ -5073,7 +5079,7 @@ namespace Game.Entities } if (changesMask[19]) { - data.WriteUInt32(GetViewerGameObjectLevel(this, owner, receiver)); + data.WriteUInt32(Level); } if (changesMask[20]) { @@ -5116,24 +5122,6 @@ namespace Game.Entities return flags; } - - uint GetViewerGameObjectLevel(GameObjectFieldData gameObjectData, GameObject gameObject, Player receiver) - { - uint level = gameObjectData.Level; - bool isStoppableTransport = gameObject.GetGoType() == GameObjectTypes.Transport && !gameObject.GetGoValue().Transport.StopFrames.Empty(); - return isStoppableTransport ? gameObject.GetGoValue().Transport.PathProgress : level; - } - - sbyte GetViewerGameObjectState(GameObjectFieldData gameObjectData, GameObject gameObject, Player receiver) - { - sbyte state = gameObjectData.State; - bool isStoppableTransport = gameObject.GetGoType() == GameObjectTypes.Transport && !gameObject.GetGoValue().Transport.StopFrames.Empty(); - if (isStoppableTransport && gameObject.GetGoState() == GameObjectState.TransportActive) - if (((gameObject.GetGoValue().Transport.StateUpdateTimer / 20000) & 1) != 0) - state = (sbyte)GameObjectState.TransportStopped; - - return state; - } } public class DynamicObjectData : BaseUpdateData diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index cbe724358..67519eeeb 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -246,14 +246,10 @@ namespace Game.Entities public void BuildMovementUpdate(WorldPacket data, CreateObjectBits flags, Player target) { - int PauseTimesCount = 0; - + List PauseTimes = null; GameObject go = ToGameObject(); if (go) - { - if (go.GetGoType() == GameObjectTypes.Transport) - PauseTimesCount = go.GetGoValue().Transport.StopFrames.Count; - } + PauseTimes = go.GetPauseTimes(); data.WriteBit(flags.NoBirthAnim); data.WriteBit(flags.EnablePortals); @@ -368,7 +364,7 @@ namespace Game.Entities MovementExtensions.WriteCreateObjectSplineDataBlock(unit.MoveSpline, data); } - data.WriteInt32(PauseTimesCount); + data.WriteInt32(PauseTimes != null ? PauseTimes.Count : 0); if (flags.Stationary) { @@ -381,20 +377,9 @@ namespace Game.Entities if (flags.CombatVictim) data.WritePackedGuid(ToUnit().GetVictim().GetGUID()); // CombatVictim - + if (flags.ServerTime) - { - GameObject go1 = 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 (go1 && go1.ToTransport()) // ServerTime - data.WriteUInt32(go1.GetGoValue().Transport.PathProgress); - else - data.WriteUInt32(GameTime.GetGameTimeMS()); - } + data.WriteUInt32(GameTime.GetGameTimeMS()); if (flags.Vehicle) { @@ -413,11 +398,9 @@ namespace Game.Entities if (flags.Rotation) data.WriteInt64(ToGameObject().GetPackedLocalRotation()); // Rotation - if (go) - { - for (int i = 0; i < PauseTimesCount; ++i) - data.WriteUInt32(go.GetGoValue().Transport.StopFrames[i]); - } + if (PauseTimes != null && !PauseTimes.Empty()) + foreach (var stopFrame in PauseTimes) + data.WriteUInt32(stopFrame); if (flags.MovementTransport) { @@ -969,19 +952,9 @@ namespace Game.Entities return; if (on) - { - if (IsTypeId(TypeId.Unit)) - map.AddToActive(ToCreature()); - else if (IsTypeId(TypeId.DynamicObject)) - map.AddToActive(ToDynamicObject()); - } + map.AddToActive(this); else - { - if (IsTypeId(TypeId.Unit)) - map.RemoveFromActive(ToCreature()); - else if (IsTypeId(TypeId.DynamicObject)) - map.RemoveFromActive(ToDynamicObject()); - } + map.RemoveFromActive(this); } bool IsFarVisible() { return m_isFarVisible; } @@ -1010,8 +983,8 @@ namespace Game.Entities if (IsInWorld) RemoveFromWorld(); - Transport transport = GetTransport(); - if (transport) + ITransport transport = GetTransport(); + if (transport != null) transport.RemovePassenger(this); } @@ -2957,12 +2930,6 @@ namespace Game.Entities public float GetObjectScale() { return m_objectData.Scale; } public virtual void SetObjectScale(float scale) { SetUpdateFieldValue(m_values.ModifyValue(m_objectData).ModifyValue(m_objectData.Scale), scale); } - public UnitDynFlags GetDynamicFlags() { return (UnitDynFlags)(uint)m_objectData.DynamicFlags; } - public bool HasDynamicFlag(UnitDynFlags flag) { return (m_objectData.DynamicFlags & (uint)flag) != 0; } - public void SetDynamicFlag(UnitDynFlags flag) { SetUpdateFieldFlagValue(m_values.ModifyValue(m_objectData).ModifyValue(m_objectData.DynamicFlags), (uint)flag); } - public void RemoveDynamicFlag(UnitDynFlags flag) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(m_objectData).ModifyValue(m_objectData.DynamicFlags), (uint)flag); } - public void ReplaceAllDynamicFlags(UnitDynFlags flag) { SetUpdateFieldValue(m_values.ModifyValue(m_objectData).ModifyValue(m_objectData.DynamicFlags), (uint)flag); } - public TypeId GetTypeId() { return ObjectTypeId; } public bool IsTypeId(TypeId typeId) { return GetTypeId() == typeId; } public bool IsTypeMask(TypeMask mask) { return Convert.ToBoolean(mask & ObjectTypeMask); } @@ -3007,7 +2974,11 @@ namespace Game.Entities public bool IsActiveObject() { return m_isActive; } public bool IsPermanentWorldObject() { return m_isWorldObject; } - public Transport GetTransport() { return m_transport; } + public ITransport GetTransport() { return m_transport; } + public T GetTransport() where T : ITransport + { + return (T)m_transport; + } public float GetTransOffsetX() { return m_movementInfo.transport.pos.GetPositionX(); } public float GetTransOffsetY() { return m_movementInfo.transport.pos.GetPositionY(); } public float GetTransOffsetZ() { return m_movementInfo.transport.pos.GetPositionZ(); } @@ -3017,12 +2988,12 @@ namespace Game.Entities public sbyte GetTransSeat() { return m_movementInfo.transport.seat; } public virtual ObjectGuid GetTransGUID() { - if (GetTransport()) - return GetTransport().GetGUID(); + if (GetTransport() != null) + return GetTransport().GetTransportGUID(); return ObjectGuid.Empty; } - public void SetTransport(Transport t) { m_transport = t; } + public void SetTransport(ITransport t) { m_transport = t; } public virtual float GetStationaryX() { return GetPositionX(); } public virtual float GetStationaryY() { return GetPositionY(); } @@ -3065,7 +3036,7 @@ namespace Game.Entities Position thisOrTransport = this; Position objOrObjTransport = obj; - if (GetTransport() && obj.GetTransport() != null && obj.GetTransport().GetGUID() == GetTransport().GetGUID()) + if (GetTransport() != null && obj.GetTransport() != null && obj.GetTransport().GetTransportGUID() == GetTransport().GetTransportGUID()) { thisOrTransport = m_movementInfo.transport.pos; objOrObjTransport = obj.m_movementInfo.transport.pos; @@ -3340,7 +3311,7 @@ namespace Game.Entities public void UpdateAllowedPositionZ(float x, float y, ref float z, ref float groundZ) { // TODO: Allow transports to be part of dynamic vmap tree - if (GetTransport()) + if (GetTransport() != null) { groundZ = z; return; @@ -3690,7 +3661,7 @@ namespace Game.Entities bool m_isWorldObject; public ZoneScript m_zoneScript; - Transport m_transport; + ITransport m_transport; Map _currMap; uint instanceId; PhaseShift _phaseShift = new(); diff --git a/Source/Game/Entities/Player/Player.DB.cs b/Source/Game/Entities/Player/Player.DB.cs index 7f1f9ae22..5ef583f3d 100644 --- a/Source/Game/Entities/Player/Player.DB.cs +++ b/Source/Game/Entities/Player/Player.DB.cs @@ -3429,8 +3429,9 @@ namespace Game.Entities stmt.AddValue(index++, finiteAlways(GetTransOffsetZ())); stmt.AddValue(index++, finiteAlways(GetTransOffsetO())); ulong transLowGUID = 0; - if (GetTransport()) - transLowGUID = GetTransport().GetGUID().GetCounter(); + Transport transport = GetTransport(); + if (transport != null) + transLowGUID = transport.GetGUID().GetCounter(); stmt.AddValue(index++, transLowGUID); StringBuilder ss = new(); @@ -3570,8 +3571,9 @@ namespace Game.Entities stmt.AddValue(index++, finiteAlways(GetTransOffsetZ())); stmt.AddValue(index++, finiteAlways(GetTransOffsetO())); ulong transLowGUID = 0; - if (GetTransport()) - transLowGUID = GetTransport().GetGUID().GetCounter(); + Transport transport = GetTransport(); + if (transport != null) + transLowGUID = transport.GetGUID().GetCounter(); stmt.AddValue(index++, transLowGUID); StringBuilder ss = new(); diff --git a/Source/Game/Entities/Player/Player.Map.cs b/Source/Game/Entities/Player/Player.Map.cs index d2f827de9..11b11983b 100644 --- a/Source/Game/Entities/Player/Player.Map.cs +++ b/Source/Game/Entities/Player/Player.Map.cs @@ -803,7 +803,7 @@ namespace Game.Entities m_MirrorTimerFlags |= PlayerUnderwaterState.InWater; // Fatigue bar state (if not on flight path or transport) - if (newLiquidData.type_flags.HasAnyFlag(LiquidHeaderTypeFlags.DarkWater) && !IsInFlight() && !GetTransport()) + if (newLiquidData.type_flags.HasAnyFlag(LiquidHeaderTypeFlags.DarkWater) && !IsInFlight() && GetTransport() == null) m_MirrorTimerFlags |= PlayerUnderwaterState.InDarkWater; // Lava state (any contact) diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index 90dc77fc6..f760ddf55 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -1686,8 +1686,8 @@ namespace Game.Entities { Log.outDebug(LogFilter.Maps, "Player {0} using client without required expansion tried teleport to non accessible map {1}", GetName(), mapid); - Transport _transport = GetTransport(); - if (_transport) + ITransport _transport = GetTransport(); + if (_transport != null) { _transport.RemovePassenger(this); RepopAtGraveyard(); // teleport to near graveyard if on transport, looks blizz like :) @@ -1708,8 +1708,8 @@ namespace Game.Entities DisableSpline(); GetMotionMaster().Remove(MovementGeneratorType.Effect); - Transport transport = GetTransport(); - if (transport) + ITransport transport = GetTransport(); + if (transport != null) { if (!options.HasAnyFlag(TeleportToOptions.NotLeaveTransport)) transport.RemovePassenger(this); @@ -1849,11 +1849,11 @@ namespace Game.Entities transferPending.MapID = (int)mapid; transferPending.OldMapPosition = GetPosition(); - transport = GetTransport(); - if (transport) + Transport transport1 = (Transport)GetTransport(); + if (transport1 != null) { TransferPending.ShipTransferPending shipTransferPending = new(); - shipTransferPending.Id = transport.GetEntry(); + shipTransferPending.Id = transport1.GetEntry(); shipTransferPending.OriginMapID = (int)GetMapId(); transferPending.Ship = shipTransferPending; } @@ -6242,14 +6242,6 @@ namespace Game.Entities { switch (target.GetTypeId()) { - case TypeId.GameObject: - // @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.ToGameObject().IsDynTransport()) - s64.Add(target.GetGUID()); - break; case TypeId.Unit: s64.Add(target.GetGUID()); v.Add(target.ToCreature()); diff --git a/Source/Game/Entities/Transport.cs b/Source/Game/Entities/Transport.cs index a45f2bee9..23715a82b 100644 --- a/Source/Game/Entities/Transport.cs +++ b/Source/Game/Entities/Transport.cs @@ -27,37 +27,102 @@ namespace Game.Entities { public interface ITransport { + ObjectGuid GetTransportGUID(); + // This method transforms supplied transport offsets into global coordinates void CalculatePassengerPosition(ref float x, ref float y, ref float z, ref float o); // This method transforms supplied global coordinates into local offsets void CalculatePassengerOffset(ref float x, ref float y, ref float z, ref float o); - } - public class TransportPosHelper - { - public static void CalculatePassengerPosition(ref float x, ref float y, ref float z, ref float o, float transX, float transY, float transZ, float transO) + float GetTransportOrientation(); + + void AddPassenger(WorldObject passenger); + + ITransport RemovePassenger(WorldObject passenger); + + public static void UpdatePassengerPosition(ITransport transport, Map map, WorldObject passenger, float x, float y, float z, float o, bool setHomePosition) + { + // transport teleported but passenger not yet (can happen for players) + if (passenger.GetMap() != map) + return; + + // 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 + Unit unit = passenger.ToUnit(); + if (unit != null) + if (unit.GetVehicle()) + return; + + // Do not use Unit::UpdatePosition here, we don't want to remove auras + // as if regular movement occurred + switch (passenger.GetTypeId()) + { + case TypeId.Unit: + { + Creature creature = passenger.ToCreature(); + map.CreatureRelocation(creature, x, y, z, o, false); + if (setHomePosition) + { + creature.GetTransportHomePosition(out x, out y, out z, out o); + transport.CalculatePassengerPosition(ref x, ref y, ref z, ref 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()) + { + map.PlayerRelocation(passenger.ToPlayer(), x, y, z, o); + passenger.ToPlayer().SetFallInformation(0, passenger.GetPositionZ()); + } + break; + case TypeId.GameObject: + map.GameObjectRelocation(passenger.ToGameObject(), x, y, z, o, false); + passenger.ToGameObject().RelocateStationaryPosition(x, y, z, o); + break; + case TypeId.DynamicObject: + map.DynamicObjectRelocation(passenger.ToDynamicObject(), x, y, z, o); + break; + case TypeId.AreaTrigger: + map.AreaTriggerRelocation(passenger.ToAreaTrigger(), x, y, z, o); + break; + default: + break; + } + + if (unit != null) + { + Vehicle vehicle = unit.GetVehicleKit(); + if (vehicle != null) + vehicle.RelocatePassengers(); + } + } + + static void CalculatePassengerPosition(ref float x, ref float y, ref float z, ref float o, float transX, float transY, float transZ, float transO) { float inx = x, iny = y, inz = z; o = Position.NormalizeOrientation(transO + o); - x = transX + inx * (float)Math.Cos(transO) - iny * (float)Math.Sin(transO); - y = transY + iny * (float)Math.Cos(transO) + inx * (float)Math.Sin(transO); + x = transX + inx * MathF.Cos(transO) - iny * MathF.Sin(transO); + y = transY + iny * MathF.Cos(transO) + inx * MathF.Sin(transO); z = transZ + inz; } - public static void CalculatePassengerOffset(ref float x, ref float y, ref float z, ref float o, float transX, float transY, float transZ, float transO) + static void CalculatePassengerOffset(ref float x, ref float y, ref float z, ref float o, float transX, float transY, float transZ, float transO) { o = Position.NormalizeOrientation(o - transO); z -= transZ; - y -= transY; - x -= transX; - + y -= transY; // y = searchedY * std::cos(o) + searchedX * std::sin(o) + x -= transX; // x = searchedX * std::cos(o) + searchedY * std::sin(o + pi) float inx = x, iny = y; - y = (iny - inx * (float)Math.Tan(transO)) / ((float)Math.Cos(transO) + (float)Math.Sin(transO) * (float)Math.Tan(transO)); - x = (inx + iny * (float)Math.Tan(transO)) / ((float)Math.Cos(transO) + (float)Math.Sin(transO) * (float)Math.Tan(transO)); + y = (iny - inx * MathF.Tan(transO)) / (MathF.Cos(transO) + MathF.Sin(transO) * MathF.Tan(transO)); + x = (inx + iny * MathF.Tan(transO)) / (MathF.Cos(transO) + MathF.Sin(transO) * MathF.Tan(transO)); } + + int GetMapIdForSpawning(); } public class Transport : GameObject, ITransport @@ -122,7 +187,6 @@ namespace Game.Entities ReplaceAllFlags(goOverride.Flags); } - m_goValue.Transport.PathProgress = 0; SetObjectScale(goinfo.size); SetPeriod(tInfo.pathTime); SetEntry(goinfo.entry); @@ -163,9 +227,9 @@ namespace Game.Entities return; if (IsMoving() || !_pendingStop) - m_goValue.Transport.PathProgress += diff; + _pathProgress += diff; - uint timer = m_goValue.Transport.PathProgress % GetTransportPeriod(); + uint timer = _pathProgress % GetTransportPeriod(); bool justStopped = false; // Set current waypoint @@ -189,9 +253,9 @@ namespace Game.Entities if (_pendingStop && GetGoState() != GameObjectState.Ready) { SetGoState(GameObjectState.Ready); - m_goValue.Transport.PathProgress /= GetTransportPeriod(); - m_goValue.Transport.PathProgress *= GetTransportPeriod(); - m_goValue.Transport.PathProgress += _currentFrame.ArriveTime; + _pathProgress /= GetTransportPeriod(); + _pathProgress *= GetTransportPeriod(); + _pathProgress += _currentFrame.ArriveTime; } break; // its a stop frame and we are waiting } @@ -296,7 +360,7 @@ namespace Game.Entities } } - public void RemovePassenger(WorldObject passenger) + public ITransport RemovePassenger(WorldObject passenger) { bool erased = _passengers.Remove(passenger); @@ -313,6 +377,8 @@ namespace Game.Entities plr.SetFallInformation(0, plr.GetPositionZ()); } } + + return this; } public Creature CreateNPCPassenger(ulong guid, CreatureData data) @@ -511,14 +577,19 @@ namespace Game.Entities public void CalculatePassengerPosition(ref float x, ref float y, ref float z, ref float o) { - TransportPosHelper.CalculatePassengerPosition(ref x, ref y, ref z, ref o, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); + ITransport.CalculatePassengerPosition(ref x, ref y, ref z, ref o, GetPositionX(), GetPositionY(), GetPositionZ(), GetTransportOrientation()); } public void CalculatePassengerOffset(ref float x, ref float y, ref float z, ref float o) { - TransportPosHelper.CalculatePassengerOffset(ref x, ref y, ref z, ref o, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); + ITransport.CalculatePassengerOffset(ref x, ref y, ref z, ref o, GetPositionX(), GetPositionY(), GetPositionZ(), GetTransportOrientation()); } + public int GetMapIdForSpawning() + { + return GetGoInfo().MoTransport.SpawnMap; + } + public void UpdatePosition(float x, float y, float z, float o) { bool newActive = GetMap().IsGridLoaded(x, y); @@ -652,7 +723,7 @@ namespace Game.Entities float destX, destY, destZ, destO; obj.m_movementInfo.transport.pos.GetPosition(out destX, out destY, out destZ, out destO); - TransportPosHelper.CalculatePassengerPosition(ref destX, ref destY, ref destZ, ref destO, x, y, z, o); + ITransport.CalculatePassengerPosition(ref destX, ref destY, ref destZ, ref destO, x, y, z, o); obj.ToUnit().NearTeleportTo(destX, destY, destZ, destO); } @@ -683,7 +754,7 @@ namespace Game.Entities { float destX, destY, destZ, destO; obj.m_movementInfo.transport.pos.GetPosition(out destX, out destY, out destZ, out destO); - TransportPosHelper.CalculatePassengerPosition(ref destX, ref destY, ref destZ, ref destO, x, y, z, o); + ITransport.CalculatePassengerPosition(ref destX, ref destY, ref destZ, ref destO, x, y, z, o); switch (obj.GetTypeId()) { @@ -707,62 +778,12 @@ namespace Game.Entities void UpdatePassengerPositions(HashSet passengers) { - foreach (var passenger in passengers) + foreach (WorldObject passenger in passengers) { - // 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 - Unit unit = passenger.ToUnit(); - if (unit) - if (unit.GetVehicle() != null) - 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(out x, out y, out z, out o); CalculatePassengerPosition(ref x, ref y, ref z, ref o); - switch (passenger.GetTypeId()) - { - case TypeId.Unit: - { - Creature creature = passenger.ToCreature(); - GetMap().CreatureRelocation(creature, x, y, z, o, false); - creature.GetTransportHomePosition(out x, out y, out z, out o); - CalculatePassengerPosition(ref x, ref y, ref z, ref o); - creature.SetHomePosition(x, y, z, o); - break; - } - case TypeId.Player: - 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.ToDynamicObject(), x, y, z, o); - break; - case TypeId.AreaTrigger: - GetMap().AreaTriggerRelocation(passenger.ToAreaTrigger(), x, y, z, o); - break; - default: - break; - } - - if (unit != null) - { - Vehicle vehicle = unit.GetVehicleKit(); - if (vehicle != null) - vehicle.RelocatePassengers(); - } + ITransport.UpdatePassengerPosition(this, GetMap(), passenger, x, y, z, o, true); } } @@ -790,9 +811,12 @@ namespace Game.Entities public HashSet GetPassengers() { return _passengers; } - public override uint GetTransportPeriod() { return m_gameObjectData.Level; } + public ObjectGuid GetTransportGUID() { return GetGUID(); } + public float GetTransportOrientation() { return GetOrientation(); } + + public uint GetTransportPeriod() { return m_gameObjectData.Level; } public void SetPeriod(uint period) { SetLevel(period); } - uint GetTimer() { return m_goValue.Transport.PathProgress; } + public uint GetTimer() { return _pathProgress; } public List GetKeyFrames() { return _transportInfo.keyFrames; } public TransportTemplate GetTransportTemplate() { return _transportInfo; } @@ -805,6 +829,7 @@ namespace Game.Entities KeyFrame _currentFrame; int _nextFrame; + uint _pathProgress; TimeTracker _positionChangeTimer = new(); bool _isMoving; bool _pendingStop; diff --git a/Source/Game/Entities/Unit/Unit.Movement.cs b/Source/Game/Entities/Unit/Unit.Movement.cs index 422052938..01a09012a 100644 --- a/Source/Game/Entities/Unit/Unit.Movement.cs +++ b/Source/Game/Entities/Unit/Unit.Movement.cs @@ -209,7 +209,7 @@ namespace Game.Entities MoveSplineInit init = new(this); init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZ(), false); - if (GetTransport()) + if (GetTransport() != null) init.DisableTransportPathTransformations(); // It makes no sense to target global orientation init.SetFacing(ori); @@ -1834,7 +1834,7 @@ namespace Game.Entities if (GetVehicle() != null) return GetVehicleBase().GetGUID(); if (GetTransport() != null) - return GetTransport().GetGUID(); + return GetTransport().GetTransportGUID(); return ObjectGuid.Empty; } diff --git a/Source/Game/Entities/Unit/Unit.Pets.cs b/Source/Game/Entities/Unit/Unit.Pets.cs index d7cbb24e6..aac84a0e2 100644 --- a/Source/Game/Entities/Unit/Unit.Pets.cs +++ b/Source/Game/Entities/Unit/Unit.Pets.cs @@ -295,7 +295,7 @@ namespace Game.Entities return false; } - if (IsTypeId(TypeId.Player) && ToPlayer().GetTransport()) + if (IsPlayer() && ToPlayer().GetTransport() != null) { Log.outFatal(LogFilter.Unit, "Unit:SetCharmedBy: Player on transport is trying to charm {0} (GUID {1})", GetEntry(), GetGUID().ToString()); return false; diff --git a/Source/Game/Entities/Unit/Unit.cs b/Source/Game/Entities/Unit/Unit.cs index 663a60120..0398e4083 100644 --- a/Source/Game/Entities/Unit/Unit.cs +++ b/Source/Game/Entities/Unit/Unit.cs @@ -1003,7 +1003,7 @@ namespace Game.Entities } Cypher.Assert(!m_vehicle); - vehicle.AddPassenger(this, seatId); + vehicle.AddVehiclePassenger(this, seatId); } public void ChangeSeat(sbyte seatId, bool next = true) @@ -1067,7 +1067,7 @@ namespace Game.Entities // This should be done before dismiss, because there may be some aura removal VehicleSeatAddon seatAddon = m_vehicle.GetSeatAddonForSeatOfPassenger(this); - Vehicle vehicle = m_vehicle.RemovePassenger(this); + Vehicle vehicle = (Vehicle)m_vehicle.RemovePassenger(this); if (vehicle == null) { @@ -1998,6 +1998,12 @@ namespace Game.Entities public void RemoveUnitFlag3(UnitFlags3 flags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Flags3), (uint)flags); } public void ReplaceAllUnitFlags3(UnitFlags3 flags) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Flags3), (uint)flags); } + public UnitDynFlags GetDynamicFlags() { return (UnitDynFlags)(uint)m_objectData.DynamicFlags; } + public bool HasDynamicFlag(UnitDynFlags flag) { return (m_objectData.DynamicFlags & (uint)flag) != 0; } + public void SetDynamicFlag(UnitDynFlags flag) { SetUpdateFieldFlagValue(m_values.ModifyValue(m_objectData).ModifyValue(m_objectData.DynamicFlags), (uint)flag); } + public void RemoveDynamicFlag(UnitDynFlags flag) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(m_objectData).ModifyValue(m_objectData.DynamicFlags), (uint)flag); } + public void ReplaceAllDynamicFlags(UnitDynFlags flag) { SetUpdateFieldValue(m_values.ModifyValue(m_objectData).ModifyValue(m_objectData.DynamicFlags), (uint)flag); } + public void SetCreatedBySpell(uint spellId) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.CreatedBySpell), spellId); } public Emote GetEmoteState() { return (Emote)(int)m_unitData.EmoteState; } diff --git a/Source/Game/Entities/Vehicle.cs b/Source/Game/Entities/Vehicle.cs index de50becc9..114030709 100644 --- a/Source/Game/Entities/Vehicle.cs +++ b/Source/Game/Entities/Vehicle.cs @@ -291,7 +291,7 @@ namespace Game.Entities // @VehicleJoinEvent.Abort } - public bool AddPassenger(Unit unit, sbyte seatId) + public bool AddVehiclePassenger(Unit unit, sbyte seatId) { // @Prevent adding passengers when vehicle is uninstalling. (Bad script in OnUninstall/OnRemovePassenger/PassengerBoarded hook.) if (_status == Status.UnInstalling) @@ -355,8 +355,12 @@ namespace Game.Entities return true; } - public Vehicle RemovePassenger(Unit unit) + public ITransport RemovePassenger(WorldObject passenger) { + Unit unit = passenger.ToUnit(); + if (unit == null) + return null; + if (unit.GetVehicle() != this) return null; @@ -421,8 +425,8 @@ namespace Game.Entities } } - foreach (var pair in seatRelocation) - pair.Item1.UpdatePosition(pair.Item2); + foreach (var (passenger, position) in seatRelocation) + ITransport.UpdatePassengerPosition(this, _me.GetMap(), passenger, position.GetPositionX(), position.GetPositionY(), position.GetPositionZ(), position.GetOrientation(), false); } public bool IsVehicleInUse() @@ -487,20 +491,28 @@ namespace Game.Entities return ret; } + public ObjectGuid GetTransportGUID() { return GetBase().GetGUID(); } + + public float GetTransportOrientation() { return GetBase().GetOrientation(); } + + public void AddPassenger(WorldObject passenger) { Log.outFatal(LogFilter.Vehicle, "Vehicle cannot directly gain passengers without auras"); } + public void CalculatePassengerPosition(ref float x, ref float y, ref float z, ref float o) { - TransportPosHelper.CalculatePassengerPosition(ref x, ref y, ref z, ref o, + ITransport.CalculatePassengerPosition(ref x, ref y, ref z, ref o, GetBase().GetPositionX(), GetBase().GetPositionY(), GetBase().GetPositionZ(), GetBase().GetOrientation()); } public void CalculatePassengerOffset(ref float x, ref float y, ref float z, ref float o) { - TransportPosHelper.CalculatePassengerOffset(ref x, ref y, ref z, ref o, + ITransport.CalculatePassengerOffset(ref x, ref y, ref z, ref o, GetBase().GetPositionX(), GetBase().GetPositionY(), GetBase().GetPositionZ(), GetBase().GetOrientation()); } + public int GetMapIdForSpawning() { return (int)GetBase().GetMapId(); } + public void RemovePendingEvent(VehicleJoinEvent e) { foreach (var Event in _pendingJoinEvents) diff --git a/Source/Game/Garrisons/Garrisons.cs b/Source/Game/Garrisons/Garrisons.cs index 11c6fa40b..a2429b269 100644 --- a/Source/Game/Garrisons/Garrisons.cs +++ b/Source/Game/Garrisons/Garrisons.cs @@ -839,7 +839,7 @@ namespace Game.Garrisons float y = spawn.GetPositionY(); float z = spawn.GetPositionZ(); float o = spawn.GetOrientation(); - TransportPosHelper.CalculatePassengerPosition(ref x, ref y, ref z, ref o, building.GetPositionX(), building.GetPositionY(), building.GetPositionZ(), building.GetOrientation()); + ITransport.CalculatePassengerPosition(ref x, ref y, ref z, ref o, building.GetPositionX(), building.GetPositionY(), building.GetPositionZ(), building.GetOrientation()); spawn.Relocate(x, y, z, o); switch (spawn.GetTypeId()) diff --git a/Source/Game/Handlers/MovementHandler.cs b/Source/Game/Handlers/MovementHandler.cs index 46bb53e9b..9944d4ab8 100644 --- a/Source/Game/Handlers/MovementHandler.cs +++ b/Source/Game/Handlers/MovementHandler.cs @@ -110,31 +110,37 @@ namespace Game if (plrMover) { - if (!plrMover.GetTransport()) + if (plrMover.GetTransport() == null) { - Transport transport = plrMover.GetMap().GetTransport(movementInfo.transport.guid); - if (transport) - transport.AddPassenger(plrMover); + GameObject go = plrMover.GetMap().GetGameObject(movementInfo.transport.guid); + if (go != null) + { + ITransport transport = go.ToTransportBase(); + if (transport != null) + transport.AddPassenger(plrMover); + } } - else if (plrMover.GetTransport().GetGUID() != movementInfo.transport.guid) + else if (plrMover.GetTransport().GetTransportGUID() != movementInfo.transport.guid) { plrMover.GetTransport().RemovePassenger(plrMover); - Transport transport = plrMover.GetMap().GetTransport(movementInfo.transport.guid); - if (transport) - transport.AddPassenger(plrMover); + GameObject go = plrMover.GetMap().GetGameObject(movementInfo.transport.guid); + if (go != null) + { + ITransport transport = go.ToTransportBase(); + if (transport != null) + transport.AddPassenger(plrMover); + else + movementInfo.ResetTransport(); + } else movementInfo.ResetTransport(); } } - if (!mover.GetTransport() && !mover.GetVehicle()) - { - GameObject go = mover.GetMap().GetGameObject(movementInfo.transport.guid); - if (!go || go.GetGoType() != GameObjectTypes.Transport) - movementInfo.transport.Reset(); - } + if (mover.GetTransport() == null && !mover.GetVehicle()) + movementInfo.transport.Reset(); } - else if (plrMover && plrMover.GetTransport()) // if we were on a transport, leave + else if (plrMover && plrMover.GetTransport() != null) // if we were on a transport, leave plrMover.GetTransport().RemovePassenger(plrMover); // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). @@ -531,7 +537,7 @@ namespace Game return; } - if (!GetPlayer().GetTransport() && Math.Abs(GetPlayer().GetSpeed(move_type) - packet.Speed) > 0.01f) + if (GetPlayer().GetTransport() == null && Math.Abs(GetPlayer().GetSpeed(move_type) - packet.Speed) > 0.01f) { if (GetPlayer().GetSpeed(move_type) > packet.Speed) // must be greater - just correct { diff --git a/Source/Game/Maps/GridNotifiers.cs b/Source/Game/Maps/GridNotifiers.cs index 94372c827..474726c43 100644 --- a/Source/Game/Maps/GridNotifiers.cs +++ b/Source/Game/Maps/GridNotifiers.cs @@ -103,7 +103,7 @@ namespace Game.Maps { // 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 - Transport transport = i_player.GetTransport(); + Transport transport = i_player.GetTransport(); if (transport) { foreach (var obj in transport.GetPassengers()) @@ -128,6 +128,9 @@ namespace Game.Maps case TypeId.DynamicObject: i_player.UpdateVisibilityOf(obj.ToDynamicObject(), i_data, i_visibleNow); break; + case TypeId.AreaTrigger: + i_player.UpdateVisibilityOf(obj.ToAreaTrigger(), i_data, i_visibleNow); + break; default: break; } diff --git a/Source/Game/Maps/Map.cs b/Source/Game/Maps/Map.cs index 0e9017767..3124ceedc 100644 --- a/Source/Game/Maps/Map.cs +++ b/Source/Game/Maps/Map.cs @@ -1131,9 +1131,7 @@ namespace Game.Maps else { go.Relocate(x, y, z, orientation); - go.UpdateModelPosition(); - go.UpdatePositionData(); - go.UpdateObjectVisibility(false); + go.AfterRelocation(); RemoveGameObjectFromMoveList(go); } @@ -1354,9 +1352,7 @@ namespace Game.Maps { // update pos go.Relocate(go._newPosition); - go.UpdateModelPosition(); - go.UpdatePositionData(); - go.UpdateObjectVisibility(false); + go.AfterRelocation(); } else { @@ -2247,7 +2243,7 @@ namespace Game.Maps var data = new UpdateData(player.GetMapId()); // attach to player data current transport data - Transport transport = player.GetTransport(); + Transport transport = player.GetTransport(); if (transport != null) { transport.BuildCreateUpdateBlockForPlayer(data, player); @@ -4168,8 +4164,8 @@ namespace Game.Maps if (!summon.Create(GenerateLowGuid(HighGuid.Creature), this, entry, pos, null, vehId, true)) return null; - Transport transport = summoner != null ? summoner.GetTransport() : null; - if (transport) + ITransport transport = summoner != null ? summoner.GetTransport() : null; + if (transport != null) { pos.GetPosition(out float x, out float y, out float z, out float o); transport.CalculatePassengerOffset(ref x, ref y, ref z, ref o); @@ -4209,7 +4205,7 @@ namespace Game.Maps if (!AddToMap(summon.ToCreature())) { // Returning false will cause the object to be deleted - remove from transport - if (transport) + if (transport != null) transport.RemovePassenger(summon); summon.Dispose(); diff --git a/Source/Game/Maps/TransportManager.cs b/Source/Game/Maps/TransportManager.cs index 55c915de0..673617bcf 100644 --- a/Source/Game/Maps/TransportManager.cs +++ b/Source/Game/Maps/TransportManager.cs @@ -425,6 +425,18 @@ namespace Game.Maps _transportAnimations[transportEntry] = animNode; } + public void AddPathRotationToTransport(uint transportEntry, uint timeSeg, TransportRotationRecord node) + { + if (!_transportAnimations.ContainsKey(transportEntry)) + _transportAnimations[transportEntry] = new(); + + TransportAnimation animNode = _transportAnimations[transportEntry]; + animNode.Rotations[timeSeg] = node; + + if (animNode.Path.Empty() && animNode.TotalTime < timeSeg) + animNode.TotalTime = timeSeg; + } + public Transport CreateTransport(uint entry, ulong guid = 0, Map map = null, PhaseUseFlagsValues phaseUseFlags = 0, uint phaseId = 0, uint phaseGroupId = 0) { // instance case, execute GetGameObjectEntry hook @@ -530,14 +542,6 @@ namespace Game.Maps { return _transportSpawns.LookupByKey(spawnId); } - - public void AddPathRotationToTransport(uint transportEntry, uint timeSeg, TransportRotationRecord node) - { - if (!_transportAnimations.ContainsKey(transportEntry)) - _transportAnimations[transportEntry] = new TransportAnimation(); - - _transportAnimations[transportEntry].Rotations[timeSeg] = node; - } Dictionary _transportTemplates = new(); MultiMap _instanceTransports = new(); @@ -557,8 +561,8 @@ namespace Game.Maps mode = Spline.EvaluationMode.Catmullrom; cyclic = false; points = new Vector3[_points.Count]; - - for(var i = 0; i < _points.Count; ++i) + + for (var i = 0; i < _points.Count; ++i) points[i] = _points[i]; lo = 1; @@ -627,26 +631,59 @@ namespace Game.Maps public class TransportAnimation { - TransportAnimationRecord GetAnimNode(uint time) + public Dictionary Path = new(); + public Dictionary Rotations = new(); + public uint TotalTime; + + public TransportAnimationRecord GetPrevAnimNode(uint time) { if (Path.Empty()) return null; - foreach (var pair in Path) - if (time >= pair.Key) - return pair.Value; + List lKeys = Path.Keys.ToList(); + int reqIndex = lKeys.IndexOf(time) - 1; - return Path.First().Value; + if (reqIndex != -1) + return Path[lKeys[reqIndex]]; + + return Path.LastOrDefault().Value; } - TransportRotationRecord GetAnimRotation(uint time) + public TransportRotationRecord GetPrevAnimRotation(uint time) { - return Rotations.LookupByKey(time); + if (Rotations.Empty()) + return null; + + List lKeys = Rotations.Keys.ToList(); + int reqIndex = lKeys.IndexOf(time) - 1; + + if (reqIndex != -1) + return Rotations[lKeys[reqIndex]]; + + return Rotations.LastOrDefault().Value; } - public Dictionary Path = new(); - public Dictionary Rotations = new(); - public uint TotalTime; + public TransportAnimationRecord GetNextAnimNode(uint time) + { + if (Path.Empty()) + return null; + + if (Path.TryGetValue(time, out TransportAnimationRecord record)) + return record; + + return Path.FirstOrDefault().Value; + } + + public TransportRotationRecord GetNextAnimRotation(uint time) + { + if (Rotations.Empty()) + return null; + + if (Rotations.TryGetValue(time, out TransportRotationRecord record)) + return record; + + return Rotations.FirstOrDefault().Value; + } } public class TransportSpawn diff --git a/Source/Game/Movement/Generators/WaypointMovement.cs b/Source/Game/Movement/Generators/WaypointMovement.cs index fc00108dd..aa67507b3 100644 --- a/Source/Game/Movement/Generators/WaypointMovement.cs +++ b/Source/Game/Movement/Generators/WaypointMovement.cs @@ -303,10 +303,10 @@ namespace Game.Movement owner.SetHomePosition(x, y, z, o); else { - Transport trans = owner.GetTransport(); - if (trans) + ITransport trans = owner.GetTransport(); + if (trans != null) { - o -= trans.GetOrientation(); + o -= trans.GetTransportOrientation(); owner.SetTransportHomePosition(x, y, z, o); trans.CalculatePassengerPosition(ref x, ref y, ref z, ref o); owner.SetHomePosition(x, y, z, o); diff --git a/Source/Game/Movement/MoveSplineInit.cs b/Source/Game/Movement/MoveSplineInit.cs index 10f4bb0fb..83b06d158 100644 --- a/Source/Game/Movement/MoveSplineInit.cs +++ b/Source/Game/Movement/MoveSplineInit.cs @@ -222,11 +222,14 @@ namespace Game.Movement if (args.TransformForTransport) { Unit vehicle = unit.GetVehicleBase(); - Transport transport = unit.GetTransport(); if (vehicle != null) - angle -= vehicle.Orientation; - else if (transport != null) - angle -= transport.Orientation; + angle -= vehicle.GetOrientation(); + else + { + ITransport transport = unit.GetTransport(); + if (transport != null) + angle -= transport.GetTransportOrientation(); + } } args.facing.angle = MathFunctions.wrap(angle, 0.0f, MathFunctions.TwoPi); diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index 728f2f5af..b93f4de7f 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -7077,8 +7077,8 @@ namespace Game.Spells if (target.GetGUID() != corpse.GetOwnerGUID()) return false; - - if (!corpse.HasDynamicFlag(UnitDynFlags.Lootable)) + + if (!corpse.HasCorpseDynamicFlag(CorpseDynFlags.Lootable)) return false; if (!corpse.IsWithinLOSInMap(m_caster, LineOfSightChecks.All, ModelIgnoreFlags.M2)) diff --git a/Source/Scripts/World/ItemScripts.cs b/Source/Scripts/World/ItemScripts.cs index 4a1363ec4..286eff035 100644 --- a/Source/Scripts/World/ItemScripts.cs +++ b/Source/Scripts/World/ItemScripts.cs @@ -186,7 +186,7 @@ namespace Scripts.World.ItemScripts if (player.GetZoneId() != Misc.ZoneIdHowling) return false; - if (!player.GetTransport() || player.GetAreaId() != Misc.AreaIdShatteredStraits) + if (player.GetTransport() == null || player.GetAreaId() != Misc.AreaIdShatteredStraits) { SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(SpellIds.PetrovBomb, Difficulty.None); if (spellInfo != null)