Core/GameObjects: Transport (type 11) improvements

Port From (https://github.com/TrinityCore/TrinityCore/commit/630b60eb0dcd3d9ce41582664ab822b049365431)
This commit is contained in:
hondacrx
2022-06-03 16:20:41 -04:00
parent e65ce31742
commit 40444525b2
30 changed files with 807 additions and 377 deletions
+496 -115
View File
@@ -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<uint>();
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<Difficulty> 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<Transport>();
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<uint> 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<uint> _stopFrames = new();
bool _autoCycleBetweenStopFrames;
TimeTracker _positionUpdateTimer;
List<WorldObject> _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<uint> 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);
}
}
}
}