Core/Transports: Path generation rewrite v2 (needs testing)
Port From (https://github.com/TrinityCore/TrinityCore/commit/a2c1b699e6e4d30c752b65241bc23191920a86fd)
This commit is contained in:
@@ -200,4 +200,10 @@ namespace Framework.Constants
|
|||||||
SetTappedToChallengePlayers = 44, // Set Tapped to Challenge Players
|
SetTappedToChallengePlayers = 44, // Set Tapped to Challenge Players
|
||||||
Max
|
Max
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum TransportMovementState
|
||||||
|
{
|
||||||
|
Moving,
|
||||||
|
WaitingOnPauseWaypoint
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace Game.Entities
|
|||||||
|
|
||||||
m_areaTriggerData = new AreaTriggerFieldData();
|
m_areaTriggerData = new AreaTriggerFieldData();
|
||||||
|
|
||||||
_spline = new Spline();
|
_spline = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void AddToWorld()
|
public override void AddToWorld()
|
||||||
@@ -756,7 +756,7 @@ namespace Game.Entities
|
|||||||
|
|
||||||
_movementTime = 0;
|
_movementTime = 0;
|
||||||
|
|
||||||
_spline.InitSpline(splinePoints.ToArray(), splinePoints.Count, Spline.EvaluationMode.Linear);
|
_spline.InitSpline(splinePoints.ToArray(), splinePoints.Count, EvaluationMode.Linear);
|
||||||
_spline.InitLengths();
|
_spline.InitLengths();
|
||||||
|
|
||||||
// should be sent in object create packets only
|
// should be sent in object create packets only
|
||||||
@@ -1078,7 +1078,7 @@ namespace Game.Entities
|
|||||||
public Vector3 GetTargetRollPitchYaw() { return _targetRollPitchYaw; }
|
public Vector3 GetTargetRollPitchYaw() { return _targetRollPitchYaw; }
|
||||||
|
|
||||||
public bool HasSplines() { return !_spline.Empty(); }
|
public bool HasSplines() { return !_spline.Empty(); }
|
||||||
public Spline GetSpline() { return _spline; }
|
public Spline<int> GetSpline() { return _spline; }
|
||||||
public uint GetElapsedTimeForMovement() { return GetTimeSinceCreated(); } // @todo: research the right value, in sniffs both timers are nearly identical
|
public uint GetElapsedTimeForMovement() { return GetTimeSinceCreated(); } // @todo: research the right value, in sniffs both timers are nearly identical
|
||||||
|
|
||||||
public AreaTriggerOrbitInfo GetCircularMovementInfo() { return _orbitInfo; }
|
public AreaTriggerOrbitInfo GetCircularMovementInfo() { return _orbitInfo; }
|
||||||
@@ -1102,7 +1102,7 @@ namespace Game.Entities
|
|||||||
Vector3 _rollPitchYaw;
|
Vector3 _rollPitchYaw;
|
||||||
Vector3 _targetRollPitchYaw;
|
Vector3 _targetRollPitchYaw;
|
||||||
List<Vector2> _polygonVertices;
|
List<Vector2> _polygonVertices;
|
||||||
Spline _spline;
|
Spline<int> _spline;
|
||||||
|
|
||||||
bool _reachedDestination;
|
bool _reachedDestination;
|
||||||
int _lastSplineIndex;
|
int _lastSplineIndex;
|
||||||
|
|||||||
@@ -126,22 +126,12 @@ namespace Game.Entities
|
|||||||
dynFlags |= GameObjectDynamicLowFlags.Sparkle | GameObjectDynamicLowFlags.Highlight;
|
dynFlags |= GameObjectDynamicLowFlags.Sparkle | GameObjectDynamicLowFlags.Highlight;
|
||||||
break;
|
break;
|
||||||
case GameObjectTypes.Transport:
|
case GameObjectTypes.Transport:
|
||||||
|
case GameObjectTypes.MapObjTransport:
|
||||||
{
|
{
|
||||||
dynFlags = (GameObjectDynamicLowFlags)((int)unitDynFlags & 0xFFFF);
|
dynFlags = (GameObjectDynamicLowFlags)((int)unitDynFlags & 0xFFFF);
|
||||||
pathProgress = (ushort)((int)unitDynFlags >> 16);
|
pathProgress = (ushort)((int)unitDynFlags >> 16);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GameObjectTypes.MapObjTransport:
|
|
||||||
{
|
|
||||||
Transport transport = gameObject.ToTransport();
|
|
||||||
uint transportPeriod = transport.GetTransportPeriod();
|
|
||||||
if (transportPeriod != 0)
|
|
||||||
{
|
|
||||||
float timer = (float)(transport.GetTimer() % transportPeriod);
|
|
||||||
pathProgress = (ushort)(timer / (float)transportPeriod * 65535.0f);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GameObjectTypes.CapturePoint:
|
case GameObjectTypes.CapturePoint:
|
||||||
if (!gameObject.CanInteractWithCapturePoint(receiver))
|
if (!gameObject.CanInteractWithCapturePoint(receiver))
|
||||||
dynFlags |= GameObjectDynamicLowFlags.NoInterract;
|
dynFlags |= GameObjectDynamicLowFlags.NoInterract;
|
||||||
@@ -152,7 +142,7 @@ namespace Game.Entities
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
unitDynFlags = (uint)((pathProgress << 16) | (ushort)dynFlags);
|
unitDynFlags = ((uint)pathProgress << 16) | (uint)dynFlags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+111
-181
@@ -19,6 +19,7 @@ using Framework.Constants;
|
|||||||
using Game.DataStorage;
|
using Game.DataStorage;
|
||||||
using Game.Maps;
|
using Game.Maps;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
@@ -129,8 +130,6 @@ namespace Game.Entities
|
|||||||
{
|
{
|
||||||
public Transport()
|
public Transport()
|
||||||
{
|
{
|
||||||
_isMoving = true;
|
|
||||||
|
|
||||||
m_updateFlag.ServerTime = true;
|
m_updateFlag.ServerTime = true;
|
||||||
m_updateFlag.Stationary = true;
|
m_updateFlag.Stationary = true;
|
||||||
m_updateFlag.Rotation = true;
|
m_updateFlag.Rotation = true;
|
||||||
@@ -175,10 +174,7 @@ namespace Game.Entities
|
|||||||
}
|
}
|
||||||
|
|
||||||
_transportInfo = tInfo;
|
_transportInfo = tInfo;
|
||||||
_nextFrame = 0;
|
_eventsToTrigger = new(tInfo.Events.Count, true);
|
||||||
_currentFrame = tInfo.keyFrames[_nextFrame++];
|
|
||||||
_triggeredArrivalEvent = false;
|
|
||||||
_triggeredDepartureEvent = false;
|
|
||||||
|
|
||||||
GameObjectOverride goOverride = GetGameObjectOverride();
|
GameObjectOverride goOverride = GetGameObjectOverride();
|
||||||
if (goOverride != null)
|
if (goOverride != null)
|
||||||
@@ -187,13 +183,16 @@ namespace Game.Entities
|
|||||||
ReplaceAllFlags(goOverride.Flags);
|
ReplaceAllFlags(goOverride.Flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_pathProgress = goinfo.MoTransport.allowstopping == 0 ? Time.GetMSTime() /*might be called before world update loop begins, don't use GameTime*/ % tInfo.TotalPathTime : 0;
|
||||||
|
SetPathProgressForClient((float)_pathProgress / (float)tInfo.TotalPathTime);
|
||||||
SetObjectScale(goinfo.size);
|
SetObjectScale(goinfo.size);
|
||||||
SetPeriod(tInfo.pathTime);
|
SetPeriod(tInfo.TotalPathTime);
|
||||||
SetEntry(goinfo.entry);
|
SetEntry(goinfo.entry);
|
||||||
SetDisplayId(goinfo.displayId);
|
SetDisplayId(goinfo.displayId);
|
||||||
SetGoState(goinfo.MoTransport.allowstopping == 0 ? GameObjectState.Ready : GameObjectState.Active);
|
SetGoState(goinfo.MoTransport.allowstopping == 0 ? GameObjectState.Ready : GameObjectState.Active);
|
||||||
SetGoType(GameObjectTypes.MapObjTransport);
|
SetGoType(GameObjectTypes.MapObjTransport);
|
||||||
SetGoAnimProgress(animprogress);
|
SetGoAnimProgress(animprogress);
|
||||||
|
SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.SpawnTrackingStateAnimID), Global.DB2Mgr.GetEmptyAnimStateID());
|
||||||
SetName(goinfo.name);
|
SetName(goinfo.name);
|
||||||
SetLocalRotation(0.0f, 0.0f, 0.0f, 1.0f);
|
SetLocalRotation(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
SetParentRotation(Quaternion.Identity);
|
SetParentRotation(Quaternion.Identity);
|
||||||
@@ -216,79 +215,103 @@ namespace Game.Entities
|
|||||||
|
|
||||||
public override void Update(uint diff)
|
public override void Update(uint diff)
|
||||||
{
|
{
|
||||||
uint positionUpdateDelay = 200;
|
TimeSpan positionUpdateDelay = TimeSpan.FromMilliseconds(200);
|
||||||
|
|
||||||
if (GetAI() != null)
|
if (GetAI() != null)
|
||||||
GetAI().UpdateAI(diff);
|
GetAI().UpdateAI(diff);
|
||||||
else if (!AIM_Initialize())
|
else if (!AIM_Initialize())
|
||||||
Log.outError(LogFilter.Transport, "Could not initialize GameObjectAI for Transport");
|
Log.outError(LogFilter.Transport, "Could not initialize GameObjectAI for Transport");
|
||||||
|
|
||||||
if (GetKeyFrames().Count <= 1)
|
Global.ScriptMgr.OnTransportUpdate(this, diff);
|
||||||
return;
|
|
||||||
|
|
||||||
if (IsMoving() || !_pendingStop)
|
_positionChangeTimer.Update(diff);
|
||||||
|
|
||||||
|
uint cycleId = _pathProgress / GetTransportPeriod();
|
||||||
|
if (GetGoInfo().MoTransport.allowstopping == 0)
|
||||||
|
_pathProgress = GameTime.GetGameTimeMS();
|
||||||
|
else if (!_requestStopTimestamp.HasValue || _requestStopTimestamp > _pathProgress + diff)
|
||||||
_pathProgress += diff;
|
_pathProgress += diff;
|
||||||
|
else
|
||||||
|
_pathProgress = _requestStopTimestamp.Value;
|
||||||
|
|
||||||
|
if (_pathProgress / GetTransportPeriod() != cycleId)
|
||||||
|
{
|
||||||
|
// reset cycle
|
||||||
|
_eventsToTrigger.SetAll(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetPathProgressForClient((float)_pathProgress / (float)GetTransportPeriod());
|
||||||
|
|
||||||
uint timer = _pathProgress % GetTransportPeriod();
|
uint timer = _pathProgress % GetTransportPeriod();
|
||||||
bool justStopped = false;
|
|
||||||
|
|
||||||
// Set current waypoint
|
int eventToTriggerIndex = -1;
|
||||||
// Desired outcome: _currentFrame.DepartureTime < timer < _nextFrame.ArriveTime
|
for (var i = 0; i < _eventsToTrigger.Count; i++)
|
||||||
// ... arrive | ... delay ... | departure
|
|
||||||
// event / event /
|
|
||||||
for (; ; )
|
|
||||||
{
|
{
|
||||||
if (timer >= _currentFrame.ArriveTime)
|
if (_eventsToTrigger.Get(i))
|
||||||
{
|
{
|
||||||
if (!_triggeredArrivalEvent)
|
eventToTriggerIndex = i;
|
||||||
{
|
break;
|
||||||
DoEventIfAny(_currentFrame, false);
|
}
|
||||||
_triggeredArrivalEvent = true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (timer < _currentFrame.DepartureTime)
|
if (eventToTriggerIndex != -1)
|
||||||
|
{
|
||||||
|
while (eventToTriggerIndex < _transportInfo.Events.Count && _transportInfo.Events[eventToTriggerIndex].Timestamp < timer)
|
||||||
|
{
|
||||||
|
GameEvents.Trigger(_transportInfo.Events[eventToTriggerIndex].EventId, this, this);
|
||||||
|
_eventsToTrigger.Set(eventToTriggerIndex, false);
|
||||||
|
++eventToTriggerIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TransportMovementState moveState;
|
||||||
|
int legIndex;
|
||||||
|
Position newPosition = _transportInfo.ComputePosition(timer, out moveState, out legIndex);
|
||||||
|
if (newPosition != null)
|
||||||
|
{
|
||||||
|
bool justStopped = _movementState == TransportMovementState.Moving && moveState != TransportMovementState.Moving;
|
||||||
|
_movementState = moveState;
|
||||||
|
|
||||||
|
if (justStopped)
|
||||||
|
{
|
||||||
|
if (_requestStopTimestamp != 0 && GetGoState() != GameObjectState.Ready)
|
||||||
{
|
{
|
||||||
justStopped = IsMoving();
|
SetGoState(GameObjectState.Ready);
|
||||||
SetMoving(false);
|
SetDynamicFlag(GameObjectDynamicLowFlags.Stopped);
|
||||||
if (_pendingStop && GetGoState() != GameObjectState.Ready)
|
|
||||||
{
|
|
||||||
SetGoState(GameObjectState.Ready);
|
|
||||||
_pathProgress /= GetTransportPeriod();
|
|
||||||
_pathProgress *= GetTransportPeriod();
|
|
||||||
_pathProgress += _currentFrame.ArriveTime;
|
|
||||||
}
|
|
||||||
break; // its a stop frame and we are waiting
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timer >= _currentFrame.DepartureTime && !_triggeredDepartureEvent)
|
if (legIndex != _currentPathLeg)
|
||||||
{
|
{
|
||||||
DoEventIfAny(_currentFrame, true); // departure event
|
_currentPathLeg = legIndex;
|
||||||
_triggeredDepartureEvent = true;
|
TeleportTransport(_transportInfo.PathLegs[legIndex].MapId, newPosition.GetPositionX(), newPosition.GetPositionY(), newPosition.GetPositionZ(), newPosition.GetOrientation());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// not waiting anymore
|
// set position
|
||||||
SetMoving(true);
|
if (_positionChangeTimer.Passed())
|
||||||
|
{
|
||||||
|
_positionChangeTimer.Reset(positionUpdateDelay);
|
||||||
|
if (_movementState == TransportMovementState.Moving || justStopped)
|
||||||
|
UpdatePosition(newPosition.GetPositionX(), newPosition.GetPositionY(), newPosition.GetPositionZ(), newPosition.GetOrientation());
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* There are four possible scenarios that trigger loading/unloading passengers:
|
||||||
|
1. transport moves from inactive to active grid
|
||||||
|
2. the grid that transport is currently in becomes active
|
||||||
|
3. transport moves from active to inactive grid
|
||||||
|
4. the grid that transport is currently in unloads
|
||||||
|
*/
|
||||||
|
bool gridActive = GetMap().IsGridLoaded(GetPositionX(), GetPositionY());
|
||||||
|
|
||||||
// Enable movement
|
if (_staticPassengers.Empty() && gridActive) // 2.
|
||||||
if (GetGoInfo().MoTransport.allowstopping != 0)
|
LoadStaticPassengers();
|
||||||
SetGoState(GameObjectState.Active);
|
else if (!_staticPassengers.Empty() && !gridActive)
|
||||||
|
// 4. - if transports stopped on grid edge, some passengers can remain in active grids
|
||||||
if (timer >= _currentFrame.DepartureTime && timer < _currentFrame.NextArriveTime)
|
// unload all static passengers otherwise passengers won't load correctly when the grid that transport is currently in becomes active
|
||||||
break; // found current waypoint
|
UnloadStaticPassengers();
|
||||||
|
}
|
||||||
MoveToNextWaypoint();
|
}
|
||||||
|
|
||||||
Global.ScriptMgr.OnRelocate(this, (uint)_currentFrame.Node.NodeIndex, _currentFrame.Node.ContinentID, _currentFrame.Node.Loc.X, _currentFrame.Node.Loc.Y, _currentFrame.Node.Loc.Z);
|
|
||||||
|
|
||||||
Log.outDebug(LogFilter.Transport, "Transport {0} ({1}) moved to node {2} {3} {4} {5} {6}", GetEntry(), GetName(), _currentFrame.Node.NodeIndex, _currentFrame.Node.ContinentID,
|
|
||||||
_currentFrame.Node.Loc.X, _currentFrame.Node.Loc.Y, _currentFrame.Node.Loc.Z);
|
|
||||||
|
|
||||||
// Departure event
|
|
||||||
var nextframe = GetKeyFrames()[_nextFrame];
|
|
||||||
if (_currentFrame.IsTeleportFrame())
|
|
||||||
if (TeleportTransport(nextframe.Node.ContinentID, nextframe.Node.Loc.X, nextframe.Node.Loc.Y, nextframe.Node.Loc.Z, nextframe.InitialOrientation))
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add model to map after we are fully done with moving maps
|
// Add model to map after we are fully done with moving maps
|
||||||
@@ -298,49 +321,10 @@ namespace Game.Entities
|
|||||||
if (m_model != null)
|
if (m_model != null)
|
||||||
GetMap().InsertGameObjectModel(m_model);
|
GetMap().InsertGameObjectModel(m_model);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set position
|
|
||||||
_positionChangeTimer.Update(diff);
|
|
||||||
if (_positionChangeTimer.Passed())
|
|
||||||
{
|
|
||||||
_positionChangeTimer.Reset(positionUpdateDelay);
|
|
||||||
if (IsMoving())
|
|
||||||
{
|
|
||||||
float t = !justStopped ? CalculateSegmentPos(timer * 0.001f) : 1.0f;
|
|
||||||
Vector3 pos, dir;
|
|
||||||
_currentFrame.Spline.Evaluate_Percent((int)_currentFrame.Index, t, out pos);
|
|
||||||
_currentFrame.Spline.Evaluate_Derivative((int)_currentFrame.Index, t, out dir);
|
|
||||||
UpdatePosition(pos.X, pos.Y, pos.Z, (float)Math.Atan2(dir.Y, dir.X) + MathFunctions.PI);
|
|
||||||
}
|
|
||||||
else if (justStopped)
|
|
||||||
UpdatePosition(_currentFrame.Node.Loc.X, _currentFrame.Node.Loc.Y, _currentFrame.Node.Loc.Z, _currentFrame.InitialOrientation);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* There are four possible scenarios that trigger loading/unloading passengers:
|
|
||||||
1. transport moves from inactive to active grid
|
|
||||||
2. the grid that transport is currently in becomes active
|
|
||||||
3. transport moves from active to inactive grid
|
|
||||||
4. the grid that transport is currently in unloads
|
|
||||||
*/
|
|
||||||
bool gridActive = GetMap().IsGridLoaded(GetPositionX(), GetPositionY());
|
|
||||||
|
|
||||||
if (_staticPassengers.Empty() && gridActive) // 2.
|
|
||||||
LoadStaticPassengers();
|
|
||||||
else if (!_staticPassengers.Empty() && !gridActive)
|
|
||||||
// 4. - if transports stopped on grid edge, some passengers can remain in active grids
|
|
||||||
// unload all static passengers otherwise passengers won't load correctly when the grid that transport is currently in becomes active
|
|
||||||
UnloadStaticPassengers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Global.ScriptMgr.OnTransportUpdate(this, diff);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DelayedUpdate(uint diff)
|
public void DelayedUpdate(uint diff)
|
||||||
{
|
{
|
||||||
if (GetKeyFrames().Count <= 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
DelayedTeleportTransport();
|
DelayedTeleportTransport();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,9 +346,7 @@ namespace Game.Entities
|
|||||||
|
|
||||||
public ITransport RemovePassenger(WorldObject passenger)
|
public ITransport RemovePassenger(WorldObject passenger)
|
||||||
{
|
{
|
||||||
bool erased = _passengers.Remove(passenger);
|
if (_passengers.Remove(passenger) || _staticPassengers.Remove(passenger)) // static passenger can remove itself in case of grid unload
|
||||||
|
|
||||||
if (erased || _staticPassengers.Remove(passenger))
|
|
||||||
{
|
{
|
||||||
passenger.SetTransport(null);
|
passenger.SetTransport(null);
|
||||||
passenger.m_movementInfo.transport.Reset();
|
passenger.m_movementInfo.transport.Reset();
|
||||||
@@ -592,6 +574,8 @@ namespace Game.Entities
|
|||||||
|
|
||||||
public void UpdatePosition(float x, float y, float z, float o)
|
public void UpdatePosition(float x, float y, float z, float o)
|
||||||
{
|
{
|
||||||
|
Global.ScriptMgr.OnRelocate(this, GetMapId(), x, y, z);
|
||||||
|
|
||||||
bool newActive = GetMap().IsGridLoaded(x, y);
|
bool newActive = GetMap().IsGridLoaded(x, y);
|
||||||
Cell oldCell = new(GetPositionX(), GetPositionY());
|
Cell oldCell = new(GetPositionX(), GetPositionY());
|
||||||
|
|
||||||
@@ -648,61 +632,27 @@ namespace Game.Entities
|
|||||||
if (GetGoInfo().MoTransport.allowstopping == 0)
|
if (GetGoInfo().MoTransport.allowstopping == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_pendingStop = !enabled;
|
if (!enabled)
|
||||||
}
|
|
||||||
|
|
||||||
public void SetDelayedAddModelToMap() { _delayedAddModel = true; }
|
|
||||||
|
|
||||||
void MoveToNextWaypoint()
|
|
||||||
{
|
|
||||||
// Clear events flagging
|
|
||||||
_triggeredArrivalEvent = false;
|
|
||||||
_triggeredDepartureEvent = false;
|
|
||||||
|
|
||||||
// Set frames
|
|
||||||
_currentFrame = GetKeyFrames()[_nextFrame++];
|
|
||||||
if (_nextFrame == GetKeyFrames().Count)
|
|
||||||
_nextFrame = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
float CalculateSegmentPos(float now)
|
|
||||||
{
|
|
||||||
KeyFrame frame = _currentFrame;
|
|
||||||
float speed = GetGoInfo().MoTransport.moveSpeed;
|
|
||||||
float accel = GetGoInfo().MoTransport.accelRate;
|
|
||||||
float timeSinceStop = frame.TimeFrom + (now - (1.0f / Time.InMilliseconds) * frame.DepartureTime);
|
|
||||||
float timeUntilStop = frame.TimeTo - (now - (1.0f / Time.InMilliseconds) * frame.DepartureTime);
|
|
||||||
float segmentPos, dist;
|
|
||||||
float accelTime = _transportInfo.accelTime;
|
|
||||||
float accelDist = _transportInfo.accelDist;
|
|
||||||
// calculate from nearest stop, less confusing calculation...
|
|
||||||
if (timeSinceStop < timeUntilStop)
|
|
||||||
{
|
{
|
||||||
if (timeSinceStop < accelTime)
|
_requestStopTimestamp = (_pathProgress / GetTransportPeriod()) * GetTransportPeriod() + _transportInfo.GetNextPauseWaypointTimestamp(_pathProgress);
|
||||||
dist = 0.5f * accel * timeSinceStop * timeSinceStop;
|
|
||||||
else
|
|
||||||
dist = accelDist + (timeSinceStop - accelTime) * speed;
|
|
||||||
segmentPos = dist - frame.DistSinceStop;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (timeUntilStop < _transportInfo.accelTime)
|
_requestStopTimestamp = null;
|
||||||
dist = (0.5f * accel) * (timeUntilStop * timeUntilStop);
|
SetGoState(GameObjectState.Active);
|
||||||
else
|
RemoveDynamicFlag(GameObjectDynamicLowFlags.Stopped);
|
||||||
dist = accelDist + (timeUntilStop - accelTime) * speed;
|
|
||||||
segmentPos = frame.DistUntilStop - dist;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return segmentPos / frame.NextDistFromPrev;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetDelayedAddModelToMap() { _delayedAddModel = true; }
|
||||||
|
|
||||||
bool TeleportTransport(uint newMapid, float x, float y, float z, float o)
|
bool TeleportTransport(uint newMapid, float x, float y, float z, float o)
|
||||||
{
|
{
|
||||||
Map oldMap = GetMap();
|
Map oldMap = GetMap();
|
||||||
|
|
||||||
if (oldMap.GetId() != newMapid)
|
if (oldMap.GetId() != newMapid)
|
||||||
{
|
{
|
||||||
_delayedTeleport = true;
|
_delayedTeleport = new(newMapid, x, y, z, o);
|
||||||
UnloadStaticPassengers();
|
UnloadStaticPassengers();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -735,22 +685,22 @@ namespace Game.Entities
|
|||||||
|
|
||||||
void DelayedTeleportTransport()
|
void DelayedTeleportTransport()
|
||||||
{
|
{
|
||||||
if (!_delayedTeleport)
|
if (_delayedTeleport == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var nextFrame = GetKeyFrames()[_nextFrame];
|
Map newMap = Global.MapMgr.CreateBaseMap(_delayedTeleport.GetMapId());
|
||||||
|
|
||||||
_delayedTeleport = false;
|
|
||||||
Map newMap = Global.MapMgr.CreateBaseMap(nextFrame.Node.ContinentID);
|
|
||||||
GetMap().RemoveFromMap(this, false);
|
GetMap().RemoveFromMap(this, false);
|
||||||
SetMap(newMap);
|
SetMap(newMap);
|
||||||
|
|
||||||
float x = nextFrame.Node.Loc.X,
|
float x = _delayedTeleport.GetPositionX(),
|
||||||
y = nextFrame.Node.Loc.Y,
|
y = _delayedTeleport.GetPositionY(),
|
||||||
z = nextFrame.Node.Loc.Z,
|
z = _delayedTeleport.GetPositionZ(),
|
||||||
o = nextFrame.InitialOrientation;
|
o = _delayedTeleport.GetOrientation();
|
||||||
|
|
||||||
foreach (WorldObject obj in _passengers.ToList())
|
_delayedTeleport = null;
|
||||||
|
|
||||||
|
List<WorldObject> passengersToTeleport = new(_passengers);
|
||||||
|
foreach (WorldObject obj in passengersToTeleport)
|
||||||
{
|
{
|
||||||
float destX, destY, destZ, destO;
|
float destX, destY, destZ, destO;
|
||||||
obj.m_movementInfo.transport.pos.GetPosition(out destX, out destY, out destZ, out destO);
|
obj.m_movementInfo.transport.pos.GetPosition(out destX, out destY, out destZ, out destO);
|
||||||
@@ -759,7 +709,7 @@ namespace Game.Entities
|
|||||||
switch (obj.GetTypeId())
|
switch (obj.GetTypeId())
|
||||||
{
|
{
|
||||||
case TypeId.Player:
|
case TypeId.Player:
|
||||||
if (!obj.ToPlayer().TeleportTo(nextFrame.Node.ContinentID, destX, destY, destZ, destO, TeleportToOptions.NotLeaveTransport))
|
if (!obj.ToPlayer().TeleportTo(newMap.GetId(), destX, destY, destZ, destO, TeleportToOptions.NotLeaveTransport, newMap.GetInstanceId()))
|
||||||
RemovePassenger(obj);
|
RemovePassenger(obj);
|
||||||
break;
|
break;
|
||||||
case TypeId.DynamicObject:
|
case TypeId.DynamicObject:
|
||||||
@@ -787,24 +737,15 @@ namespace Game.Entities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoEventIfAny(KeyFrame node, bool departure)
|
|
||||||
{
|
|
||||||
uint eventid = departure ? node.Node.DepartureEventID : node.Node.ArrivalEventID;
|
|
||||||
if (eventid != 0)
|
|
||||||
{
|
|
||||||
Log.outDebug(LogFilter.Scripts, "Taxi {0} event {1} of node {2} of {3} path", departure ? "departure" : "arrival", eventid, node.Node.NodeIndex, GetName());
|
|
||||||
GameEvents.Trigger(eventid, this, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void BuildUpdate(Dictionary<Player, UpdateData> data_map)
|
public override void BuildUpdate(Dictionary<Player, UpdateData> data_map)
|
||||||
{
|
{
|
||||||
var players = GetMap().GetPlayers();
|
var players = GetMap().GetPlayers();
|
||||||
if (players.Empty())
|
if (players.Empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var pl in players)
|
foreach (var playerReference in players)
|
||||||
BuildFieldsUpdate(pl, data_map);
|
if (playerReference.IsInPhase(this))
|
||||||
|
BuildFieldsUpdate(playerReference, data_map);
|
||||||
|
|
||||||
ClearUpdateMask(true);
|
ClearUpdateMask(true);
|
||||||
}
|
}
|
||||||
@@ -818,30 +759,19 @@ namespace Game.Entities
|
|||||||
public void SetPeriod(uint period) { SetLevel(period); }
|
public void SetPeriod(uint period) { SetLevel(period); }
|
||||||
public uint GetTimer() { return _pathProgress; }
|
public uint GetTimer() { return _pathProgress; }
|
||||||
|
|
||||||
public List<KeyFrame> GetKeyFrames() { return _transportInfo.keyFrames; }
|
|
||||||
public TransportTemplate GetTransportTemplate() { return _transportInfo; }
|
|
||||||
|
|
||||||
//! Helpers to know if stop frame was reached
|
|
||||||
bool IsMoving() { return _isMoving; }
|
|
||||||
void SetMoving(bool val) { _isMoving = val; }
|
|
||||||
|
|
||||||
TransportTemplate _transportInfo;
|
TransportTemplate _transportInfo;
|
||||||
|
|
||||||
KeyFrame _currentFrame;
|
TransportMovementState _movementState;
|
||||||
int _nextFrame;
|
BitArray _eventsToTrigger;
|
||||||
|
int _currentPathLeg;
|
||||||
|
uint? _requestStopTimestamp;
|
||||||
uint _pathProgress;
|
uint _pathProgress;
|
||||||
TimeTracker _positionChangeTimer = new();
|
TimeTracker _positionChangeTimer = new();
|
||||||
bool _isMoving;
|
|
||||||
bool _pendingStop;
|
|
||||||
|
|
||||||
//! These are needed to properly control events triggering only once for each frame
|
|
||||||
bool _triggeredArrivalEvent;
|
|
||||||
bool _triggeredDepartureEvent;
|
|
||||||
|
|
||||||
HashSet<WorldObject> _passengers = new();
|
HashSet<WorldObject> _passengers = new();
|
||||||
HashSet<WorldObject> _staticPassengers = new();
|
HashSet<WorldObject> _staticPassengers = new();
|
||||||
|
|
||||||
bool _delayedAddModel;
|
bool _delayedAddModel;
|
||||||
bool _delayedTeleport;
|
WorldLocation _delayedTeleport;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,14 +71,16 @@ namespace Game.Maps
|
|||||||
|
|
||||||
// paths are generated per template, saves us from generating it again in case of instanced transports
|
// paths are generated per template, saves us from generating it again in case of instanced transports
|
||||||
TransportTemplate transport = new();
|
TransportTemplate transport = new();
|
||||||
transport.entry = entry;
|
|
||||||
GeneratePath(goInfo, transport);
|
List<uint> mapsUsed = new();
|
||||||
|
|
||||||
|
GeneratePath(goInfo, transport, mapsUsed);
|
||||||
|
|
||||||
_transportTemplates[entry] = transport;
|
_transportTemplates[entry] = transport;
|
||||||
|
|
||||||
// transports in instance are only on one map
|
// transports in instance are only on one map
|
||||||
if (transport.inInstance)
|
if (transport.InInstance)
|
||||||
_instanceTransports.Add(transport.mapsUsed.First(), entry);
|
_instanceTransports.Add(mapsUsed.First(), entry);
|
||||||
|
|
||||||
++count;
|
++count;
|
||||||
} while (result.NextRow());
|
} while (result.NextRow());
|
||||||
@@ -173,245 +175,209 @@ namespace Game.Maps
|
|||||||
Log.outInfo(LogFilter.ServerLoading, $"Spawned {count} continent transports in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
|
Log.outInfo(LogFilter.ServerLoading, $"Spawned {count} continent transports in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeneratePath(GameObjectTemplate goInfo, TransportTemplate transport)
|
static void InitializeLeg(TransportPathLeg leg, List<TransportPathEvent> outEvents, List<TaxiPathNodeRecord> pathPoints, List<TaxiPathNodeRecord> pauses, List<TaxiPathNodeRecord> events, GameObjectTemplate goInfo, ref uint totalTime)
|
||||||
|
{
|
||||||
|
List<Vector3> splinePath = new(pathPoints.Select(node => new Vector3(node.Loc.X, node.Loc.Y, node.Loc.Z)));
|
||||||
|
SplineRawInitializer initer = new(splinePath);
|
||||||
|
leg.Spline = new Spline<double>();
|
||||||
|
leg.Spline.set_steps_per_segment(20);
|
||||||
|
leg.Spline.InitSplineCustom(initer);
|
||||||
|
leg.Spline.InitLengths();
|
||||||
|
|
||||||
|
uint legTimeAccelDecel(double dist)
|
||||||
|
{
|
||||||
|
double speed = (double)goInfo.MoTransport.moveSpeed;
|
||||||
|
double accel = (double)goInfo.MoTransport.accelRate;
|
||||||
|
double accelDist = 0.5 * speed * speed / accel;
|
||||||
|
if (accelDist >= dist * 0.5)
|
||||||
|
return (uint)(Math.Sqrt(dist / accel) * 2000.0);
|
||||||
|
else
|
||||||
|
return (uint)((dist - (accelDist + accelDist)) / speed * 1000.0 + speed / accel * 2000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint legTimeAccel(double dist)
|
||||||
|
{
|
||||||
|
double speed = (double)goInfo.MoTransport.moveSpeed;
|
||||||
|
double accel = (double)goInfo.MoTransport.accelRate;
|
||||||
|
double accelDist = 0.5 * speed * speed / accel;
|
||||||
|
if (accelDist >= dist)
|
||||||
|
return (uint)(Math.Sqrt((dist + dist) / accel) * 1000.0);
|
||||||
|
else
|
||||||
|
return (uint)(((dist - accelDist) / speed + speed / accel) * 1000.0);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Init segments
|
||||||
|
int pauseItr = 0;
|
||||||
|
int eventItr = 0;
|
||||||
|
double splineLengthToPreviousNode = 0.0;
|
||||||
|
uint delaySum = 0;
|
||||||
|
if (!pauses.Empty())
|
||||||
|
{
|
||||||
|
for (; pauseItr < pauses.Count; ++pauseItr)
|
||||||
|
{
|
||||||
|
var pausePointIndex = pathPoints.IndexOf(pauses[pauseItr]);
|
||||||
|
if (pausePointIndex == -1) // last point is a "fake" spline point, its position can never be reached so transport cannot stop there
|
||||||
|
break;
|
||||||
|
|
||||||
|
for (; eventItr < events.Count; ++eventItr)
|
||||||
|
{
|
||||||
|
var eventPointIndex = pathPoints.IndexOf(events[eventItr]);
|
||||||
|
if (eventPointIndex > pausePointIndex)
|
||||||
|
break;
|
||||||
|
|
||||||
|
double eventLength = leg.Spline.Length(eventPointIndex) - splineLengthToPreviousNode;
|
||||||
|
uint eventSplineTime = 0;
|
||||||
|
if (pauseItr != 0)
|
||||||
|
eventSplineTime = legTimeAccelDecel(eventLength);
|
||||||
|
else
|
||||||
|
eventSplineTime = legTimeAccel(eventLength);
|
||||||
|
|
||||||
|
if (pathPoints[eventPointIndex].ArrivalEventID != 0)
|
||||||
|
{
|
||||||
|
TransportPathEvent Event = new();
|
||||||
|
Event.Timestamp = totalTime + eventSplineTime + leg.Duration;
|
||||||
|
Event.EventId = pathPoints[eventPointIndex].ArrivalEventID;
|
||||||
|
outEvents.Add(Event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pathPoints[eventPointIndex].DepartureEventID != 0)
|
||||||
|
{
|
||||||
|
TransportPathEvent Event = new();
|
||||||
|
Event.Timestamp = totalTime + eventSplineTime + leg.Duration + (pausePointIndex == eventPointIndex ? pathPoints[eventPointIndex].Delay * Time.InMilliseconds : 0);
|
||||||
|
Event.EventId = pathPoints[eventPointIndex].DepartureEventID;
|
||||||
|
outEvents.Add(Event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double splineLengthToCurrentNode = leg.Spline.Length(pausePointIndex);
|
||||||
|
double length1 = splineLengthToCurrentNode - splineLengthToPreviousNode;
|
||||||
|
uint movementTime = 0;
|
||||||
|
if (pauseItr != 0)
|
||||||
|
movementTime = legTimeAccelDecel(length1);
|
||||||
|
else
|
||||||
|
movementTime = legTimeAccel(length1);
|
||||||
|
|
||||||
|
leg.Duration += movementTime;
|
||||||
|
var segment = leg.Segments[pauseItr];
|
||||||
|
segment.SegmentEndArrivalTimestamp = leg.Duration + delaySum;
|
||||||
|
segment.Delay = pathPoints[pausePointIndex].Delay * Time.InMilliseconds;
|
||||||
|
segment.DistanceFromLegStartAtEnd = splineLengthToCurrentNode;
|
||||||
|
delaySum += pathPoints[pausePointIndex].Delay * Time.InMilliseconds;
|
||||||
|
splineLengthToPreviousNode = splineLengthToCurrentNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process events happening after last pause
|
||||||
|
for (; eventItr < events.Count; ++eventItr)
|
||||||
|
{
|
||||||
|
var eventPointIndex = pathPoints.IndexOf(events[eventItr]);
|
||||||
|
if (eventPointIndex == -1) // last point is a "fake" spline node, events cannot happen there
|
||||||
|
break;
|
||||||
|
|
||||||
|
double eventLength = leg.Spline.Length(eventPointIndex) - splineLengthToPreviousNode;
|
||||||
|
uint eventSplineTime = 0;
|
||||||
|
if (pauseItr != 0)
|
||||||
|
eventSplineTime = legTimeAccel(eventLength);
|
||||||
|
else
|
||||||
|
eventSplineTime = (uint)(eventLength / (double)goInfo.MoTransport.moveSpeed * 1000.0);
|
||||||
|
|
||||||
|
if (pathPoints[eventPointIndex].ArrivalEventID != 0)
|
||||||
|
{
|
||||||
|
TransportPathEvent Event = new();
|
||||||
|
Event.Timestamp = totalTime + eventSplineTime + leg.Duration;
|
||||||
|
Event.EventId = pathPoints[eventPointIndex].ArrivalEventID;
|
||||||
|
outEvents.Add(Event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pathPoints[eventPointIndex].DepartureEventID != 0)
|
||||||
|
{
|
||||||
|
TransportPathEvent Event = new();
|
||||||
|
Event.Timestamp = totalTime + eventSplineTime + leg.Duration;
|
||||||
|
Event.EventId = pathPoints[eventPointIndex].DepartureEventID;
|
||||||
|
outEvents.Add(Event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add segment after last pause
|
||||||
|
double length = leg.Spline.Length() - splineLengthToPreviousNode;
|
||||||
|
uint splineTime = 0;
|
||||||
|
if (pauseItr != 0)
|
||||||
|
splineTime = legTimeAccel(length);
|
||||||
|
else
|
||||||
|
splineTime = (uint)(length / (double)goInfo.MoTransport.moveSpeed * 1000.0);
|
||||||
|
|
||||||
|
leg.StartTimestamp = totalTime;
|
||||||
|
leg.Duration += splineTime + delaySum;
|
||||||
|
var pauseSegment = leg.Segments[pauseItr];
|
||||||
|
pauseSegment.SegmentEndArrivalTimestamp = leg.Duration;
|
||||||
|
pauseSegment.Delay = 0;
|
||||||
|
pauseSegment.DistanceFromLegStartAtEnd = leg.Spline.Length();
|
||||||
|
totalTime += leg.Segments[pauseItr].SegmentEndArrivalTimestamp + leg.Segments[pauseItr].Delay;
|
||||||
|
|
||||||
|
for (var i = 0; i < leg.Segments.Count; ++i)
|
||||||
|
{
|
||||||
|
var segment = leg.Segments[i];
|
||||||
|
segment.SegmentEndArrivalTimestamp += leg.StartTimestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeneratePath(GameObjectTemplate goInfo, TransportTemplate transport, List<uint> mapsUsed)
|
||||||
{
|
{
|
||||||
uint pathId = goInfo.MoTransport.taxiPathID;
|
uint pathId = goInfo.MoTransport.taxiPathID;
|
||||||
var path = CliDB.TaxiPathNodesByPath[pathId];
|
TaxiPathNodeRecord[] path = CliDB.TaxiPathNodesByPath[pathId];
|
||||||
List<KeyFrame> keyFrames = transport.keyFrames;
|
|
||||||
List<Vector3> splinePath = new();
|
|
||||||
List<Vector3> allPoints = new();
|
|
||||||
bool mapChange = false;
|
|
||||||
|
|
||||||
for (uint i = 0; i < path.Length; ++i)
|
transport.Speed = (double)goInfo.MoTransport.moveSpeed;
|
||||||
allPoints.Add(new Vector3(path[i].Loc.X, path[i].Loc.Y, path[i].Loc.Z));
|
transport.AccelerationRate = (double)goInfo.MoTransport.accelRate;
|
||||||
|
transport.AccelerationTime = transport.Speed / transport.AccelerationRate;
|
||||||
|
transport.AccelerationDistance = 0.5 * transport.Speed * transport.Speed / transport.AccelerationRate;
|
||||||
|
|
||||||
// Add extra points to allow derivative calculations for all path nodes
|
List<TaxiPathNodeRecord> pathPoints = new();
|
||||||
allPoints.Insert(0, Vector3.Lerp(allPoints.First(), allPoints[1], -0.2f));
|
List<TaxiPathNodeRecord> pauses = new();
|
||||||
allPoints.Add(Vector3.Lerp(allPoints.Last(), allPoints[^2], -0.2f));
|
List<TaxiPathNodeRecord> events = new();
|
||||||
allPoints.Add(Vector3.Lerp(allPoints.Last(), allPoints[^2], -1.0f));
|
TransportPathLeg leg = new();
|
||||||
|
leg.MapId = path[0].ContinentID;
|
||||||
SplineRawInitializer initer = new(allPoints);
|
bool prevNodeWasTeleport = false;
|
||||||
Spline orientationSpline = new();
|
uint totalTime = 0;
|
||||||
orientationSpline.InitSplineCustom(initer);
|
foreach (TaxiPathNodeRecord node in path)
|
||||||
orientationSpline.InitLengths();
|
|
||||||
|
|
||||||
for (uint i = 0; i < path.Length; ++i)
|
|
||||||
{
|
{
|
||||||
if (!mapChange)
|
if (node.ContinentID != leg.MapId || prevNodeWasTeleport)
|
||||||
{
|
{
|
||||||
var node_i = path[i];
|
InitializeLeg(leg, transport.Events, pathPoints, pauses, events, goInfo, ref totalTime);
|
||||||
if (i != path.Length - 1 && (node_i.Flags.HasAnyFlag(TaxiPathNodeFlags.Teleport) || node_i.ContinentID != path[i + 1].ContinentID))
|
|
||||||
{
|
|
||||||
keyFrames.Last().Teleport = true;
|
|
||||||
mapChange = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
KeyFrame k = new(node_i);
|
|
||||||
Vector3 h;
|
|
||||||
orientationSpline.Evaluate_Derivative((int)(i + 1), 0.0f, out h);
|
|
||||||
k.InitialOrientation = Position.NormalizeOrientation((float)Math.Atan2(h.Y, h.X) + MathFunctions.PI);
|
|
||||||
|
|
||||||
keyFrames.Add(k);
|
leg = new();
|
||||||
splinePath.Add(new Vector3(node_i.Loc.X, node_i.Loc.Y, node_i.Loc.Z));
|
leg.MapId = node.ContinentID;
|
||||||
if (!transport.mapsUsed.Contains(k.Node.ContinentID))
|
pathPoints.Clear();
|
||||||
transport.mapsUsed.Add(k.Node.ContinentID);
|
pauses.Clear();
|
||||||
}
|
events.Clear();
|
||||||
|
transport.PathLegs.Add(leg);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
mapChange = false;
|
prevNodeWasTeleport = node.Flags.HasFlag(TaxiPathNodeFlags.Teleport);
|
||||||
|
pathPoints.Add(node);
|
||||||
|
if (node.Flags.HasFlag(TaxiPathNodeFlags.Stop))
|
||||||
|
pauses.Add(node);
|
||||||
|
|
||||||
|
if (node.ArrivalEventID != 0 || node.DepartureEventID != 0)
|
||||||
|
events.Add(node);
|
||||||
|
|
||||||
|
mapsUsed.Add(node.ContinentID);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (splinePath.Count >= 2)
|
if (leg.Spline == null)
|
||||||
{
|
InitializeLeg(leg, transport.Events, pathPoints, pauses, events, goInfo, ref totalTime);
|
||||||
// Remove special catmull-rom spline points
|
|
||||||
if (!keyFrames.First().IsStopFrame() && keyFrames.First().Node.ArrivalEventID == 0 && keyFrames.First().Node.DepartureEventID == 0)
|
|
||||||
{
|
|
||||||
splinePath.RemoveAt(0);
|
|
||||||
keyFrames.RemoveAt(0);
|
|
||||||
}
|
|
||||||
if (!keyFrames.Last().IsStopFrame() && keyFrames.Last().Node.ArrivalEventID == 0 && keyFrames.Last().Node.DepartureEventID == 0)
|
|
||||||
{
|
|
||||||
splinePath.RemoveAt(splinePath.Count - 1);
|
|
||||||
keyFrames.RemoveAt(keyFrames.Count - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Cypher.Assert(!keyFrames.Empty());
|
if (mapsUsed.Count > 1)
|
||||||
|
|
||||||
if (transport.mapsUsed.Count > 1)
|
|
||||||
{
|
{
|
||||||
foreach (var mapId in transport.mapsUsed)
|
foreach (uint mapId in mapsUsed)
|
||||||
Cypher.Assert(!CliDB.MapStorage.LookupByKey(mapId).Instanceable());
|
Cypher.Assert(!CliDB.MapStorage.LookupByKey(mapId).Instanceable());
|
||||||
|
|
||||||
transport.inInstance = false;
|
transport.InInstance = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
transport.inInstance = CliDB.MapStorage.LookupByKey(transport.mapsUsed.First()).Instanceable();
|
transport.InInstance = CliDB.MapStorage.LookupByKey(mapsUsed.First()).Instanceable();
|
||||||
|
|
||||||
// last to first is always "teleport", even for closed paths
|
transport.TotalPathTime = totalTime;
|
||||||
keyFrames.Last().Teleport = true;
|
transport.PathLegs.Add(leg);
|
||||||
|
|
||||||
float speed = goInfo.MoTransport.moveSpeed;
|
|
||||||
float accel = goInfo.MoTransport.accelRate;
|
|
||||||
float accel_dist = 0.5f * speed * speed / accel;
|
|
||||||
|
|
||||||
transport.accelTime = speed / accel;
|
|
||||||
transport.accelDist = accel_dist;
|
|
||||||
|
|
||||||
int firstStop = -1;
|
|
||||||
int lastStop = -1;
|
|
||||||
|
|
||||||
// first cell is arrived at by teleportation :S
|
|
||||||
keyFrames[0].DistFromPrev = 0;
|
|
||||||
keyFrames[0].Index = 1;
|
|
||||||
if (keyFrames[0].IsStopFrame())
|
|
||||||
{
|
|
||||||
firstStop = 0;
|
|
||||||
lastStop = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find the rest of the distances between key points
|
|
||||||
// Every path segment has its own spline
|
|
||||||
int start = 0;
|
|
||||||
for (int i = 1; i < keyFrames.Count; ++i)
|
|
||||||
{
|
|
||||||
if (keyFrames[i - 1].Teleport || i + 1 == keyFrames.Count)
|
|
||||||
{
|
|
||||||
int extra = !keyFrames[i - 1].Teleport ? 1 : 0;
|
|
||||||
Spline spline = new();
|
|
||||||
Span<Vector3> span = splinePath.ToArray();
|
|
||||||
spline.InitSpline(span[start..], i - start + extra, Spline.EvaluationMode.Catmullrom);
|
|
||||||
spline.InitLengths();
|
|
||||||
for (int j = start; j < i + extra; ++j)
|
|
||||||
{
|
|
||||||
keyFrames[j].Index = (uint)(j - start + 1);
|
|
||||||
keyFrames[j].DistFromPrev = spline.Length(j - start, j + 1 - start);
|
|
||||||
if (j > 0)
|
|
||||||
keyFrames[j - 1].NextDistFromPrev = keyFrames[j].DistFromPrev;
|
|
||||||
keyFrames[j].Spline = spline;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyFrames[i - 1].Teleport)
|
|
||||||
{
|
|
||||||
keyFrames[i].Index = (uint)(i - start + 1);
|
|
||||||
keyFrames[i].DistFromPrev = 0.0f;
|
|
||||||
keyFrames[i - 1].NextDistFromPrev = 0.0f;
|
|
||||||
keyFrames[i].Spline = spline;
|
|
||||||
}
|
|
||||||
|
|
||||||
start = i;
|
|
||||||
}
|
|
||||||
if (keyFrames[i].IsStopFrame())
|
|
||||||
{
|
|
||||||
// remember first stop frame
|
|
||||||
if (firstStop == -1)
|
|
||||||
firstStop = i;
|
|
||||||
lastStop = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
keyFrames.Last().NextDistFromPrev = keyFrames.First().DistFromPrev;
|
|
||||||
|
|
||||||
if (firstStop == -1 || lastStop == -1)
|
|
||||||
firstStop = lastStop = 0;
|
|
||||||
|
|
||||||
// at stopping keyframes, we define distSinceStop == 0,
|
|
||||||
// and distUntilStop is to the next stopping keyframe.
|
|
||||||
// this is required to properly handle cases of two stopping frames in a row (yes they do exist)
|
|
||||||
float tmpDist = 0.0f;
|
|
||||||
for (int i = 0; i < keyFrames.Count; ++i)
|
|
||||||
{
|
|
||||||
int j = (i + lastStop) % keyFrames.Count;
|
|
||||||
if (keyFrames[j].IsStopFrame() || j == lastStop)
|
|
||||||
tmpDist = 0.0f;
|
|
||||||
else
|
|
||||||
tmpDist += keyFrames[j].DistFromPrev;
|
|
||||||
keyFrames[j].DistSinceStop = tmpDist;
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpDist = 0.0f;
|
|
||||||
for (int i = (keyFrames.Count - 1); i >= 0; i--)
|
|
||||||
{
|
|
||||||
int j = (i + firstStop) % keyFrames.Count;
|
|
||||||
tmpDist += keyFrames[(j + 1) % keyFrames.Count].DistFromPrev;
|
|
||||||
keyFrames[j].DistUntilStop = tmpDist;
|
|
||||||
if (keyFrames[j].IsStopFrame() || j == firstStop)
|
|
||||||
tmpDist = 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < keyFrames.Count; ++i)
|
|
||||||
{
|
|
||||||
float total_dist = keyFrames[i].DistSinceStop + keyFrames[i].DistUntilStop;
|
|
||||||
if (total_dist < 2 * accel_dist) // won't reach full speed
|
|
||||||
{
|
|
||||||
if (keyFrames[i].DistSinceStop < keyFrames[i].DistUntilStop) // is still accelerating
|
|
||||||
{
|
|
||||||
// calculate accel+brake time for this short segment
|
|
||||||
float segment_time = 2.0f * (float)Math.Sqrt((keyFrames[i].DistUntilStop + keyFrames[i].DistSinceStop) / accel);
|
|
||||||
// substract acceleration time
|
|
||||||
keyFrames[i].TimeTo = segment_time - (float)Math.Sqrt(2 * keyFrames[i].DistSinceStop / accel);
|
|
||||||
}
|
|
||||||
else // slowing down
|
|
||||||
keyFrames[i].TimeTo = (float)Math.Sqrt(2 * keyFrames[i].DistUntilStop / accel);
|
|
||||||
}
|
|
||||||
else if (keyFrames[i].DistSinceStop < accel_dist) // still accelerating (but will reach full speed)
|
|
||||||
{
|
|
||||||
// calculate accel + cruise + brake time for this long segment
|
|
||||||
float segment_time = (keyFrames[i].DistUntilStop + keyFrames[i].DistSinceStop) / speed + (speed / accel);
|
|
||||||
// substract acceleration time
|
|
||||||
keyFrames[i].TimeTo = segment_time - (float)Math.Sqrt(2 * keyFrames[i].DistSinceStop / accel);
|
|
||||||
}
|
|
||||||
else if (keyFrames[i].DistUntilStop < accel_dist) // already slowing down (but reached full speed)
|
|
||||||
keyFrames[i].TimeTo = (float)Math.Sqrt(2 * keyFrames[i].DistUntilStop / accel);
|
|
||||||
else // at full speed
|
|
||||||
keyFrames[i].TimeTo = (keyFrames[i].DistUntilStop / speed) + (0.5f * speed / accel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate tFrom times from tTo times
|
|
||||||
float segmentTime = 0.0f;
|
|
||||||
for (int i = 0; i < keyFrames.Count; ++i)
|
|
||||||
{
|
|
||||||
int j = (i + lastStop) % keyFrames.Count;
|
|
||||||
if (keyFrames[j].IsStopFrame() || j == lastStop)
|
|
||||||
segmentTime = keyFrames[j].TimeTo;
|
|
||||||
keyFrames[j].TimeFrom = segmentTime - keyFrames[j].TimeTo;
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate path times
|
|
||||||
keyFrames[0].ArriveTime = 0;
|
|
||||||
float curPathTime = 0.0f;
|
|
||||||
if (keyFrames[0].IsStopFrame())
|
|
||||||
{
|
|
||||||
curPathTime = keyFrames[0].Node.Delay;
|
|
||||||
keyFrames[0].DepartureTime = (uint)(curPathTime * Time.InMilliseconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 1; i < keyFrames.Count; ++i)
|
|
||||||
{
|
|
||||||
curPathTime += keyFrames[i - 1].TimeTo;
|
|
||||||
if (keyFrames[i].IsStopFrame())
|
|
||||||
{
|
|
||||||
keyFrames[i].ArriveTime = (uint)(curPathTime * Time.InMilliseconds);
|
|
||||||
keyFrames[i - 1].NextArriveTime = keyFrames[i].ArriveTime;
|
|
||||||
curPathTime += keyFrames[i].Node.Delay;
|
|
||||||
keyFrames[i].DepartureTime = (uint)(curPathTime * Time.InMilliseconds);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
curPathTime -= keyFrames[i].TimeTo;
|
|
||||||
keyFrames[i].ArriveTime = (uint)(curPathTime * Time.InMilliseconds);
|
|
||||||
keyFrames[i - 1].NextArriveTime = keyFrames[i].ArriveTime;
|
|
||||||
keyFrames[i].DepartureTime = keyFrames[i].ArriveTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keyFrames.Last().NextArriveTime = keyFrames.Last().DepartureTime;
|
|
||||||
|
|
||||||
transport.pathTime = keyFrames.Last().DepartureTime;
|
|
||||||
if (transport.pathTime == 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddPathNodeToTransport(uint transportEntry, uint timeSeg, TransportAnimationRecord node)
|
public void AddPathNodeToTransport(uint transportEntry, uint timeSeg, TransportAnimationRecord node)
|
||||||
@@ -461,16 +427,22 @@ namespace Game.Maps
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Position startingPosition = tInfo.ComputePosition(0, out _, out _);
|
||||||
|
if (startingPosition == null)
|
||||||
|
{
|
||||||
|
Log.outError(LogFilter.Sql, $"Transport {entry} will not be loaded, failed to compute starting position");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// create transport...
|
// create transport...
|
||||||
Transport trans = new();
|
Transport trans = new();
|
||||||
|
|
||||||
// ...at first waypoint
|
// ...at first waypoint
|
||||||
TaxiPathNodeRecord startNode = tInfo.keyFrames.First().Node;
|
uint mapId = tInfo.PathLegs.First().MapId;
|
||||||
uint mapId = startNode.ContinentID;
|
float x = startingPosition.GetPositionX();
|
||||||
float x = startNode.Loc.X;
|
float y = startingPosition.GetPositionY();
|
||||||
float y = startNode.Loc.Y;
|
float z = startingPosition.GetPositionZ();
|
||||||
float z = startNode.Loc.Z;
|
float o = startingPosition.GetOrientation();
|
||||||
float o = tInfo.keyFrames.First().InitialOrientation;
|
|
||||||
|
|
||||||
// initialize the gameobject base
|
// initialize the gameobject base
|
||||||
ulong guidLow = guid != 0 ? guid : map.GenerateLowGuid(HighGuid.Transport);
|
ulong guidLow = guid != 0 ? guid : map.GenerateLowGuid(HighGuid.Transport);
|
||||||
@@ -482,7 +454,7 @@ namespace Game.Maps
|
|||||||
MapRecord mapEntry = CliDB.MapStorage.LookupByKey(mapId);
|
MapRecord mapEntry = CliDB.MapStorage.LookupByKey(mapId);
|
||||||
if (mapEntry != null)
|
if (mapEntry != null)
|
||||||
{
|
{
|
||||||
if (mapEntry.Instanceable() != tInfo.inInstance)
|
if (mapEntry.Instanceable() != tInfo.InInstance)
|
||||||
{
|
{
|
||||||
Log.outError(LogFilter.Transport, "Transport {0} (name: {1}) attempted creation in instance map (id: {2}) but it is not an instanced transport!", entry, trans.GetName(), mapId);
|
Log.outError(LogFilter.Transport, "Transport {0} (name: {1}) attempted creation in instance map (id: {2}) but it is not an instanced transport!", entry, trans.GetName(), mapId);
|
||||||
//return null;
|
//return null;
|
||||||
@@ -508,7 +480,7 @@ namespace Game.Maps
|
|||||||
uint count = 0;
|
uint count = 0;
|
||||||
|
|
||||||
foreach (var pair in _transportSpawns)
|
foreach (var pair in _transportSpawns)
|
||||||
if (!GetTransportTemplate(pair.Value.TransportGameObjectId).inInstance)
|
if (!GetTransportTemplate(pair.Value.TransportGameObjectId).InInstance)
|
||||||
if (CreateTransport(pair.Value.TransportGameObjectId, pair.Value.SpawnId, null, pair.Value.PhaseUseFlags, pair.Value.PhaseId, pair.Value.PhaseGroup))
|
if (CreateTransport(pair.Value.TransportGameObjectId, pair.Value.SpawnId, null, pair.Value.PhaseUseFlags, pair.Value.PhaseId, pair.Value.PhaseGroup))
|
||||||
++count;
|
++count;
|
||||||
|
|
||||||
@@ -549,6 +521,177 @@ namespace Game.Maps
|
|||||||
Dictionary<ulong, TransportSpawn> _transportSpawns = new();
|
Dictionary<ulong, TransportSpawn> _transportSpawns = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct TransportPathSegment
|
||||||
|
{
|
||||||
|
public uint SegmentEndArrivalTimestamp;
|
||||||
|
public uint Delay;
|
||||||
|
public double DistanceFromLegStartAtEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct TransportPathEvent
|
||||||
|
{
|
||||||
|
public uint Timestamp;
|
||||||
|
public uint EventId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TransportPathLeg
|
||||||
|
{
|
||||||
|
public uint MapId;
|
||||||
|
public Spline<double> Spline;
|
||||||
|
public uint StartTimestamp;
|
||||||
|
public uint Duration;
|
||||||
|
public List<TransportPathSegment> Segments = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TransportTemplate
|
||||||
|
{
|
||||||
|
public uint TotalPathTime;
|
||||||
|
public double Speed;
|
||||||
|
public double AccelerationRate;
|
||||||
|
public double AccelerationTime;
|
||||||
|
public double AccelerationDistance;
|
||||||
|
public List<TransportPathLeg> PathLegs = new();
|
||||||
|
public List<TransportPathEvent> Events = new();
|
||||||
|
|
||||||
|
public bool InInstance;
|
||||||
|
|
||||||
|
public Position ComputePosition(uint time, out TransportMovementState moveState, out int legIndex)
|
||||||
|
{
|
||||||
|
moveState = TransportMovementState.Moving;
|
||||||
|
|
||||||
|
time %= TotalPathTime;
|
||||||
|
|
||||||
|
// find leg
|
||||||
|
legIndex = 0;
|
||||||
|
while (PathLegs[legIndex].StartTimestamp + PathLegs[legIndex].Duration <= time)
|
||||||
|
{
|
||||||
|
++legIndex;
|
||||||
|
|
||||||
|
if (PathLegs.Count >= legIndex)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var legItr = PathLegs[legIndex];
|
||||||
|
|
||||||
|
// find segment
|
||||||
|
uint prevSegmentTime = legItr.StartTimestamp;
|
||||||
|
var segmentIndex = 0;
|
||||||
|
double distanceMoved = 0.0;
|
||||||
|
bool isOnPause = false;
|
||||||
|
for (segmentIndex = 0; segmentIndex < legItr.Segments.Count; ++segmentIndex)
|
||||||
|
{
|
||||||
|
var segment = legItr.Segments[segmentIndex];
|
||||||
|
if (time < segment.SegmentEndArrivalTimestamp)
|
||||||
|
break;
|
||||||
|
|
||||||
|
distanceMoved = segment.DistanceFromLegStartAtEnd;
|
||||||
|
if (time < segment.SegmentEndArrivalTimestamp + segment.Delay)
|
||||||
|
{
|
||||||
|
isOnPause = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
prevSegmentTime = segment.SegmentEndArrivalTimestamp + segment.Delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pathSegment = legItr.Segments[segmentIndex];
|
||||||
|
|
||||||
|
if (!isOnPause)
|
||||||
|
distanceMoved += CalculateDistanceMoved(
|
||||||
|
(double)(time - prevSegmentTime) * 0.001,
|
||||||
|
(double)(pathSegment.SegmentEndArrivalTimestamp - prevSegmentTime) * 0.001,
|
||||||
|
segmentIndex == 0,
|
||||||
|
segmentIndex == legItr.Segments.Count);
|
||||||
|
|
||||||
|
int splineIndex = 0;
|
||||||
|
float splinePointProgress = 0;
|
||||||
|
legItr.Spline.ComputeIndex((float)Math.Min(distanceMoved / legItr.Spline.Length(), 1.0), ref splineIndex, ref splinePointProgress);
|
||||||
|
|
||||||
|
Vector3 pos, dir;
|
||||||
|
legItr.Spline.Evaluate_Percent(splineIndex, splinePointProgress, out pos);
|
||||||
|
legItr.Spline.Evaluate_Derivative(splineIndex, splinePointProgress, out dir);
|
||||||
|
|
||||||
|
moveState = isOnPause ? TransportMovementState.WaitingOnPauseWaypoint : TransportMovementState.Moving;
|
||||||
|
|
||||||
|
return new Position(pos.X, pos.Y, pos.Z, MathF.Atan2(dir.Y, dir.X) + MathF.PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
double CalculateDistanceMoved(double timePassedInSegment, double segmentDuration, bool isFirstSegment, bool isLastSegment)
|
||||||
|
{
|
||||||
|
if (isFirstSegment)
|
||||||
|
{
|
||||||
|
if (!isLastSegment)
|
||||||
|
{
|
||||||
|
double accelerationTime = Math.Min(AccelerationTime, segmentDuration);
|
||||||
|
double segmentTimeAtFullSpeed = segmentDuration - accelerationTime;
|
||||||
|
if (timePassedInSegment <= segmentTimeAtFullSpeed)
|
||||||
|
{
|
||||||
|
return timePassedInSegment * Speed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double segmentAccelerationTime = timePassedInSegment - segmentTimeAtFullSpeed;
|
||||||
|
double segmentAccelerationDistance = AccelerationRate * accelerationTime;
|
||||||
|
double segmentDistanceAtFullSpeed = segmentTimeAtFullSpeed * Speed;
|
||||||
|
return (2.0 * segmentAccelerationDistance - segmentAccelerationTime * AccelerationRate) * 0.5 * segmentAccelerationTime + segmentDistanceAtFullSpeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return timePassedInSegment * Speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLastSegment)
|
||||||
|
{
|
||||||
|
if (!isFirstSegment)
|
||||||
|
{
|
||||||
|
if (timePassedInSegment <= Math.Min(AccelerationTime, segmentDuration))
|
||||||
|
return AccelerationRate * timePassedInSegment * 0.5 * timePassedInSegment;
|
||||||
|
else
|
||||||
|
return (timePassedInSegment - AccelerationTime) * Speed + AccelerationDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
return timePassedInSegment * Speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
double accelerationTime1 = Math.Min(segmentDuration * 0.5, AccelerationTime);
|
||||||
|
if (timePassedInSegment <= segmentDuration - accelerationTime1)
|
||||||
|
{
|
||||||
|
if (timePassedInSegment <= accelerationTime1)
|
||||||
|
return AccelerationRate * timePassedInSegment * 0.5 * timePassedInSegment;
|
||||||
|
else
|
||||||
|
return (timePassedInSegment - AccelerationTime) * Speed + AccelerationDistance;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double segmentTimeSpentAccelerating = timePassedInSegment - (segmentDuration - accelerationTime1);
|
||||||
|
return (segmentDuration - 2 * accelerationTime1) * Speed
|
||||||
|
+ AccelerationRate * accelerationTime1 * 0.5 * accelerationTime1
|
||||||
|
+ (2.0 * AccelerationRate * accelerationTime1 - segmentTimeSpentAccelerating * AccelerationRate) * 0.5 * segmentTimeSpentAccelerating;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint GetNextPauseWaypointTimestamp(uint time)
|
||||||
|
{
|
||||||
|
var legIndex = 0;
|
||||||
|
while (PathLegs[legIndex].StartTimestamp + PathLegs[legIndex].Duration <= time)
|
||||||
|
{
|
||||||
|
++legIndex;
|
||||||
|
|
||||||
|
if (legIndex >= PathLegs.Count)
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
var leg = PathLegs[legIndex];
|
||||||
|
|
||||||
|
var segmentIndex = 0;
|
||||||
|
for (; segmentIndex != leg.Segments.Count - 1; ++segmentIndex)
|
||||||
|
if (time < leg.Segments[segmentIndex].SegmentEndArrivalTimestamp + leg.Segments[segmentIndex].Delay)
|
||||||
|
break;
|
||||||
|
|
||||||
|
return leg.Segments[segmentIndex].SegmentEndArrivalTimestamp + leg.Segments[segmentIndex].Delay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class SplineRawInitializer
|
public class SplineRawInitializer
|
||||||
{
|
{
|
||||||
public SplineRawInitializer(List<Vector3> points)
|
public SplineRawInitializer(List<Vector3> points)
|
||||||
@@ -556,9 +699,9 @@ namespace Game.Maps
|
|||||||
_points = points;
|
_points = points;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(ref Spline.EvaluationMode mode, ref bool cyclic, ref Vector3[] points, ref int lo, ref int hi)
|
public void Initialize(ref EvaluationMode mode, ref bool cyclic, ref Vector3[] points, ref int lo, ref int hi)
|
||||||
{
|
{
|
||||||
mode = Spline.EvaluationMode.Catmullrom;
|
mode = EvaluationMode.Catmullrom;
|
||||||
cyclic = false;
|
cyclic = false;
|
||||||
points = new Vector3[_points.Count];
|
points = new Vector3[_points.Count];
|
||||||
|
|
||||||
@@ -572,63 +715,6 @@ namespace Game.Maps
|
|||||||
List<Vector3> _points;
|
List<Vector3> _points;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class KeyFrame
|
|
||||||
{
|
|
||||||
public KeyFrame(TaxiPathNodeRecord _node)
|
|
||||||
{
|
|
||||||
Node = _node;
|
|
||||||
DistSinceStop = -1.0f;
|
|
||||||
DistUntilStop = -1.0f;
|
|
||||||
DistFromPrev = -1.0f;
|
|
||||||
TimeFrom = 0.0f;
|
|
||||||
TimeTo = 0.0f;
|
|
||||||
Teleport = false;
|
|
||||||
ArriveTime = 0;
|
|
||||||
DepartureTime = 0;
|
|
||||||
Spline = null;
|
|
||||||
NextDistFromPrev = 0.0f;
|
|
||||||
NextArriveTime = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint Index;
|
|
||||||
public TaxiPathNodeRecord Node;
|
|
||||||
public float InitialOrientation;
|
|
||||||
public float DistSinceStop;
|
|
||||||
public float DistUntilStop;
|
|
||||||
public float DistFromPrev;
|
|
||||||
public float TimeFrom;
|
|
||||||
public float TimeTo;
|
|
||||||
public bool Teleport;
|
|
||||||
public uint ArriveTime;
|
|
||||||
public uint DepartureTime;
|
|
||||||
public Spline Spline;
|
|
||||||
|
|
||||||
// Data needed for next frame
|
|
||||||
public float NextDistFromPrev;
|
|
||||||
public uint NextArriveTime;
|
|
||||||
|
|
||||||
public bool IsTeleportFrame() { return Teleport; }
|
|
||||||
public bool IsStopFrame() { return Node.Flags.HasAnyFlag(TaxiPathNodeFlags.Stop); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TransportTemplate
|
|
||||||
{
|
|
||||||
public TransportTemplate()
|
|
||||||
{
|
|
||||||
pathTime = 0;
|
|
||||||
accelTime = 0.0f;
|
|
||||||
accelDist = 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<uint> mapsUsed = new();
|
|
||||||
public bool inInstance;
|
|
||||||
public uint pathTime;
|
|
||||||
public List<KeyFrame> keyFrames = new();
|
|
||||||
public float accelTime;
|
|
||||||
public float accelDist;
|
|
||||||
public uint entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TransportAnimation
|
public class TransportAnimation
|
||||||
{
|
{
|
||||||
public Dictionary<uint, TransportAnimationRecord> Path = new();
|
public Dictionary<uint, TransportAnimationRecord> Path = new();
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ namespace Game.Movement
|
|||||||
|
|
||||||
void InitSpline(MoveSplineInitArgs args)
|
void InitSpline(MoveSplineInitArgs args)
|
||||||
{
|
{
|
||||||
Spline.EvaluationMode[] modes = new Spline.EvaluationMode[2] { Spline.EvaluationMode.Linear, Spline.EvaluationMode.Catmullrom };
|
EvaluationMode[] modes = new EvaluationMode[2] { EvaluationMode.Linear, EvaluationMode.Catmullrom };
|
||||||
if (args.flags.HasFlag(SplineFlag.Cyclic))
|
if (args.flags.HasFlag(SplineFlag.Cyclic))
|
||||||
{
|
{
|
||||||
int cyclic_point = 0;
|
int cyclic_point = 0;
|
||||||
@@ -351,7 +351,7 @@ namespace Game.Movement
|
|||||||
|
|
||||||
#region Fields
|
#region Fields
|
||||||
public MoveSplineInitArgs InitArgs;
|
public MoveSplineInitArgs InitArgs;
|
||||||
public Spline spline = new();
|
public Spline<int> spline = new();
|
||||||
public FacingInfo facing;
|
public FacingInfo facing;
|
||||||
public MoveSplineFlag splineflags = new();
|
public MoveSplineFlag splineflags = new();
|
||||||
public bool onTransport;
|
public bool onTransport;
|
||||||
@@ -368,7 +368,7 @@ namespace Game.Movement
|
|||||||
public AnimTierTransition anim_tier;
|
public AnimTierTransition anim_tier;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public class CommonInitializer : IInitializer
|
public class CommonInitializer : IInitializer<int>
|
||||||
{
|
{
|
||||||
public CommonInitializer(float _velocity)
|
public CommonInitializer(float _velocity)
|
||||||
{
|
{
|
||||||
@@ -377,20 +377,23 @@ namespace Game.Movement
|
|||||||
}
|
}
|
||||||
public float velocityInv;
|
public float velocityInv;
|
||||||
public int time;
|
public int time;
|
||||||
public int SetGetTime(Spline s, int i)
|
|
||||||
|
public int Invoke(Spline<int> s, int i)
|
||||||
{
|
{
|
||||||
time += (int)(s.SegLength(i) * velocityInv);
|
time += (int)(s.SegLength(i) * velocityInv);
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public class FallInitializer : IInitializer
|
|
||||||
|
public class FallInitializer : IInitializer<int>
|
||||||
{
|
{
|
||||||
public FallInitializer(float startelevation)
|
public FallInitializer(float startelevation)
|
||||||
{
|
{
|
||||||
startElevation = startelevation;
|
startElevation = startelevation;
|
||||||
}
|
}
|
||||||
float startElevation;
|
float startElevation;
|
||||||
public int SetGetTime(Spline s, int i)
|
|
||||||
|
public int Invoke(Spline<int> s, int i)
|
||||||
{
|
{
|
||||||
return (int)(ComputeFallTime(startElevation - s.GetPoint(i + 1).Z, false) * 1000.0f);
|
return (int)(ComputeFallTime(startElevation - s.GetPoint(i + 1).Z, false) * 1000.0f);
|
||||||
}
|
}
|
||||||
@@ -427,9 +430,9 @@ namespace Game.Movement
|
|||||||
NextSegment = 0x08
|
NextSegment = 0x08
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public interface IInitializer
|
public interface IInitializer<T>
|
||||||
{
|
{
|
||||||
int SetGetTime(Spline s, int i);
|
int Invoke(Spline<T> s, int i);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SplineChainLink
|
public class SplineChainLink
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ using System.Numerics;
|
|||||||
|
|
||||||
namespace Game.Movement
|
namespace Game.Movement
|
||||||
{
|
{
|
||||||
public class Spline
|
public class Spline<T>
|
||||||
{
|
{
|
||||||
public int GetPointCount() { return points.Length; }
|
public int GetPointCount() { return points.Length; }
|
||||||
public Vector3 GetPoint(int i) { return points[i]; }
|
public Vector3 GetPoint(int i) { return points[i]; }
|
||||||
@@ -262,18 +262,18 @@ namespace Game.Movement
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
void set_steps_per_segment(int newStepsPerSegment) { stepsPerSegment = newStepsPerSegment; }
|
public void set_steps_per_segment(int newStepsPerSegment) { stepsPerSegment = newStepsPerSegment; }
|
||||||
|
|
||||||
public void ComputeIndex(float t, ref int index, ref float u)
|
public void ComputeIndex(float t, ref int index, ref float u)
|
||||||
{
|
{
|
||||||
//ASSERT(t >= 0.f && t <= 1.f);
|
//ASSERT(t >= 0.f && t <= 1.f);
|
||||||
int length_ = (int)(t * Length());
|
T length_ = t * (dynamic)Length();
|
||||||
index = ComputeIndexInBounds(length_);
|
index = ComputeIndexInBounds(length_);
|
||||||
//ASSERT(index < index_hi);
|
//ASSERT(index < index_hi);
|
||||||
u = (length_ - Length(index)) / (float)Length(index, index + 1);
|
u = (length_ - Length(index)) / (float)Length(index, index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ComputeIndexInBounds(int length_)
|
int ComputeIndexInBounds(T length_)
|
||||||
{
|
{
|
||||||
// Temporary disabled: causes infinite loop with t = 1.f
|
// Temporary disabled: causes infinite loop with t = 1.f
|
||||||
/*
|
/*
|
||||||
@@ -294,11 +294,12 @@ namespace Game.Movement
|
|||||||
|
|
||||||
int i = index_lo;
|
int i = index_lo;
|
||||||
int N = index_hi;
|
int N = index_hi;
|
||||||
while (i + 1 < N && lengths[i + 1] < length_)
|
while (i + 1 < N && (dynamic)lengths[i + 1] < length_)
|
||||||
++i;
|
++i;
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Matrix4x4 s_catmullRomCoeffs = new(-0.5f, 1.5f, -1.5f, 0.5f, 1.0f, -2.5f, 2.0f, -0.5f, -0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
|
private static readonly Matrix4x4 s_catmullRomCoeffs = new(-0.5f, 1.5f, -1.5f, 0.5f, 1.0f, -2.5f, 2.0f, -0.5f, -0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
private static readonly Matrix4x4 s_Bezier3Coeffs = new(-1.0f, 3.0f, -3.0f, 1.0f, 3.0f, -6.0f, 3.0f, 0.0f, -3.0f, 3.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
|
private static readonly Matrix4x4 s_Bezier3Coeffs = new(-1.0f, 3.0f, -3.0f, 1.0f, 3.0f, -6.0f, 3.0f, 0.0f, -3.0f, 3.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
|
||||||
@@ -320,31 +321,31 @@ namespace Game.Movement
|
|||||||
+ vertice[2] * weights.Z + vertice[3] * weights.W;
|
+ vertice[2] * weights.Z + vertice[3] * weights.W;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Length()
|
public dynamic Length()
|
||||||
{
|
{
|
||||||
if (lengths.Length == 0)
|
if (lengths.Length == 0)
|
||||||
return 0;
|
return default;
|
||||||
|
|
||||||
return lengths[index_hi];
|
return lengths[index_hi];
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Length(int first, int last) { return lengths[last] - lengths[first]; }
|
public dynamic Length(int first, int last) { return lengths[last] - (dynamic)lengths[first]; }
|
||||||
|
|
||||||
public int Length(int Idx) { return lengths[Idx]; }
|
public dynamic Length(int Idx) { return lengths[Idx]; }
|
||||||
|
|
||||||
public void Set_length(int i, int length) { lengths[i] = length; }
|
public void Set_length(int i, T length) { lengths[i] = length; }
|
||||||
|
|
||||||
public void InitLengths(IInitializer cacher)
|
public void InitLengths(IInitializer<T> cacher)
|
||||||
{
|
{
|
||||||
int i = index_lo;
|
int i = index_lo;
|
||||||
Array.Resize(ref lengths, index_hi+1);
|
Array.Resize(ref lengths, index_hi+1);
|
||||||
int prev_length;
|
T prev_length;
|
||||||
int new_length;
|
T new_length;
|
||||||
while (i < index_hi)
|
while (i < index_hi)
|
||||||
{
|
{
|
||||||
new_length = cacher.SetGetTime(this, i);
|
new_length = (dynamic)cacher.Invoke(this, i);
|
||||||
if (new_length < 0)
|
if ((dynamic)new_length < 0)// todo fix me this is a ulgy hack.
|
||||||
new_length = int.MaxValue;
|
new_length = (dynamic)(Type.GetTypeCode(typeof(T)) == TypeCode.Int32 ? int.MaxValue : double.MaxValue);
|
||||||
lengths[++i] = new_length;
|
lengths[++i] = new_length;
|
||||||
|
|
||||||
prev_length = new_length;
|
prev_length = new_length;
|
||||||
@@ -354,18 +355,18 @@ namespace Game.Movement
|
|||||||
public void InitLengths()
|
public void InitLengths()
|
||||||
{
|
{
|
||||||
int i = index_lo;
|
int i = index_lo;
|
||||||
int length = 0;
|
dynamic length = default(T);
|
||||||
Array.Resize(ref lengths, index_hi + 1);
|
Array.Resize(ref lengths, index_hi + 1);
|
||||||
while (i < index_hi)
|
while (i < index_hi)
|
||||||
{
|
{
|
||||||
length += (int)SegLength(i);
|
length += SegLength(i);
|
||||||
lengths[++i] = length;
|
lengths[++i] = length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Empty() { return index_lo == index_hi;}
|
public bool Empty() { return index_lo == index_hi;}
|
||||||
|
|
||||||
int[] lengths = Array.Empty<int>();
|
T[] lengths = Array.Empty<T>();
|
||||||
Vector3[] points = Array.Empty<Vector3>();
|
Vector3[] points = Array.Empty<Vector3>();
|
||||||
public EvaluationMode m_mode;
|
public EvaluationMode m_mode;
|
||||||
bool _cyclic;
|
bool _cyclic;
|
||||||
@@ -379,14 +380,6 @@ namespace Game.Movement
|
|||||||
|
|
||||||
int index_lo;
|
int index_lo;
|
||||||
int index_hi;
|
int index_hi;
|
||||||
public enum EvaluationMode
|
|
||||||
{
|
|
||||||
Linear,
|
|
||||||
Catmullrom,
|
|
||||||
Bezier3_Unused,
|
|
||||||
UninitializedMode,
|
|
||||||
ModesEnd
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FacingInfo
|
public class FacingInfo
|
||||||
@@ -396,4 +389,13 @@ namespace Game.Movement
|
|||||||
public float angle;
|
public float angle;
|
||||||
public MonsterMoveType type;
|
public MonsterMoveType type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum EvaluationMode
|
||||||
|
{
|
||||||
|
Linear,
|
||||||
|
Catmullrom,
|
||||||
|
Bezier3_Unused,
|
||||||
|
UninitializedMode,
|
||||||
|
ModesEnd
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -301,7 +301,7 @@ namespace Game.Networking.Packets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void WriteCreateObjectAreaTriggerSpline(Spline spline, WorldPacket data)
|
public static void WriteCreateObjectAreaTriggerSpline(Spline<int> spline, WorldPacket data)
|
||||||
{
|
{
|
||||||
data.WriteBits(spline.GetPoints().Length, 16);
|
data.WriteBits(spline.GetPoints().Length, 16);
|
||||||
foreach (var point in spline.GetPoints())
|
foreach (var point in spline.GetPoints())
|
||||||
@@ -425,7 +425,7 @@ namespace Game.Networking.Packets
|
|||||||
movementSpline.SpellEffectExtraData = spellEffectExtraData;
|
movementSpline.SpellEffectExtraData = spellEffectExtraData;
|
||||||
}
|
}
|
||||||
|
|
||||||
Spline spline = moveSpline.spline;
|
var spline = moveSpline.spline;
|
||||||
Vector3[] array = spline.GetPoints();
|
Vector3[] array = spline.GetPoints();
|
||||||
|
|
||||||
if (splineFlags.HasFlag(SplineFlag.UncompressedPath))
|
if (splineFlags.HasFlag(SplineFlag.UncompressedPath))
|
||||||
|
|||||||
@@ -575,7 +575,7 @@ namespace Game.Scripting
|
|||||||
public virtual void OnRemovePassenger(Transport transport, Player player) { }
|
public virtual void OnRemovePassenger(Transport transport, Player player) { }
|
||||||
|
|
||||||
// Called when a transport moves.
|
// Called when a transport moves.
|
||||||
public virtual void OnRelocate(Transport transport, uint waypointId, uint mapId, float x, float y, float z) { }
|
public virtual void OnRelocate(Transport transport, uint mapId, float x, float y, float z) { }
|
||||||
|
|
||||||
public virtual void OnUpdate(Transport obj, uint diff) { }
|
public virtual void OnUpdate(Transport obj, uint diff) { }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -863,9 +863,9 @@ namespace Game.Scripting
|
|||||||
|
|
||||||
RunScript<TransportScript>(p => p.OnUpdate(transport, diff), transport.GetScriptId());
|
RunScript<TransportScript>(p => p.OnUpdate(transport, diff), transport.GetScriptId());
|
||||||
}
|
}
|
||||||
public void OnRelocate(Transport transport, uint waypointId, uint mapId, float x, float y, float z)
|
public void OnRelocate(Transport transport, uint mapId, float x, float y, float z)
|
||||||
{
|
{
|
||||||
RunScript<TransportScript>(p => p.OnRelocate(transport, waypointId, mapId, x, y, z), transport.GetScriptId());
|
RunScript<TransportScript>(p => p.OnRelocate(transport, mapId, x, y, z), transport.GetScriptId());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Achievement
|
// Achievement
|
||||||
|
|||||||
Reference in New Issue
Block a user