Integrate faction and random settings menus, generated objectives, scouts, convoys, air support, mortar stations and body-intel recovery. Add identified-body aggression, finite hunter UAZ deployment, dedicated objective defence and maximum AI skill with separate reduced aim accuracy. Include policy tests, native probes, evidence and documentation.
96 lines
4.4 KiB
Python
96 lines
4.4 KiB
Python
"""Execute the actual prison search body with a recording clearance predicate.
|
|
This verifies bounded search order/termination, not terrain or body clearance.
|
|
"""
|
|
from policy_harness import ROOT,body,run
|
|
source=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_SpawnPlacement.c'
|
|
search=(body(source,'bool Prison(').replace('map<int, vector> reservations','map<int, vector>* reservations')
|
|
.replace('out vector position','vector& position').replace('ERR_Actors.', 'ERR_Actors::').replace('Math.', 'Math::').replace('ERR_CampTerrain.', 'ERR_CampTerrain::'))
|
|
run('''#include <cassert>
|
|
#include <cmath>
|
|
#include <vector>
|
|
#include <set>
|
|
#include <utility>
|
|
struct vector {float x,y,z;};
|
|
vector operator+(vector a,vector b){return {a.x+b.x,a.y+b.y,a.z+b.z};}
|
|
vector Vector(float x,float y,float z){return {x,y,z};}
|
|
template<class K,class V> struct map {};
|
|
namespace ERR_Actors {vector Ground(vector value){return value;}}
|
|
namespace Math {int AbsInt(int value){return std::abs(value);}}
|
|
namespace ERR_CampTerrain {vector Rotate(vector v,float a){float r=a*3.14159265359f/180;return {v.x*std::cos(r)+v.z*std::sin(r),v.y,-v.x*std::sin(r)+v.z*std::cos(r)};}}
|
|
struct Search {
|
|
std::vector<vector> visited;
|
|
int accept=-1;
|
|
bool Clear(vector candidate,map<int,vector>*){visited.push_back(candidate);return int(visited.size())-1==accept;}
|
|
''' + search + '''
|
|
};
|
|
int main(){
|
|
Search rotated;rotated.accept=0;vector turned;assert(rotated.Prison(Vector(0,0,0),nullptr,turned,90));assert(std::abs(turned.x+1.5)<.001&&std::abs(turned.z-1.5)<.001);
|
|
const vector center={6090,100,7160};
|
|
vector result={-1,-1,-1};
|
|
Search blocked;
|
|
assert(!blocked.Prison(center,nullptr,result));
|
|
assert(blocked.visited.size()==16);
|
|
assert(result.x==-1&&result.y==-1&&result.z==-1); // no outside fallback
|
|
std::set<std::pair<float,float>> unique;
|
|
for(int i=0;i<16;++i){
|
|
vector candidate=blocked.visited[i];
|
|
float x=candidate.x-center.x,z=candidate.z-center.z;
|
|
assert(std::abs(x)<=4.5f&&std::abs(z)<=4.5f);
|
|
assert(unique.insert({x,z}).second);
|
|
assert(std::abs(x)==1.5f||std::abs(x)==4.5f);
|
|
assert(std::abs(z)==1.5f||std::abs(z)==4.5f);
|
|
int outer=(std::abs(x)==4.5f)+(std::abs(z)==4.5f);
|
|
assert(outer==(i<4?0:(i<12?1:2)));
|
|
if(i<12)assert(std::hypot(x,z)<5); // inner and edge spawns meet prior 5m check
|
|
for(int j=0;j<i;++j){
|
|
vector previous=blocked.visited[j];
|
|
assert(std::hypot(candidate.x-previous.x,candidate.z-previous.z)>=3);
|
|
}
|
|
}
|
|
// Select any first available position; never bypass the native clearance predicate.
|
|
for(int accepted=0;accepted<16;++accepted){
|
|
Search search;search.accept=accepted;
|
|
assert(search.Prison(center,nullptr,result));
|
|
assert(search.visited.size()==unsigned(accepted+1));
|
|
vector expected=blocked.visited[accepted];
|
|
assert(result.x==expected.x&&result.y==expected.y&&result.z==expected.z);
|
|
}
|
|
|
|
}
|
|
''')
|
|
print('PASS: prison search packs 16 unique 3m-spaced inside-wall centers, prioritizes inner/edge positions and never bypasses clearance')
|
|
|
|
occupancy = (body(source, 'protected bool Occupied(').replace('protected ', '')
|
|
.replace('IEntity entity', 'IEntity* entity').replace('entity.', 'entity->')
|
|
.replace('ChimeraCharacter.Cast', 'ChimeraCharacter::Cast')
|
|
.replace('vector.', 'vector::').replace('Math.', 'Math::'))
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <initializer_list>
|
|
struct vector {
|
|
float x=0,y=0,z=0;
|
|
float operator[](int i) const {return i==0?x:(i==1?y:z);}
|
|
static float DistanceXZ(vector a,vector b){return std::hypot(a.x-b.x,a.z-b.z);}
|
|
};
|
|
struct IEntity {vector position;bool character=true;vector GetOrigin(){return position;}};
|
|
namespace ChimeraCharacter {bool Cast(IEntity* e){return e&&e->character;}}
|
|
namespace Math {float AbsFloat(float f){return std::abs(f);}}
|
|
struct Placement {
|
|
IEntity* m_Exclude=nullptr; vector m_Candidate; bool m_Blocked=false;
|
|
''' + occupancy + r'''
|
|
};
|
|
int main(){
|
|
IEntity body;
|
|
for(float distance : {0.f,1.199f,1.2f,2.12f,3.f}) {
|
|
Placement p;body.position={distance,0,0};
|
|
assert(p.Occupied(&body)==(distance>=1.2f));
|
|
}
|
|
Placement vertical;body.position={0,2,0};assert(vertical.Occupied(&body));
|
|
Placement excluded;excluded.m_Exclude=&body;body.position={0,0,0};assert(excluded.Occupied(&body));
|
|
Placement scenery;body.character=false;assert(scenery.Occupied(&body)); // geometry uses the separate native trace
|
|
assert(scenery.Occupied(nullptr));
|
|
}
|
|
''')
|
|
print('PASS: physical body occupancy rejects overlap, permits diagonal neighboring slots, and excludes own body and unrelated broad-phase bounds')
|