@@ -115,6 +115,42 @@ class ERR_PoweredFlightRules
{
return contact & & height < 1 & & height > - 1 & & speed < 0.5 & & angular < 0.1 & & upright > 0.98 ;
}
static bool TranslationInputs ( bool selected , bool actions , bool autohover )
{
return ! selected | | ( actions & & autohover ) ;
}
static bool TranslationActive ( bool selected , bool frozen , bool actions , bool autohover , int stage )
{
return selected & & frozen & & actions & & autohover & & stage = = 5 ;
}
static bool TranslationTarget ( vector home , vector start , vector fallback , out vector target )
{
target = vector . Zero ;
if ( ! ( vector . DistanceXZ ( home , start ) < 8 ) ) return false ;
vector direction = home - start ; direction [ 1 ] = 0 ;
if ( direction . Length ( ) < 0.5 ) { direction = fallback ; direction [ 1 ] = 0 ; }
float length = direction . Length ( ) ;
if ( ! ( length > = 0.5 ) ) return false ;
target = start + direction * ( 4 / length ) ;
return vector . DistanceXZ ( start , target ) > = 3.99 & & vector . DistanceXZ ( start , target ) < = 4.01 & & vector . DistanceXZ ( home , target ) < = 5 ;
}
static vector TranslationDemand ( vector target , vector position , vector velocity )
{
vector error = target - position ; error [ 1 ] = 0 ;
vector desired = error * 0.25 ;
float speed = desired . Length ( ) ;
if ( speed > 0.5 ) desired = desired * ( 0.5 / speed ) ;
velocity [ 1 ] = 0 ;
// Experimental outer velocity request only; native autohover stabilizes attitude.
vector demand = ( desired - velocity ) * 0.05 ;
float magnitude = demand . Length ( ) ;
if ( magnitude > 0.03 ) demand = demand * ( 0.03 / magnitude ) ;
return demand ;
}
static bool TranslationHeld ( bool contact , float height , float speed , float vertical , float upright , float drift , float horizontalSpeed , float targetError , float displacement )
{
return ! contact & & Hover ( height , speed , vertical , upright , drift ) & & horizontalSpeed > = 0 & & horizontalSpeed < 0.25 & & targetError > = 0 & & targetError < = 0.75 & & displacement > = 3.25 ;
}
}
class ERR_NativePoweredFlightProbe
{
@@ -167,6 +203,12 @@ class ERR_NativePoweredFlightProbe
protected bool m_ActionInputs ;
protected bool m_Started ;
protected bool m_AutohoverMode ;
protected bool m_TranslationMode ;
protected bool m_TranslationFrozen ;
protected bool m_TranslationProven ;
protected vector m_TranslationOrigin ;
protected vector m_TranslationTarget ;
protected int m_TranslationUpdates ;
protected bool m_AutohoverSaved ;
protected bool m_OriginalAutohover ;
protected HelicopterControllerComponent m_AutohoverController ;
@@ -178,13 +220,15 @@ class ERR_NativePoweredFlightProbe
static bool Enabled ( )
{
string enabled , outcome ;
return Replication . IsServer ( ) & & System . GetCLIParam ( " errBaseSmoke " , enabled ) & & enabled = = " 1 " & & System . GetCLIParam ( " errSmokeOutcome " , outcome ) & & outcome = = " powered-flight " ;
return Replication . IsServer ( ) & & System . GetCLIParam ( " errBaseSmoke " , enabled ) & & enabled = = " 1 " & & System . GetCLIParam ( " errSmokeOutcome " , outcome ) & & ( outcome = = " powered-flight " | | outcome = = " powered-translate " ) ;
}
static ERR_NativePoweredFlightProbe Current ( ) { return s_Current ; }
void ERR_NativePoweredFlightProbe ( ERR_GameMode mode )
{
string actionInputs ;
m_ActionInputs = System . GetCLIParam ( " errFlightActionInputs " , actionInputs ) & & actionInputs = = " 1 " ;
string outcome ;
m_TranslationMode = System . GetCLIParam ( " errSmokeOutcome " , outcome ) & & outcome = = " powered-translate " ;
m_Mode = mode ; m_Deadline = GetGame ( ) . GetWorld ( ) . GetWorldTime ( ) + 240000 ;
if ( Enabled ( ) & & ! s_Current ) s_Current = this ;
else m_Stopped = true ;
@@ -299,6 +343,13 @@ class ERR_NativePoweredFlightProbe
// Give its lateral controller zero custom cyclic in this experiment.
// Preserve collective/yaw and bounds; never combine two cyclic controllers.
if ( m_AutohoverMode ) { forward = 0 ; aside = 0 ; }
if ( ERR_PoweredFlightRules . TranslationActive ( m_TranslationMode , m_TranslationFrozen , m_ActionInputs , m_AutohoverMode , m_Stage ) )
{
vector demand = ERR_PoweredFlightRules . TranslationDemand ( m_TranslationTarget , transform [ 3 ] , velocity ) ;
forward = ERR_PoweredFlightRules . ClampInput ( vector . Dot ( demand , transform [ 2 ] ) , 0.03 ) ;
aside = ERR_PoweredFlightRules . ClampInput ( vector . Dot ( demand , transform [ 0 ] ) , 0.03 ) ;
m_TranslationUpdates + + ;
}
float headingError = m_Heading - m_Aircraft . GetYawPitchRoll ( ) [ 0 ] ;
while ( headingError > 180 ) headingError - = 360 ;
while ( headingError < - 180 ) headingError + = 360 ;
@@ -415,6 +466,7 @@ class ERR_NativePoweredFlightProbe
if ( ! m_Simulation | | ! m_Aircraft | | now < m_NextLog ) return ;
m_NextLog = now + 2000 ;
if ( m_Stage = = 4 & & now < m_StageDeadline - 57000 ) m_NextLog = now + 250 ;
if ( m_TranslationMode & & m_Stage = = 5 ) m_NextLog = now + 500 ;
if ( m_ActionInputs )
{
bool ownedActions = OwnActionManager ( m_OwnedActions ) ;
@@ -429,6 +481,22 @@ class ERR_NativePoweredFlightProbe
PrintFormat ( " ERR POWERED ROTOR AXIS: local=%1 acceleration=%2 " , rotorAxis , m_Simulation . GetAcceleration ( ) ) ;
PrintFormat ( " ERR POWERED MOTION: offset=%1 velocity=%2 angularLocal=%3 forward=%4 right=%5 " , m_Home - transform [ 3 ] , m_Simulation . GetVelocity ( ) , m_Simulation . GetAngularVelocityLocal ( ) , transform [ 2 ] , transform [ 0 ] ) ;
PrintFormat ( " ERR POWERED INPUT: pilotHooks=%1 vehicleHooks=%2 readback=%3 collective=%4 cyclicF=%5 cyclicR=%6 antiTorque=%7 rotorRPM=%8 targetRPM=%9 " , m_ControlUpdates , m_HelicopterUpdates , m_ReadbackOK , m_Input . GetVehicleCollective ( ) , m_Input . GetVehicleCyclicForward ( ) , m_Input . GetVehicleCyclicAside ( ) , m_Input . GetVehicleAntiTorque ( ) , m_Simulation . RotorGetRPM ( 0 ) , m_Simulation . RotorGetRPMTarget ( 0 ) ) ;
if ( m_TranslationMode & & m_TranslationFrozen )
{
vector direction = m_TranslationTarget - m_TranslationOrigin ; direction [ 1 ] = 0 ; direction = direction * 0.25 ;
vector travel = transform [ 3 ] - m_TranslationOrigin ; travel [ 1 ] = 0 ;
float along = vector . Dot ( travel , direction ) ;
vector cross = travel - direction * along ;
vector horizontal = m_Simulation . GetVelocity ( ) ; horizontal [ 1 ] = 0 ;
PrintFormat ( " ERR POWERED TRANSLATION: stage=%1 origin=%2 target=%3 position=%4 along=%5 cross=%6 error=%7 horizontalSpeed=%8 updates=%9 " , m_Stage , m_TranslationOrigin , m_TranslationTarget , transform [ 3 ] , along , cross . Length ( ) , vector . DistanceXZ ( transform [ 3 ] , m_TranslationTarget ) , horizontal . Length ( ) , m_TranslationUpdates ) ;
SignalsManagerComponent signals = m_Controller . GetSignalsManager ( ) ;
if ( signals )
{
int cyclicForward = signals . FindSignal ( " MainRotorCyclicForward " ) ;
int cyclicAside = signals . FindSignal ( " MainRotorCyclicAside " ) ;
if ( cyclicForward > = 0 & & cyclicAside > = 0 ) PrintFormat ( " ERR POWERED TRANSLATION SIGNALS: pilotForward=%1 pilotAside=%2 rotorForward=%3 rotorAside=%4 (native autohover may modify rotor commands) " , m_Input . GetVehicleCyclicForward ( ) , m_Input . GetVehicleCyclicAside ( ) , signals . GetSignalValue ( cyclicForward ) , signals . GetSignalValue ( cyclicAside ) ) ;
}
}
}
protected bool PollNativeFlightComponents ( float now )
{
@@ -533,12 +601,42 @@ class ERR_NativePoweredFlightProbe
// Keep real airborne/failed test actors for diagnosis and mode-owned cleanup.
// Stop never repairs, stabilizes, teleports, refuels or replaces them.
}
protected bool BeginTranslation ( float now , vector position , vector fallback )
{
if ( ! Check ( m_TranslationMode & & ! m_TranslationFrozen & & m_Stage = = 4 & & m_ActionInputs & & m_AutohoverMode & & AutohoverReady ( ) , " translation starts once after the original native hover stage " ) ) return false ;
vector target ;
if ( ! Check ( ERR_PoweredFlightRules . TranslationTarget ( m_Home , position , fallback , target ) , " frozen four-metre horizontal target remains within five metres of original home " ) ) return false ;
SignalsManagerComponent signals = m_Controller . GetSignalsManager ( ) ;
if ( ! Check ( signals & & signals . FindSignal ( " MainRotorCyclicForward " ) > = 0 & & signals . FindSignal ( " MainRotorCyclicAside " ) > = 0 , " existing native cyclic signals available for translation observation " ) ) return false ;
m_TranslationOrigin = position ;
m_TranslationTarget = target ;
m_TranslationFrozen = true ;
m_Stage = 5 ; m_StageDeadline = now + 45000 ; m_Stable = - 1 ; m_NextLog = 0 ;
PrintFormat ( " ERR POWERED TRANSLATION BEGIN: origin=%1 target=%2 originalHome=%3 deadline=%4 overallDeadline=%5 " , m_TranslationOrigin , m_TranslationTarget , m_Home , m_StageDeadline , m_Deadline ) ;
return true ;
}
protected bool CheckTranslation ( float now , vector position , float height , float speed , float upright , float drift )
{
if ( ! ERR_PoweredFlightRules . TranslationActive ( m_TranslationMode , m_TranslationFrozen , m_ActionInputs , m_AutohoverMode , m_Stage ) ) return Check ( false , " translation retains its selected inputs and frozen stage ownership " ) ;
if ( now > = m_StageDeadline ) return Check ( false , " four-metre translation and five-second target hold finish within 45 seconds " ) ;
vector velocity = m_Simulation . GetVelocity ( ) ;
vector horizontal = velocity ; horizontal [ 1 ] = 0 ;
bool held = m_TranslationUpdates > = 3 & & ERR_PoweredFlightRules . TranslationHeld ( m_Simulation . HasAnyGroundContact ( ) , height , speed , velocity [ 1 ] , upright , drift , horizontal . Length ( ) , vector . DistanceXZ ( position , m_TranslationTarget ) , vector . DistanceXZ ( position , m_TranslationOrigin ) ) ;
if ( ! held ) m_Stable = - 1 ;
if ( held & & m_Stable < 0 ) m_Stable = now ;
if ( m_Stable < 0 | | now - m_Stable < 5000 ) return true ;
if ( ! Check ( true , " actual horizontal translation reaches frozen target and holds five seconds at 7– 9m with horizontal speed below 0.25m/s " ) ) return false ;
m_TranslationProven = true ;
m_Stage = 6 ; m_StageDeadline = now + 65000 ; m_Stable = - 1 ;
return true ;
}
int Tick ( float now )
{
if ( m_Complete ) return 1 ;
if ( m_Stopped ) return - 1 ;
m_Started = true ;
if ( ! Enabled ( ) | | ! m_Mode | | now > = m_Deadline | | ERR_MissionRules . Terminal ( m_Mode . Phase ( ) ) ) { Check ( false , " server fixture active within 240-second bound " ) ; return - 1 ; }
if ( ! ERR_PoweredFlightRules . TranslationInputs ( m_TranslationMode , m_ActionInputs , m_AutohoverMode ) ) { Check ( false , " translation requires actor-local actions and selected native autohover before allocation " ) ; return - 1 ; }
if ( m_PreviousTick > = 0 & & ( now < m_PreviousTick | | now - m_PreviousTick > 1500 ) ) { m_Stable = - 1 ; m_PositiveSignalSince = - 1 ; m_NegativeSignalSince = - 1 ; }
m_PreviousTick = now ;
if ( m_Stage = = 0 )
@@ -672,6 +770,11 @@ class ERR_NativePoweredFlightProbe
if ( ! Check ( BeginAutohover ( ) , " selected native autohover enabled only after both direct collective consumption proofs " ) ) return - 1 ;
m_Collective = - 0.8 ; m_Stage = 4 ; m_StageDeadline = now + 65000 ; m_Stable = - 1 ; return 0 ;
}
if ( m_TranslationMode & & m_Stage = = 5 )
{
if ( ! CheckTranslation ( now , transform [ 3 ] , height , speed , transform [ 1 ] [ 1 ] , drift ) ) return - 1 ;
return 0 ;
}
if ( m_Stage = = 4 | | m_Stage = = 5 )
{
bool hover = ! m_Simulation . HasAnyGroundContact ( ) & & ERR_PoweredFlightRules . Hover ( height , speed , m_Simulation . GetVelocity ( ) [ 1 ] , transform [ 1 ] [ 1 ] , drift ) ;
@@ -681,12 +784,18 @@ class ERR_NativePoweredFlightProbe
if ( m_Stable > = 0 & & now - m_Stable > = 5000 )
{
if ( ! Check ( m_ControlUpdates > 100 & & m_Fuel . GetTotalFuel ( ) < m_InitialFuel - 0.05 , " actual 7– 9 metre powered hover stable five seconds with native fuel consumption " ) ) return - 1 ;
if ( m_TranslationMode )
{
if ( ! BeginTranslation ( now , transform [ 3 ] , transform [ 0 ] ) ) return - 1 ;
return 0 ;
}
m_Stage = 6 ; m_StageDeadline = now + 65000 ; m_Stable = - 1 ;
}
return 0 ;
}
if ( m_Stage = = 6 )
{
if ( m_TranslationMode & & ! m_TranslationProven ) { Check ( false , " translation reaches its frozen target before landing " ) ; return - 1 ; }
bool landed = ERR_PoweredFlightRules . Landed ( m_Simulation . HasAnyGroundContact ( ) , height , speed , m_Simulation . GetAngularVelocity ( ) . Length ( ) , transform [ 1 ] [ 1 ] ) ;
if ( ! landed ) m_Stable = - 1 ;
if ( landed & & m_Stable < 0 ) m_Stable = now ;