Multi-threaded mob movement (#12611)
This commit is contained in:
@@ -1,11 +1,14 @@
|
|||||||
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.Movement.Components;
|
using Content.Shared.Movement.Components;
|
||||||
using Content.Shared.Movement.Systems;
|
using Content.Shared.Movement.Systems;
|
||||||
using Content.Shared.Pulling.Components;
|
using Content.Shared.Pulling.Components;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.Physics;
|
using Robust.Shared.Physics;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Client.Physics.Controllers
|
namespace Content.Client.Physics.Controllers
|
||||||
{
|
{
|
||||||
@@ -134,8 +137,30 @@ namespace Content.Client.Physics.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mobQuery = GetEntityQuery<MobMoverComponent>();
|
||||||
|
var inventoryQuery = GetEntityQuery<InventoryComponent>();
|
||||||
|
var containerQuery = GetEntityQuery<ContainerManagerComponent>();
|
||||||
|
var footQuery = GetEntityQuery<FootstepModifierComponent>();
|
||||||
|
DebugTools.Assert(!UsedMobMovement.ContainsKey(mover.Owner));
|
||||||
|
|
||||||
// Server-side should just be handled on its own so we'll just do this shizznit
|
// Server-side should just be handled on its own so we'll just do this shizznit
|
||||||
HandleMobMovement(mover, body, xformMover, frameTime, xformQuery);
|
HandleMobMovement(mover, body, xformMover, frameTime, xformQuery, mobQuery, inventoryQuery, containerQuery, footQuery, out var dirtyMover, out var linearVelocity, out var sound, out var audio);
|
||||||
|
|
||||||
|
MetaDataComponent? metadata = null;
|
||||||
|
|
||||||
|
if (dirtyMover)
|
||||||
|
{
|
||||||
|
Dirty(mover, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (linearVelocity != null)
|
||||||
|
{
|
||||||
|
PhysicsSystem.SetLinearVelocity(body, linearVelocity.Value, false);
|
||||||
|
PhysicsSystem.SetAngularVelocity(body, 0f, false);
|
||||||
|
Dirty(body, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
Audio.PlayPredicted(sound, mover.Owner, mover.Owner, audio);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool CanSound()
|
protected override bool CanSound()
|
||||||
|
|||||||
@@ -1,20 +1,28 @@
|
|||||||
|
using System.Buffers;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Cargo.Components;
|
using Content.Server.Cargo.Components;
|
||||||
using Content.Server.Shuttles.Components;
|
using Content.Server.Shuttles.Components;
|
||||||
using Content.Server.Shuttles.Systems;
|
using Content.Server.Shuttles.Systems;
|
||||||
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.Movement.Components;
|
using Content.Shared.Movement.Components;
|
||||||
using Content.Shared.Movement.Systems;
|
using Content.Shared.Movement.Systems;
|
||||||
using Content.Shared.Shuttles.Components;
|
using Content.Shared.Shuttles.Components;
|
||||||
using Content.Shared.Shuttles.Systems;
|
using Content.Shared.Shuttles.Systems;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Threading;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Physics.Controllers
|
namespace Content.Server.Physics.Controllers
|
||||||
{
|
{
|
||||||
public sealed class MoverController : SharedMoverController
|
public sealed class MoverController : SharedMoverController
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
[Dependency] private readonly IParallelManager _parallel = default!;
|
||||||
[Dependency] private readonly ThrusterSystem _thruster = default!;
|
[Dependency] private readonly ThrusterSystem _thruster = default!;
|
||||||
|
|
||||||
private Dictionary<ShuttleComponent, List<(PilotComponent, InputMoverComponent, TransformComponent)>> _shuttlePilots = new();
|
private Dictionary<ShuttleComponent, List<(PilotComponent, InputMoverComponent, TransformComponent)>> _shuttlePilots = new();
|
||||||
@@ -64,7 +72,12 @@ namespace Content.Server.Physics.Controllers
|
|||||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||||
var moverQuery = GetEntityQuery<InputMoverComponent>();
|
var moverQuery = GetEntityQuery<InputMoverComponent>();
|
||||||
|
|
||||||
foreach (var mover in EntityQuery<InputMoverComponent>(true))
|
var movers = AllEntityQuery<InputMoverComponent>();
|
||||||
|
var totalCount = EntityManager.Count<InputMoverComponent>();
|
||||||
|
var moveInput = ArrayPool<(InputMoverComponent Mover, TransformComponent Transform, PhysicsComponent Physics)>.Shared.Rent(totalCount);
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
while (movers.MoveNext(out var mover))
|
||||||
{
|
{
|
||||||
if (relayQuery.TryGetComponent(mover.Owner, out var relayed) && relayed.RelayEntity != null)
|
if (relayQuery.TryGetComponent(mover.Owner, out var relayed) && relayed.RelayEntity != null)
|
||||||
{
|
{
|
||||||
@@ -83,13 +96,11 @@ namespace Content.Server.Physics.Controllers
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysicsComponent? body = null;
|
PhysicsComponent? body;
|
||||||
TransformComponent? xformMover = xform;
|
|
||||||
|
|
||||||
if (mover.ToParent && relayQuery.HasComponent(xform.ParentUid))
|
if (mover.ToParent && relayQuery.HasComponent(xform.ParentUid))
|
||||||
{
|
{
|
||||||
if (!bodyQuery.TryGetComponent(xform.ParentUid, out body) ||
|
if (!bodyQuery.TryGetComponent(xform.ParentUid, out body) ||
|
||||||
!TryComp(xform.ParentUid, out xformMover))
|
!xformQuery.HasComponent(xform.ParentUid))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -99,9 +110,64 @@ namespace Content.Server.Physics.Controllers
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
HandleMobMovement(mover, body, xformMover, frameTime, xformQuery);
|
DebugTools.Assert(!UsedMobMovement.ContainsKey(mover.Owner));
|
||||||
|
|
||||||
|
// To avoid threading issues on adding dictionary entries later.
|
||||||
|
UsedMobMovement[mover.Owner] = false;
|
||||||
|
moveInput[count++] = (mover, xform, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var moveResults = ArrayPool<(bool DirtyMover, Vector2? LinearVelocity, SoundSpecifier? sound, AudioParams audio)>.Shared.Rent(count);
|
||||||
|
var mobQuery = GetEntityQuery<MobMoverComponent>();
|
||||||
|
var inventoryQuery = GetEntityQuery<InventoryComponent>();
|
||||||
|
var containerQuery = GetEntityQuery<ContainerManagerComponent>();
|
||||||
|
var footQuery = GetEntityQuery<FootstepModifierComponent>();
|
||||||
|
|
||||||
|
var options = new ParallelOptions()
|
||||||
|
{
|
||||||
|
MaxDegreeOfParallelism = _parallel.ParallelProcessCount,
|
||||||
|
};
|
||||||
|
|
||||||
|
Parallel.For(0, count, options, i =>
|
||||||
|
{
|
||||||
|
var (mover, xform, body) = moveInput[i];
|
||||||
|
HandleMobMovement(mover, body, xform, frameTime, xformQuery, mobQuery, inventoryQuery, containerQuery, footQuery, out var dirtyMover, out var linearVelocity, out var sound, out var audio);
|
||||||
|
moveResults[i] = (dirtyMover, linearVelocity, sound, audio);
|
||||||
|
});
|
||||||
|
|
||||||
|
var metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||||
|
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var results = moveResults[i];
|
||||||
|
var input = moveInput[i];
|
||||||
|
MetaDataComponent? metadata = null;
|
||||||
|
|
||||||
|
// Calling dirty isn't thread-safe sadly.
|
||||||
|
if (results.DirtyMover)
|
||||||
|
{
|
||||||
|
metadata ??= metaQuery.GetComponent(input.Mover.Owner);
|
||||||
|
Dirty(input.Mover, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.LinearVelocity != null)
|
||||||
|
{
|
||||||
|
metadata ??= metaQuery.GetComponent(input.Physics.Owner);
|
||||||
|
PhysicsSystem.SetLinearVelocity(input.Physics, results.LinearVelocity.Value, false);
|
||||||
|
PhysicsSystem.SetAngularVelocity(input.Physics, 0f, false);
|
||||||
|
Dirty(input.Physics, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.sound != null)
|
||||||
|
{
|
||||||
|
Audio.PlayPredicted(results.sound, input.Mover.Owner, input.Mover.Owner, results.audio);
|
||||||
|
}
|
||||||
|
|
||||||
|
moveInput[i] = default;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayPool<(bool DirtyMover, Vector2? LinearVelocity, SoundSpecifier? Sound, AudioParams Audio)>.Shared.Return(moveResults);
|
||||||
|
ArrayPool<(InputMoverComponent, TransformComponent, PhysicsComponent)>.Shared.Return(moveInput);
|
||||||
HandleShuttleMovement(frameTime);
|
HandleShuttleMovement(frameTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Content.Shared.Movement.Systems;
|
using Content.Shared.Movement.Systems;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Shared.Movement.Components
|
namespace Content.Shared.Movement.Components
|
||||||
@@ -61,8 +62,8 @@ namespace Content.Shared.Movement.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// If we traverse on / off a grid then set a timer to update our relative inputs.
|
/// If we traverse on / off a grid then set a timer to update our relative inputs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite), DataField("lerpTarget", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
public float LerpAccumulator;
|
public TimeSpan LerpTarget;
|
||||||
|
|
||||||
public const float LerpTime = 1.0f;
|
public const float LerpTime = 1.0f;
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ namespace Content.Shared.Movement.Systems
|
|||||||
component.RelativeRotation = state.RelativeRotation;
|
component.RelativeRotation = state.RelativeRotation;
|
||||||
component.TargetRelativeRotation = state.TargetRelativeRotation;
|
component.TargetRelativeRotation = state.TargetRelativeRotation;
|
||||||
component.RelativeEntity = state.RelativeEntity;
|
component.RelativeEntity = state.RelativeEntity;
|
||||||
component.LerpAccumulator = state.LerpAccumulator;
|
component.LerpTarget = state.LerpTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnInputGetState(EntityUid uid, InputMoverComponent component, ref ComponentGetState args)
|
private void OnInputGetState(EntityUid uid, InputMoverComponent component, ref ComponentGetState args)
|
||||||
@@ -91,7 +91,7 @@ namespace Content.Shared.Movement.Systems
|
|||||||
component.RelativeRotation,
|
component.RelativeRotation,
|
||||||
component.TargetRelativeRotation,
|
component.TargetRelativeRotation,
|
||||||
component.RelativeEntity,
|
component.RelativeEntity,
|
||||||
component.LerpAccumulator);
|
component.LerpTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShutdownInput()
|
private void ShutdownInput()
|
||||||
@@ -118,13 +118,73 @@ namespace Content.Shared.Movement.Systems
|
|||||||
|
|
||||||
public void ResetCamera(EntityUid uid)
|
public void ResetCamera(EntityUid uid)
|
||||||
{
|
{
|
||||||
if (CameraRotationLocked || !TryComp<InputMoverComponent>(uid, out var mover) || mover.TargetRelativeRotation.Equals(Angle.Zero))
|
if (CameraRotationLocked ||
|
||||||
|
!TryComp<InputMoverComponent>(uid, out var mover))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we updated parent then cancel the accumulator and force it now.
|
||||||
|
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||||
|
|
||||||
|
if (!TryUpdateRelative(mover, xformQuery.GetComponent(uid), xformQuery) && mover.TargetRelativeRotation.Equals(Angle.Zero))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
mover.LerpTarget = TimeSpan.Zero;
|
||||||
mover.TargetRelativeRotation = Angle.Zero;
|
mover.TargetRelativeRotation = Angle.Zero;
|
||||||
Dirty(mover);
|
Dirty(mover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool TryUpdateRelative(InputMoverComponent mover, TransformComponent xform, EntityQuery<TransformComponent> xformQuery)
|
||||||
|
{
|
||||||
|
var relative = xform.GridUid;
|
||||||
|
relative ??= xform.MapUid;
|
||||||
|
|
||||||
|
// So essentially what we want:
|
||||||
|
// 1. If we go from grid to map then preserve our rotation and continue as usual
|
||||||
|
// 2. If we go from grid -> grid then (after lerp time) snap to nearest cardinal (probably imperceptible)
|
||||||
|
// 3. If we go from map -> grid then (after lerp time) snap to nearest cardinal
|
||||||
|
|
||||||
|
if (mover.RelativeEntity.Equals(relative))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Okay need to get our old relative rotation with respect to our new relative rotation
|
||||||
|
// e.g. if we were right side up on our current grid need to get what that is on our new grid.
|
||||||
|
var currentRotation = Angle.Zero;
|
||||||
|
var targetRotation = Angle.Zero;
|
||||||
|
|
||||||
|
// Get our current relative rotation
|
||||||
|
if (xformQuery.TryGetComponent(mover.RelativeEntity, out var oldRelativeXform))
|
||||||
|
{
|
||||||
|
currentRotation = _transform.GetWorldRotation(oldRelativeXform, xformQuery) + mover.RelativeRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xformQuery.TryGetComponent(relative, out var relativeXform))
|
||||||
|
{
|
||||||
|
// This is our current rotation relative to our new parent.
|
||||||
|
mover.RelativeRotation = (currentRotation - _transform.GetWorldRotation(relativeXform, xformQuery)).FlipPositive();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we went from grid -> map we'll preserve our worldrotation
|
||||||
|
if (relative != null && _mapManager.IsMap(relative.Value))
|
||||||
|
{
|
||||||
|
targetRotation = currentRotation.FlipPositive().Reduced();
|
||||||
|
}
|
||||||
|
// If we went from grid -> grid OR grid -> map then snap the target to cardinal and lerp there.
|
||||||
|
// OR just rotate to zero (depending on cvar)
|
||||||
|
else if (relative != null && _mapManager.IsGrid(relative.Value))
|
||||||
|
{
|
||||||
|
if (CameraRotationLocked)
|
||||||
|
targetRotation = Angle.Zero;
|
||||||
|
else
|
||||||
|
targetRotation = mover.RelativeRotation.GetCardinalDir().ToAngle().Reduced();
|
||||||
|
}
|
||||||
|
|
||||||
|
mover.RelativeEntity = relative;
|
||||||
|
mover.TargetRelativeRotation = targetRotation;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public Angle GetParentGridAngle(InputMoverComponent mover, EntityQuery<TransformComponent> xformQuery)
|
public Angle GetParentGridAngle(InputMoverComponent mover, EntityQuery<TransformComponent> xformQuery)
|
||||||
{
|
{
|
||||||
var rotation = mover.RelativeRotation;
|
var rotation = mover.RelativeRotation;
|
||||||
@@ -161,16 +221,16 @@ namespace Content.Shared.Movement.Systems
|
|||||||
// If we go on a grid and back off then just reset the accumulator.
|
// If we go on a grid and back off then just reset the accumulator.
|
||||||
if (relative == component.RelativeEntity)
|
if (relative == component.RelativeEntity)
|
||||||
{
|
{
|
||||||
if (component.LerpAccumulator != 0f)
|
if (component.LerpTarget >= Timing.CurTime)
|
||||||
{
|
{
|
||||||
component.LerpAccumulator = 0f;
|
component.LerpTarget = TimeSpan.Zero;
|
||||||
Dirty(component);
|
Dirty(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
component.LerpAccumulator = InputMoverComponent.LerpTime;
|
component.LerpTarget = TimeSpan.FromSeconds(InputMoverComponent.LerpTime) + Timing.CurTime;
|
||||||
Dirty(component);
|
Dirty(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,16 +559,16 @@ namespace Content.Shared.Movement.Systems
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Angle TargetRelativeRotation;
|
public Angle TargetRelativeRotation;
|
||||||
public EntityUid? RelativeEntity;
|
public EntityUid? RelativeEntity;
|
||||||
public float LerpAccumulator = 0f;
|
public TimeSpan LerpTarget;
|
||||||
|
|
||||||
public InputMoverComponentState(MoveButtons buttons, bool canMove, Angle relativeRotation, Angle targetRelativeRotation, EntityUid? relativeEntity, float lerpAccumulator)
|
public InputMoverComponentState(MoveButtons buttons, bool canMove, Angle relativeRotation, Angle targetRelativeRotation, EntityUid? relativeEntity, TimeSpan lerpTarget)
|
||||||
{
|
{
|
||||||
Buttons = buttons;
|
Buttons = buttons;
|
||||||
CanMove = canMove;
|
CanMove = canMove;
|
||||||
RelativeRotation = relativeRotation;
|
RelativeRotation = relativeRotation;
|
||||||
TargetRelativeRotation = targetRelativeRotation;
|
TargetRelativeRotation = targetRelativeRotation;
|
||||||
RelativeEntity = relativeEntity;
|
RelativeEntity = relativeEntity;
|
||||||
LerpAccumulator = lerpAccumulator;
|
LerpTarget = lerpTarget;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,17 +28,17 @@ namespace Content.Shared.Movement.Systems
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract partial class SharedMoverController : VirtualController
|
public abstract partial class SharedMoverController : VirtualController
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||||
[Dependency] protected readonly IGameTiming Timing = default!;
|
[Dependency] protected readonly IGameTiming Timing = default!;
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
|
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
|
||||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||||
[Dependency] private readonly SharedGravitySystem _gravity = default!;
|
[Dependency] private readonly SharedGravitySystem _gravity = default!;
|
||||||
[Dependency] private readonly SharedMobStateSystem _mobState = default!;
|
[Dependency] private readonly SharedMobStateSystem _mobState = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] protected readonly SharedAudioSystem Audio = default!;
|
||||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
[Dependency] private readonly TagSystem _tags = default!;
|
[Dependency] private readonly TagSystem _tags = default!;
|
||||||
|
|
||||||
private const float StepSoundMoveDistanceRunning = 2;
|
private const float StepSoundMoveDistanceRunning = 2;
|
||||||
private const float StepSoundMoveDistanceWalking = 1.5f;
|
private const float StepSoundMoveDistanceWalking = 1.5f;
|
||||||
@@ -96,69 +96,35 @@ namespace Content.Shared.Movement.Systems
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Movement while considering actionblockers, weightlessness, etc.
|
/// Movement while considering actionblockers, weightlessness, etc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Yes this signature is massive but this is also called a lot.
|
||||||
|
/// </remarks>
|
||||||
protected void HandleMobMovement(
|
protected void HandleMobMovement(
|
||||||
InputMoverComponent mover,
|
InputMoverComponent mover,
|
||||||
PhysicsComponent physicsComponent,
|
PhysicsComponent physicsComponent,
|
||||||
TransformComponent xform,
|
TransformComponent xform,
|
||||||
float frameTime,
|
float frameTime,
|
||||||
EntityQuery<TransformComponent> xformQuery)
|
EntityQuery<TransformComponent> xformQuery,
|
||||||
|
EntityQuery<MobMoverComponent> mobQuery,
|
||||||
|
EntityQuery<InventoryComponent> inventoryQuery,
|
||||||
|
EntityQuery<ContainerManagerComponent> containerQuery,
|
||||||
|
EntityQuery<FootstepModifierComponent> footQuery,
|
||||||
|
out bool dirtyMover,
|
||||||
|
out Vector2? linearVelocity,
|
||||||
|
out SoundSpecifier? sound,
|
||||||
|
out AudioParams audio)
|
||||||
{
|
{
|
||||||
DebugTools.Assert(!UsedMobMovement.ContainsKey(mover.Owner));
|
dirtyMover = false;
|
||||||
|
linearVelocity = Vector2.Zero;
|
||||||
|
sound = null;
|
||||||
|
audio = default;
|
||||||
|
|
||||||
// Update relative movement
|
// Update relative movement
|
||||||
if (mover.LerpAccumulator > 0f)
|
if (mover.LerpTarget < Timing.CurTime)
|
||||||
{
|
{
|
||||||
Dirty(mover);
|
if (TryUpdateRelative(mover, xform, xformQuery))
|
||||||
mover.LerpAccumulator -= frameTime;
|
|
||||||
|
|
||||||
if (mover.LerpAccumulator <= 0f)
|
|
||||||
{
|
{
|
||||||
mover.LerpAccumulator = 0f;
|
dirtyMover = true;
|
||||||
var relative = xform.GridUid;
|
|
||||||
relative ??= xform.MapUid;
|
|
||||||
|
|
||||||
// So essentially what we want:
|
|
||||||
// 1. If we go from grid to map then preserve our rotation and continue as usual
|
|
||||||
// 2. If we go from grid -> grid then (after lerp time) snap to nearest cardinal (probably imperceptible)
|
|
||||||
// 3. If we go from map -> grid then (after lerp time) snap to nearest cardinal
|
|
||||||
|
|
||||||
if (!mover.RelativeEntity.Equals(relative))
|
|
||||||
{
|
|
||||||
// Okay need to get our old relative rotation with respect to our new relative rotation
|
|
||||||
// e.g. if we were right side up on our current grid need to get what that is on our new grid.
|
|
||||||
var currentRotation = Angle.Zero;
|
|
||||||
var targetRotation = Angle.Zero;
|
|
||||||
|
|
||||||
// Get our current relative rotation
|
|
||||||
if (xformQuery.TryGetComponent(mover.RelativeEntity, out var oldRelativeXform))
|
|
||||||
{
|
|
||||||
currentRotation = oldRelativeXform.WorldRotation + mover.RelativeRotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xformQuery.TryGetComponent(relative, out var relativeXform))
|
|
||||||
{
|
|
||||||
// This is our current rotation relative to our new parent.
|
|
||||||
mover.RelativeRotation = (currentRotation - relativeXform.WorldRotation).FlipPositive();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we went from grid -> map we'll preserve our worldrotation
|
|
||||||
if (relative != null && _mapManager.IsMap(relative.Value))
|
|
||||||
{
|
|
||||||
targetRotation = currentRotation.FlipPositive().Reduced();
|
|
||||||
}
|
|
||||||
// If we went from grid -> grid OR grid -> map then snap the target to cardinal and lerp there.
|
|
||||||
// OR just rotate to zero (depending on cvar)
|
|
||||||
else if (relative != null && _mapManager.IsGrid(relative.Value))
|
|
||||||
{
|
|
||||||
if (CameraRotationLocked)
|
|
||||||
targetRotation = Angle.Zero;
|
|
||||||
else
|
|
||||||
targetRotation = mover.RelativeRotation.GetCardinalDir().ToAngle().Reduced();
|
|
||||||
}
|
|
||||||
|
|
||||||
mover.RelativeEntity = relative;
|
|
||||||
mover.TargetRelativeRotation = targetRotation;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,13 +149,13 @@ namespace Content.Shared.Movement.Systems
|
|||||||
|
|
||||||
mover.RelativeRotation += adjustment;
|
mover.RelativeRotation += adjustment;
|
||||||
mover.RelativeRotation.FlipPositive();
|
mover.RelativeRotation.FlipPositive();
|
||||||
Dirty(mover);
|
dirtyMover = true;
|
||||||
}
|
}
|
||||||
else if (!angleDiff.Equals(Angle.Zero))
|
else if (!angleDiff.Equals(Angle.Zero))
|
||||||
{
|
{
|
||||||
mover.TargetRelativeRotation.FlipPositive();
|
mover.TargetRelativeRotation.FlipPositive();
|
||||||
mover.RelativeRotation = mover.TargetRelativeRotation;
|
mover.RelativeRotation = mover.TargetRelativeRotation;
|
||||||
Dirty(mover);
|
dirtyMover = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!UseMobMovement(mover, physicsComponent))
|
if (!UseMobMovement(mover, physicsComponent))
|
||||||
@@ -217,7 +183,7 @@ namespace Content.Shared.Movement.Systems
|
|||||||
// No gravity: is our entity touching anything?
|
// No gravity: is our entity touching anything?
|
||||||
touching = ev.CanMove;
|
touching = ev.CanMove;
|
||||||
|
|
||||||
if (!touching && TryComp<MobMoverComponent>(xform.Owner, out var mobMover))
|
if (!touching && mobQuery.TryGetComponent(xform.Owner, out var mobMover))
|
||||||
touching |= IsAroundCollider(PhysicsSystem, xform, mobMover, physicsComponent);
|
touching |= IsAroundCollider(PhysicsSystem, xform, mobMover, physicsComponent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -274,19 +240,20 @@ namespace Content.Shared.Movement.Systems
|
|||||||
{
|
{
|
||||||
// This should have its event run during island solver soooo
|
// This should have its event run during island solver soooo
|
||||||
xform.DeferUpdates = true;
|
xform.DeferUpdates = true;
|
||||||
xform.WorldRotation = worldTotal.ToWorldAngle();
|
_transform.SetWorldRotation(xform, worldTotal.ToWorldAngle(), xformQuery);
|
||||||
xform.DeferUpdates = false;
|
xform.DeferUpdates = false;
|
||||||
|
|
||||||
if (!weightless && TryComp<MobMoverComponent>(mover.Owner, out var mobMover) &&
|
if (!weightless && TryComp<MobMoverComponent>(mover.Owner, out var mobMover) &&
|
||||||
TryGetSound(weightless, mover, mobMover, xform, out var sound))
|
TryGetSound(weightless, mover, mobMover, xform, inventoryQuery, containerQuery, footQuery, out var soundSpec))
|
||||||
{
|
{
|
||||||
var soundModifier = mover.Sprinting ? 1.0f : FootstepWalkingAddedVolumeMultiplier;
|
var soundModifier = mover.Sprinting ? 1.0f : FootstepWalkingAddedVolumeMultiplier;
|
||||||
|
|
||||||
var audioParams = sound.Params
|
var audioParams = soundSpec.Params
|
||||||
.WithVolume(FootstepVolume * soundModifier)
|
.WithVolume(FootstepVolume * soundModifier)
|
||||||
.WithVariation(sound.Params.Variation ?? FootstepVariation);
|
.WithVariation(soundSpec.Params.Variation ?? FootstepVariation);
|
||||||
|
|
||||||
_audio.PlayPredicted(sound, mover.Owner, mover.Owner, audioParams);
|
sound = soundSpec;
|
||||||
|
audio = audioParams;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,10 +262,13 @@ namespace Content.Shared.Movement.Systems
|
|||||||
if (!weightless || touching)
|
if (!weightless || touching)
|
||||||
Accelerate(ref velocity, in worldTotal, accel, frameTime);
|
Accelerate(ref velocity, in worldTotal, accel, frameTime);
|
||||||
|
|
||||||
PhysicsSystem.SetLinearVelocity(physicsComponent, velocity);
|
if (physicsComponent.LinearVelocity.EqualsApprox(velocity, 0.0001f) &&
|
||||||
|
MathHelper.CloseTo(physicsComponent.AngularVelocity, 0f))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Ensures that players do not spiiiiiiin
|
linearVelocity = velocity;
|
||||||
PhysicsSystem.SetAngularVelocity(physicsComponent, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Friction(float minimumFrictionSpeed, float frameTime, float friction, ref Vector2 velocity)
|
private void Friction(float minimumFrictionSpeed, float frameTime, float friction, ref Vector2 velocity)
|
||||||
@@ -379,7 +349,16 @@ namespace Content.Shared.Movement.Systems
|
|||||||
|
|
||||||
protected abstract bool CanSound();
|
protected abstract bool CanSound();
|
||||||
|
|
||||||
private bool TryGetSound(bool weightless, InputMoverComponent mover, MobMoverComponent mobMover, TransformComponent xform, [NotNullWhen(true)] out SoundSpecifier? sound)
|
private bool TryGetSound(
|
||||||
|
bool weightless,
|
||||||
|
InputMoverComponent mover,
|
||||||
|
MobMoverComponent mobMover,
|
||||||
|
TransformComponent xform,
|
||||||
|
EntityQuery<InventoryComponent> inventoryQuery,
|
||||||
|
EntityQuery<ContainerManagerComponent> containerQuery,
|
||||||
|
EntityQuery<FootstepModifierComponent> footQuery,
|
||||||
|
|
||||||
|
[NotNullWhen(true)] out SoundSpecifier? sound)
|
||||||
{
|
{
|
||||||
sound = null;
|
sound = null;
|
||||||
|
|
||||||
@@ -413,18 +392,21 @@ namespace Content.Shared.Movement.Systems
|
|||||||
if (mobMover.StepSoundDistance < distanceNeeded) return false;
|
if (mobMover.StepSoundDistance < distanceNeeded) return false;
|
||||||
|
|
||||||
mobMover.StepSoundDistance -= distanceNeeded;
|
mobMover.StepSoundDistance -= distanceNeeded;
|
||||||
|
EntityUid? shoes = null;
|
||||||
|
|
||||||
if (_inventory.TryGetSlotEntity(mover.Owner, "shoes", out var shoes) &&
|
if (inventoryQuery.TryGetComponent(mover.Owner, out var inventory) &&
|
||||||
EntityManager.TryGetComponent<FootstepModifierComponent>(shoes, out var modifier))
|
containerQuery.TryGetComponent(mover.Owner, out var containerManager) &&
|
||||||
|
_inventory.TryGetSlotEntity(mover.Owner, "shoes", out shoes, inventory, containerManager) &&
|
||||||
|
footQuery.TryGetComponent(shoes, out var modifier))
|
||||||
{
|
{
|
||||||
sound = modifier.Sound;
|
sound = modifier.Sound;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return TryGetFootstepSound(coordinates, shoes != null, out sound);
|
return TryGetFootstepSound(coordinates, shoes != null, footQuery, out sound);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetFootstepSound(EntityCoordinates coordinates, bool haveShoes, [NotNullWhen(true)] out SoundSpecifier? sound)
|
private bool TryGetFootstepSound(EntityCoordinates coordinates, bool haveShoes, EntityQuery<FootstepModifierComponent> footQuery, [NotNullWhen(true)] out SoundSpecifier? sound)
|
||||||
{
|
{
|
||||||
sound = null;
|
sound = null;
|
||||||
var gridUid = coordinates.GetGridUid(EntityManager);
|
var gridUid = coordinates.GetGridUid(EntityManager);
|
||||||
@@ -432,7 +414,7 @@ namespace Content.Shared.Movement.Systems
|
|||||||
// Fallback to the map
|
// Fallback to the map
|
||||||
if (gridUid == null)
|
if (gridUid == null)
|
||||||
{
|
{
|
||||||
if (TryComp<FootstepModifierComponent>(coordinates.GetMapUid(EntityManager), out var modifier))
|
if (footQuery.TryGetComponent(coordinates.GetMapUid(EntityManager), out var modifier))
|
||||||
{
|
{
|
||||||
sound = modifier.Sound;
|
sound = modifier.Sound;
|
||||||
return true;
|
return true;
|
||||||
@@ -444,17 +426,20 @@ namespace Content.Shared.Movement.Systems
|
|||||||
var grid = _mapManager.GetGrid(gridUid.Value);
|
var grid = _mapManager.GetGrid(gridUid.Value);
|
||||||
var tile = grid.GetTileRef(coordinates);
|
var tile = grid.GetTileRef(coordinates);
|
||||||
|
|
||||||
if (tile.IsSpace(_tileDefinitionManager)) return false;
|
if (tile.IsSpace(_tileDefinitionManager))
|
||||||
|
return false;
|
||||||
|
|
||||||
// If the coordinates have a FootstepModifier component
|
// If the coordinates have a FootstepModifier component
|
||||||
// i.e. component that emit sound on footsteps emit that sound
|
// i.e. component that emit sound on footsteps emit that sound
|
||||||
foreach (var maybeFootstep in grid.GetAnchoredEntities(tile.GridIndices))
|
var anchoredEnumerator = grid.GetAnchoredEntitiesEnumerator(tile.GridIndices);
|
||||||
|
|
||||||
|
while (anchoredEnumerator.MoveNext(out var maybeFootstep))
|
||||||
{
|
{
|
||||||
if (EntityManager.TryGetComponent(maybeFootstep, out FootstepModifierComponent? footstep))
|
if (!footQuery.TryGetComponent(maybeFootstep, out var footstep))
|
||||||
{
|
continue;
|
||||||
sound = footstep.Sound;
|
|
||||||
return true;
|
sound = footstep.Sound;
|
||||||
}
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walking on a tile.
|
// Walking on a tile.
|
||||||
|
|||||||
Reference in New Issue
Block a user