Merge origin/master into eureka/aetheria-release-consumable-host
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
c22456b52c
164 changed files with 8443 additions and 60 deletions
|
|
@ -5,9 +5,11 @@
|
|||
using CultMath;
|
||||
using static CultMath.math;
|
||||
|
||||
// Where a presenter draws the latest sim state when the frame falls Lead sim seconds after the last step. Pure reads
|
||||
// of the simulation for presenters (never called by ServerShared): the sim owns every pose and no presenter stores
|
||||
// one, a previous state or a velocity of its own.
|
||||
// Pure presentation statics over sim state, never called by ServerShared, with no state of their own: the sim owns every
|
||||
// pose and no presenter stores one, a previous state or a velocity of its own. Ships are drawn where the frame falls Lead
|
||||
// sim seconds after the last step, by their last step's velocity and turn. A ballistic round's flight is presentation
|
||||
// (ruling projectile-flight-is-presentation), drawn from the sim's facts: its frozen line, the published outcome, the
|
||||
// commit tick, the impact cell on the target's drawn pose, the burst point.
|
||||
public static class DrawAhead
|
||||
{
|
||||
// A ship in a wormhole animation has its pose scripted by the step, not integrated from Velocity and TurnRate,
|
||||
|
|
@ -37,4 +39,62 @@ public static class DrawAhead
|
|||
t.w * q.z + t.x * q.y - t.y * q.x + t.z * q.w,
|
||||
t.w * q.w - t.x * q.x - t.y * q.y - t.z * q.z));
|
||||
}
|
||||
|
||||
// A ballistic round at a sim time (the clock's Zone.Time + Lead): on its frozen line while the outcome is unknown or a
|
||||
// miss, with the barrel's offset from that line decaying to nothing over blendTime sim seconds from the moment of firing.
|
||||
// A round the sim published as a hit or burst steers from its line, from the commit tick, onto where it ends by the
|
||||
// time damage lands: a burst's frozen point, else the impact cell on the target's drawn pose; and is held there after.
|
||||
public static float3 Round(in PendingShot shot, ShotOutcome known, float3 barrel, float blendTime, float simTime, float lead)
|
||||
{
|
||||
var time = simTime + lead;
|
||||
var drawn = Lifted(shot, Line(shot, known?.Result, time), barrel, blendTime, time);
|
||||
if (known == null || known.Result == ShotResult.Miss) return drawn;
|
||||
var end = known.HasBurstPoint
|
||||
? float3(known.BurstPoint.x, shot.FireOrigin.y, known.BurstPoint.y)
|
||||
: Impact(known.Target, known.Cell, lead);
|
||||
var commit = shot.ArrivalTime - known.ArrivalIn;
|
||||
var steer = shot.ArrivalTime > commit ? saturate((time - commit) / (shot.ArrivalTime - commit)) : 1f;
|
||||
return drawn + (end - drawn) * steer;
|
||||
}
|
||||
|
||||
// The impact cell's centre on the target's drawn pose: the cell at the sim pose, drawn ahead by the target's last
|
||||
// step velocity and turned by its last step turn for lead seconds, as Position and Rotation draw the entity.
|
||||
public static float3 Impact(Entity target, int2 cell, float lead)
|
||||
{
|
||||
var at = Position(target, lead);
|
||||
var offset = target.ToWorldPoint((float2) cell) - target.Position.xz;
|
||||
var turned = mul(offset, CultMath.float2x2.Rotate(Turn(target, lead)));
|
||||
return float3(at.x + turned.x, at.y, at.z + turned.y);
|
||||
}
|
||||
|
||||
// Whether the drawn flight is over by time: only a published miss ends by the clock. A hit or burst ends on ShotResolved.
|
||||
public static bool RoundOver(in PendingShot shot, ShotOutcome known, float simTime, float lead) =>
|
||||
known != null && known.Result == ShotResult.Miss && simTime + lead >= RoundEnd(shot, ShotResult.Miss);
|
||||
|
||||
// The sim time a round's drawn line ends, for what is known of its outcome. A round that hit or burst (or whose outcome
|
||||
// is not yet published) ends at its ArrivalTime; a round the sim has published as a Miss flies on to the weapon's frozen
|
||||
// range and never ends short of its own arrival (a direct round's FireRange is measured to any target, in range or not).
|
||||
public static float RoundEnd(in PendingShot shot, ShotResult? known) =>
|
||||
known == ShotResult.Miss
|
||||
? max(shot.ArrivalTime, shot.FireTime + (shot.Speed > .01f ? shot.MaxRange / shot.Speed : 0f))
|
||||
: shot.ArrivalTime;
|
||||
|
||||
// The frozen line a round follows until it steers: from its frozen origin along its frozen direction at its frozen
|
||||
// speed, held at RoundEnd once it has flown that far. With no outcome known the round is drawn no further than its
|
||||
// arrival point and waits there for the sim to publish one.
|
||||
private static float2 Line(in PendingShot shot, ShotResult? known, float time)
|
||||
{
|
||||
if (shot.Speed <= .01f) return shot.FireOrigin.xz;
|
||||
var flown = clamp(time - shot.FireTime, 0f, RoundEnd(shot, known) - shot.FireTime);
|
||||
return shot.FireOrigin.xz + shot.TravelDirection * (shot.Speed * flown);
|
||||
}
|
||||
|
||||
private static float Turn(Entity entity, float lead) =>
|
||||
entity is Ship { WormholeAnimationInProgress: true } ? 0f : entity.TurnRate * lead;
|
||||
|
||||
private static float3 Lifted(in PendingShot shot, float2 planar, float3 barrel, float blendTime, float time)
|
||||
{
|
||||
var blend = blendTime > 0 ? saturate(1 - (time - shot.FireTime) / blendTime) : 0f;
|
||||
return float3(planar.x, shot.FireOrigin.y, planar.y) + (barrel - shot.FireOrigin) * blend;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -755,6 +755,8 @@ public static class FireControl
|
|||
Spread = weapon.Spread,
|
||||
FireRange = fireRange,
|
||||
FireTime = now,
|
||||
Speed = weapon.Velocity,
|
||||
MaxRange = weapon.Range,
|
||||
FireOrigin = origin,
|
||||
FireTargetPosition = targetPosition,
|
||||
FireTargetVelocity = targetVelocity,
|
||||
|
|
@ -853,6 +855,8 @@ public static class FireControl
|
|||
// A contact burst short of max range arrives when the round has flown that far, not at the frozen
|
||||
// max-range time (CommitBurst). Never later than the frozen time.
|
||||
if (shot.Outcome.Result == ShotResult.Burst) shot.ArrivalTime = min(shot.ArrivalTime, now + shot.Outcome.ArrivalIn);
|
||||
// Written back before ShotCommitted fires: a presenter re-reading the record there (Zone.TryGetShot) sees
|
||||
// the committed ArrivalTime a contact burst has just shortened.
|
||||
shots[i] = shot;
|
||||
zone.ShotCommitted.OnNext(shot.Outcome);
|
||||
}
|
||||
|
|
@ -1999,6 +2003,11 @@ public struct PendingShot
|
|||
public float3 FireTargetPosition;
|
||||
public float3 FireTargetVelocity;
|
||||
public float FireTime;
|
||||
// The weapon's Velocity at Fire, frozen with the rest so a presenter draws the same line whatever happens to the stat later.
|
||||
public float Speed;
|
||||
// The weapon's Range at Fire, likewise frozen: how far a presenter may draw a round that misses. FireRange above
|
||||
// is the distance to the engaged target (0 for a round with none), not how far a round that misses flies on.
|
||||
public float MaxRange;
|
||||
public float CommitTime;
|
||||
public float ArrivalTime;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue