From 56845da37b3f112506d2b324fe06752266e5e8fb Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Fri, 3 Dec 2021 13:03:06 +1100 Subject: [PATCH] Piloting and thruster tuning (#5594) Co-authored-by: metalgearsloth --- .../Physics/Controllers/MoverController.cs | 279 ++++++++++-------- .../Shuttles/Components/ThrusterComponent.cs | 2 +- .../Shuttles/EntitySystems/ShuttleSystem.cs | 10 +- .../Shuttles/EntitySystems/ThrusterSystem.cs | 130 +++----- .../Machines/Computers/computers.yml | 2 + .../Structures/Shuttles/thrusters.yml | 3 +- 6 files changed, 218 insertions(+), 208 deletions(-) diff --git a/Content.Server/Physics/Controllers/MoverController.cs b/Content.Server/Physics/Controllers/MoverController.cs index 401a2d2eed..1dfb49bdd2 100644 --- a/Content.Server/Physics/Controllers/MoverController.cs +++ b/Content.Server/Physics/Controllers/MoverController.cs @@ -36,6 +36,7 @@ namespace Content.Server.Physics.Controllers private float _shuttleDockSpeedCap; private HashSet _excludedMobs = new(); + private Dictionary> _shuttlePilots = new(); public override void Initialize() { @@ -56,12 +57,7 @@ namespace Content.Server.Physics.Controllers HandleMobMovement(mover, physics, mobMover); } - foreach (var (pilot, mover) in EntityManager.EntityQuery()) - { - if (pilot.Console == null) continue; - _excludedMobs.Add(mover.Owner.Uid); - HandleShuttleMovement(mover); - } + HandleShuttleMovement(frameTime); foreach (var (mover, physics) in EntityManager.EntityQuery(true)) { @@ -71,43 +67,120 @@ namespace Content.Server.Physics.Controllers } } - /* - * Some thoughts: - * Unreal actually doesn't predict vehicle movement at all, it's purely server-side which I thought was interesting - * The reason for this is that vehicles change direction very slowly compared to players so you don't really have the requirement for quick movement anyway - * As such could probably just look at applying a force / impulse to the shuttle server-side only so it controls like the titanic. - */ - private void HandleShuttleMovement(SharedPlayerInputMoverComponent mover) + private void HandleShuttleMovement(float frameTime) { - var gridId = mover.Owner.Transform.GridID; + var newPilots = new Dictionary>(); - if (!_mapManager.TryGetGrid(gridId, out var grid) || !EntityManager.TryGetEntity(grid.GridEntityId, out var gridEntity)) return; - - if (!gridEntity.TryGetComponent(out ShuttleComponent? shuttleComponent) || - !gridEntity.TryGetComponent(out PhysicsComponent? physicsComponent)) + // We just mark off their movement and the shuttle itself does its own movement + foreach (var (pilot, mover, xform) in EntityManager.EntityQuery()) { - return; + if (pilot.Console == null) continue; + _excludedMobs.Add(mover.Owner.Uid); + + var gridId = xform.GridID; + + if (!_mapManager.TryGetGrid(gridId, out var grid) || + !EntityManager.TryGetComponent(grid.GridEntityId, out ShuttleComponent? shuttleComponent)) continue; + + if (!newPilots.TryGetValue(shuttleComponent, out var pilots)) + { + pilots = new List<(PilotComponent, IMoverComponent)>(); + newPilots[shuttleComponent] = pilots; + } + + pilots.Add((pilot, mover)); } - // ShuttleSystem has already worked out the ratio so we'll just multiply it back by the mass. - var movement = mover.VelocityDir.walking + mover.VelocityDir.sprinting; - var system = EntitySystem.Get(); + var shuttleSystem = EntitySystem.Get(); + var thrusterSystem = EntitySystem.Get(); - if (movement.Length.Equals(0f)) + // Reset inputs for non-piloted shuttles. + foreach (var (shuttle, _) in _shuttlePilots) { - // TODO: This visualization doesn't work with multiple pilots so need to address that somehow. - system.DisableAllThrustDirections(shuttleComponent); - return; + if (newPilots.ContainsKey(shuttle)) continue; + + thrusterSystem.DisableLinearThrusters(shuttle); } - var speedCap = 0f; + _shuttlePilots = newPilots; - switch (shuttleComponent.Mode) + // Collate all of the linear / angular velocites for a shuttle + // then do the movement input once for it. + foreach (var (shuttle, pilots) in _shuttlePilots) { - case ShuttleMode.Docking: - system.DisableAllThrustDirections(shuttleComponent); - var dockDirection = movement.ToWorldAngle().GetDir(); - var dockFlag = dockDirection.AsFlag(); + if (shuttle.Paused || !EntityManager.TryGetComponent(shuttle.OwnerUid, out PhysicsComponent? body)) continue; + + // Collate movement linear and angular inputs together + var linearInput = Vector2.Zero; + var angularInput = 0f; + + switch (shuttle.Mode) + { + case ShuttleMode.Cruise: + foreach (var (pilot, mover) in pilots) + { + var console = pilot.Console; + + if (console == null) + { + DebugTools.Assert(false); + continue; + } + + var sprint = mover.VelocityDir.sprinting; + + if (sprint.Equals(Vector2.Zero)) continue; + + var offsetRotation = EntityManager.GetComponent(console.OwnerUid).LocalRotation; + + linearInput += offsetRotation.RotateVec(new Vector2(0f, sprint.Y)); + angularInput += sprint.X; + } + break; + case ShuttleMode.Docking: + // No angular input possible + foreach (var (pilot, mover) in pilots) + { + var console = pilot.Console; + + if (console == null) + { + DebugTools.Assert(false); + continue; + } + + var sprint = mover.VelocityDir.sprinting; + + if (sprint.Equals(Vector2.Zero)) continue; + + var offsetRotation = EntityManager.GetComponent(console.OwnerUid).LocalRotation; + sprint = offsetRotation.RotateVec(sprint); + + linearInput += sprint; + } + break; + default: + throw new ArgumentOutOfRangeException(); + } + + var count = pilots.Count; + linearInput /= count; + angularInput /= count; + + // Handle shuttle movement + if (linearInput.Length.Equals(0f)) + { + thrusterSystem.DisableLinearThrusters(shuttle); + body.LinearDamping = shuttleSystem.ShuttleIdleLinearDamping; + } + else + { + body.LinearDamping = shuttleSystem.ShuttleMovingLinearDamping; + + var angle = linearInput.ToWorldAngle(); + var linearDir = angle.GetDir(); + var dockFlag = linearDir.AsFlag(); + var shuttleNorth = EntityManager.GetComponent(body.OwnerUid).WorldRotation.ToWorldVec(); // Won't just do cardinal directions. foreach (DirectionFlag dir in Enum.GetValues(typeof(DirectionFlag))) @@ -124,108 +197,74 @@ namespace Content.Server.Physics.Controllers continue; } - if ((dir & dockFlag) == 0x0) continue; + if ((dir & dockFlag) == 0x0) + { + thrusterSystem.DisableLinearThrustDirection(shuttle, dir); + continue; + } - system.EnableThrustDirection(shuttleComponent, dir); + float length; + + switch (dir) + { + case DirectionFlag.North: + length = linearInput.Y; + break; + case DirectionFlag.South: + length = -linearInput.Y; + break; + case DirectionFlag.East: + length = linearInput.X; + break; + case DirectionFlag.West: + length = -linearInput.X; + break; + default: + throw new ArgumentOutOfRangeException(); + } + + thrusterSystem.EnableLinearThrustDirection(shuttle, dir); var index = (int) Math.Log2((int) dir); + var speed = shuttle.LinearThrusterImpulse[index] * length; - var dockSpeed = shuttleComponent.LinearThrusterImpulse[index]; - - // TODO: Cvar for the speed drop - dockSpeed /= 5f; - - if (physicsComponent.LinearVelocity.LengthSquared == 0f) + if (body.LinearVelocity.LengthSquared < 0.5f) { - dockSpeed *= 5f; + speed *= 5f; } - var moveAngle = movement.ToWorldAngle(); - - physicsComponent.ApplyLinearImpulse(moveAngle.RotateVec(physicsComponent.Owner.Transform.WorldRotation.ToWorldVec()) * - dockSpeed); + body.ApplyLinearImpulse( + angle.RotateVec(shuttleNorth) * + speed * + frameTime); } + } - speedCap = _shuttleDockSpeedCap; - break; - case ShuttleMode.Cruise: - if (movement.Y == 0f) + if (MathHelper.CloseTo(angularInput, 0f)) + { + thrusterSystem.SetAngularThrust(shuttle, false); + body.AngularDamping = shuttleSystem.ShuttleIdleAngularDamping; + } + else + { + body.AngularDamping = shuttleSystem.ShuttleMovingAngularDamping; + var angularSpeed = shuttle.AngularThrust; + + if (body.AngularVelocity < 0.5f) { - system.DisableThrustDirection(shuttleComponent, DirectionFlag.South); - system.DisableThrustDirection(shuttleComponent, DirectionFlag.North); - } - else - { - var direction = movement.Y > 0f ? Direction.North : Direction.South; - var linearSpeed = shuttleComponent.LinearThrusterImpulse[(int) direction / 2]; - - if (physicsComponent.LinearVelocity.LengthSquared == 0f) - { - linearSpeed *= 5f; - } - - // Currently this is slow BUT we'd have a separate multiplier for docking and cruising or whatever. - physicsComponent.ApplyLinearImpulse(physicsComponent.Owner.Transform.WorldRotation.Opposite().ToWorldVec() * - linearSpeed * - movement.Y); - - switch (direction) - { - case Direction.North: - system.DisableThrustDirection(shuttleComponent, DirectionFlag.South); - system.EnableThrustDirection(shuttleComponent, DirectionFlag.North); - break; - case Direction.South: - system.DisableThrustDirection(shuttleComponent, DirectionFlag.North); - system.EnableThrustDirection(shuttleComponent, DirectionFlag.South); - break; - } + angularSpeed *= 5f; } - var angularSpeed = shuttleComponent.AngularThrust; + // Scale rotation by mass just to make rotating larger things a bit more bearable. + body.ApplyAngularImpulse( + -angularInput * + angularSpeed * + frameTime * + body.Mass / 100f); - if (movement.X == 0f) - { - system.DisableThrustDirection(shuttleComponent, DirectionFlag.West); - system.DisableThrustDirection(shuttleComponent, DirectionFlag.East); - } - else if (movement.X != 0f) - { - physicsComponent.ApplyAngularImpulse(-movement.X * angularSpeed); - - if (movement.X < 0f) - { - system.EnableThrustDirection(shuttleComponent, DirectionFlag.West); - system.DisableThrustDirection(shuttleComponent, DirectionFlag.East); - } - else - { - system.EnableThrustDirection(shuttleComponent, DirectionFlag.East); - system.DisableThrustDirection(shuttleComponent, DirectionFlag.West); - } - } - - // TODO WHEN THIS ACTUALLY WORKS - speedCap = _shuttleDockSpeedCap * 10; - break; - default: - throw new ArgumentOutOfRangeException(); + thrusterSystem.SetAngularThrust(shuttle, true); + } } - - var velocity = physicsComponent.LinearVelocity; - var angVelocity = physicsComponent.AngularVelocity; - - if (velocity.Length > speedCap) - { - physicsComponent.LinearVelocity = velocity.Normalized * speedCap; - } - - /* TODO: Need to suss something out but this PR already BEEG - if (angVelocity > speedCap) - { - physicsComponent.AngularVelocity = speedCap; - } - */ } protected override void HandleFootsteps(IMoverComponent mover, IMobMoverComponent mobMover) diff --git a/Content.Server/Shuttles/Components/ThrusterComponent.cs b/Content.Server/Shuttles/Components/ThrusterComponent.cs index 9a04851d8e..c91828d0da 100644 --- a/Content.Server/Shuttles/Components/ThrusterComponent.cs +++ b/Content.Server/Shuttles/Components/ThrusterComponent.cs @@ -51,7 +51,7 @@ namespace Content.Server.Shuttles.Components [ViewVariables] [DataField("impulse")] - public float Impulse = 5f; + public float Impulse = 450f; [ViewVariables] [DataField("thrusterType")] diff --git a/Content.Server/Shuttles/EntitySystems/ShuttleSystem.cs b/Content.Server/Shuttles/EntitySystems/ShuttleSystem.cs index e19dc756d0..d7052f2851 100644 --- a/Content.Server/Shuttles/EntitySystems/ShuttleSystem.cs +++ b/Content.Server/Shuttles/EntitySystems/ShuttleSystem.cs @@ -11,6 +11,12 @@ namespace Content.Server.Shuttles.EntitySystems { private const float TileMassMultiplier = 4f; + public float ShuttleIdleLinearDamping = 0.1f; + public float ShuttleIdleAngularDamping = 0.2f; + + public float ShuttleMovingLinearDamping = 0.05f; + public float ShuttleMovingAngularDamping = 0.05f; + public override void Initialize() { base.Initialize(); @@ -88,8 +94,8 @@ namespace Content.Server.Shuttles.EntitySystems component.BodyStatus = BodyStatus.InAir; //component.FixedRotation = false; TODO WHEN ROTATING SHUTTLES FIXED. component.FixedRotation = false; - component.LinearDamping = 0.2f; - component.AngularDamping = 0.3f; + component.LinearDamping = ShuttleIdleLinearDamping; + component.AngularDamping = ShuttleIdleAngularDamping; } private void Disable(PhysicsComponent component) diff --git a/Content.Server/Shuttles/EntitySystems/ThrusterSystem.cs b/Content.Server/Shuttles/EntitySystems/ThrusterSystem.cs index c579cc6a10..80b555a920 100644 --- a/Content.Server/Shuttles/EntitySystems/ThrusterSystem.cs +++ b/Content.Server/Shuttles/EntitySystems/ThrusterSystem.cs @@ -404,46 +404,60 @@ namespace Content.Server.Shuttles.EntitySystems /// /// Considers a thrust direction as being active. /// - public void EnableThrustDirection(ShuttleComponent component, DirectionFlag direction) + public void EnableLinearThrustDirection(ShuttleComponent component, DirectionFlag direction) { if ((component.ThrustDirections & direction) != 0x0) return; component.ThrustDirections |= direction; - if ((direction & (DirectionFlag.East | DirectionFlag.West)) != 0x0) + var index = GetFlagIndex(direction); + + foreach (var comp in component.LinearThrusters[index]) { - switch (component.Mode) - { - case ShuttleMode.Cruise: - foreach (var comp in component.AngularThrusters) - { - if (!EntityManager.TryGetComponent(comp.OwnerUid, out AppearanceComponent? appearanceComponent)) - continue; + if (!EntityManager.TryGetComponent(comp.OwnerUid, out AppearanceComponent? appearanceComponent)) + continue; - comp.Firing = true; - appearanceComponent.SetData(ThrusterVisualState.Thrusting, true); - } - break; - case ShuttleMode.Docking: - var index = GetFlagIndex(direction); - - foreach (var comp in component.LinearThrusters[index]) - { - if (!EntityManager.TryGetComponent(comp.OwnerUid, out AppearanceComponent? appearanceComponent)) - continue; - - comp.Firing = true; - appearanceComponent.SetData(ThrusterVisualState.Thrusting, true); - } - - break; - } + comp.Firing = true; + appearanceComponent.SetData(ThrusterVisualState.Thrusting, true); } - else - { - var index = GetFlagIndex(direction); + } - foreach (var comp in component.LinearThrusters[index]) + /// + /// Disables a thrust direction. + /// + public void DisableLinearThrustDirection(ShuttleComponent component, DirectionFlag direction) + { + if ((component.ThrustDirections & direction) == 0x0) return; + + component.ThrustDirections &= ~direction; + + var index = GetFlagIndex(direction); + + foreach (var comp in component.LinearThrusters[index]) + { + if (!EntityManager.TryGetComponent(comp.OwnerUid, out AppearanceComponent? appearanceComponent)) + continue; + + comp.Firing = false; + appearanceComponent.SetData(ThrusterVisualState.Thrusting, false); + } + } + + public void DisableLinearThrusters(ShuttleComponent component) + { + foreach (DirectionFlag dir in Enum.GetValues(typeof(DirectionFlag))) + { + DisableLinearThrustDirection(component, dir); + } + + DebugTools.Assert(component.ThrustDirections == DirectionFlag.None); + } + + public void SetAngularThrust(ShuttleComponent component, bool on) + { + if (on) + { + foreach (var comp in component.AngularThrusters) { if (!EntityManager.TryGetComponent(comp.OwnerUid, out AppearanceComponent? appearanceComponent)) continue; @@ -452,51 +466,9 @@ namespace Content.Server.Shuttles.EntitySystems appearanceComponent.SetData(ThrusterVisualState.Thrusting, true); } } - } - - /// - /// Disables a thrust direction. - /// - public void DisableThrustDirection(ShuttleComponent component, DirectionFlag direction) - { - if ((component.ThrustDirections & direction) == 0x0) return; - - component.ThrustDirections &= ~direction; - - if ((direction & (DirectionFlag.East | DirectionFlag.West)) != 0x0) - { - switch (component.Mode) - { - case ShuttleMode.Cruise: - foreach (var comp in component.AngularThrusters) - { - if (!EntityManager.TryGetComponent(comp.OwnerUid, out AppearanceComponent? appearanceComponent)) - continue; - - comp.Firing = false; - appearanceComponent.SetData(ThrusterVisualState.Thrusting, false); - } - break; - case ShuttleMode.Docking: - var index = GetFlagIndex(direction); - - foreach (var comp in component.LinearThrusters[index]) - { - if (!EntityManager.TryGetComponent(comp.OwnerUid, out AppearanceComponent? appearanceComponent)) - continue; - - comp.Firing = false; - appearanceComponent.SetData(ThrusterVisualState.Thrusting, false); - } - - break; - } - } else { - var index = GetFlagIndex(direction); - - foreach (var comp in component.LinearThrusters[index]) + foreach (var comp in component.AngularThrusters) { if (!EntityManager.TryGetComponent(comp.OwnerUid, out AppearanceComponent? appearanceComponent)) continue; @@ -507,16 +479,6 @@ namespace Content.Server.Shuttles.EntitySystems } } - public void DisableAllThrustDirections(ShuttleComponent component) - { - foreach (DirectionFlag dir in Enum.GetValues(typeof(DirectionFlag))) - { - DisableThrustDirection(component, dir); - } - - DebugTools.Assert(component.ThrustDirections == DirectionFlag.None); - } - #endregion [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index f990f8429e..a9b3337978 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml @@ -25,6 +25,8 @@ radius: 1.5 energy: 1.6 color: "#43ccb5" + - type: Rotatable + rotateWhileAnchored: true - type: entity parent: ComputerShuttleBase diff --git a/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml b/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml index 83fcb36daa..36be517bcc 100644 --- a/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml +++ b/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml @@ -47,6 +47,7 @@ name: thruster description: It goes nyooooooom. components: + - type: Thruster - type: Sprite sprite: Structures/Shuttles/thruster.rsi layers: @@ -68,7 +69,7 @@ components: - type: Thruster thrusterType: Angular - impulse: 2 + impulse: 200 - type: Sprite # Listen I'm not the biggest fan of the sprite but it was the most appropriate thing I could find. sprite: Structures/Shuttles/gyroscope.rsi