#ifdef WORKBENCH // Separate opt-in experimental route fixture; original aircraft outcomes unchanged. // Full terrain/clearance refusal is retained; no operational dispatch is installed. modded class ERR_AircraftRouteFlight { float ERR_NativeRouteStageDeadline() { return m_StageDeadline; } } modded class SCR_CharacterControllerComponent { override void OnPrepareControls(IEntity owner, ActionManager am, float dt, bool player) { ERR_NativeAircraftRouteProbe probe = ERR_NativeAircraftRouteProbe.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_NativeAircraftRouteProbe probe = ERR_NativeAircraftRouteProbe.Current(); if (probe) probe.PrepareHelicopter(this); } } class ERR_NativeAircraftRouteRules { static bool Terminal(bool cancellation, int state, int cancellationCount) { if (cancellation) return state == ERR_AircraftFlightState.CANCELLED && cancellationCount == 1; return state == ERR_AircraftFlightState.COMPLETE && cancellationCount == 0; } static bool TranslateProof(bool proven, bool held, float requestedDistance, float displacement, float targetError) { return proven && held && requestedDistance >= 10 && requestedDistance <= 28.05 && displacement >= requestedDistance - 0.75 && targetError >= 0 && targetError <= 0.75; } static bool PhysicalFinish(bool originalOccupancy, bool landed, bool engineOff, float fuelUsed, int wakes, int starts, int controllerStarts, int pilotHooks, int helicopterHooks) { if (!originalOccupancy || !landed || !engineOff) return false; return fuelUsed > 0.05 && 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_NativeAircraftRouteProbe { protected static ERR_NativeAircraftRouteProbe 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_AircraftRouteEnvelope m_Envelope; protected ref ERR_AircraftRouteFlight m_Flight; 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 bool m_TranslationFixture; protected bool m_TranslationObserved; protected bool m_TranslationHeld; protected vector m_TranslationOrigin; protected vector m_TranslationTarget; protected float m_TranslationStable = -1; protected vector m_Pad; protected vector m_Destination; 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 = 771001; 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-route"; } static ERR_NativeAircraftRouteProbe Current() { return s_Current; } void ERR_NativeAircraftRouteProbe(ERR_GameMode mode) { m_Mode = mode; if (!GetGame() || !GetGame().GetWorld() || !Enabled() || s_Current) { m_Stopped = true; return; } string outcome; System.GetCLIParam("errSmokeOutcome", outcome); m_CancelFixture = false; m_TranslationFixture = true; m_Deadline = GetGame().GetWorld().GetWorldTime() + 300000; s_Current = this; } protected bool Check(bool passed, string detail) { string outcome = "aircraft-route"; 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 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; m_Flight.PrepareActions(controller, owner, am, dt); m_PilotHooks++; } void PrepareHelicopter(HelicopterControllerComponent controller) { if (m_Stopped || !m_Flight || controller != m_Controller) return; m_Flight.PrepareHelicopter(controller); m_HelicopterHooks++; } protected bool FindPad(out vector result) { vector anchor = m_Mode.ExtractionVehicle().GetOrigin(); float bestSlope = float.MAX; vector bestCandidate, refinementAnchor; int localCandidates = ERR_PoweredFlightRules.PAD_CANDIDATES + ERR_PoweredFlightRules.PAD_REFINEMENTS; for (int index = 0; index < localCandidates + ERR_PoweredFlightRules.AIRPORT_CANDIDATES; index++) { if (index == ERR_PoweredFlightRules.PAD_CANDIDATES) { refinementAnchor = anchor; if (bestSlope != float.MAX) refinementAnchor = bestCandidate; } vector candidate; if (index < ERR_PoweredFlightRules.PAD_CANDIDATES) candidate = ERR_Actors.Ground(anchor + ERR_PoweredFlightRules.PadOffset(index)); else if (index < localCandidates) candidate = ERR_Actors.Ground(refinementAnchor + ERR_PoweredFlightRules.RefineOffset(index - ERR_PoweredFlightRules.PAD_CANDIDATES)); else candidate = ERR_Actors.Ground("4900.953 0 11787.412" + ERR_PoweredFlightRules.AirportOffset(index - localCandidates)); if (candidate[1] < 2) continue; float delta; for (int edge = 0; edge < 4; edge++) { float side = edge * Math.PI / 2; vector point = candidate + Vector(Math.Cos(side) * 14, 0, Math.Sin(side) * 14); delta = Math.Max(delta, Math.AbsFloat(SCR_TerrainHelper.GetTerrainY(point) - candidate[1])); } if (delta < bestSlope) { bestSlope = delta; bestCandidate = candidate; } if (delta > 0.8) continue; if (!SCR_WorldTools.TraceCilinderUtil(candidate + "0 2.5 0", 40, 4, TraceFlags.ENTS)) continue; TraceBox clearance = new TraceBox(); clearance.Start = candidate + "0 3 0"; clearance.End = candidate + "0 60 0"; clearance.Mins = "-40 -1 -40"; clearance.Maxs = "40 1 40"; clearance.Flags = TraceFlags.ENTS | TraceFlags.WORLD; if (!(GetGame().GetWorld().TraceMove(clearance, null) >= 1)) continue; PrintFormat("ERR AIRCRAFT ROUTE FIXTURE PAD: candidate=%1 tested=%2 terrainDelta=%3; no fallback", candidate, index + 1, delta); vector destination = ERR_Actors.Ground(candidate + "20 0 0"); if (!ERR_AircraftRouteEnvelope.Survey(candidate, destination)) continue; PrintFormat("ERR AIRCRAFT ROUTE FIXTURE CORRIDOR: acceptedStart=%1 destination=%2 tested=%3; complete survey passed", candidate, destination, index + 1); m_Destination = destination; result = candidate; return true; } return false; } 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 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 void Diagnostic(float now) { if (!m_Aircraft || !m_Controller || !m_Simulation || now < m_NextLog) return; m_NextLog = now + 2000; vector transform[4]; m_Aircraft.GetWorldTransform(transform); vector home = m_Pad; if (m_Envelope) home = m_Envelope.Home(); int state = -1; string reason; if (m_Flight) { state = m_Flight.State(); reason = m_Flight.Reason(); } PrintFormat("ERR AIRCRAFT ROUTE FIXTURE: step=%1 state=%2 height=%3 drift=%4 speed=%5 contact=%6 engine=%7 fuelUsed=%8 reason=%9", m_Step, state, transform[3][1] - home[1], vector.DistanceXZ(transform[3], home), m_Simulation.GetVelocity().Length(), m_Simulation.HasAnyGroundContact(), m_Controller.IsEngineOn(), m_InitialFuel - m_Fuel.GetTotalFuel(), reason); if (!m_Flight || !m_Envelope) return; vector destination = m_Envelope.Destination(); vector leg = destination - home; leg[1] = 0; float length = leg.Length(); if (!(length > 0)) return; vector offset = transform[3] - home; offset[1] = 0; float along = vector.Dot(offset, leg * (1 / length)); float cross = vector.DistanceXZ(transform[3], ERR_AircraftRouteRules.Nearest(home, destination, transform[3])); vector target = destination; if (state == ERR_AircraftFlightState.LANDING || state == ERR_AircraftFlightState.SHUTDOWN) target = m_Flight.LandingPoint(); vector velocity = m_Simulation.GetVelocity(); vector demand; if (state == ERR_AircraftFlightState.TRANSLATING) demand = ERR_AircraftRouteRules.Demand(target, transform[3], velocity); else if (state == ERR_AircraftFlightState.LANDING) demand = ERR_AircraftTranslationRules.Demand(target, transform[3], velocity); float forward = Math.Clamp(vector.Dot(demand, transform[2]), -0.03, 0.03); float aside = Math.Clamp(vector.Dot(demand, transform[0]), -0.03, 0.03); PrintFormat("ERR AIRCRAFT ROUTE MOTION: token=%1 position=%2 velocity=%3 along=%4 cross=%5 targetError=%6 computedDemand=%7 projectedForward=%8 projectedAside=%9", m_Flight.OrderToken(), transform[3], velocity, along, cross, vector.DistanceXZ(transform[3], target), demand, forward, aside); PrintFormat("ERR AIRCRAFT ROUTE BASIS: forward=%1 right=%2 up=%3 target=%4 stageRemaining=%5 overallRemaining=%6 now=%7", transform[2], transform[0], transform[1], target, m_Flight.ERR_NativeRouteStageDeadline() - now, m_Flight.ERR_NativeControlDeadline() - now, now); } 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_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_AircraftRouteEnvelope.QualifyRoute(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_Flight = new ERR_AircraftRouteFlight(); m_ControllerStarts++; m_ControlDeadline = Math.Min(m_Deadline, now + 240000); bool started; started = m_Flight.StartRoute(m_Mode, m_Aircraft, m_Group, m_Pilot, m_Seat, m_Envelope, ORDER_TOKEN, m_ControlDeadline); if (!Check(started, "actual controller accepts one qualified original pilot/aircraft order")) return -1; m_Step = 3; return 0; } if (m_Step == 3) { if (!OriginalOccupancy()) { Check(false, "original pilot remains conscious and in its exact seat/group"); return -1; } bool engine = m_Controller.IsEngineOn(); if (engine && !m_LastEngine) m_EngineStarts++; m_LastEngine = engine; vector transform[4]; m_Aircraft.GetWorldTransform(transform); vector home = m_Envelope.Home(); float height = transform[3][1] - home[1]; bool contact = m_Simulation.HasAnyGroundContact(); bool landed = ERR_AircraftFlightRules.Landed(contact, height, m_Simulation.GetVelocity().Length(), m_Simulation.GetAngularVelocity().Length(), transform[1][1]); if (m_TranslationFixture && m_Flight.State() == ERR_AircraftFlightState.TRANSLATING) { if (!m_TranslationObserved) { m_TranslationObserved = true; m_TranslationOrigin = m_Flight.TranslationOrigin(); m_TranslationTarget = m_Flight.TranslationTarget(); if (!Check(vector.DistanceXZ(m_TranslationTarget, m_Destination) < 0.001 && ERR_AircraftRouteRules.Length(vector.DistanceXZ(home, m_TranslationTarget)), "pre-takeoff route endpoint remains the original qualified destination")) return -1; } if (m_Flight.TranslationTarget() != m_TranslationTarget || m_Flight.TranslationOrigin() != m_TranslationOrigin || m_Flight.ERR_NativeControlDeadline() != m_ControlDeadline) { Check(false, "translation never retargets or renews its original deadline"); return -1; } vector horizontal = m_Simulation.GetVelocity(); horizontal[1] = 0; bool held = ERR_AircraftRouteRules.Held(contact, height, m_Simulation.GetVelocity().Length(), m_Simulation.GetVelocity()[1], transform[1][1], horizontal.Length(), vector.DistanceXZ(transform[3], m_TranslationTarget)); if (!held) m_TranslationStable = -1; else if (m_TranslationStable < 0) m_TranslationStable = now; if (m_TranslationStable >= 0 && now - m_TranslationStable >= 5000) m_TranslationHeld = true; } m_Flight.Tick(); if (!m_Flight.Finished()) return 0; PrintFormat("ERR AIRCRAFT ROUTE FIXTURE RESULT: state=%1 token=%2 engineStarts=%3 controllerStarts=%4 wakes=%5 pilotHooks=%6 helicopterHooks=%7 reason=%8", m_Flight.State(), m_Flight.OrderToken(), m_EngineStarts, m_ControllerStarts, m_Wakes, m_PilotHooks, m_HelicopterHooks, m_Flight.Reason()); bool complete = ERR_NativeAircraftRouteRules.Terminal(m_CancelFixture, m_Flight.State(), m_CancelCount); complete = complete && ERR_NativeAircraftRouteRules.PhysicalFinish(OriginalOccupancy(), landed, !m_Controller.IsEngineOn(), m_InitialFuel - m_Fuel.GetTotalFuel(), m_Wakes, m_EngineStarts, m_ControllerStarts, m_PilotHooks, m_HelicopterHooks); if (m_TranslationFixture) { float requestedDistance = vector.DistanceXZ(m_TranslationOrigin, m_TranslationTarget); float displacement = vector.DistanceXZ(transform[3], m_TranslationOrigin); float targetError = vector.DistanceXZ(transform[3], m_TranslationTarget); bool translated = ERR_NativeAircraftRouteRules.TranslateProof(m_Flight.TranslationProven(), m_TranslationObserved && m_TranslationHeld, requestedDistance, displacement, targetError); PrintFormat("ERR AIRCRAFT ROUTE RESULT: proven=%1 held=%2 requested=%3 displacement=%4 targetError=%5 updates=%6", m_Flight.TranslationProven(), m_TranslationHeld, requestedDistance, displacement, targetError, m_Flight.TranslationUpdates()); complete = complete && translated && m_Flight.Arrived() && vector.DistanceXZ(transform[3], m_Destination) <= 0.75; } if (!Check(complete && m_Flight.OrderToken() == ORDER_TOKEN, "actual control order completes on original physical grounded aircraft with engine off, finite fuel use and exact pilot")) 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_Flight && !m_Flight.Finished() && m_CancelCount == 0) m_Flight.Cancel("native fixture stopped"); // End-of-test disposal is not successful flight or a production cancellation. // These assets are fixture-owned only, never registered with mission cleanup. // Release the controller before deleting disposable unclaimed physical assets. m_Flight = null; 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_NativeAircraftRouteRules.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 ROUTE FIXTURE CLEANUP: ownership changed; captured entities preserved"); m_Envelope = null; } void ~ERR_NativeAircraftRouteProbe() { Stop(); } } #endif