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.
77 lines
7.2 KiB
Python
77 lines
7.2 KiB
Python
"""Execute actual authored storage-anchor selection and native trace guard."""
|
|
import os,re
|
|
from pathlib import Path
|
|
from policy_harness import ROOT,run
|
|
s=Path(os.environ.get('ERR_INFANTRY_ACCESS_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryAmmoAccess.c')).read_text()
|
|
s=re.sub(r'//[^\n]*','',s).replace('class ERR_InfantryAmmoAccess','struct ERR_InfantryAmmoAccess').replace('protected ','').replace('notnull ','')
|
|
s=s.replace('ActionsManagerComponent.Cast(donor.FindComponent(ActionsManagerComponent))','donor->manager').replace('SCR_OpenVehicleStorageAction.Cast(action)','dynamic_cast<SCR_OpenVehicleStorageAction*>(action)').replace('ChimeraCharacter.Cast(recipient)','dynamic_cast<Character*>(recipient)')
|
|
s=s.replace('array<UserActionContext>', 'array<UserActionContext*>').replace('array<BaseUserAction>', 'array<BaseUserAction*>')
|
|
s=s.replace('out array<vector> points','Array<vector>& points').replace('array<','Array<').replace('foreach (','for (')
|
|
for name in ('ActionsManagerComponent','UserActionContext','BaseUserAction','ChimeraCharacter','TraceParam','ERR_InfantryAmmoAccess'):
|
|
s=re.sub(r'\b'+name+r' (\w+)',name+r'* \1',s)
|
|
for name in ('manager','context','action','character','trace','access'):
|
|
s=s.replace(name+'.',name+'->')
|
|
for name in ('ERR_ConvoyRules','ERR_ForceRules','TraceFlags','vector'):
|
|
s=s.replace(name+'.',name+'::')
|
|
s=s.replace('GetGame().GetWorld()','GetGame()->GetWorld()').replace('GetWorld().','GetWorld()->').replace('null','nullptr')
|
|
s=s.replace('TraceMove(trace, Filter)','TraceMove(trace, [this](IEntity e){return Filter(e);})')
|
|
s=re.sub(r'\b(bool|IEntity) (\w+);',r'\1 \2{};',s).replace('\n}\n','\n};\n')
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <vector>
|
|
#include <cmath>
|
|
#include <limits>
|
|
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;using std::vector<T>::operator=;int Count(){return this->size();}bool IsEmpty(){return this->empty();}void Insert(T t){this->push_back(t);}};
|
|
struct vector{float x=0,y=0,z=0;float& operator[](int n){return n==0?x:n==1?y:z;}static float Distance(vector a,vector b){return std::sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));}};
|
|
struct Entity;struct Manager;using IEntity=Entity*;using Vehicle=Entity*;
|
|
struct Entity{virtual ~Entity(){}Manager*manager=nullptr;IEntity parent=nullptr;};
|
|
struct Character:Entity{vector aim={0,1.4,0};vector AimingPosition(){return aim;}};using ChimeraCharacter=Character;
|
|
struct BaseUserAction{virtual ~BaseUserAction(){}IEntity owner=nullptr;auto GetOwner(){return owner;}};
|
|
struct SCR_OpenVehicleStorageAction:BaseUserAction{};
|
|
struct UserActionContext{Array<BaseUserAction*>actions;vector anchor={2,1.4,0};bool valid=true;void GetActionsList(Array<BaseUserAction*>&a){a=actions;}bool GetTransformationWorld(vector (&mat)[4]){mat[3]=anchor;return valid;}};
|
|
struct Manager{bool enabled=true;Array<UserActionContext*>contexts;bool IsEnabled(){return enabled;}void GetContextList(Array<UserActionContext*>&a){a=contexts;}};using ActionsManagerComponent=Manager;
|
|
struct TraceFlags{static constexpr int WORLD=1,ENTS=2;};static constexpr int TRACE_LAYER_CAMERA=99;
|
|
struct TraceParam{vector Start,End;int Flags=0,LayerMask=0;};
|
|
struct World{float fraction=1;Array<IEntity>obstacles;int traces=0;vector lastStart,lastEnd;
|
|
template<class F>float TraceMove(TraceParam*p,F filter){traces++;lastStart=p->Start;lastEnd=p->End;assert(p->Flags==3&&p->LayerMask==99);for(auto entity:obstacles)if(filter(entity))return .5;return fraction;}};
|
|
struct Game{World world;auto GetWorld(){return &world;}} game;Game*GetGame(){return &game;}
|
|
struct ERR_ConvoyRules{static bool PartOf(IEntity a,IEntity root){int n=0;while(a&&n++<32){if(a==root)return true;a=a->parent;}return false;}};
|
|
struct ERR_ForceRules{static bool FiniteVector(vector v){return std::isfinite(v.x)&&std::isfinite(v.y)&&std::isfinite(v.z);}};
|
|
''' + s + r'''
|
|
int main(){
|
|
Entity truck,truckPart,gear,wall,otherTruck,otherChild;Character actor;truckPart.parent=&truck;gear.parent=&actor;otherChild.parent=&otherTruck;
|
|
Manager manager;truck.manager=&manager;UserActionContext context;SCR_OpenVehicleStorageAction storage;storage.owner=&truck;context.actions={&storage};manager.contexts={&context};
|
|
Array<vector>points;assert(ERR_InfantryAmmoAccess::Points(&truck,points)&&points.Count()==1);
|
|
ERR_InfantryAmmoAccess access;assert(access.Check(&truck,&actor));assert(game.world.lastStart.y==1.4f&&game.world.lastEnd.x==2);
|
|
// Own hull/gear cannot falsely block an interior-authored native storage anchor.
|
|
game.world.obstacles={&truck,&truckPart,&actor,&gear};assert(access.Check(&truck,&actor));
|
|
// A wall or another vehicle still blocks even when owned geometry preceded it.
|
|
game.world.obstacles.push_back(&wall);assert(!access.Check(&truck,&actor));
|
|
game.world.obstacles={&truck,&otherChild};assert(!access.Check(&truck,&actor));game.world.obstacles.clear();
|
|
for(float value:{0.f,.99f,-1.f,2.f,std::numeric_limits<float>::quiet_NaN()}){game.world.fraction=value;assert(!access.Check(&truck,&actor));}game.world.fraction=1;
|
|
context.anchor.x=4.01;assert(!access.Check(&truck,&actor));context.anchor.x=0;assert(!access.Check(&truck,&actor));context.anchor.x=2;
|
|
actor.aim.y=std::numeric_limits<float>::quiet_NaN();assert(!access.Check(&truck,&actor));actor.aim.y=1.4;
|
|
context.valid=false;assert(!access.Check(&truck,&actor));context.valid=true;
|
|
context.anchor.z=std::numeric_limits<float>::quiet_NaN();assert(!access.Check(&truck,&actor));context.anchor.z=0;
|
|
storage.owner=&otherTruck;assert(!access.Check(&truck,&actor));storage.owner=&truck;
|
|
BaseUserAction door;door.owner=&truck;context.actions={&door};assert(!access.Check(&truck,&actor));context.actions={&storage};
|
|
manager.enabled=false;assert(!access.Check(&truck,&actor));manager.enabled=true;
|
|
// Stock Ural has 37 contexts and Mi-8 has 82: a valid storage action near the end must remain discoverable.
|
|
for(int count:{37,64,82,128}){
|
|
std::vector<UserActionContext> stockContexts(count);manager.contexts.clear();
|
|
for(auto& authored:stockContexts){authored.actions={&door};manager.contexts.push_back(&authored);}
|
|
stockContexts.back().actions={&storage};
|
|
assert(ERR_InfantryAmmoAccess::Points(&truck,points)&&points.Count()==1&&access.Check(&truck,&actor));
|
|
}
|
|
manager.contexts.clear();manager.contexts.resize(129,&context);assert(!access.Check(&truck,&actor));manager.contexts={&context};
|
|
// Native helicopter door contexts include 38 actions, most unrelated to storage.
|
|
for(int count:{33,38,64}){context.actions.clear();context.actions.resize(count,&door);context.actions.back()=&storage;assert(access.Check(&truck,&actor));}
|
|
context.actions.resize(65,&storage);assert(!access.Check(&truck,&actor));context.actions={&storage};
|
|
manager.contexts={nullptr};assert(!access.Check(&truck,&actor));manager.contexts={&context};
|
|
assert(!access.Check(&truck,&wall));assert(!access.Check(nullptr,&actor));
|
|
// A blocked context never skips an intervening external entity; another independently clear authored context may qualify.
|
|
UserActionContext distant;distant.actions={&storage};distant.anchor={9,1.4,0};manager.contexts={&distant,&context};assert(access.Check(&truck,&actor));
|
|
}
|
|
''')
|
|
print('PASS: actual authored storage anchors, native trace flags, own-hierarchy exclusion, external wall/vehicle obstruction, range, unknown geometry and bounded context refusal')
|