Merge cut orbital-layering (closing verdicts cut-orbital-layering.s1, cut-orbital-layering.s2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-16 05:08:26 +02:00
commit 949fa005d6
27 changed files with 291 additions and 64 deletions

View file

@ -167,5 +167,5 @@ public class Pickup : Behavior, IAlwaysUpdatedBehavior
}
private void Publish(GrabPhase phase, FloatingItem item, float duration) =>
Entity.Capabilities.PublishGrab(new GrabEvent(item.Id, phase, float3(item.Body.Position.x, Entity.Position.y, item.Body.Position.y), duration));
Entity.Capabilities.PublishGrab(new GrabEvent(item.Id, phase, float3(item.Body.Position.x, DrawAhead.FloaterHeight(Entity.Zone, Entity.ItemManager.GameplaySettings, item.Body.Position, 0), item.Body.Position.y), duration));
}

View file

@ -7,8 +7,9 @@ using CultMath;
using static CultMath.math;
using float2 = CultMath.float2;
// A hull touching a hull (aetheria-release cut sim-contact; ruling unity-presents-only-r3): the simulation owns
// contact between entities. Each hull is a disc about its centre of mass that bounds every occupied cell, and
// A ship touching a ship (aetheria-release cut sim-contact; ruling unity-presents-only-r3): the simulation owns
// contact between ships. Orbital entities (stations, turrets) sit on the orbital layer above the ship plane and floaters
// below it, so neither touches a ship (ruling aetheria-gravity:orbitals-above-floaters-below). Each hull is a disc about its centre of mass that bounds every occupied cell, and
// two discs that touch while closing along the line between their centres take the normal impulse
// j = -(1 + e)(v_rel . n) / (1/m1 + 1/m2) (ruling ship-bounce-restitution-setting): momentum is conserved, the
// result is frame-independent, and a pair that has separated is not bounced again. e is
@ -35,7 +36,7 @@ public static class Contacts
{
public static float Radius(Entity e) => e.HullData.Reach(e.ItemManager.GameplaySettings.SchematicCellSize);
// For each unordered pair of the zone's entities, in list order: if their discs touch and are closing along
// For each unordered pair of the zone's ships, in list order: if their discs touch and are closing along
// the normal, the impulse moves each one's velocity along the normal only (the tangent components are
// untouched), and the contact is published. Pairs resolve one after another in list order, each from the
// velocities the earlier pairs left, so a body touching two others in one tick depends on that order; the
@ -43,7 +44,7 @@ public static class Contacts
// are not hulls in the zone. No positional push-out.
public static void Step(Zone zone)
{
var entities = zone.Entities.Where(e => e.Active && e.Parent == null).ToArray();
var entities = zone.Entities.OfType<Ship>().Where(e => e.Active && e.Parent == null).ToArray();
var radii = entities.Select(Radius).ToArray();
for (var i = 0; i < entities.Length; i++)
for (var j = i + 1; j < entities.Length; j++)

View file

@ -26,6 +26,9 @@ public static class DrawAhead
// The field's height at a point at the drawn time, its wells where their rails put them at T.
public static float Height(Zone zone, float2 planar, float lead) => zone.GetHeight(planar, Time(zone, lead));
// The drawn height of a floater (loot, mines): the field at the drawn time plus the floater layer.
public static float FloaterHeight(Zone zone, GameplaySettings settings, float2 planar, float lead) => Height(zone, planar, lead) + settings.FloaterLayerHeight;
// A ship in a wormhole animation has its pose scripted by the step, not integrated from Velocity and TurnRate,
// so it is drawn where the step put it: extrapolating a scripted pose draws a streak that resets every step.
// A child is drawn where its parent is drawn, as the step copies the parent's pose onto it.

View file

@ -1974,9 +1974,13 @@ public abstract class Entity
Manoeuvre.Observe(Acceleration, delta, ItemManager.GameplaySettings.SolutionWindow);
}
// Where an unparented entity sits on the field at a planar point: the zone's height there plus the hull's grid offset.
// The step rides at its own time and xz, the presenter at the drawn time and its drawn xz (DrawAhead).
public float RideHeight(float2 planar, double time) => Zone.GetHeight(planar, time) + (ItemManager.GetData(Hull) as HullData).GridOffset;
// The height above the field of the layer this kind of entity rides on (GameplaySettings): ships the ship plane,
// orbital entities the orbital layer above it.
public abstract float LayerHeight { get; }
// Where an unparented entity sits on the field at a planar point: the zone's height there plus the layer this kind of
// entity rides on. The step rides at its own time and xz, the presenter at the drawn time and its drawn xz (DrawAhead).
public float RideHeight(float2 planar, double time) => Zone.GetHeight(planar, time) + LayerHeight;
public float RideHeight(float2 planar) => RideHeight(planar, Zone.ExactTime);

View file

@ -519,8 +519,7 @@ public class HullData : EquippableItemData
[Inspectable, JsonProperty("hullType"), Key(25)]
public HullType HullType;
[Inspectable, JsonProperty("gridOffset"), Key(26)]
public float GridOffset;
// Key(26) retired 2026-10-16 (per-hull GridOffset; the layer is the kind's, GameplaySettings). Do not reuse.
[Inspectable, JsonProperty("armor"), Key(27)]
public float Armor;

View file

@ -23,6 +23,8 @@ public class OrbitalEntity : Entity
OrbitData = orbit;
}
public override float LayerHeight => ItemManager.GameplaySettings.OrbitalLayerHeight;
public override void Update(float delta)
{
if (OrbitData.IsSet())

View file

@ -278,6 +278,13 @@ public class GameplaySettings
// normal is this times the speed at which it closed. 0 is perfectly inelastic, 1 perfectly elastic.
public float ContactRestitution = .5f;
// Heights above the field of the ship plane, the orbital layer (stations, turrets: every OrbitalEntity) and the floater
// layer (loot, mines): orbitals above ships and floaters below them, so no ship ever touches either (ruling
// aetheria-gravity:orbitals-above-floaters-below).
public float ShipLayerHeight = 3f;
public float OrbitalLayerHeight = 15f;
public float FloaterLayerHeight = 0f;
// The floor an AI's own predicted hit probability (FireControl.HitProbability) must clear before it counts
// a firing solution worth taking (Combat.cs, TurretController.cs). Player fire is gated on arc alone
// (Weapon.ArcAllowsFire, Q2) -- this threshold is an AI fire-discipline heuristic, not part of the roll.

View file

@ -16,6 +16,8 @@ using quaternion = CultMath.quaternion;
[MessagePackObject]
public class Ship : Entity
{
public override float LayerHeight => ItemManager.GameplaySettings.ShipLayerHeight;
// [Key("bindings")] public Dictionary<KeyCode,Guid> Bindings = new Dictionary<KeyCode,Guid>();
//[IgnoreMember] public int HullHardpointCount;