Throttle lock is a forward floor on the solve; allocation moves after the performance update
The lock stops being an intent rewrite in Ship.Update: ThrustAllocator.Allocate takes forwardFloor, which ignores the stick, asks full forward and holds every column with y > 0 at full (ruling overdrive-forward-floor). Ship allocates in Entity's new UpdateThrottles phase, after every item's UpdatePerformance and before the power bus and Execute, so liveness and thrust are the ones Execute acts on (finding liveness-read-before-performance). Tests follow in the next commits; the test project still compiles, new behaviour is untested here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
caa424f424
commit
534e402fdf
3 changed files with 26 additions and 12 deletions
|
|
@ -1373,6 +1373,10 @@ public abstract class Entity
|
|||
yield return b;
|
||||
}
|
||||
|
||||
// The phase between the items' performance update and the power bus: what an entity commands its items to do this
|
||||
// tick, read from the same liveness and stats Execute then acts on. Ship's allocation lives here.
|
||||
protected virtual void UpdateThrottles() { }
|
||||
|
||||
public virtual void Update(float delta)
|
||||
{
|
||||
TurnRate = 0;
|
||||
|
|
@ -1402,6 +1406,7 @@ public abstract class Entity
|
|||
UpdateTemperature(delta);
|
||||
|
||||
foreach (var item in _orderedEquipment) item.UpdatePerformance();
|
||||
UpdateThrottles();
|
||||
|
||||
if (_active)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -98,19 +98,21 @@ public class Ship : Entity
|
|||
|
||||
#endregion
|
||||
|
||||
// Called by Entity.Update once the items' performance is current and before the power bus is stepped and the items
|
||||
// execute: liveness, thrust and the throttles they decide are one phase, the one Execute acts on. The lock is a
|
||||
// bound on the allocator's solve; intent reaches the allocator here and nowhere else.
|
||||
protected override void UpdateThrottles()
|
||||
{
|
||||
if (_exitingWormhole || _enteringWormhole) return;
|
||||
RecalculateEnvelope();
|
||||
for (var i = 0; i < _allThrusters.Length; i++)
|
||||
_columns[i] = _allThrusters[i].Column(_allThrusters[i].NominalThrust);
|
||||
_allocator.Allocate(_columns, MovementDirection, Turn, _throttles, ThrottleLocked);
|
||||
for (var i = 0; i < _allThrusters.Length; i++) _allThrusters[i].Axis = _throttles[i];
|
||||
}
|
||||
|
||||
public override void Update(float delta)
|
||||
{
|
||||
if (_active && !_exitingWormhole && !_enteringWormhole)
|
||||
{
|
||||
RecalculateEnvelope();
|
||||
// The lock replaces intent here, once; player and agent intent both pass through this one read.
|
||||
var move = ThrottleLocked ? float2(0, 1) : MovementDirection;
|
||||
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);
|
||||
if(velocityMagnitude > .01f)
|
||||
Velocity = normalize(Velocity) * decay(velocityMagnitude, HullData.Drag, delta);
|
||||
|
|
|
|||
|
|
@ -31,11 +31,18 @@ public sealed class ThrustAllocator
|
|||
int _columns = -1;
|
||||
float[] _a, _b, _lo, _hi, _x, _workspace;
|
||||
|
||||
public void Allocate(ReadOnlySpan<float3> columns, float2 move, float turn, Span<float> throttle)
|
||||
/// <summary>
|
||||
/// Solves the throttles for the intent. <paramref name="forwardFloor"/> is the throttle lock: every thruster whose
|
||||
/// column pushes forward is bounded below at full, the stick is ignored (forward is the only translation asked, so
|
||||
/// nothing fires against the floored drives), and the turn is served by what remains.
|
||||
/// </summary>
|
||||
public void Allocate(ReadOnlySpan<float3> columns, float2 move, float turn, Span<float> throttle, bool forwardFloor = false)
|
||||
{
|
||||
var n = columns.Length;
|
||||
if (n != _columns) Resize(n);
|
||||
for (var i = 0; i < n; i++) throttle[i] = 0;
|
||||
if (forwardFloor) move = new float2(0, 1);
|
||||
for (var i = 0; i < n; i++) _lo[i] = forwardFloor && columns[i].y > 0 ? 1f : 0f;
|
||||
|
||||
// Nothing asked, nothing fired: that is a fact about the intent, decided before the solve. The solver's warm
|
||||
// start is left as the last solved point, which is where the next non-zero call resumes.
|
||||
|
|
|
|||
Loading…
Reference in a new issue