LockOn: the hostile is ordered to close on the player; the warning is measured against the poses it was stamped from

ScenarioStage.Approach(entity, target) states a piloted entity's task as a follow task on the target, passed through Zone.Admit to the flight as the member's baseline task; it replaces Patrol(entity, orbit) and the orbit index, and RunStart's post-admit circuit overwrite. LockOn stages at .6, the quality Crossing and the other RunStart scenarios use (its brief states none), not .8 picked by outcome.

The bearing check keeps well-law r4's intent (measure against the hostile's actual pose, not the staged line) but reads the poses of the tick before the paint was first seen, which is the tick it was stamped in, with a .05 deg limit for float error; the live-pose check at .5 deg spent its budget on the paint inbox's deliberate one-tick delay. LockOnHoldsUnderOnePercentOfEachStagedInput moves hostile x, y and quality by 1% each way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-18 00:51:33 +02:00
commit 14d3327c19
6 changed files with 100 additions and 49 deletions

View file

@ -279,13 +279,14 @@ public class Zone
// turret all join through here. A piloted entity gets the zone's Minion; an unpiloted one gets no agent at all.
// Only a ship can be piloted. The death watch is installed here, for every join, whichever caller admits it.
// An entity that is already a member is refused: it would hold a second watch and a second agent.
public void Admit(Entity entity, bool piloted)
// A piloted entity starts on the zone's drawn patrol circuit unless the caller states its task (a scenario's approach).
public void Admit(Entity entity, bool piloted, AgentTask task = null)
{
if (_entities.Contains(entity)) throw new InvalidOperationException("entity: already a member of this zone");
_entities.Add(entity);
_deathWatches.Add(entity, entity.Death.Subscribe(_ => { DropLoot(entity); Remove(entity); entity.Deactivate(); }));
entity.Activate();
if (piloted) Agents.Add(CreateAgent((Ship) entity));
if (piloted) Agents.Add(CreateAgent((Ship) entity, task));
}
// The one removal from this zone (death, dock, jump): membership and its death watch leave together. False, and
@ -300,20 +301,23 @@ public class Zone
return true;
}
private Agent CreateAgent(Ship ship)
private Agent CreateAgent(Ship ship, AgentTask stated)
{
var key = ship.Faction ?? Unaffiliated;
if (!_flights.TryGetValue(key, out var flight))
_flights[key] = flight = new Flight(ship.Faction?.Doctrine, _itemManager.GameplaySettings);
var id = NextAgentId();
var agent = new Minion(ship, flight.Combatant) { Id = id };
var task = new PatrolOrbitsTask();
var dice = SimulationDice.For(CombatSeed, SimulationDice.PatrolStream, id);
task.Circuit = Orbits.OrderBy(_ => dice.NextFloat()).Take(4).Select(x => x.Key).ToArray();
flight.Join(agent, task);
flight.Join(agent, stated ?? PatrolCircuit(id));
return agent;
}
private PatrolOrbitsTask PatrolCircuit(uint id)
{
var dice = SimulationDice.For(CombatSeed, SimulationDice.PatrolStream, id);
return new PatrolOrbitsTask { Circuit = Orbits.OrderBy(_ => dice.NextFloat()).Take(4).Select(x => x.Key).ToArray() };
}
public ZonePack PackZone()
{
return new ZonePack