diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index 3667507aa0..5e0c0a5de8 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -23,7 +23,6 @@ namespace Content.Client.Entry "EmitSoundOnLand", "NameIdentifier", "EmitSoundOnActivate", - "FootstepModifier", "HeatResistance", "EntityStorage", "MeleeWeapon", diff --git a/Content.Client/Physics/Controllers/MoverController.cs b/Content.Client/Physics/Controllers/MoverController.cs index 91d1735d39..db1bd3d1cf 100644 --- a/Content.Client/Physics/Controllers/MoverController.cs +++ b/Content.Client/Physics/Controllers/MoverController.cs @@ -3,15 +3,16 @@ using Content.Shared.Movement; using Content.Shared.Movement.Components; using Content.Shared.Pulling.Components; using Robust.Client.Player; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Physics; +using Robust.Shared.Player; +using Robust.Shared.Timing; namespace Content.Client.Physics.Controllers { public sealed class MoverController : SharedMoverController { + [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; public override void UpdateBeforeSolve(bool prediction, float frameTime) @@ -76,5 +77,15 @@ namespace Content.Client.Physics.Controllers HandleKinematicMovement(mover, body); } + + protected override Filter GetSoundPlayers(EntityUid mover) + { + return Filter.Local(); + } + + protected override bool CanSound() + { + return _timing.IsFirstTimePredicted; + } } } diff --git a/Content.Server/Movement/Components/FootstepModifierComponent.cs b/Content.Server/Movement/Components/FootstepModifierComponent.cs deleted file mode 100644 index 1e83eeb6b6..0000000000 --- a/Content.Server/Movement/Components/FootstepModifierComponent.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Content.Shared.Audio; -using Content.Shared.Sound; -using Robust.Shared.Audio; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Player; -using Robust.Shared.Serialization.Manager.Attributes; - -namespace Content.Server.Movement.Components -{ - /// - /// Changes footstep sound - /// - [RegisterComponent] - public sealed class FootstepModifierComponent : Component - { - [DataField("footstepSoundCollection", required: true)] - public SoundSpecifier SoundCollection = default!; - - [DataField("variation")] - public float Variation = default; - - public void PlayFootstep() - { - SoundSystem.Play(Filter.Pvs(Owner), SoundCollection.GetSound(), IoCManager.Resolve().GetComponent(Owner).Coordinates, AudioHelpers.WithVariation(Variation).WithVolume(-2f)); - } - } -} diff --git a/Content.Server/Physics/Controllers/MoverController.cs b/Content.Server/Physics/Controllers/MoverController.cs index 0e571480ad..48a2949ced 100644 --- a/Content.Server/Physics/Controllers/MoverController.cs +++ b/Content.Server/Physics/Controllers/MoverController.cs @@ -1,24 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using Content.Server.Movement.Components; using Content.Server.Shuttles.Components; using Content.Server.Shuttles.EntitySystems; -using Content.Shared.CCVar; -using Content.Shared.Inventory; -using Content.Shared.Item; -using Content.Shared.Maps; using Content.Shared.Movement; using Content.Shared.Movement.Components; using Content.Shared.Shuttles.Components; -using Content.Shared.Tag; -using Robust.Shared.Audio; -using Robust.Shared.Configuration; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Log; using Robust.Shared.Map; -using Robust.Shared.Maths; using Robust.Shared.Player; using Robust.Shared.Utility; @@ -26,24 +11,21 @@ namespace Content.Server.Physics.Controllers { public sealed class MoverController : SharedMoverController { - [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly TagSystem _tags = default!; - - private const float StepSoundMoveDistanceRunning = 2; - private const float StepSoundMoveDistanceWalking = 1.5f; - - private float _shuttleDockSpeedCap; + [Dependency] private readonly ShuttleSystem _shuttle = default!; + [Dependency] private readonly ThrusterSystem _thruster = default!; private HashSet _excludedMobs = new(); private Dictionary> _shuttlePilots = new(); - public override void Initialize() + protected override Filter GetSoundPlayers(EntityUid mover) { - base.Initialize(); + return Filter.Pvs(mover, entityManager: EntityManager).RemoveWhereAttachedEntity(o => o == mover); + } - var configManager = IoCManager.Resolve(); - configManager.OnValueChanged(CCVars.ShuttleDockSpeedCap, value => _shuttleDockSpeedCap = value, true); + protected override bool CanSound() + { + return true; } public override void UpdateBeforeSolve(bool prediction, float frameTime) @@ -91,15 +73,12 @@ namespace Content.Server.Physics.Controllers pilots.Add((pilot, mover)); } - var shuttleSystem = EntitySystem.Get(); - var thrusterSystem = EntitySystem.Get(); - // Reset inputs for non-piloted shuttles. foreach (var (shuttle, _) in _shuttlePilots) { if (newPilots.ContainsKey(shuttle)) continue; - thrusterSystem.DisableLinearThrusters(shuttle); + _thruster.DisableLinearThrusters(shuttle); } _shuttlePilots = newPilots; @@ -170,8 +149,8 @@ namespace Content.Server.Physics.Controllers // Handle shuttle movement if (linearInput.Length.Equals(0f)) { - thrusterSystem.DisableLinearThrusters(shuttle); - body.LinearDamping = shuttleSystem.ShuttleIdleLinearDamping * body.InvMass; + _thruster.DisableLinearThrusters(shuttle); + body.LinearDamping = _shuttle.ShuttleIdleLinearDamping * body.InvMass; if (body.LinearVelocity.Length < 0.08) { body.LinearVelocity = Vector2.Zero; @@ -204,7 +183,7 @@ namespace Content.Server.Physics.Controllers if ((dir & dockFlag) == 0x0) { - thrusterSystem.DisableLinearThrustDirection(shuttle, dir); + _thruster.DisableLinearThrustDirection(shuttle, dir); continue; } @@ -233,7 +212,7 @@ namespace Content.Server.Physics.Controllers throw new ArgumentOutOfRangeException(); } - thrusterSystem.EnableLinearThrustDirection(shuttle, dir); + _thruster.EnableLinearThrustDirection(shuttle, dir); var index = (int) Math.Log2((int) dir); var force = thrustAngle.RotateVec(shuttleNorth) * shuttle.LinearThrust[index] * length; @@ -241,14 +220,14 @@ namespace Content.Server.Physics.Controllers totalForce += force; } - var dragForce = body.LinearVelocity * (totalForce.Length / shuttleSystem.ShuttleMaxLinearSpeed); + var dragForce = body.LinearVelocity * (totalForce.Length / _shuttle.ShuttleMaxLinearSpeed); body.ApplyLinearImpulse((totalForce - dragForce) * frameTime); } if (MathHelper.CloseTo(angularInput, 0f)) { - thrusterSystem.SetAngularThrust(shuttle, false); - body.AngularDamping = shuttleSystem.ShuttleIdleAngularDamping * body.InvI; + _thruster.SetAngularThrust(shuttle, false); + body.AngularDamping = _shuttle.ShuttleIdleAngularDamping * body.InvI; body.SleepingAllowed = true; if (Math.Abs(body.AngularVelocity) < 0.01f) @@ -261,107 +240,17 @@ namespace Content.Server.Physics.Controllers body.AngularDamping = 0; body.SleepingAllowed = false; - var maxSpeed = Math.Min(shuttleSystem.ShuttleMaxAngularMomentum * body.InvI, shuttleSystem.ShuttleMaxAngularSpeed); - var maxTorque = body.Inertia * shuttleSystem.ShuttleMaxAngularAcc; + var maxSpeed = Math.Min(_shuttle.ShuttleMaxAngularMomentum * body.InvI, _shuttle.ShuttleMaxAngularSpeed); + var maxTorque = body.Inertia * _shuttle.ShuttleMaxAngularAcc; var torque = Math.Min(shuttle.AngularThrust, maxTorque); var dragTorque = body.AngularVelocity * (torque / maxSpeed); body.ApplyAngularImpulse((-angularInput * torque - dragTorque) * frameTime); - thrusterSystem.SetAngularThrust(shuttle, true); + _thruster.SetAngularThrust(shuttle, true); } } } - - protected override void HandleFootsteps(IMoverComponent mover, IMobMoverComponent mobMover) - { - if (!_tags.HasTag(mover.Owner, "FootstepSound")) return; - - var transform = EntityManager.GetComponent(mover.Owner); - var coordinates = transform.Coordinates; - var gridId = coordinates.GetGridId(EntityManager); - var distanceNeeded = mover.Sprinting ? StepSoundMoveDistanceRunning : StepSoundMoveDistanceWalking; - - // Handle footsteps. - if (_mapManager.GridExists(gridId)) - { - // Can happen when teleporting between grids. - if (!coordinates.TryDistance(EntityManager, mobMover.LastPosition, out var distance) || - distance > distanceNeeded) - { - mobMover.StepSoundDistance = distanceNeeded; - } - else - { - mobMover.StepSoundDistance += distance; - } - } - else - { - // In space no one can hear you squeak - return; - } - - DebugTools.Assert(gridId != GridId.Invalid); - mobMover.LastPosition = coordinates; - - if (mobMover.StepSoundDistance < distanceNeeded) return; - - mobMover.StepSoundDistance -= distanceNeeded; - - var invSystem = EntitySystem.Get(); - - if (invSystem.TryGetSlotEntity(mover.Owner, "shoes", out var shoes) && - EntityManager.TryGetComponent(shoes, out var modifier)) - { - modifier.PlayFootstep(); - } - else - { - PlayFootstepSound(mover.Owner, gridId, coordinates, mover.Sprinting); - } - } - - private void PlayFootstepSound(EntityUid mover, GridId gridId, EntityCoordinates coordinates, bool sprinting) - { - var grid = _mapManager.GetGrid(gridId); - var tile = grid.GetTileRef(coordinates); - - if (tile.IsSpace(_tileDefinitionManager)) return; - - // If the coordinates have a FootstepModifier component - // i.e. component that emit sound on footsteps emit that sound - string? soundToPlay = null; - foreach (var maybeFootstep in grid.GetAnchoredEntities(tile.GridIndices)) - { - if (EntityManager.TryGetComponent(maybeFootstep, out FootstepModifierComponent? footstep)) - { - soundToPlay = footstep.SoundCollection.GetSound(); - break; - } - } - // if there is no FootstepModifierComponent, determine sound based on tiles - if (soundToPlay == null) - { - // Walking on a tile. - var def = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId]; - soundToPlay = def.FootstepSounds?.GetSound(); - if (string.IsNullOrEmpty(soundToPlay)) - return; - } - - if (string.IsNullOrWhiteSpace(soundToPlay)) - { - Logger.ErrorS("sound", $"Unable to find sound in {nameof(PlayFootstepSound)}"); - return; - } - - SoundSystem.Play( - Filter.Pvs(coordinates), - soundToPlay, - EntityManager.GetComponent(mover).Coordinates, - sprinting ? AudioParams.Default.WithVolume(0.75f) : null); - } } } diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 2e2e4bc5e5..5bd1dafe68 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -609,10 +609,6 @@ namespace Content.Shared.CCVar /* * Shuttles */ - // Once cruising actually gets implemented I'd likely drop this speed to 3 maybe. - public static readonly CVarDef ShuttleDockSpeedCap = - CVarDef.Create("shuttle.dock_speed_cap", 5f, CVar.SERVERONLY); - public static readonly CVarDef ShuttleMaxLinearSpeed = CVarDef.Create("shuttle.max_linear_speed", 13f, CVar.SERVERONLY); diff --git a/Content.Shared/Movement/Components/FootstepModifierComponent.cs b/Content.Shared/Movement/Components/FootstepModifierComponent.cs new file mode 100644 index 0000000000..54d20757fb --- /dev/null +++ b/Content.Shared/Movement/Components/FootstepModifierComponent.cs @@ -0,0 +1,17 @@ +using Content.Shared.Sound; + +namespace Content.Shared.Movement.Components +{ + /// + /// Changes footstep sound + /// + [RegisterComponent] + public sealed class FootstepModifierComponent : Component + { + [DataField("footstepSoundCollection", required: true)] + public SoundSpecifier SoundCollection = default!; + + [DataField("variation")] + public float Variation = default; + } +} diff --git a/Content.Shared/Movement/SharedMoverController.cs b/Content.Shared/Movement/SharedMoverController.cs index 74f22cf7d7..b02637b21a 100644 --- a/Content.Shared/Movement/SharedMoverController.cs +++ b/Content.Shared/Movement/SharedMoverController.cs @@ -1,18 +1,20 @@ -using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Content.Shared.ActionBlocker; +using Content.Shared.Audio; using Content.Shared.CCVar; using Content.Shared.Friction; +using Content.Shared.Inventory; +using Content.Shared.Maps; using Content.Shared.MobState.Components; using Content.Shared.Movement.Components; using Content.Shared.Pulling.Components; -using JetBrains.Annotations; +using Content.Shared.Tag; +using Robust.Shared.Audio; using Robust.Shared.Configuration; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; using Robust.Shared.Map; -using Robust.Shared.Maths; using Robust.Shared.Physics; using Robust.Shared.Physics.Controllers; +using Robust.Shared.Player; using Robust.Shared.Utility; namespace Content.Shared.Movement @@ -24,9 +26,17 @@ namespace Content.Shared.Movement public abstract class SharedMoverController : VirtualController { [Dependency] private readonly IMapManager _mapManager = default!; - - [Dependency] private ActionBlockerSystem _blocker = default!; + [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; + [Dependency] private readonly ActionBlockerSystem _blocker = default!; + [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly TagSystem _tags = default!; + + private const float StepSoundMoveDistanceRunning = 2; + private const float StepSoundMoveDistanceWalking = 1.5f; + + private const float FootstepVariation = 0f; + private const float FootstepVolume = 1f; private bool _relativeMovement; @@ -155,7 +165,15 @@ namespace Content.Shared.Movement ? total.ToWorldAngle() : worldTotal.ToWorldAngle(); xform.DeferUpdates = false; - HandleFootsteps(mover, mobMover); + + if (TryGetSound(mover, mobMover, xform, out var variation, out var sound)) + { + SoundSystem.Play( + GetSoundPlayers(mover.Owner), + sound, + mover.Owner, + AudioHelpers.WithVariation(variation).WithVolume(FootstepVolume)); + } } _physics.SetLinearVelocity(physicsComponent, worldTotal); @@ -178,7 +196,7 @@ namespace Content.Shared.Movement /// /// Used for weightlessness to determine if we are near a wall. /// - public static bool IsAroundCollider(SharedPhysicsSystem broadPhaseSystem, TransformComponent transform, IMobMoverComponent mover, IPhysBody collider) + private bool IsAroundCollider(SharedPhysicsSystem broadPhaseSystem, TransformComponent transform, IMobMoverComponent mover, IPhysBody collider) { var enlargedAABB = collider.GetWorldAABB().Enlarged(mover.GrabRange); @@ -191,7 +209,7 @@ namespace Content.Shared.Movement !otherCollider.CanCollide || ((collider.CollisionMask & otherCollider.CollisionLayer) == 0 && (otherCollider.CollisionMask & collider.CollisionLayer) == 0) || - (IoCManager.Resolve().TryGetComponent(otherCollider.Owner, out SharedPullableComponent? pullable) && pullable.BeingPulled)) + (TryComp(otherCollider.Owner, out SharedPullableComponent? pullable) && pullable.BeingPulled)) { continue; } @@ -202,7 +220,87 @@ namespace Content.Shared.Movement return false; } - // TODO: Need a predicted client version that only plays for our own entity and then have server-side ignore our session (for that entity only) - protected virtual void HandleFootsteps(IMoverComponent mover, IMobMoverComponent mobMover) {} + // TODO: Predicted audio moment. + protected abstract Filter GetSoundPlayers(EntityUid mover); + + protected abstract bool CanSound(); + + private bool TryGetSound(IMoverComponent mover, IMobMoverComponent mobMover, TransformComponent xform, out float variation, [NotNullWhen(true)] out string? sound) + { + sound = null; + variation = 0f; + + if (!CanSound() || !_tags.HasTag(mover.Owner, "FootstepSound")) return false; + + var coordinates = xform.Coordinates; + var gridId = coordinates.GetGridId(EntityManager); + var distanceNeeded = mover.Sprinting ? StepSoundMoveDistanceRunning : StepSoundMoveDistanceWalking; + + // Handle footsteps. + if (_mapManager.GridExists(gridId)) + { + // Can happen when teleporting between grids. + if (!coordinates.TryDistance(EntityManager, mobMover.LastPosition, out var distance) || + distance > distanceNeeded) + { + mobMover.StepSoundDistance = distanceNeeded; + } + else + { + mobMover.StepSoundDistance += distance; + } + } + else + { + // In space no one can hear you squeak + return false; + } + + DebugTools.Assert(gridId != GridId.Invalid); + mobMover.LastPosition = coordinates; + + if (mobMover.StepSoundDistance < distanceNeeded) return false; + + mobMover.StepSoundDistance -= distanceNeeded; + + if (_inventory.TryGetSlotEntity(mover.Owner, "shoes", out var shoes) && + EntityManager.TryGetComponent(shoes, out var modifier)) + { + sound = modifier.SoundCollection.GetSound(); + variation = modifier.Variation; + return true; + } + + return TryGetFootstepSound(gridId, coordinates, out variation, out sound); + } + + private bool TryGetFootstepSound(GridId gridId, EntityCoordinates coordinates, out float variation, [NotNullWhen(true)] out string? sound) + { + variation = 0f; + sound = null; + var grid = _mapManager.GetGrid(gridId); + var tile = grid.GetTileRef(coordinates); + + if (tile.IsSpace(_tileDefinitionManager)) return false; + + // If the coordinates have a FootstepModifier component + // i.e. component that emit sound on footsteps emit that sound + foreach (var maybeFootstep in grid.GetAnchoredEntities(tile.GridIndices)) + { + if (EntityManager.TryGetComponent(maybeFootstep, out FootstepModifierComponent? footstep)) + { + sound = footstep.SoundCollection.GetSound(); + variation = footstep.Variation; + return true; + } + } + + // Walking on a tile. + var def = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId]; + sound = def.FootstepSounds?.GetSound(); + variation = FootstepVariation; + + return !string.IsNullOrEmpty(sound); + } } } diff --git a/Content.Shared/Tag/TagSystem.cs b/Content.Shared/Tag/TagSystem.cs index 294bcba302..d037abf95b 100644 --- a/Content.Shared/Tag/TagSystem.cs +++ b/Content.Shared/Tag/TagSystem.cs @@ -177,6 +177,15 @@ public sealed class TagSystem : EntitySystem HasTag(component, id); } + /// + /// Checks if a tag has been added to an entity. + /// + public bool HasTag(EntityUid entity, string id, EntityQuery tagQuery) + { + return tagQuery.TryGetComponent(entity, out var component) && + HasTag(component, id); + } + /// /// Checks if all of the given tags have been added to an entity. ///