Files
EscapistsReintroduced/tests/test_aircraft_route_policy.py
jakub cab998fb64 Combine Workshop addon and fix prison startup
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.
2026-09-20 16:42:52 +02:00

104 lines
10 KiB
Python

"""Actual route entry/stage/cancellation bodies with physical/native boundaries stubbed."""
from policy_harness import ROOT, body, run
P=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_AircraftRouteFlight.c'
BASE=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_AircraftFlight.c'
ENV=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_AircraftRouteEnvelope.c'
def cpp(s):
s=s.replace('protected ','').replace('override ','').replace('super.', 'Base::')
for a,b in [('ERR_AircraftFlightState.','State::'),('ERR_AircraftFlightRules.','FlightRules::'),('ERR_AircraftRouteRules.','RouteRules::'),('Math.','Math::'),('vector.','vector::')]:s=s.replace(a,b)
s=s.replace('ERR_AircraftRouteEnvelope route','ERR_AircraftRouteEnvelope* route').replace('SignalsManagerComponent signals','SignalsManagerComponent* signals')
for var in ('route','m_Route','signals'):s=s.replace(var+'.',var+'->')
return s
methods='\n'.join(cpp(body(P,key)) for key in ('bool StartRoute(', 'protected bool RouteContains(', 'protected override bool Grounded(', 'protected override bool Landing(', 'protected override bool BeginTranslation(', 'protected override void CheckTranslation(', 'protected override bool ControlLease('))
run(r'''#include <cassert>
#include <cmath>
#include <algorithm>
#include <string>
#include <initializer_list>
#define PrintFormat(...) ((void)0)
using string=std::string;using ERR_GameMode=int;using Vehicle=int;using SCR_AIGroup=int;using IEntity=int;using PilotCompartmentSlot=int;
namespace State {enum {IDLE,HOOKS,STARTING,POSITIVE,NEGATIVE,CLIMB,LANDING,SHUTDOWN,COMPLETE,CANCELLED,FAILED,RELINQUISHED,TRANSLATING};}
struct Math {static float Clamp(float a,float lo,float hi){return std::clamp(a,lo,hi);}static float Min(float a,float b){return std::min(a,b);}static float AbsFloat(float a){return std::abs(a);}};
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+(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;}static float DistanceXZ(vector a,vector b){return std::hypot(a.x-b.x,a.z-b.z);}};
struct FlightRules {
'''+cpp(body(BASE,'static bool Hover('))+cpp(body(BASE,'static int CancelAction('))+r'''
};
struct RouteRules {
'''+''.join(cpp(body(ENV,k)) for k in ('static bool Length(', 'static vector Nearest(', 'static bool Contains(', 'static bool Held('))+r'''
};
struct ERR_AircraftRouteEnvelope {vector home={0,0,0},target={20,0,0};vector Home(){return home;}vector Destination(){return target;}bool Contains(vector p,float upright){return RouteRules::Contains(home,target,p,upright);}};
struct SignalsManagerComponent {bool present=true;int FindSignal(const char*){return present?0:-1;}};
struct Controller {bool hover=true,engine=true;SignalsManagerComponent signals;explicit operator bool()const{return true;}SignalsManagerComponent* GetSignalsManager(){return &signals;}bool GetAutohoverEnabled(){return hover;}bool IsEngineOn(){return engine;}};
struct Simulation {vector velocity;bool contact=false;vector GetVelocity(){return velocity;}bool HasAnyGroundContact(){return contact;}};
struct Aircraft {vector origin;float upright=1;vector GetOrigin(){return origin;}void GetWorldTransform(vector (&t)[4]){t[3]=origin;t[1]={0,upright,0};}};
struct Base {
bool m_StartAttempted=false,m_TranslationRequested=false,m_TranslationStarted=false,m_TranslationProven=false,m_Readback=true,m_Cancelled=false;
vector m_TranslationTarget,m_TranslationOrigin,m_Home;
float m_TranslationDistance=0,m_Stable=-1,m_Deadline=240000,m_StageDeadline=200000,clock=1000;
int m_State=State::CLIMB,m_Token=123,m_TranslationUpdates=10,landings=0,starts=0,failures=0,relinquished=0,restored=0;
bool owner=true,healthy=true,leased=true,grounded=false;
Controller m_Controller;Simulation m_Simulation;Aircraft m_Aircraft;string m_Reason;
bool Start(ERR_GameMode,Vehicle,SCR_AIGroup,IEntity,PilotCompartmentSlot,ERR_AircraftRouteEnvelope* q,int,float){m_StartAttempted=true;starts++;m_Home=q->Home();return true;}
bool Grounded(){return grounded;}bool Landing(float now){landings++;m_State=State::LANDING;m_Stable=-1;m_StageDeadline=Math::Min(m_Deadline,now+65000);return true;}
bool Owns(){return owner;}bool Healthy(){return healthy;}bool Lease(){return leased;}float Now(){return clock;}void Fail(string r){m_Reason=r;failures++;m_State=State::FAILED;}void Relinquish(string r){m_Reason=r;relinquished++;m_State=State::RELINQUISHED;}void RecordState(){}void Release(bool){restored++;}void Shutdown(float){m_State=State::SHUTDOWN;}
};
struct Probe:Base {
ERR_AircraftRouteEnvelope* m_Route=nullptr;vector m_LandingPoint;bool m_RouteArrived=false;
'''+methods+cpp(body(BASE,'void Cancel('))+r'''
};
int main(){
ERR_AircraftRouteEnvelope q;Probe invalid;assert(!invalid.StartRoute(1,1,1,1,1,nullptr,3,200000)&&invalid.starts==0);
q.target={21,0,0};assert(!invalid.StartRoute(1,1,1,1,1,&q,3,200000)&&invalid.starts==0);q.target={20,0,0};
Probe flight;assert(flight.StartRoute(1,1,1,1,1,&q,3,200000)&&flight.starts==1&&flight.m_TranslationRequested);
assert(flight.m_TranslationTarget.x==20&&flight.m_TranslationTarget.y==8&&flight.m_Home.x==0);
assert(!flight.StartRoute(1,1,1,1,1,&q,3,200000)&&flight.starts==1);
assert(!flight.RouteContains({9,8,0},1));assert(flight.BeginTranslation(1000,{2,8,0}));assert(flight.m_StageDeadline==161000&&flight.m_Deadline==240000);
assert(flight.RouteContains({19,8,0},1)&&!flight.RouteContains({10,8,8},1));
flight.m_Aircraft.origin={19.5f,8,0};
flight.CheckTranslation(2000,flight.m_Aircraft.origin,8,0,1);flight.CheckTranslation(5000,{18,8,0},8,0,1);assert(flight.m_Stable==-1);
flight.CheckTranslation(6000,flight.m_Aircraft.origin,8,0,1);flight.CheckTranslation(10000,flight.m_Aircraft.origin,8,0,1);assert(!flight.m_RouteArrived);
flight.CheckTranslation(11000,flight.m_Aircraft.origin,8,0,1);assert(flight.m_RouteArrived&&flight.m_TranslationProven&&flight.m_LandingPoint.x==20&&flight.landings==1);
flight.grounded=true;flight.m_Aircraft.origin={19,0,0};assert(!flight.Grounded());flight.m_Aircraft.origin={19.5f,0,0};assert(flight.Grounded());
Probe limited;limited.m_Deadline=60000;assert(limited.StartRoute(1,1,1,1,1,&q,3,60000));assert(limited.BeginTranslation(1000,{2,8,0}));assert(limited.m_StageDeadline==60000&&limited.m_Deadline==60000);
// Same parent cancellation body calls route Landing: freeze current centre,
// preserve absolute deadline and never move it on repeated cancellation.
Probe cancel;assert(cancel.StartRoute(1,1,1,1,1,&q,3,200000));assert(cancel.BeginTranslation(1000,{2,8,0}));cancel.m_Aircraft.origin={10,8,1};
cancel.Cancel("radio lost");assert(cancel.m_State==State::LANDING&&cancel.m_Cancelled&&cancel.m_LandingPoint.x==10&&cancel.m_LandingPoint.z==1&&cancel.m_Deadline==240000&&cancel.landings==1);
float stage=cancel.m_StageDeadline;cancel.clock=9000;cancel.m_Aircraft.origin={12,8,1};cancel.Cancel("again");assert(cancel.m_LandingPoint.x==10&&cancel.landings==1&&cancel.m_StageDeadline==stage);
// Shared native write gate refuses stale ownership, expiry and corridor escape.
Probe capture;capture.m_Route=&q;capture.owner=false;assert(!capture.ControlLease()&&capture.relinquished==1);
Probe expiry;expiry.m_Route=&q;expiry.clock=240000;assert(!expiry.ControlLease()&&expiry.failures==1);
Probe drift;drift.m_Route=&q;drift.m_TranslationStarted=true;drift.m_State=State::TRANSLATING;drift.m_Aircraft.origin={10,8,8};assert(!drift.ControlLease()&&drift.failures==1);
Probe healthy;healthy.m_Route=&q;healthy.m_TranslationStarted=true;healthy.m_State=State::TRANSLATING;healthy.m_Aircraft.origin={10,8,0};assert(healthy.ControlLease());
Probe ownershipCancel;ownershipCancel.m_Route=&q;ownershipCancel.owner=false;ownershipCancel.Cancel("captured");assert(ownershipCancel.relinquished==1&&ownershipCancel.landings==0&&ownershipCancel.restored==0);
}
''')
s=P.read_text()
assert 'class ERR_AircraftRouteFlight : ERR_AircraftFlight' in s
for unchanged in ('protected bool Owns(', 'protected bool Healthy(', 'protected bool Lease(', 'protected void Release(', 'void Cancel('):assert unchanged not in s
assert 'return super.Start(' in body(P,'bool StartRoute(')
assert 's_Owners' not in s, 'Use parent shared airframe registry, never another exclusivity domain'
prepare=body(P,'override void PrepareActions(')
assert prepare.index('!ControlLease()')<prepare.index('SetActionValue') if 'SetActionValue' in prepare else prepare.index('!ControlLease()')<prepare.index('Pair(')
assert 'target = m_LandingPoint' in prepare and 'ERR_AircraftTranslationRules.Demand(target,' in prepare
assert 'forward = 0; aside = 0;' in prepare
assert 'SetWorldTransform' not in s and 'SetVelocity' not in s and 'SetActive' not in s
# The large native startup/tick sequence is preserved verbatim except route geometry.
expected=body(BASE,'void Tick(').replace('void Tick(', 'override void Tick(',1).replace('ERR_AircraftFlightRules.Envelope(height, vector.DistanceXZ(transform[3], m_Home), transform[1][1])','RouteContains(transform[3], transform[1][1])')
assert body(P,'override void Tick(')==expected
print('PASS: actual route entry, fixed corridor/target, hover hold, cancellation landing freeze, shared lease/capture/deadline gate and unchanged native startup/tick sequence')
# Native compile exposed a getter return-path diagnostic. Keep the actual
# defensive getter executable, including its default pre-start observation.
run(r'''#include <cassert>
#include <cstdio>
struct vector {float x=0,y=0,z=0;vector(float a,float b,float c):x(a),y(b),z(c){}vector(const char* s){sscanf(s,"%f %f %f",&x,&y,&z);}};
struct Route {vector Destination(){return {20,3,4};}};
struct Observer {Route* m_Route=nullptr;
'''+cpp(body(P,'vector Destination('))+r'''
};
int main(){Observer p;auto absent=p.Destination();assert(absent.x==0&&absent.y==0&&absent.z==0);Route q;p.m_Route=&q;auto value=p.Destination();assert(value.x==20&&value.y==3&&value.z==4);}
''')
assert "m_StageDeadline = Math.Min(m_Deadline, now + 160000);" in body(P,"protected override bool BeginTranslation(")
assert "m_Deadline =" not in body(P,"protected override bool BeginTranslation(")