425 lines
26 KiB
C
425 lines
26 KiB
C
#ifdef WORKBENCH
|
|
// Separate opt-in experimental100m field fixture; original aircraft outcomes unchanged.
|
|
// Full terrain/clearance refusal is retained; no operational dispatch is installed.
|
|
modded class ERR_AircraftFieldFlight
|
|
{
|
|
float ERR_NativeFieldStageDeadline() { return m_StageDeadline; }
|
|
int ERR_NativeFieldActionUpdates() { return m_ActionUpdates; }
|
|
int ERR_NativeFieldHelicopterUpdates() { return m_HelicopterUpdates; }
|
|
bool ERR_NativeFieldFreshReadback(float now) { return m_Readback && m_LastControl >= 0 && now >= m_LastControl && now - m_LastControl <= 1000; }
|
|
vector ERR_NativeFieldInputDemand()
|
|
{
|
|
vector result;
|
|
if (m_Actions) result = Vector(m_Actions.GetActionValue("HelicopterCyclicRight") - m_Actions.GetActionValue("HelicopterCyclicLeft"), 0, m_Actions.GetActionValue("HelicopterCyclicForward") - m_Actions.GetActionValue("HelicopterCyclicBack"));
|
|
return result;
|
|
}
|
|
protected override bool BeginFieldBrake(float now)
|
|
{
|
|
bool alreadyFrozen = m_FieldStopFrozen;
|
|
bool result = super.BeginFieldBrake(now);
|
|
ERR_NativeAircraftFieldProbe probe = ERR_NativeAircraftFieldProbe.Current();
|
|
if (result && !alreadyFrozen && probe && m_Aircraft && m_Simulation) probe.BrakeBegan(this, now, m_Aircraft.GetOrigin(), m_Simulation.GetVelocity());
|
|
return result;
|
|
}
|
|
protected override void CheckTranslation(float now, vector position, float height, float speed, float upright)
|
|
{
|
|
int oldPhase = m_FieldPhase;
|
|
super.CheckTranslation(now, position, height, speed, upright);
|
|
ERR_NativeAircraftFieldProbe probe = ERR_NativeAircraftFieldProbe.Current();
|
|
if (oldPhase == 2 && m_FieldPhase == 3 && probe && m_Aircraft && m_Simulation) probe.BrakeStopped(this, now, m_Aircraft.GetOrigin(), m_Simulation.GetVelocity());
|
|
}
|
|
|
|
}
|
|
// Read-only fixture access to the service's exact retained controller.
|
|
modded class ERR_AircraftService
|
|
{
|
|
ERR_AircraftFieldFlight ERR_NativeOriginalFieldFlight(Vehicle aircraft)
|
|
{
|
|
ERR_AircraftFieldFlight result;
|
|
ERR_AircraftServiceEntry entry = Find(aircraft);
|
|
if (entry) result = ERR_AircraftFieldFlight.Cast(entry.Flight);
|
|
return result;
|
|
}
|
|
}
|
|
modded class SCR_CharacterControllerComponent
|
|
{
|
|
override void OnPrepareControls(IEntity owner, ActionManager am, float dt, bool player)
|
|
{
|
|
ERR_NativeAircraftFieldProbe probe = ERR_NativeAircraftFieldProbe.Current();
|
|
if (probe && !player) probe.PrepareActions(this, owner, am, dt);
|
|
super.OnPrepareControls(owner, am, dt, player);
|
|
}
|
|
}
|
|
modded class SCR_HelicopterControllerComponent
|
|
{
|
|
protected override event void OnPrepareControls()
|
|
{
|
|
super.OnPrepareControls();
|
|
ERR_NativeAircraftFieldProbe probe = ERR_NativeAircraftFieldProbe.Current();
|
|
if (probe) probe.PrepareHelicopter(this);
|
|
}
|
|
}
|
|
class ERR_NativeAircraftFieldRules
|
|
{
|
|
static bool Cruise(float alongSpeed, float horizontal, bool contact, int phase)
|
|
{
|
|
return phase == 1 && !contact && alongSpeed >= 1 && horizontal >= 1 && horizontal <= 2.5;
|
|
}
|
|
static bool Braking(float reserve, float sampledTravel, float displacement, float lowSince, float now, float horizontal)
|
|
{
|
|
return reserve >= 3 && reserve <= 26.334 && sampledTravel >= 0 && sampledTravel <= reserve && displacement >= 0 && displacement <= reserve && horizontal >= 0 && horizontal < 0.25 && lowSince >= 0 && now - lowSince >= 2000;
|
|
}
|
|
static bool Terminal(bool cancellation, int state, int stops, bool arrived, bool cruiseHeld, bool endpointHeld, bool braking, float displacement, float targetError)
|
|
{
|
|
if (!braking || !(targetError >= 0 && targetError <= 0.75)) return false;
|
|
if (cancellation) return state == ERR_AircraftFlightState.CANCELLED && stops == 1 && !arrived;
|
|
return state == ERR_AircraftFlightState.COMPLETE && stops == 0 && arrived && cruiseHeld && endpointHeld && displacement >= 99.25 && displacement <= 100.8;
|
|
}
|
|
static bool PhysicalFinish(bool originalOccupancy, bool landed, bool engineOff, float fuelUsed, int wakes, int starts, int controllerStarts, int pilotHooks, int helicopterHooks)
|
|
{
|
|
return originalOccupancy && landed && engineOff && fuelUsed > 0.05 && fuelUsed < 1000000 && wakes == 1 && starts == 1 && controllerStarts == 1 && pilotHooks > 100 && helicopterHooks > 100;
|
|
}
|
|
static bool CanDispose(bool context, bool noPlayers, bool sameFaction, bool groupUnchanged, bool pilotUnchanged)
|
|
{
|
|
return context && noPlayers && sameFaction && groupUnchanged && pilotUnchanged;
|
|
}
|
|
}
|
|
class ERR_NativeAircraftFieldProbe
|
|
{
|
|
protected static ERR_NativeAircraftFieldProbe s_Current;
|
|
protected ERR_GameMode m_Mode;
|
|
protected Vehicle m_Aircraft;
|
|
protected SCR_AIGroup m_Group;
|
|
protected IEntity m_Pilot;
|
|
protected PilotCompartmentSlot m_Seat;
|
|
protected HelicopterControllerComponent m_Controller;
|
|
protected VehicleHelicopterSimulation m_Simulation;
|
|
protected FuelManagerComponent m_Fuel;
|
|
protected ref ERR_AircraftFieldEnvelope m_Envelope;
|
|
protected ref ERR_AircraftFieldFlight m_Flight;
|
|
protected ref ERR_AircraftService m_Service;
|
|
protected float m_Deadline;
|
|
protected float m_StageDeadline;
|
|
protected float m_Stable = -1;
|
|
protected float m_LastTick = -1;
|
|
protected float m_NextLog;
|
|
protected float m_InitialFuel;
|
|
protected float m_ControlDeadline;
|
|
protected int m_CancelCount;
|
|
protected bool m_CancelFixture;
|
|
protected float m_CancelAt = -1;
|
|
protected float m_LandingStable = -1;
|
|
protected int m_CancelActionUpdates;
|
|
protected int m_CancelHelicopterUpdates;
|
|
protected bool m_ContinuedAirborneControl;
|
|
protected bool m_LandingHeld;
|
|
protected vector m_CancelLandingPoint;
|
|
protected bool m_TranslationHeld;
|
|
protected float m_TranslationStable = -1;
|
|
protected vector m_Pad;
|
|
protected vector m_Destination;
|
|
protected float m_CruiseSince = -1;
|
|
protected bool m_CruiseHeld;
|
|
protected bool m_BrakeObserved;
|
|
protected bool m_BrakeStopped;
|
|
protected bool m_BrakePassed;
|
|
protected string m_ObservationFailure;
|
|
protected vector m_BrakeOrigin;
|
|
protected vector m_BrakeLandingPoint;
|
|
protected vector m_BrakePrevious;
|
|
protected float m_BrakeReserve;
|
|
protected float m_BrakeSampledTravel;
|
|
protected float m_BrakeLowSince = -1;
|
|
protected float m_BrakeAt;
|
|
protected float m_BrakeStageDeadline;
|
|
protected int m_Step;
|
|
protected int m_Wakes;
|
|
protected int m_ControllerStarts;
|
|
protected int m_EngineStarts;
|
|
protected int m_PilotHooks;
|
|
protected int m_HelicopterHooks;
|
|
protected bool m_LastEngine;
|
|
protected bool m_AllocationRequested;
|
|
protected bool m_PilotRequested;
|
|
protected bool m_Stopped;
|
|
protected bool m_Complete;
|
|
static const int ORDER_TOKEN = 771101;
|
|
static const ResourceName AIRCRAFT = "{DF5CCB7C0FF049F4}Prefabs/Vehicles/Helicopters/Mi8MT/Mi8MT_unarmed_transport.et";
|
|
static const ResourceName GROUP = "{000CD338713F2B5A}Prefabs/AI/Groups/Group_Base.et";
|
|
static const ResourceName PILOT = "{86E565387A634103}Prefabs/Characters/Campaign/Final/OPFOR/USSR_Army/Regular/Campaign_USSR_Player_MortarCrew.et";
|
|
static bool Enabled()
|
|
{
|
|
string enabled, outcome;
|
|
return Replication.IsServer() && System.GetCLIParam("errSmoke", enabled) && enabled == "1" && System.GetCLIParam("errSmokeOutcome", outcome) && outcome == "aircraft-field";
|
|
}
|
|
static ERR_NativeAircraftFieldProbe Current() { return s_Current; }
|
|
void ERR_NativeAircraftFieldProbe(ERR_GameMode mode)
|
|
{
|
|
m_Mode = mode;
|
|
if (!GetGame() || !GetGame().GetWorld() || !Enabled() || s_Current) { m_Stopped = true; return; }
|
|
string cancellation; m_CancelFixture = System.GetCLIParam("errSmokeFieldServiceCancel", cancellation) && cancellation == "1";
|
|
m_Deadline = GetGame().GetWorld().GetWorldTime() + 300000;
|
|
s_Current = this;
|
|
}
|
|
protected bool Check(bool passed, string detail)
|
|
{
|
|
string outcome = "aircraft-field";
|
|
if (passed) { Print("ERR SMOKE PASS: " + outcome + " " + detail); return true; }
|
|
Print("ERR SMOKE FAIL: " + outcome + " " + detail, LogLevel.ERROR); Stop(); return false;
|
|
}
|
|
protected bool NoPlayers()
|
|
{
|
|
if (!GetGame() || !GetGame().GetPlayerManager()) return false;
|
|
if (m_Group && m_Group.GetPlayerCount(true) > 0) return false;
|
|
array<int> players = {}; GetGame().GetPlayerManager().GetPlayers(players);
|
|
foreach (int id : players)
|
|
{
|
|
IEntity actor = GetGame().GetPlayerManager().GetPlayerControlledEntity(id);
|
|
if (actor && (actor == m_Pilot || actor == m_Aircraft || (m_Aircraft && CompartmentAccessComponent.GetVehicleIn(actor) == m_Aircraft))) return false;
|
|
}
|
|
return true;
|
|
}
|
|
protected bool OriginalOccupancy()
|
|
{
|
|
if (!m_Pilot || !m_Group || !m_Aircraft || !m_Controller || !m_Seat || !NoPlayers()) return false;
|
|
if (m_Group.GetAgentsCount() != 1 || ERR_Actors.Group(m_Pilot) != m_Group || !ERR_Actors.CanFight(m_Pilot)) return false;
|
|
return m_Seat.GetOccupant() == m_Pilot && m_Controller.GetPilotCompartmentSlot() == m_Seat && CompartmentAccessComponent.GetVehicleIn(m_Pilot) == m_Aircraft;
|
|
}
|
|
void PrepareActions(CharacterControllerComponent controller, IEntity owner, ActionManager am, float dt)
|
|
{
|
|
if (m_Stopped || !m_Flight || owner != m_Pilot) return;
|
|
// Observation only: the production bridge owns the sole control dispatch.
|
|
m_PilotHooks++;
|
|
}
|
|
void PrepareHelicopter(HelicopterControllerComponent controller)
|
|
{
|
|
if (m_Stopped || !m_Flight || controller != m_Controller) return;
|
|
// Observation only: super-chain order cannot cause a second control write.
|
|
m_HelicopterHooks++;
|
|
}
|
|
protected bool FindPad(out vector result)
|
|
{
|
|
// This exact airport start was observed in the twenty-metre fixture. The
|
|
// longer corridor still requires its own complete survey; there is no search.
|
|
vector candidate = ERR_Actors.Ground("4850.953 0 11787.412");
|
|
vector destination = ERR_Actors.Ground(candidate + "100 0 0");
|
|
if (!ERR_AircraftFieldEnvelope.Survey(candidate, destination)) return false;
|
|
PrintFormat("ERR AIRCRAFT FIELD FIXTURE CORRIDOR: singleStart=%1 destination=%2; fixed100m complete survey passed", candidate, destination);
|
|
result = candidate; m_Destination = destination; return true;
|
|
}
|
|
protected bool ReadyComponents()
|
|
{
|
|
m_Controller = HelicopterControllerComponent.Cast(m_Aircraft.FindComponent(HelicopterControllerComponent));
|
|
if (!m_Controller) return false;
|
|
m_Simulation = m_Controller.GetSimulation();
|
|
m_Fuel = FuelManagerComponent.Cast(m_Aircraft.FindComponent(FuelManagerComponent));
|
|
if (!m_Simulation || !m_Simulation.IsValid() || !m_Fuel || !m_Fuel.HasFuel()) return false;
|
|
BaseCompartmentManagerComponent manager = BaseCompartmentManagerComponent.Cast(m_Aircraft.FindComponent(BaseCompartmentManagerComponent));
|
|
if (!manager) return false;
|
|
array<BaseCompartmentSlot> slots = {}; manager.GetCompartments(slots);
|
|
if (slots.Count() > 64) return false;
|
|
foreach (BaseCompartmentSlot slot : slots)
|
|
{
|
|
PilotCompartmentSlot pilotSlot = PilotCompartmentSlot.Cast(slot);
|
|
if (!pilotSlot || pilotSlot.GetOwner() != m_Aircraft || pilotSlot.GetVehicle() != m_Aircraft || pilotSlot.GetCompartmentName() != "PilotCompartment") continue;
|
|
if (pilotSlot.GetType() != ECompartmentType.PILOT) continue;
|
|
if (pilotSlot.GetOccupant() || pilotSlot.IsReserved() || pilotSlot.IsGetInLocked() || !pilotSlot.IsCompartmentAccessible()) continue;
|
|
m_Seat = pilotSlot; return true;
|
|
}
|
|
return false;
|
|
}
|
|
protected bool RetainedIdentity()
|
|
{
|
|
return m_Service && m_Flight && m_Service.ERR_NativeOriginalFieldFlight(m_Aircraft) == m_Flight && m_Service.RegisteredCount() == 1 && m_Service.AttemptedOrders() == 1 && m_Service.OrderToken(m_Aircraft) == ORDER_TOKEN && m_Service.OrderDeadline(m_Aircraft) == m_ControlDeadline;
|
|
}
|
|
protected bool StartRetainedField()
|
|
{
|
|
if (m_Service || m_Flight) return Check(false, "one retained aircraft service only");
|
|
m_Service = new ERR_AircraftService(m_Mode);
|
|
if (!Check(m_Service.Register(m_Aircraft, m_Group, m_Pilot, m_Seat) && m_Service.RegisteredCount() == 1 && m_Service.Ready(m_Aircraft), "retention service registers one exact physical aircraft, original pilot/group/seat and current readiness")) return false;
|
|
m_ControllerStarts++;
|
|
bool started = m_Service.StartField(m_Aircraft, m_Envelope, ORDER_TOKEN, m_ControlDeadline);
|
|
m_Flight = m_Service.ERR_NativeOriginalFieldFlight(m_Aircraft);
|
|
return Check(started && RetainedIdentity() && m_Service.Pending(m_Aircraft), "actual controller accepts one qualified original pilot/aircraft order through retention service");
|
|
}
|
|
protected bool StopRetainedField(float now)
|
|
{
|
|
if (m_CancelCount != 0 || !RetainedIdentity() || m_Service.Stopping()) return Check(false, "one explicit retained field service stop only");
|
|
m_CancelCount = 1; m_CancelAt = now;
|
|
m_CancelActionUpdates = m_Flight.ERR_NativeFieldActionUpdates();
|
|
m_CancelHelicopterUpdates = m_Flight.ERR_NativeFieldHelicopterUpdates();
|
|
m_Service.Stop(); m_CancelLandingPoint = m_Flight.LandingPoint();
|
|
return Check(RetainedIdentity() && m_Service.Stopping() && m_Service.Pending(m_Aircraft) && !m_Service.Drained() && !m_Service.Ready(m_Aircraft) && m_Flight.State() == ERR_AircraftFlightState.TRANSLATING && m_Flight.FieldPhase() == 2 && m_Flight.ERR_NativeControlDeadline() == m_ControlDeadline && m_Envelope.Contains(m_CancelLandingPoint, 1), "service Stop retains airborne braking, original controller and frozen qualified point under same lease");
|
|
}
|
|
protected void Diagnostic(float now)
|
|
{
|
|
if (!m_Flight || !m_Aircraft || !m_Simulation || !m_Envelope || now < m_NextLog) return;
|
|
m_NextLog = now + 1000;
|
|
vector position = m_Aircraft.GetOrigin(); vector velocity = m_Simulation.GetVelocity();
|
|
PrintFormat("ERR AIRCRAFT FIELD MOTION: now=%1 state=%2 phase=%3 position=%4 velocity=%5 refVelocity=%6 integral=%7 observedLocalInput=%8 fuel=%9", now, m_Flight.State(), m_Flight.FieldPhase(), position, velocity, m_Flight.ReferenceVelocity(), m_Flight.IntegralDemand(), m_Flight.ERR_NativeFieldInputDemand(), m_Fuel.GetTotalFuel());
|
|
PrintFormat("ERR AIRCRAFT FIELD LEASE: token=%1 originalDeadline=%2 stageDeadline=%3 actions=%4 helicopter=%5 contact=%6 engine=%7 targetError=%8", m_Flight.OrderToken(), m_ControlDeadline, m_Flight.ERR_NativeFieldStageDeadline(), m_Flight.ERR_NativeFieldActionUpdates(), m_Flight.ERR_NativeFieldHelicopterUpdates(), m_Simulation.HasAnyGroundContact(), m_Controller.IsEngineOn(), vector.DistanceXZ(position, m_Flight.LandingPoint()));
|
|
}
|
|
void BrakeBegan(ERR_AircraftFieldFlight flight, float now, vector position, vector velocity)
|
|
{
|
|
if (m_Stopped || flight != m_Flight) return;
|
|
if (m_BrakeObserved) { m_ObservationFailure = "production brake event occurs exactly once"; return; }
|
|
m_BrakeObserved = true; m_BrakeAt = now;
|
|
m_BrakeOrigin = position; m_BrakePrevious = position;
|
|
m_BrakeReserve = ERR_AircraftFieldRules.BrakingReserve(ERR_AircraftFieldRules.HorizontalSpeed(velocity));
|
|
m_BrakeStageDeadline = flight.ERR_NativeFieldStageDeadline(); m_BrakeLandingPoint = flight.LandingPoint();
|
|
PrintFormat("ERR AIRCRAFT FIELD BRAKE OBSERVED: now=%1 position=%2 velocity=%3 declaredHypothesisReserve=%4 stageDeadline=%5 originalDeadline=%6", now, position, velocity, m_BrakeReserve, m_BrakeStageDeadline, m_ControlDeadline);
|
|
}
|
|
void BrakeStopped(ERR_AircraftFieldFlight flight, float now, vector position, vector velocity)
|
|
{
|
|
if (m_Stopped || flight != m_Flight) return;
|
|
if (!m_BrakeObserved || m_BrakeStopped) { m_ObservationFailure = "one actual low-speed braking transition after original brake event"; return; }
|
|
m_BrakeStopped = true;
|
|
m_BrakeSampledTravel += vector.DistanceXZ(position, m_BrakePrevious);
|
|
m_BrakePrevious = position;
|
|
float displacement = vector.DistanceXZ(position, m_BrakeOrigin);
|
|
m_BrakePassed = ERR_NativeAircraftFieldRules.Braking(m_BrakeReserve, m_BrakeSampledTravel, displacement, m_BrakeLowSince, now, ERR_AircraftFieldRules.HorizontalSpeed(velocity));
|
|
PrintFormat("ERR AIRCRAFT FIELD STOP OBSERVED: now=%1 position=%2 sampledTravel=%3 displacement=%4 declaredReserve=%5 lowSince=%6 passed=%7", now, position, m_BrakeSampledTravel, displacement, m_BrakeReserve, m_BrakeLowSince, m_BrakePassed);
|
|
if (!m_BrakePassed) m_ObservationFailure = "observed braking exceeded declared reserve or lacked independent two-second low-speed observations";
|
|
}
|
|
int Tick(float now)
|
|
{
|
|
if (m_Complete) return 1;
|
|
if (m_Stopped) return -1;
|
|
if (!Enabled() || !m_Mode || m_Mode != ERR_GameMode.Get() || !m_Mode.Active() || now >= m_Deadline) { Check(false, "active exact mission and bounded300-second fixture"); return -1; }
|
|
if (!NoPlayers()) { Check(false, "fixture remains unclaimed by any human"); return -1; }
|
|
if (m_LastTick >= 0 && (now < m_LastTick || now - m_LastTick > 1500)) { m_Stable = -1; m_TranslationStable = -1; m_LandingStable = -1; m_CruiseSince = -1; m_BrakeLowSince = -1; }
|
|
m_LastTick = now;
|
|
if (m_Step == 0)
|
|
{
|
|
if (!m_Mode.ExtractionVehicle()) return 0;
|
|
if (!Check(!m_AllocationRequested && FindPad(m_Pad), "independent qualified candidate found before one finite allocation")) return -1;
|
|
m_AllocationRequested = true;
|
|
m_Aircraft = Vehicle.Cast(ERR_Actors.Spawn(AIRCRAFT, m_Pad));
|
|
m_Group = SCR_AIGroup.Cast(ERR_Actors.Spawn(GROUP, m_Pad + "20 0 0"));
|
|
if (!Check(m_Aircraft && m_Group && m_Group.InitFactionKey("USSR") && m_Group.GetFaction() == m_Mode.EnemyFaction(), "one finite native Mi8 and memberless enemy group created")) return -1;
|
|
m_Group.SetPermanentLOD(0); m_Group.ActivateAI();
|
|
m_Step = 1; m_StageDeadline = now + 30000; return 0;
|
|
}
|
|
if (!m_Aircraft || !m_Group || !ERR_Actors.Alive(m_Aircraft)) { Check(false, "original airframe and group survive without replacement"); return -1; }
|
|
if (m_Step == 1)
|
|
{
|
|
if (now >= m_StageDeadline) { Check(false, "real components and original pilot initialize within30seconds"); return -1; }
|
|
if (!ReadyComponents()) return 0;
|
|
if (!Check(!m_PilotRequested && m_Group.GetAgentsCount() == 0, "one original native pilot request only")) return -1;
|
|
m_PilotRequested = true; AIGroup originalGroup = m_Group;
|
|
m_Pilot = m_Seat.SpawnCharacterInCompartment(PILOT, originalGroup, GROUP);
|
|
AIControlComponent control; if (m_Pilot) control = AIControlComponent.Cast(m_Pilot.FindComponent(AIControlComponent));
|
|
AIAgent agent; if (control) agent = control.GetControlAIAgent();
|
|
if (!Check(m_Pilot && originalGroup == m_Group && agent, "pilot seated by native mechanism in original group")) return -1;
|
|
agent.SetPermanentLOD(0); agent.ActivateAI(); m_Step = 2; return 0;
|
|
}
|
|
Diagnostic(now);
|
|
if (m_Step == 2)
|
|
{
|
|
if (now >= m_StageDeadline) { Check(false, "actual initial seating and native ground settling within30seconds"); return -1; }
|
|
if (!OriginalOccupancy()) { m_Stable = -1; return 0; }
|
|
Physics physics = m_Aircraft.GetPhysics();
|
|
if (!physics || !physics.IsDynamic()) return 0;
|
|
if (m_Wakes == 0) { m_Wakes = 1; physics.SetActive(ActiveState.ACTIVE); m_Stable = -1; return 0; }
|
|
if (!m_Simulation.HasAnyGroundContact() || !ERR_CasualtyLoadingRules.AircraftReady(m_Aircraft)) { m_Stable = -1; return 0; }
|
|
if (m_Stable < 0) m_Stable = now;
|
|
if (now - m_Stable < 1000) return 0;
|
|
if (!Check(!m_Controller.IsEngineOn(), "post-spawn qualification precedes native engine start")) return -1;
|
|
m_Envelope = ERR_AircraftFieldEnvelope.QualifyField(m_Mode, m_Aircraft, m_Pilot, m_Destination);
|
|
if (!Check(m_Envelope != null, "actual full corridor, both pads and complete native rotor/model footprint issue exact route qualification")) return -1;
|
|
m_InitialFuel = m_Fuel.GetTotalFuel();
|
|
m_ControlDeadline = Math.Min(m_Deadline, now + 240000);
|
|
if (!StartRetainedField()) return -1;
|
|
m_Step = 3; return 0;
|
|
}
|
|
if (m_Step == 3)
|
|
{
|
|
if (!RetainedIdentity() || !OriginalOccupancy()) { Check(false, "service and exact original airframe/pilot/group/seat retained"); return -1; }
|
|
if (m_Flight.ERR_NativeControlDeadline() != m_ControlDeadline || m_Flight.Destination() != m_Envelope.Destination()) { Check(false, "original endpoint/token/deadline never change"); return -1; }
|
|
if (m_BrakeObserved)
|
|
{
|
|
if (m_Flight.LandingPoint() != m_BrakeLandingPoint) { Check(false, "first production braking point remains immutable"); return -1; }
|
|
if (m_Flight.State() == ERR_AircraftFlightState.TRANSLATING && m_Flight.ERR_NativeFieldStageDeadline() != m_BrakeStageDeadline) { Check(false, "braking and settling never renew their stage deadline"); return -1; }
|
|
}
|
|
bool engine = m_Controller.IsEngineOn(); if (engine && !m_LastEngine) m_EngineStarts++; m_LastEngine = engine;
|
|
vector transform[4]; m_Aircraft.GetWorldTransform(transform);
|
|
vector position = transform[3]; vector velocity = m_Simulation.GetVelocity();
|
|
vector home = m_Envelope.Home(); vector leg = m_Envelope.Destination() - home; leg[1] = 0;
|
|
float alongSpeed = vector.Dot(velocity, leg * (1 / leg.Length()));
|
|
float horizontal = ERR_AircraftFieldRules.HorizontalSpeed(velocity);
|
|
bool contact = m_Simulation.HasAnyGroundContact(); float height = position[1] - home[1];
|
|
bool cruise = ERR_NativeAircraftFieldRules.Cruise(alongSpeed, horizontal, contact, m_Flight.FieldPhase());
|
|
if (!cruise) m_CruiseSince = -1;
|
|
else if (m_CruiseSince < 0) m_CruiseSince = now;
|
|
if (m_CruiseSince >= 0 && now - m_CruiseSince >= 10000) m_CruiseHeld = true;
|
|
if (m_CancelFixture && m_CancelCount == 0 && m_CruiseSince >= 0 && now - m_CruiseSince >= 1000)
|
|
if (!StopRetainedField(now)) return -1;
|
|
if (m_BrakeObserved && !m_BrakeStopped)
|
|
{
|
|
m_BrakeSampledTravel += vector.DistanceXZ(position, m_BrakePrevious); m_BrakePrevious = position;
|
|
if (!(horizontal < 0.25)) m_BrakeLowSince = -1;
|
|
else if (m_BrakeLowSince < 0) m_BrakeLowSince = now;
|
|
}
|
|
bool held = !contact && ERR_AircraftFlightRules.Hover(height, velocity.Length(), velocity[1], transform[1][1]) && horizontal < 0.25 && vector.DistanceXZ(position, m_Envelope.Destination()) <= 0.75;
|
|
if (!held) m_TranslationStable = -1;
|
|
else if (m_TranslationStable < 0) m_TranslationStable = now;
|
|
if (m_TranslationStable >= 0 && now - m_TranslationStable >= 5000) m_TranslationHeld = true;
|
|
bool landed = ERR_AircraftFlightRules.Landed(contact, height, velocity.Length(), m_Simulation.GetAngularVelocity().Length(), transform[1][1]) && vector.DistanceXZ(position, m_Flight.LandingPoint()) <= 0.75;
|
|
if (!landed) m_LandingStable = -1;
|
|
else if (m_LandingStable < 0) m_LandingStable = now;
|
|
if (m_CancelCount == 1)
|
|
{
|
|
if (!m_Service.Stopping() || m_Service.Ready(m_Aircraft) || m_Flight.LandingPoint() != m_CancelLandingPoint || !m_Envelope.Contains(m_CancelLandingPoint, 1)) { Check(false, "cancelled order retains frozen qualified point and no new admission"); return -1; }
|
|
if (!m_Flight.Finished() && (!m_Service.Pending(m_Aircraft) || m_Service.Drained())) { Check(false, "nonterminal cancellation stays pending"); return -1; }
|
|
if (!contact && height > 1 && m_Flight.ERR_NativeFieldActionUpdates() > m_CancelActionUpdates + 10 && m_Flight.ERR_NativeFieldHelicopterUpdates() > m_CancelHelicopterUpdates + 10 && m_Flight.ERR_NativeFieldFreshReadback(now)) m_ContinuedAirborneControl = true;
|
|
}
|
|
m_Service.Tick();
|
|
if (!m_ObservationFailure.IsEmpty()) { Check(false, m_ObservationFailure); return -1; } // outside production callback stack
|
|
if (m_Flight.State() == ERR_AircraftFlightState.LANDING && !m_BrakePassed) { Check(false, "no descent before qualified observed braking"); return -1; }
|
|
if (m_Flight.State() == ERR_AircraftFlightState.SHUTDOWN)
|
|
{
|
|
if (!m_LandingHeld && !Check(landed && m_LandingStable >= 0 && now - m_LandingStable >= 2000, "shutdown follows two independently observed stable grounded seconds")) return -1;
|
|
m_LandingHeld = true;
|
|
}
|
|
if (!m_Flight.Finished()) return 0;
|
|
float targetError = vector.DistanceXZ(position, m_Flight.LandingPoint());
|
|
float displacement = vector.DistanceXZ(position, home);
|
|
bool complete = ERR_NativeAircraftFieldRules.Terminal(m_CancelFixture, m_Flight.State(), m_CancelCount, m_Flight.Arrived(), m_CruiseHeld, m_TranslationHeld, m_BrakePassed, displacement, targetError);
|
|
complete = complete && m_LandingHeld && ERR_NativeAircraftFieldRules.PhysicalFinish(OriginalOccupancy(), landed, !m_Controller.IsEngineOn(), m_InitialFuel - m_Fuel.GetTotalFuel(), m_Wakes, m_EngineStarts, m_ControllerStarts, m_PilotHooks, m_HelicopterHooks);
|
|
if (m_CancelFixture) complete = complete && m_ContinuedAirborneControl && m_Service.Stopping();
|
|
PrintFormat("ERR AIRCRAFT FIELD RESULT: state=%1 cancellation=%2 displacement=%3 targetError=%4 cruise10s=%5 endpoint5s=%6 braking=%7 grounded2s=%8 fuelUsed=%9", m_Flight.State(), m_CancelFixture, displacement, targetError, m_CruiseHeld, m_TranslationHeld, m_BrakePassed, m_LandingHeld, m_InitialFuel - m_Fuel.GetTotalFuel());
|
|
if (!Check(complete, "original physical aircraft completes exact field outcome, measured motion/braking, grounded engine-off and finite fuel use")) return -1;
|
|
if (!Check(RetainedIdentity() && m_Service.Drained() && !m_Service.Pending(m_Aircraft) && !m_Service.Retired(m_Aircraft), "one original retained flight drains only after actual terminal observation")) return -1;
|
|
m_Complete = true; Stop(); return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
void Stop()
|
|
{
|
|
if (m_Stopped) return;
|
|
m_Stopped = true;
|
|
if (s_Current == this) s_Current = null;
|
|
if (m_Service && !m_Service.Stopping()) m_Service.Stop();
|
|
// End-of-test disposal is not successful flight or a production cancellation.
|
|
// These assets are fixture-owned only, never registered with mission cleanup.
|
|
// Retain service/controller through guarded disposal; then observe ownership
|
|
// loss before releasing their references. No direct second Cancel or Tick.
|
|
|
|
bool context = GetGame() && GetGame().GetWorld() && GetGame().GetPlayerManager() && m_Mode && m_Mode == ERR_GameMode.Get();
|
|
bool faction = context && (!m_Group || (!m_PilotRequested && m_Group.GetAgentsCount() == 0 && !m_Group.GetFaction()) || m_Group.GetFaction() == m_Mode.EnemyFaction()) && (!m_Aircraft || SCR_FactionManager.SGetFaction(m_Aircraft) == m_Mode.EnemyFaction());
|
|
bool groupUnchanged = context && (!m_Group || m_Group.GetAgentsCount() == 0 || (m_Group.GetAgentsCount() == 1 && m_Pilot && ERR_Actors.Group(m_Pilot) == m_Group));
|
|
bool pilotUnchanged = context && (!m_Pilot || (m_Group && ERR_Actors.Group(m_Pilot) == m_Group && !ERR_Actors.Player(m_Pilot)));
|
|
if (ERR_NativeAircraftFieldRules.CanDispose(context, NoPlayers(), faction, groupUnchanged, pilotUnchanged))
|
|
{
|
|
ERR_Actors.DeleteSpawned(m_Group); ERR_Actors.DeleteSpawned(m_Pilot); ERR_Actors.DeleteSpawned(m_Aircraft);
|
|
}
|
|
else Print("ERR AIRCRAFT FIELD FIXTURE CLEANUP: ownership changed; captured entities preserved");
|
|
if (m_Service) m_Service.Tick();
|
|
m_Flight = null;
|
|
m_Service = null;
|
|
m_Envelope = null;
|
|
}
|
|
void ~ERR_NativeAircraftFieldProbe() { Stop(); }
|
|
}
|
|
#endif
|