Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
57 lines
4.6 KiB
Python
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')
|