Files
EscapistsReintroduced/tests/test_soldier_observation.py
T
jakub 6a667d8deb Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
2026-10-13 22:52:53 +02:00

57 lines
4.6 KiB
Python

"""Run the production observation sampler against controlled native perception snapshots."""
import re
from policy_harness import ROOT,body,run
p=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_SoldierSpeechContext.c'
s=body(p,'protected void ERR_SpeechObserve(').replace('protected ','')
s=s.replace('SCR_AIInfoComponent.Cast(m_Agent.FindComponent(SCR_AIInfoComponent))','infoValue')
s=s.replace('array<BaseTarget> targets = {}','Array<BaseTarget*> targets').replace('array<IEntity> counted = {}','Array<IEntity*> counted')
s=s.replace('foreach (','for (').replace('null','nullptr').replace('bool threat;','bool threat=false;').replace('BaseTarget anchor;','BaseTarget* anchor=nullptr;')
for typ in ['SCR_AIInfoComponent','SCR_AIThreatSystem','BaseTarget','IEntity']:
s=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',s)
s=s.replace('vector position','float position').replace('ChimeraCharacter.Cast(seen)','(seen && seen->character)')
for obj in ['info','system','target','seen','anchor','m_Entity']:
s=s.replace(obj+'.',obj+'->')
s=s.replace('m_Perception.','m_Perception->')
for obj in ['EAIThreatState','ERR_HQObservation','ERR_Actors','ERR_SoldierConversationRules']:
s=s.replace(obj+'.',obj+'::')
s=s.replace('Math.Max','std::max').replace('vector.DistanceXZ','Distance')
run(r'''
#include <cassert>
#include <vector>
#include <algorithm>
#include <cmath>
template<class T>struct Array:std::vector<T>{using std::vector<T>::operator=;void Insert(T x){this->push_back(x);}bool Contains(T x){return std::find(this->begin(),this->end(),x)!=this->end();}int Count(){return this->size();}};
struct IEntity{bool character=true,fit=true;float pos=0;float GetOrigin(){return pos;}};
struct BaseTarget{IEntity*actor=nullptr;float age=0,trace=1,exposure=1,pos=100;float GetTimeSinceSeen(){return age;}float GetTraceFraction(){return trace;}float GetExposure(){return exposure;}IEntity*GetTargetEntity(){return actor;}float GetLastSeenPosition(){return pos;}};
namespace ERR_HQObservation{bool IsClearSighting(float age,float trace,float exposure){return age>=0&&age<=.5f&&trace>=1&&exposure>0;}}
namespace ERR_Actors{bool CanFight(IEntity*a){return a&&a->fit;}}
namespace ETargetCategory{enum{ENEMY};}
namespace EAIThreatState{enum{SAFE,ALERTED};}
float Distance(float a,float b){return std::abs(a-b);}
struct ERR_SoldierConversationRules{
'''+body(p,'static bool Approaching(')+r'''
};
struct Perception{Array<BaseTarget*>targets;void GetTargetsList(Array<BaseTarget*>&out,int){out=targets;}};
struct SCR_AIThreatSystem{float m_ERRSpeechOvertUntil=0;};
struct SCR_AIInfoComponent{int state=EAIThreatState::SAFE;Perception perception,*m_Perception=&perception;SCR_AIThreatSystem threat;int GetThreatState(){return state;}auto GetThreatSystem(){return &threat;}};
struct Sampler{SCR_AIInfoComponent info,*infoValue=&info;IEntity actor,*m_Entity=&actor,*m_ERRSpeechObserved=nullptr;int m_ERRSpeechVisible=0;
bool m_ERRSpeechApproaching=false,m_ERRSpeechThreat=false,m_ERRSpeechOvert=false;
float m_ERRSpeechOvertUntil=0,m_ERRSpeechThreatUntil=0,m_ERRSpeechApproachingUntil=0,m_ERRSpeechObservedAt=0,m_ERRSpeechTargetPosition=0,m_ERRSpeechObserverPosition=0;
'''+s.replace('ETargetCategory.ENEMY','ETargetCategory::ENEMY')+r'''
};
int main(){
Sampler s;IEntity a,b,c;BaseTarget one,two,three;one.actor=&a;two.actor=&b;three.actor=&c;
s.info.perception.targets={&one,&one,&two,&three};
s.ERR_SpeechObserve(10000);assert(s.m_ERRSpeechVisible==3&&s.m_ERRSpeechThreat&&!s.m_ERRSpeechOvert); // sighting is not open combat; duplicate ignored
two.trace=.5;three.age=1;s.ERR_SpeechObserve(11000);assert(s.m_ERRSpeechVisible==1); // occluded and stale do not count
s.actor.pos=50;s.ERR_SpeechObserve(12000);assert(!s.m_ERRSpeechApproaching); // our advance is not their advance
one.pos=95;s.ERR_SpeechObserve(14000);assert(s.m_ERRSpeechApproaching); // actual observed target motion
s.ERR_SpeechObserve(14001);assert(s.m_ERRSpeechApproaching); // immediate transmit retains observation
s.info.threat.m_ERRSpeechOvertUntil=35000;s.ERR_SpeechObserve(15000);assert(s.m_ERRSpeechOvert);
s.info.perception.targets.clear();s.ERR_SpeechObserve(16000);assert(!s.m_ERRSpeechVisible&&!s.m_ERRSpeechApproaching&&s.m_ERRSpeechThreat); // memory silences chatter, no invented visible count
s.ERR_SpeechObserve(80000);assert(!s.m_ERRSpeechThreat&&!s.m_ERRSpeechOvert);
c.character=false;three.age=0;s.info.perception.targets={&three};s.ERR_SpeechObserve(81000);assert(!s.m_ERRSpeechVisible); // vehicle never reveals passengers
}
''')
print('PASS: production perception sampler deduplicates visible humans, excludes hidden contacts and vehicle occupants, distinguishes target motion, retains threat memory')