Files
EscapistsReintroduced/tests/test_powered_flight_pad.py
T

78 lines
4.1 KiB
Python

"""Execute the fixture's deterministic pad survey geometry; no terrain claims."""
from policy_harness import ROOT, body, run
p=ROOT/'addons/core/Scripts/Game/ERR/ERR_NativePoweredFlightProbe.c'
source=body(p,'static vector PadOffset(')
source+='\n'+body(p,'static vector RefineOffset(')
source+='\n'+body(p,'static vector AirportOffset(')
# C++ accepts %, but Enforce does not. Fail before the C++ harness can hide
# that native compilation error; only namespace spelling is translated.
assert '%' not in source, 'Enforce remainder requires Math.Mod, not the C++ % operator'
s=source.replace('Math.','Math::').replace('vector.Zero','vector::Zero')
run('''#include <cassert>
#include <cmath>
#include <vector>
struct vector {float x,y,z;static const vector Zero;};const vector vector::Zero{0,0,0};
vector Vector(float x,float y,float z){return {x,y,z};}
namespace Math {constexpr float PI=3.14159265358979323846f;float Cos(float a){return std::cos(a);}float Sin(float a){return std::sin(a);}float Mod(float value,float divisor){return std::fmod(value,divisor);}}
constexpr int PAD_CANDIDATES=128;
constexpr int PAD_REFINEMENTS=80;
constexpr int AIRPORT_CANDIDATES=169;
'''+s+'''
int main(){
std::vector<vector> seen;
for(int i=0;i<PAD_CANDIDATES;i++){
auto p=PadOffset(i);float radius=std::hypot(p.x,p.z);assert(p.y==0&&radius>=119.99f&&radius<=500.01f);
for(auto old:seen)assert(std::hypot(p.x-old.x,p.z-old.z)>1);
seen.push_back(p);
if(i<12){float angle=i*Math::PI/6;assert(std::hypot(p.x-std::cos(angle)*120,p.z-std::sin(angle)*120)<.001f);}
if(i>=12&&i<108){
int ring=(i-12)/24;int sample=(i-12)-24*ring;
float phase=(ring==1||ring==3)?0.5f:0.f;
float expectedRadius=200+80*ring;float angle=(sample+phase)*2*Math::PI/24;
assert(std::fabs(radius-expectedRadius)<.001f);
assert(std::hypot(p.x-std::cos(angle)*expectedRadius,p.z-std::sin(angle)*expectedRadius)<.001f);
}
if(i>=108)assert(std::fabs(radius-500)<.001f);
}
assert(seen.size()==128);
for(int invalid:{-1,128,999}){auto p=PadOffset(invalid);assert(p.x==0&&p.y==0&&p.z==0);}
seen.clear();
for(int i=0;i<PAD_REFINEMENTS;i++){
auto p=RefineOffset(i);assert(p.y==0&&std::hypot(p.x,p.z)>9.99f&&std::hypot(p.x,p.z)<56.57f);
assert(std::fabs(p.x)<=40&&std::fabs(p.z)<=40);
for(auto old:seen)assert(std::hypot(p.x-old.x,p.z-old.z)>9.99f);
seen.push_back(p);
}
assert(seen.size()==80);
for(int x=-40;x<=40;x+=10)for(int z=-40;z<=40;z+=10){
if(x==0&&z==0)continue;
int matches=0;for(auto p:seen)if(p.x==x&&p.z==z)matches++;assert(matches==1);
}
for(int invalid:{-1,80,999}){auto p=RefineOffset(invalid);assert(p.x==0&&p.y==0&&p.z==0);}
seen.clear();
for(int i=0;i<AIRPORT_CANDIDATES;i++){
auto p=AirportOffset(i);assert(p.y==0&&std::fabs(p.x)<=300&&std::fabs(p.z)<=300);
for(auto old:seen)assert(std::hypot(p.x-old.x,p.z-old.z)>49.99f);
seen.push_back(p);
}
assert(seen.size()==169);
for(int x=-300;x<=300;x+=50)for(int z=-300;z<=300;z+=50){
int matches=0;for(auto p:seen)if(p.x==x&&p.z==z)matches++;assert(matches==1);
}
for(int invalid:{-1,169,999}){auto p=AirportOffset(invalid);assert(p.x==0&&p.y==0&&p.z==0);}
}
''')
# The native safety queries remain boundaries, not fake geometry measurements.
find=body(p,'protected bool FindPad(')
assert 'maximumDelta > 0.8' in find and 'Math.Cos(side) * 14' in find
assert 'TraceCilinderUtil(candidate + "0 2.5 0", 40, 4, TraceFlags.ENTS)' in find
assert 'clearance.Start = candidate + "0 3 0"; clearance.End = candidate + "0 60 0"' in find
assert 'clearance.Mins = "-40 -1 -40"; clearance.Maxs = "40 1 40"' in find
assert find.index('TraceMove(clearance, null) < 1') < find.index('result = candidate; return true;')
assert 'result = bestCandidate' not in find
assert find.count('refinementAnchor = bestCandidate') == 1
assert 'index == ERR_PoweredFlightRules.PAD_CANDIDATES' in find
assert 'ERR_PoweredFlightRules.PAD_CANDIDATES + ERR_PoweredFlightRules.PAD_REFINEMENTS' in find
assert 'localCandidates + ERR_PoweredFlightRules.AIRPORT_CANDIDATES' in find
print('PASS: 128 coarse+80 local refinement+169 airport grid candidates retain all native clearance gates; real terrain untested')