From c349de9c315bcf809383944aa401fdeb7972f7c9 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sat, 10 Oct 2026 13:40:06 +0200 Subject: [PATCH] Delete the thrust mixer, buckets, aggregates, trim and pruning; wire ThrustAllocator into Ship.Update Ship.Update hands the intent (the throttle lock's one line) and every thruster's live Column(NominalThrust) to one ThrustAllocator and writes each Axis from its throttle. Thruster gains NominalThrust and Column; the .01 throttle gates become > 0. Does not build the test project: EvasionTermTests still read the deleted aggregates (next commit rewrites them). Not compiled in Unity. Co-Authored-By: Claude Opus 5.5 --- .../ServerShared/Behaviors/Thruster.cs | 16 +- Assets/Scripts/ServerShared/Ship.cs | 167 ++---------------- 2 files changed, 26 insertions(+), 157 deletions(-) diff --git a/Assets/Scripts/ServerShared/Behaviors/Thruster.cs b/Assets/Scripts/ServerShared/Behaviors/Thruster.cs index 35894febb..ff4ce05cf 100644 --- a/Assets/Scripts/ServerShared/Behaviors/Thruster.cs +++ b/Assets/Scripts/ServerShared/Behaviors/Thruster.cs @@ -41,8 +41,20 @@ public class Thruster : Behavior, IPowerConsumer { // Live: heat, quality, durability and power move this stat after construction, so it is read, never cached. public float Thrust => Evaluate(_data.Thrust); + // Thrust at a full grant, the same stat read without the power-supply term: what a column promises. Live, like Thrust. + public float NominalThrust => EvaluateNominalPower(_data.Thrust); public float Torque { get; } + // This thruster's effect per unit throttle at the given thrust, in the body frame: xy is the push (x starboard, y + // forward), z the clockwise yaw rate in rad/s. Zero when the item is absent or offline. The one geometry owner for + // the allocator and the envelope. + public float3 Column(float thrust) + { + if (Item == null || !Item.Active.Value) return default; + var push = -float2(0, 1).Rotate(Item.EquippableItem.Rotation) * thrust / Entity.Mass; + return float3(push.x, push.y, Torque * thrust * ItemManager.GameplaySettings.TorqueMultiplier / Entity.Mass); + } + public float Axis { get => _input; @@ -84,7 +96,7 @@ public class Thruster : Behavior, IPowerConsumer // registered request field (StatValidation.PowerRequestFields) -- read nominally, same reasoning as every // other IPowerConsumer in this cut, though EnergyUsage carries no PowerSupply term in Thruster's own shipped // catalog today; Thrust (the field Cut 7 curves) is a separate stat Execute reads with the real Evaluate. - public float PowerRequest(float dt) => _input > .01f ? _input * EvaluateNominalPower(_data.EnergyUsage) : 0f; + public float PowerRequest(float dt) => _input > 0f ? _input * EvaluateNominalPower(_data.EnergyUsage) : 0f; // Cut 5 (docs/stats-and-power-cut.md ยง1.3, PowerTiers.cs): Medium -- mobility. Losing thrust for a tick // under brownout is an inconvenience, not the cascading failure a starved radiator or shield causes. @@ -121,7 +133,7 @@ public class Thruster : Behavior, IPowerConsumer // Thrust stat carries a PowerSupply term, a partial grant already comes back reduced -- this gate no // longer demands a full grant, only that the thruster is being asked to do anything (_input) and that it // has not been cut to true zero supply (the epsilon PowerBus itself already treats as "nothing granted"). - if(_input > .01f && Item.PowerSupply > 1e-4f) + if(_input > 0f && Item.PowerSupply > 1e-4f) { var thrust = Thrust; Entity.Velocity -= Direction.xz * _input * thrust / Entity.Mass * dt; diff --git a/Assets/Scripts/ServerShared/Ship.cs b/Assets/Scripts/ServerShared/Ship.cs index 6848c6073..7daae5c9c 100644 --- a/Assets/Scripts/ServerShared/Ship.cs +++ b/Assets/Scripts/ServerShared/Ship.cs @@ -30,14 +30,11 @@ public class Ship : Entity public float Turn; public bool IsPlayerShip; - private HashSet _thrusterItems; private Thruster[] _allThrusters; - private HashSet _forwardThrusters; - private HashSet _reverseThrusters; - private HashSet _rightThrusters; - private HashSet _leftThrusters; - private HashSet _clockwiseThrusters; - private HashSet _counterClockwiseThrusters; + // One column per thruster (its effect per unit throttle, in the body frame) and the throttles the allocator answers with. + private float3[] _columns; + private float[] _throttles; + private readonly ThrustAllocator _allocator = new ThrustAllocator(); private bool _exitingWormhole = false; private bool _enteringWormhole = false; @@ -48,16 +45,6 @@ public class Ship : Entity private float2 _wormholeExitVelocity; public bool WormholeAnimationInProgress => _enteringWormhole || _exitingWormhole; - public float ForwardThrust { get; private set; } - public float ReverseThrust { get; private set; } - public float LeftStrafeThrust { get; private set; } - public float RightStrafeThrust { get; private set; } - public float ClockwiseTorque { get; private set; } - public float CounterClockwiseTorque { get; private set; } - public float LeftStrafeTotalTorque { get; private set; } - private List LeftStrafeTorqueThrusters = new List(); - public float RightStrafeTotalTorque { get; private set; } - private List RightStrafeTorqueThrusters = new List(); public event Action OnExitedWormhole; public event Action OnEnteredWormhole; @@ -88,49 +75,18 @@ public class Ship : Entity base.Activate(); _allThrusters = GetBehaviors().ToArray(); - _thrusterItems = new HashSet(_allThrusters.Select(x=>x.Item)); - - _forwardThrusters = new HashSet(_allThrusters - .Where(x => x.Item.EquippableItem.Rotation == ItemRotation.Reversed)); - - _reverseThrusters = new HashSet(_allThrusters - .Where(x => x.Item.EquippableItem.Rotation == ItemRotation.None)); - - _rightThrusters = new HashSet(_allThrusters - .Where(x => x.Item.EquippableItem.Rotation == ItemRotation.CounterClockwise)); - - _leftThrusters = new HashSet(_allThrusters - .Where(x => x.Item.EquippableItem.Rotation == ItemRotation.Clockwise)); - - _counterClockwiseThrusters = new HashSet(_allThrusters - .Where(x => x.Torque < -ItemManager.GameplaySettings.TorqueFloor)); - - _clockwiseThrusters = new HashSet(_allThrusters - .Where(x => x.Torque > ItemManager.GameplaySettings.TorqueFloor)); + _columns = new float3[_allThrusters.Length]; + _throttles = new float[_allThrusters.Length]; } public Ship(ItemManager itemManager, Zone zone, EquippableItem hull, EntitySettings settings) : base(itemManager, zone, hull, settings) { - ItemDestroyed.Where(item=>_thrusterItems.Contains(item)).Subscribe(RemoveThruster); - } - - private void RemoveThruster(EquippedItem item) - { - _thrusterItems.Remove(item); - var thruster = item.GetBehavior(); - if (_forwardThrusters.Contains(thruster)) _forwardThrusters.Remove(thruster); - if (_reverseThrusters.Contains(thruster)) _reverseThrusters.Remove(thruster); - if (_rightThrusters.Contains(thruster)) _rightThrusters.Remove(thruster); - if (_leftThrusters.Contains(thruster)) _leftThrusters.Remove(thruster); - if (_clockwiseThrusters.Contains(thruster)) _clockwiseThrusters.Remove(thruster); - if (_counterClockwiseThrusters.Contains(thruster)) _counterClockwiseThrusters.Remove(thruster); } #region ThrustCalculation // What this ship can do at this instant (see ManoeuvreEnvelope): the sum of what each live propulsor reports with - // its own Execute arithmetic; thrusters are its only propulsors. Derived each update, never saved. The aggregates above are not an envelope (they - // mix units and cancel a lone off-axis strafer), so nothing in it reads them. + // its own Execute arithmetic; thrusters are its only propulsors. Derived each update, never saved. public ManoeuvreEnvelope Envelope { get; private set; } private void RecalculateEnvelope() @@ -140,118 +96,19 @@ public class Ship : Entity Envelope = envelope; } - private void RecalculateThrust() - { - RecalculateEnvelope(); - RecalculateForwardThrust(); - RecalculateReverseThrust(); - RecalculateLeftStrafeThrust(); - RecalculateRightStrafeThrust(); - RecalculateClockwiseTorque(); - RecalculateCounterClockwiseTorque(); - } - - private void RecalculateForwardThrust() - { - ForwardThrust = 0; - foreach (var thruster in _forwardThrusters) - if (thruster.Item.Active.Value) - ForwardThrust += thruster.Thrust; - } - - private void RecalculateReverseThrust() - { - ReverseThrust = 0; - foreach (var thruster in _reverseThrusters) - if (thruster.Item.Active.Value) - ReverseThrust += thruster.Thrust; - } - - private void RecalculateLeftStrafeThrust() - { - LeftStrafeThrust = 0; - LeftStrafeTotalTorque = 0; - foreach (var thruster in _leftThrusters) - { - if(thruster.Item.Active.Value) - { - LeftStrafeThrust += thruster.Thrust; - LeftStrafeTotalTorque += thruster.Torque * thruster.Thrust; - } - } - LeftStrafeTorqueThrusters.Clear(); - foreach(var thruster in _leftThrusters) - if (abs(sign(thruster.Torque) - sign(LeftStrafeTotalTorque)) < .01f) - LeftStrafeTorqueThrusters.Add(thruster); - } - - private void RecalculateRightStrafeThrust() - { - RightStrafeThrust = 0; - RightStrafeTotalTorque = 0; - foreach (var thruster in _rightThrusters) - { - if(thruster.Item.Active.Value) - { - RightStrafeThrust += thruster.Thrust; - RightStrafeTotalTorque += thruster.Torque * thruster.Thrust; - } - } - RightStrafeTorqueThrusters.Clear(); - foreach(var thruster in _rightThrusters) - if (abs(sign(thruster.Torque) - sign(RightStrafeTotalTorque)) < .01f) - RightStrafeTorqueThrusters.Add(thruster); - } - - private void RecalculateClockwiseTorque() - { - ClockwiseTorque = 0; - foreach (var thruster in _clockwiseThrusters) - if (thruster.Item.Active.Value) - ClockwiseTorque += thruster.Torque; - } - - private void RecalculateCounterClockwiseTorque() - { - CounterClockwiseTorque = 0; - foreach (var thruster in _counterClockwiseThrusters) - if (thruster.Item.Active.Value) - CounterClockwiseTorque -= thruster.Torque; - } - #endregion public override void Update(float delta) { if (_active && !_exitingWormhole && !_enteringWormhole) { - RecalculateThrust(); + RecalculateEnvelope(); // The lock replaces intent here, once; player and agent intent both pass through this one read. var move = ThrottleLocked ? float2(0, 1) : MovementDirection; - foreach (var thruster in _allThrusters) thruster.Axis = 0; - var rightThrusterTorqueCompensation = abs(RightStrafeTotalTorque) / RightStrafeTorqueThrusters.Count; - foreach (var thruster in _rightThrusters) - { - var thrust = 0f; - thrust += move.x; - if (RightStrafeTorqueThrusters.Contains(thruster)) - thrust -= move.x * (rightThrusterTorqueCompensation / (abs(thruster.Torque) * thruster.Thrust)); - thruster.Axis = thrust; - } - var leftThrusterTorqueCompensation = abs(LeftStrafeTotalTorque) / LeftStrafeTorqueThrusters.Count; - foreach (var thruster in _leftThrusters) - { - var thrust = 0f; - thrust += -move.x; - if (LeftStrafeTorqueThrusters.Contains(thruster)) - thrust += move.x * (leftThrusterTorqueCompensation / (abs(thruster.Torque) * thruster.Thrust)); - thruster.Axis = thrust; - } - foreach (var thruster in _forwardThrusters) thruster.Axis += move.y; - foreach (var thruster in _reverseThrusters) thruster.Axis += -move.y; - - foreach (var thruster in _clockwiseThrusters) thruster.Axis += Turn; - foreach (var thruster in _counterClockwiseThrusters) thruster.Axis += -Turn; + for (var i = 0; i < _allThrusters.Length; i++) + _columns[i] = _allThrusters[i].Column(_allThrusters[i].NominalThrust); + _allocator.Allocate(_columns, move, Turn, _throttles); + for (var i = 0; i < _allThrusters.Length; i++) _allThrusters[i].Axis = _throttles[i]; } var velocityMagnitude = length(Velocity);