Merge owned systems into one addon while preserving resource identities. Gate player admission on the replicated prison scene, restore the original framed door, and hold enemy AI until escape. Correct scenario branding and author metadata, include publishing assets, and retain regression and native verification evidence.
134 lines
8.2 KiB
Python
134 lines
8.2 KiB
Python
"""Execute actual route demand and control callback; native boundaries are fakes."""
|
|
from policy_harness import ROOT, body, run
|
|
|
|
BASE = ROOT / 'addons/core/Scripts/Game/ERR/10_AI/ERR_AircraftFlight.c'
|
|
ROUTE = ROOT / 'addons/core/Scripts/Game/ERR/10_AI/ERR_AircraftRouteFlight.c'
|
|
RULES = ROOT / 'addons/core/Scripts/Game/ERR/10_AI/ERR_AircraftRouteEnvelope.c'
|
|
PROBE = ROOT / 'addons/core/Scripts/Game/ERR/10_AI/ERR_NativeAircraftRouteProbe.c'
|
|
|
|
|
|
def cpp(text):
|
|
for old, new in (
|
|
('protected ', ''), ('override ', ''), ('super.', 'Base::'),
|
|
('ERR_AircraftFlightState.', 'State::'),
|
|
('ERR_AircraftFlightRules.', 'FlightRules::'),
|
|
('ERR_AircraftTranslationRules.', 'Shared::'),
|
|
('ERR_AircraftRouteRules.', 'RouteRules::'),
|
|
('Math.', 'Math::'), ('vector.', 'vector::'),
|
|
('CharacterControllerComponent controller', 'int controller'),
|
|
('IEntity owner', 'int owner'), ('ActionManager actions', 'Actions* actions'),
|
|
('actions.', 'actions->'), ('m_Route.', 'm_Route->'),
|
|
):
|
|
text = text.replace(old, new)
|
|
# Enforce default-initializes scalar locals, unlike C++.
|
|
return text.replace('float collective, torque, forward, aside;',
|
|
'float collective=0, torque=0, forward=0, aside=0;')
|
|
|
|
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <initializer_list>
|
|
using string=std::string;
|
|
namespace State {enum {IDLE,HOOKS,STARTING,POSITIVE,NEGATIVE,CLIMB,LANDING,SHUTDOWN,COMPLETE,CANCELLED,FAILED,RELINQUISHED,TRANSLATING};}
|
|
struct Math {
|
|
static float Clamp(float v,float lo,float hi){return std::clamp(v,lo,hi);}
|
|
static float Min(float a,float b){return std::min(a,b);}
|
|
};
|
|
struct vector {
|
|
float x=0,y=0,z=0;
|
|
float& operator[](int i){return i==0?x:(i==1?y:z);}
|
|
float operator[](int i)const{return i==0?x:(i==1?y:z);}
|
|
vector operator-(vector b)const{return {x-b.x,y-b.y,z-b.z};}
|
|
vector operator*(float n)const{return {x*n,y*n,z*n};}
|
|
float Length()const{return std::sqrt(x*x+y*y+z*z);}
|
|
static float Dot(vector a,vector b){return a.x*b.x+a.y*b.y+a.z*b.z;}
|
|
};
|
|
bool close(float a,float b){return std::abs(a-b)<.000001f;}
|
|
struct Shared {
|
|
''' + cpp(body(BASE, 'static vector Demand(')) + r'''
|
|
};
|
|
struct RouteRules {
|
|
''' + cpp(body(RULES, 'static vector Demand(')) + r'''
|
|
};
|
|
struct FlightRules {
|
|
''' + cpp(body(BASE, 'static float Vertical(')) + cpp(body(BASE, 'static int CancelAction(')) + r'''
|
|
};
|
|
struct Actions {bool active=true;bool IsContextActive(const char*){return active;}void ActivateContext(const char*,int){active=true;}};
|
|
struct Aircraft {
|
|
vector position={0,8,0};
|
|
vector GetOrigin(){return position;}
|
|
vector GetYawPitchRoll(){return {};}
|
|
void GetWorldTransform(vector (&t)[4]){t[0]={1,0,0};t[1]={0,1,0};t[2]={0,0,1};t[3]=position;}
|
|
};
|
|
struct Controller {bool hover=true;bool GetAutohoverEnabled(){return hover;}explicit operator bool()const{return true;}bool IsEngineOn(){return true;}};
|
|
struct Simulation {vector velocity;vector GetVelocity(){return velocity;}vector GetAngularVelocityLocal(){return {};}};
|
|
struct Route {vector target={20,0,0};vector Destination(){return target;}bool Contains(vector,float){return true;}};
|
|
struct Base {
|
|
bool leased=true,owned=true,health=true,grounded=false,control=true,m_Readback=true,m_Cancelled=false;
|
|
int m_State=State::TRANSLATING,m_Character=1,m_Pilot=2,m_ActionUpdates=0,m_TranslationUpdates=0,pairs=0,landings=0;
|
|
float clock=1000,m_Deadline=240000,m_StageDeadline=160000,m_Stable=-1,m_Collective=0,m_Heading=0,m_LastControl=-1;
|
|
float writtenForward=0,writtenAside=0,writtenCollective=0;
|
|
vector m_Home;string m_Reason;Actions local;Actions* m_Actions=&local;
|
|
Aircraft m_Aircraft;Controller m_Controller;Simulation m_Simulation;
|
|
bool Lease(){return leased;}bool Owns(){return owned;}bool Healthy(){return health;}bool Grounded(){return grounded;}bool ControlLease(){return control;}
|
|
float Now(){return clock;}void Fail(string){m_State=State::FAILED;}void Relinquish(string){m_State=State::RELINQUISHED;}
|
|
void RecordState(){}void Release(bool){}void Shutdown(float){m_State=State::SHUTDOWN;}
|
|
bool Landing(float now){landings++;m_State=State::LANDING;m_Stable=-1;m_StageDeadline=Math::Min(m_Deadline,now+65000);return true;}
|
|
bool Pair(string increase,string,float value){pairs++;if(increase=="HelicopterCyclicForward")writtenForward=value;if(increase=="HelicopterCyclicRight")writtenAside=value;if(increase=="HelicopterCollectiveIncrease")writtenCollective=value;return true;}
|
|
};
|
|
struct Flight:Base {
|
|
Route route;Route* m_Route=&route;bool m_RouteArrived=false;vector m_LandingPoint;
|
|
''' + cpp(body(ROUTE, 'protected override bool Landing(')) + cpp(body(BASE, 'void Cancel(')) + cpp(body(ROUTE, 'override void PrepareActions(')) + r'''
|
|
void Apply(){PrepareActions(1,2,m_Actions,.05f);}
|
|
};
|
|
int main(){
|
|
// The unchanged .5m/s requested speed is visible at its zero-error velocity.
|
|
assert(RouteRules::Demand({20,900,0},{0,-900,0},{.5f,500,0}).Length()==0);
|
|
assert(RouteRules::Demand({200,0,0},{0,0,0},{.5f,0,0}).Length()==0);
|
|
// Below saturation the route candidate doubles gain, not desired speed.
|
|
auto route=RouteRules::Demand({20,0,0},{0,8,0},{.4f,0,0});
|
|
auto shared=Shared::Demand({20,0,0},{0,8,0},{.4f,0,0});
|
|
assert(close(route.x,.01f)&&close(shared.x,.005f));
|
|
for(float x:{-100.f,-2.f,-.1f,0.f,.1f,2.f,100.f})
|
|
for(float z:{-100.f,-1.f,0.f,1.f,100.f})
|
|
for(float v:{-100.f,-1.f,0.f,1.f,100.f}){
|
|
auto d=RouteRules::Demand({x,999,z},{0,-999,0},{v,999,-v});
|
|
assert(d.Length()<=.030001f&&d.y==0);
|
|
}
|
|
auto away=RouteRules::Demand({20,0,0},{0,8,0},{-1,50,0});assert(close(away.x,.03f)&&away.y==0&&away.z==0);
|
|
auto overspeed=RouteRules::Demand({20,0,0},{0,8,0},{1,-50,0});assert(close(overspeed.x,-.03f));
|
|
auto brake=RouteRules::Demand({0,0,0},{0,8,0},{1,0,-1});assert(brake.x<0&&brake.z>0&&close(brake.Length(),.03f));
|
|
assert(RouteRules::Demand({0,100,0},{0,-100,0},{0,99,0}).Length()==0);
|
|
// Execute the real callback: TRANSLATING selects candidate; ordinary landing
|
|
// keeps the original .05 gain with precisely the same target and velocity.
|
|
Flight translating;translating.m_Simulation.velocity={.4f,0,0};translating.Apply();
|
|
assert(close(translating.writtenAside,.01f)&&translating.writtenForward==0&&translating.m_TranslationUpdates==1);
|
|
Flight landing;landing.m_State=State::LANDING;landing.m_LandingPoint={20,0,0};landing.m_Simulation.velocity={.4f,0,0};landing.Apply();
|
|
assert(close(landing.writtenAside,.005f)&&landing.m_TranslationUpdates==0);
|
|
// The actual inherited Cancel and route Landing freeze the original position,
|
|
// retain authority/lease, and thereafter use old landing braking—not route gain.
|
|
Flight cancelled;cancelled.m_Aircraft.position={10,8,1};cancelled.m_Simulation.velocity={.2f,0,0};cancelled.Cancel("radio lost");
|
|
assert(cancelled.m_State==State::LANDING&&cancelled.m_Cancelled&&cancelled.leased&&cancelled.landings==1);
|
|
assert(cancelled.m_LandingPoint.x==10&&cancelled.m_LandingPoint.z==1&&cancelled.m_Deadline==240000);
|
|
cancelled.Apply();assert(close(cancelled.writtenAside,-.01f));
|
|
float deadline=cancelled.m_StageDeadline;cancelled.clock=5000;cancelled.m_Aircraft.position={11,8,1};cancelled.Cancel("duplicate");
|
|
assert(cancelled.landings==1&&cancelled.m_LandingPoint.x==10&&cancelled.m_StageDeadline==deadline);
|
|
// Grounded landing suppresses both axes; wrong owner or revoked lease writes none.
|
|
landing.grounded=true;landing.Apply();assert(landing.writtenAside==0&&landing.writtenForward==0&&landing.writtenCollective==-1);
|
|
Flight foreign;foreign.PrepareActions(1,99,foreign.m_Actions,.05f);assert(foreign.pairs==0);
|
|
foreign.control=false;foreign.Apply();assert(foreign.pairs==0);
|
|
}
|
|
''')
|
|
|
|
# Read-only diagnostic must describe the same state-specific equation, not the
|
|
# historical shared demand. It remains a computed sample, not input readback.
|
|
diagnostic = body(PROBE, 'protected void Diagnostic(')
|
|
assert 'if (state == ERR_AircraftFlightState.TRANSLATING) demand = ERR_AircraftRouteRules.Demand(' in diagnostic
|
|
assert 'else if (state == ERR_AircraftFlightState.LANDING) demand = ERR_AircraftTranslationRules.Demand(' in diagnostic
|
|
# The shared controller still has no dependency on the experimental route policy.
|
|
assert 'ERR_AircraftRouteRules' not in BASE.read_text()
|
|
print('PASS: actual route gain/caps/braking, callback selection, old landing/cancellation control and matching read-only telemetry')
|