// Experimental immutable 18..20m direct leg. No HQ selection or global dispatch. // Inheritance deliberately shares the proven controller's exclusive airframe // registry, exact native ownership/health, signed-input startup and release paths. class ERR_AircraftRouteFlight : ERR_AircraftFlight { protected ref ERR_AircraftRouteEnvelope m_Route; protected vector m_LandingPoint; protected bool m_RouteArrived; bool Arrived() { return m_RouteArrived; } vector Destination() { vector destination = "0 0 0"; if (m_Route) destination = m_Route.Destination(); return destination; } vector LandingPoint() { return m_LandingPoint; } bool StartRoute(ERR_GameMode mode, Vehicle aircraft, SCR_AIGroup group, IEntity pilot, PilotCompartmentSlot seat, ERR_AircraftRouteEnvelope route, int token, float deadline) { if (m_StartAttempted || m_Route || !route || !ERR_AircraftRouteRules.Length(vector.DistanceXZ(route.Home(), route.Destination()))) return false; m_Route = route; m_TranslationRequested = true; m_TranslationTarget = route.Destination(); vector home = route.Home(); m_TranslationTarget[1] = home[1] + 8; m_LandingPoint = route.Home(); return super.Start(mode, aircraft, group, pilot, seat, route, token, deadline); } // A derived controller cannot be started through the point-only public APIs. override bool Start(ERR_GameMode mode, Vehicle aircraft, SCR_AIGroup group, IEntity pilot, PilotCompartmentSlot seat, ERR_AircraftFlightEnvelope qualification, int token, float deadline) { return false; } override bool StartTranslation(ERR_GameMode mode, Vehicle aircraft, SCR_AIGroup group, IEntity pilot, PilotCompartmentSlot seat, ERR_AircraftFlightEnvelope qualification, int token, float deadline, vector target) { return false; } protected bool RouteContains(vector position, float upright) { if (!m_Route || !m_Route.Contains(position, upright)) return false; // Until the command actually enters translation, retain the original pad cap. if (m_State < ERR_AircraftFlightState.LANDING && !m_TranslationStarted) return vector.DistanceXZ(position, m_Home) < 8; return true; } protected override bool Grounded() { if (!super.Grounded()) return false; if (m_State == ERR_AircraftFlightState.LANDING || m_State == ERR_AircraftFlightState.SHUTDOWN) return vector.DistanceXZ(m_Aircraft.GetOrigin(), m_LandingPoint) <= 0.75; return true; } protected override bool Landing(float now) { if (!Owns() || !m_Route || !m_Route.Contains(m_Aircraft.GetOrigin(), 1)) { Fail("route landing ownership or corridor unavailable"); return false; } // Normal destination is immutable. Cancellation freezes the actual landing // centre once inside the fully qualified corridor, never rebases the route. if (m_Cancelled || !m_RouteArrived) m_LandingPoint = m_Aircraft.GetOrigin(); else m_LandingPoint = m_Route.Destination(); m_LandingPoint[1] = m_Home[1]; return super.Landing(now); } protected override bool BeginTranslation(float now, vector position) { if (!m_Route || !m_TranslationRequested || m_TranslationStarted || m_State != ERR_AircraftFlightState.CLIMB) { Cancel("route stage ownership refused"); return false; } SignalsManagerComponent signals = m_Controller.GetSignalsManager(); if (!signals || signals.FindSignal("MainRotorCyclicForward") < 0 || signals.FindSignal("MainRotorCyclicAside") < 0 || !m_Controller.GetAutohoverEnabled()) { Cancel("native route cyclic signals unavailable"); return false; } m_TranslationOrigin = position; m_TranslationDistance = vector.DistanceXZ(position, m_Route.Destination()); m_TranslationStarted = true; m_State = ERR_AircraftFlightState.TRANSLATING; m_Stable = -1; m_StageDeadline = Math.Min(m_Deadline, now + 160000); PrintFormat("ERR AIRCRAFT ROUTE BEGIN: token=%1 home=%2 destination=%3 actualOrigin=%4 distance=%5 deadline=%6", m_Token, m_Home, m_Route.Destination(), position, m_TranslationDistance, m_StageDeadline); return true; } protected override void CheckTranslation(float now, vector position, float height, float speed, float upright) { if (!m_Route || !m_TranslationStarted || !m_Controller.GetAutohoverEnabled()) { Cancel("route ownership or autohover lost"); return; } vector velocity = m_Simulation.GetVelocity(); vector horizontal = velocity; horizontal[1] = 0; bool held = m_TranslationUpdates >= 3 && ERR_AircraftRouteRules.Held(m_Simulation.HasAnyGroundContact(), height, speed, velocity[1], upright, horizontal.Length(), vector.DistanceXZ(position, m_Route.Destination())); if (!held) { m_Stable = -1; return; } if (m_Stable < 0) m_Stable = now; if (now - m_Stable < 5000) return; m_RouteArrived = true; m_TranslationProven = true; PrintFormat("ERR AIRCRAFT ROUTE HELD: token=%1 targetError=%2 displacement=%3 updates=%4", m_Token, vector.DistanceXZ(position, m_Route.Destination()), vector.DistanceXZ(position, m_TranslationOrigin), m_TranslationUpdates); Landing(now); } protected override bool ControlLease() { if (!Lease()) return false; if (!Owns()) { Relinquish("ownership or world unavailable at control callback"); return false; } float now = Now(); if (now < 0 || now >= m_Deadline || now >= m_StageDeadline) { Fail("control callback reached bounded flight deadline"); return false; } if (!Healthy() || !m_Readback) { Fail("flight capability or input readback lost"); return false; } vector transform[4]; m_Aircraft.GetWorldTransform(transform); if (!RouteContains(transform[3], transform[1][1])) { Fail("control callback exceeded qualified flight envelope"); return false; } return true; } override void PrepareActions(CharacterControllerComponent controller, IEntity owner, ActionManager actions, float dt) { if (!Lease() || controller != m_Character || owner != m_Pilot || actions != m_Actions) return; if (!ControlLease()) return; if (!m_Readback || dt <= 0 || dt > 0.2) return; if (!actions.IsContextActive("HelicopterContext")) actions.ActivateContext("HelicopterContext", 1); if (!actions.IsContextActive("HelicopterContext")) { m_Readback = false; return; } float collective, torque, forward, aside; if (m_State == ERR_AircraftFlightState.POSITIVE) collective = 0.05; else if (m_State == ERR_AircraftFlightState.NEGATIVE) collective = -0.05; else if (m_State == ERR_AircraftFlightState.SHUTDOWN) collective = -1; else if (m_State == ERR_AircraftFlightState.CLIMB || m_State == ERR_AircraftFlightState.LANDING || m_State == ERR_AircraftFlightState.TRANSLATING) { if (!m_Controller.GetAutohoverEnabled()) { Fail("native autohover lost while flying"); return; } float height = m_Aircraft.GetOrigin()[1] - m_Home[1]; float error = ERR_AircraftFlightRules.Vertical(height, m_State == ERR_AircraftFlightState.LANDING) - m_Simulation.GetVelocity()[1]; m_Collective = Math.Clamp(m_Collective + error * dt * 0.12, -0.95, 0.8); collective = Math.Clamp(m_Collective + error * 0.12, -1, 0.9); float headingError = m_Heading - m_Aircraft.GetYawPitchRoll()[0]; while (headingError > 180) headingError -= 360; while (headingError < -180) headingError += 360; torque = Math.Clamp(headingError * 0.01 - m_Simulation.GetAngularVelocityLocal()[1] * 0.3, -0.3, 0.3); if (m_State == ERR_AircraftFlightState.TRANSLATING || m_State == ERR_AircraftFlightState.LANDING) { if (!m_Route) { Fail("immutable route unavailable"); return; } vector target = m_Route.Destination(); if (m_State == ERR_AircraftFlightState.LANDING) target = m_LandingPoint; vector transform[4]; m_Aircraft.GetWorldTransform(transform); vector demand; if (m_State == ERR_AircraftFlightState.TRANSLATING) demand = ERR_AircraftRouteRules.Demand(target, transform[3], m_Simulation.GetVelocity()); else demand = ERR_AircraftTranslationRules.Demand(target, transform[3], m_Simulation.GetVelocity()); forward = Math.Clamp(vector.Dot(demand, transform[2]), -0.03, 0.03); aside = Math.Clamp(vector.Dot(demand, transform[0]), -0.03, 0.03); if (m_State == ERR_AircraftFlightState.TRANSLATING) m_TranslationUpdates++; } if (m_State == ERR_AircraftFlightState.LANDING && Grounded()) { collective = -1; forward = 0; aside = 0; } } bool a = Pair("HelicopterCollectiveIncrease", "HelicopterCollectiveDecrease", collective); bool b = Pair("HelicopterCyclicForward", "HelicopterCyclicBack", forward); bool c = Pair("HelicopterCyclicRight", "HelicopterCyclicLeft", aside); bool d = Pair("HelicopterAntiTorqueRight", "HelicopterAntiTorqueLeft", torque); m_Readback = a && b && c && d; m_ActionUpdates++; m_LastControl = Now(); } override void Tick() { if (!Lease()) return; float now = Now(); if (!Owns()) { Relinquish("ownership or mission changed"); return; } if (!Healthy()) { Fail("flight capability lost"); return; } if (!m_Readback) { Fail("native action readback failed"); return; } if (now >= m_Deadline || now >= m_StageDeadline || now < 0) { Fail("bounded flight stage expired"); return; } if (m_LastTick >= 0 && (now < m_LastTick || now - m_LastTick > 1500)) m_Stable = -1; m_LastTick = now; vector transform[4]; m_Aircraft.GetWorldTransform(transform); float height = transform[3][1] - m_Home[1], speed = m_Simulation.GetVelocity().Length(); if (!RouteContains(transform[3], transform[1][1])) { Fail("qualified flight envelope exceeded"); return; } if (m_State == ERR_AircraftFlightState.HOOKS) { if (m_ActionUpdates < 3 || m_HelicopterUpdates < 3) return; if (m_Controller.GetCollectiveMode() != CollectiveMode.Direct || m_Input.GetVehicleCollective() != 0 || m_Controller.IsEngineOn()) { Fail("native initial control state refused"); return; } m_Controller.StartEngine(); m_State = ERR_AircraftFlightState.STARTING; m_StageDeadline = Math.Min(m_Deadline, now + 65000); return; } if (m_LastControl < 0 || now - m_LastControl > 1000) { Fail("pilot callback stopped"); return; } if (m_State == ERR_AircraftFlightState.STARTING) { float rpm = m_Simulation.RotorGetRPMTarget(0); if (!m_Controller.IsEngineOn() || rpm <= 0 || m_Simulation.RotorGetRPM(0) < rpm * 0.9 || m_Fuel.GetTotalFuel() >= m_InitialFuel) return; m_State = ERR_AircraftFlightState.POSITIVE; m_StageDeadline = Math.Min(m_Deadline, now + 3000); m_GateUpdates = m_ActionUpdates; m_Stable = -1; return; } if (m_State != ERR_AircraftFlightState.SHUTDOWN && !m_Controller.IsEngineOn()) { Fail("running engine lost"); return; } if (m_State == ERR_AircraftFlightState.POSITIVE || m_State == ERR_AircraftFlightState.NEGATIVE) { SignalsManagerComponent signals = m_Controller.GetSignalsManager(); int index = -1; if (signals) index = signals.FindSignal("MainRotorCollective"); if (index < 0 || m_Controller.GetCollectiveMode() != CollectiveMode.Direct || m_Controller.GetAutohoverEnabled()) { Fail("native collective proof unavailable"); return; } float expected = 0.05; if (m_State == ERR_AircraftFlightState.NEGATIVE) expected = -0.05; bool matched = m_ActionUpdates >= m_GateUpdates + 3 && Math.AbsFloat(signals.GetSignalValue(index) - expected) <= 0.01 && Math.AbsFloat(m_Input.GetVehicleCollective() - expected) <= 0.01; if (!matched) { m_Stable = -1; return; } if (m_Stable < 0) m_Stable = now; if (now - m_Stable < 500) return; m_Stable = -1; if (m_State == ERR_AircraftFlightState.POSITIVE) { m_State = ERR_AircraftFlightState.NEGATIVE; m_StageDeadline = Math.Min(m_Deadline, now + 3000); m_GateUpdates = m_ActionUpdates; return; } m_Controller.SetAutohoverEnabled(true); if (!m_Controller.GetAutohoverEnabled()) { Fail("native autohover enable refused"); return; } m_State = ERR_AircraftFlightState.CLIMB; m_StageDeadline = Math.Min(m_Deadline, now + 65000); return; } if (m_State == ERR_AircraftFlightState.TRANSLATING) { CheckTranslation(now, transform[3], height, speed, transform[1][1]); return; } if (m_State == ERR_AircraftFlightState.CLIMB || m_State == ERR_AircraftFlightState.LANDING) { if (!m_Controller.GetAutohoverEnabled()) { Fail("native autohover lost"); return; } bool stable = Grounded(); float duration = 2000; if (m_State == ERR_AircraftFlightState.CLIMB) { stable = !m_Simulation.HasAnyGroundContact() && ERR_AircraftFlightRules.Hover(height, speed, m_Simulation.GetVelocity()[1], transform[1][1]); duration = 5000; } if (!stable) m_Stable = -1; if (stable && m_Stable < 0) m_Stable = now; if (m_Stable < 0 || now - m_Stable < duration) return; if (m_State == ERR_AircraftFlightState.CLIMB) { if (m_TranslationRequested) BeginTranslation(now, transform[3]); else Landing(now); return; } Shutdown(now); return; } if (m_State == ERR_AircraftFlightState.SHUTDOWN && !m_Controller.IsEngineOn()) { if (!Grounded()) { Fail("engine-off physical ground check failed"); return; } if (m_Cancelled) m_State = ERR_AircraftFlightState.CANCELLED; else m_State = ERR_AircraftFlightState.COMPLETE; Release(true); RecordState(); } } }