diff --git a/Content.Client/Physics/Controllers/MoverController.cs b/Content.Client/Physics/Controllers/MoverController.cs index 7cf1e72255..2baebe1182 100644 --- a/Content.Client/Physics/Controllers/MoverController.cs +++ b/Content.Client/Physics/Controllers/MoverController.cs @@ -140,7 +140,7 @@ namespace Content.Client.Physics.Controllers protected override bool CanSound() { - return _timing.IsFirstTimePredicted && _timing.InSimulation; + return _timing is { IsFirstTimePredicted: true, InSimulation: true }; } } } diff --git a/Content.Server/Physics/Controllers/MoverController.cs b/Content.Server/Physics/Controllers/MoverController.cs index 0793b07e77..6e46fa0e13 100644 --- a/Content.Server/Physics/Controllers/MoverController.cs +++ b/Content.Server/Physics/Controllers/MoverController.cs @@ -84,7 +84,7 @@ namespace Content.Server.Physics.Controllers } PhysicsComponent? body = null; - TransformComponent? xformMover = xform; + var xformMover = xform; if (mover.ToParent && relayQuery.HasComponent(xform.ParentUid)) { diff --git a/Content.Shared/Movement/Components/MovementRelayTargetComponent.cs b/Content.Shared/Movement/Components/MovementRelayTargetComponent.cs new file mode 100644 index 0000000000..18776ab5b0 --- /dev/null +++ b/Content.Shared/Movement/Components/MovementRelayTargetComponent.cs @@ -0,0 +1,12 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Movement.Components; + +[RegisterComponent, NetworkedComponent] +public sealed class MovementRelayTargetComponent : Component +{ + /// + /// Entities that are relaying to us. + /// + [ViewVariables] public readonly List Entities = new(); +} diff --git a/Content.Shared/Movement/Systems/SharedMoverController.Relay.cs b/Content.Shared/Movement/Systems/SharedMoverController.Relay.cs index 77ed9e9755..67595bbabe 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.Relay.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.Relay.cs @@ -12,6 +12,10 @@ public abstract partial class SharedMoverController SubscribeLocalEvent(OnRelayGetState); SubscribeLocalEvent(OnRelayHandleState); SubscribeLocalEvent(OnRelayShutdown); + + SubscribeLocalEvent(OnTargetRelayGetState); + SubscribeLocalEvent(OnTargetRelayHandleState); + SubscribeLocalEvent(OnTargetRelayShutdown); } /// @@ -20,7 +24,7 @@ public abstract partial class SharedMoverController /// public void SetRelay(EntityUid uid, EntityUid relayEntity, RelayInputMoverComponent? component = null) { - if (!Resolve(uid, ref component)) + if (!Resolve(uid, ref component) || component.RelayEntity == relayEntity) return; if (uid == relayEntity) @@ -29,14 +33,38 @@ public abstract partial class SharedMoverController return; } + if (TryComp(relayEntity, out var targetComp)) + { + targetComp.Entities.Remove(uid); + + if (targetComp.Entities.Count == 0) + RemComp(relayEntity); + } + component.RelayEntity = relayEntity; + targetComp = EnsureComp(relayEntity); + targetComp.Entities.Add(uid); Dirty(component); + Dirty(targetComp); } private void OnRelayShutdown(EntityUid uid, RelayInputMoverComponent component, ComponentShutdown args) { // If relay is removed then cancel all inputs. - if (!TryComp(component.RelayEntity, out var inputMover)) return; + if (!TryComp(component.RelayEntity, out var inputMover)) + return; + + if (TryComp(component.RelayEntity, out var targetComp) && + targetComp.LifeStage < ComponentLifeStage.Stopping) + { + targetComp.Entities.Remove(uid); + + if (targetComp.Entities.Count == 0) + RemCompDeferred(component.RelayEntity.Value); + else + Dirty(targetComp); + } + SetMoveInput(inputMover, MoveButtons.None); } @@ -56,9 +84,59 @@ public abstract partial class SharedMoverController }; } + #region Target Relay + + private void OnTargetRelayShutdown(EntityUid uid, MovementRelayTargetComponent component, ComponentShutdown args) + { + if (component.Entities.Count == 0) + return; + + var relayQuery = GetEntityQuery(); + + foreach (var ent in component.Entities) + { + if (!relayQuery.TryGetComponent(ent, out var relay)) + continue; + + DebugTools.Assert(relay.RelayEntity == uid); + + if (relay.RelayEntity != uid) + continue; + + RemCompDeferred(ent); + } + } + + private void OnTargetRelayHandleState(EntityUid uid, MovementRelayTargetComponent component, ref ComponentHandleState args) + { + if (args.Current is not MovementRelayTargetComponentState state) + return; + + component.Entities.Clear(); + component.Entities.AddRange(state.Entities); + } + + private void OnTargetRelayGetState(EntityUid uid, MovementRelayTargetComponent component, ref ComponentGetState args) + { + args.State = new MovementRelayTargetComponentState(component.Entities); + } + + #endregion + [Serializable, NetSerializable] private sealed class RelayInputMoverComponentState : ComponentState { public EntityUid? Entity; } + + [Serializable, NetSerializable] + private sealed class MovementRelayTargetComponentState : ComponentState + { + public List Entities; + + public MovementRelayTargetComponentState(List entities) + { + Entities = entities; + } + } } diff --git a/Content.Shared/Movement/Systems/SharedMoverController.cs b/Content.Shared/Movement/Systems/SharedMoverController.cs index 328b2b5e0d..6a8b4ebfd7 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.cs @@ -287,7 +287,18 @@ namespace Content.Shared.Movement.Systems .WithVolume(FootstepVolume * soundModifier) .WithVariation(sound.Params.Variation ?? FootstepVariation); - _audio.PlayPredicted(sound, mover.Owner, mover.Owner, audioParams); + // If we're a relay target then predict the sound for all relays. + if (TryComp(mover.Owner, out var targetComp)) + { + foreach (var ent in targetComp.Entities) + { + _audio.PlayPredicted(sound, mover.Owner, ent, audioParams); + } + } + else + { + _audio.PlayPredicted(sound, mover.Owner, mover.Owner, audioParams); + } } }