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);