Files
EscapistsReintroduced/tests/test_soldier_speech.py
T

95 lines
7.7 KiB
Python

"""Execute production direct-callout routing with HTTP/radio/native entities doubled."""
import re
from policy_harness import ROOT,body,run
p=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_SoldierSpeech.c'
rules=body(p,'static string Text(')+body(p,'static bool Near(')
names=re.findall(r'case ECommunicationType\.(\w+)',rules)
rules=rules.replace('ECommunicationType.','ECommunicationType::')
s='\n'.join(body(p,x) for x in ['bool Current()', 'void Prepared(', 'bool Speak(', 'bool ReadyClip(', 'bool Clip('])
s=s.replace('ERR_BakedSpeech.', 'ERR_BakedSpeech::')
s=s.replace('out string url','string& url').replace('array<SCR_PlayerController> listeners,','Array<SCR_PlayerController*> listeners,')
s=s.replace('protected ','').replace('null','nullptr').replace('ref ','')
for typ in ['IEntity','SCR_AITalkRequest','VoNComponent','RplComponent','SCR_PlayerController','ERR_SpeechService']:
s=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',s)
s=s.replace('array<int> players = {}','Array<int> players').replace('array<SCR_PlayerController> listeners = {}','Array<SCR_PlayerController*> listeners')
s=s.replace('foreach (','for (')
s=s.replace('RplComponent.Cast(actor.FindComponent(RplComponent))','actor->rpl')
s=s.replace('SCR_PlayerController.Cast(GetGame().GetPlayerManager().GetPlayerController(player))','GetGame()->GetPlayerManager()->GetPlayerController(player)')
for n in ['GetGame()','m_Mode','actor','request','von','service','m_Request','component','controller','listener','recipient']:
s=s.replace(n+'.',n+'->')
s=s.replace('GetPlayerManager().','GetPlayerManager()->').replace('ERR_Speech().','ERR_Speech()->').replace('GetControlledEntity().','GetControlledEntity()->')
s=s.replace('m_Clips.','m_Clips.').replace('ERR_GameMode.Get()','ERR_GameMode::Get()').replace('ERR_SoldierSpeechRules.','ERR_SoldierSpeechRules::').replace('ERR_Actors.','ERR_Actors::').replace('vector.Distance','Distance').replace('ECommMethod.DIRECT','ECommMethod::DIRECT').replace('LogLevel.WARNING','1')
run(r'''
#include <cassert>
#include <string>
#include <vector>
#include <map>
#include <cmath>
#include <limits>
struct string:std::string {using std::string::string;string(std::string s):std::string(s){}bool IsEmpty(){return empty();}};
template<class T>struct Array:std::vector<T>{bool IsEmpty(){return this->empty();}void Insert(T v){this->push_back(v);}int Count(){return this->size();}};
struct Map{std::map<int,string>values;bool Find(int k,string&v){if(!values.count(k))return false;v=values[k];return true;}void Set(int k,string v){values[k]=v;}};
#define Print(...)
#define PrintFormat(...)
namespace ECommunicationType{enum{'''+','.join(names)+r''',REPORT_CONTACT=100,REPORT_MOVE};}
struct ERR_SoldierSpeechRules{
'''+rules+r'''
};
bool baked=false;struct ERR_BakedSpeech{static string Soldier(int kind){return baked&&!ERR_SoldierSpeechRules::Text(kind).IsEmpty()?"baked:line":"";}};
namespace ECommMethod{enum{DIRECT,RADIO};}
struct VoNComponent{int method=ECommMethod::DIRECT;int GetCommMethod(){return method;}};
struct SCR_AITalkRequest{int m_eCommType=ECommunicationType::REPORT_MOVING;};
struct RplComponent{int Id(){return 77;}};
struct IEntity{bool fit=true,player=false;float pos=0;RplComponent value;RplComponent*rpl=&value;float GetOrigin(){return pos;}};
namespace ERR_Actors{bool CanFight(IEntity*a){return a&&a->fit;}bool Player(IEntity*a){return a&&a->player;}}
float Distance(float a,float b){return std::abs(a-b);}
struct ERR_SoldierSpeechPreparation{void*Server=nullptr;int Kind=0;};
struct ERR_SpeechService{bool ready=true,idle=true,accept=true;int requests=0;string endpoint="bridge",text;bool Ready(){return ready;}string CacheNamespace(){return endpoint;}bool IdleFor(float n){assert(n==65000);return idle;}bool Request(string t,ERR_SoldierSpeechPreparation*){requests++;text=t;return accept;}};
struct ERR_GameMode{static ERR_GameMode*live;bool active=true,recordings=true;bool ERR_RecordingsEnabled(){return recordings;}ERR_SpeechService voice;bool Active(){return active;}auto ERR_Speech(){return &voice;}static auto Get(){return live;}};ERR_GameMode*ERR_GameMode::live=nullptr;
struct SCR_PlayerController{IEntity*body=nullptr;bool ready=false;int preparations=0,delivered=0;IEntity*GetControlledEntity(){return body;}bool ERR_SoldierClipReady(ERR_GameMode*,int,string){return ready;}void ERR_PrepareSoldierClip(int,string){preparations++;}void ERR_DeliverSoldierLine(int,string,int){delivered++;}};
struct Manager{Array<SCR_PlayerController*>controllers;void GetPlayers(Array<int>&ids){for(int i=0;i<controllers.Count();i++)ids.Insert(i);}auto GetPlayerController(int i){return controllers[i];}};
struct Game{Manager players;auto GetPlayerManager(){return &players;}}game;auto GetGame(){return &game;}
struct Server{ERR_GameMode*m_Mode=nullptr;string m_Endpoint="bridge";Map m_Clips;ERR_SoldierSpeechPreparation*m_Request=nullptr;
'''+s+r'''
};
struct Fixture{ERR_GameMode mode;Server server;IEntity actor,one,two;SCR_PlayerController c1,c2;SCR_AITalkRequest request;VoNComponent von;
Fixture(){ERR_GameMode::live=&mode;server.m_Mode=&mode;c1.body=&one;c2.body=&two;one.pos=5;two.pos=40;game.players.controllers.clear();game.players.controllers.Insert(&c1);game.players.controllers.Insert(&c2);}
bool Speak(){return server.Speak(&actor,&request,&von);}void Prepared(){server.Prepared(request.m_eCommType,"clip");}
};
int main(){
{Fixture f;f.mode.recordings=false;assert(!f.Speak()&&f.mode.voice.requests==0&&f.c1.preparations==0&&f.c1.delivered==0);}
{Fixture f;baked=true;f.server.m_Endpoint="baked:speech-v1";f.mode.voice.ready=false;
assert(!f.Speak()&&f.mode.voice.requests==0&&f.c1.preparations==1);
f.c1.ready=true;f.c2.ready=true;assert(f.Speak()&&f.c1.delivered==1&&f.mode.voice.requests==0);baked=false;
}
assert(!ERR_SoldierSpeechRules::Near(-1)&&ERR_SoldierSpeechRules::Near(80)&&!ERR_SoldierSpeechRules::Near(80.1f)&&!ERR_SoldierSpeechRules::Near(std::numeric_limits<float>::quiet_NaN()));
assert(ERR_SoldierSpeechRules::Text(100).IsEmpty());
{Fixture f;assert(!f.Speak()&&f.mode.voice.requests==1);assert(!f.Speak()&&f.mode.voice.requests==1);f.Prepared();assert(!f.Speak()&&f.c1.preparations==1&&f.c2.preparations==1);f.c1.ready=true;assert(!f.Speak()&&!f.c1.delivered&&!f.c2.delivered);f.c2.ready=true;assert(f.Speak()&&f.c1.delivered==1&&f.c2.delivered==1);assert(f.server.Clip(f.request.m_eCommType,"clip")&&!f.server.Clip(f.request.m_eCommType,"other"));}
{Fixture f;f.mode.voice.accept=false;assert(!f.Speak()&&!f.server.m_Request);}
{Fixture f;f.Speak();f.server.Prepared(f.request.m_eCommType,"");assert(f.server.m_Clips.values.empty()&&!f.server.m_Request);}
{Fixture f;f.c1.ready=true;f.c2.ready=true;
assert(!f.server.Speak(&f.actor,&f.request,&f.von,1100)&&f.mode.voice.requests==1);
f.server.Prepared(1100,"opening");
assert(!f.server.Speak(&f.actor,&f.request,&f.von,1100)&&f.mode.voice.requests==2&&!f.c1.delivered);
f.server.Prepared(1101,"reply");
assert(f.server.Speak(&f.actor,&f.request,&f.von,1100)&&f.c1.delivered==1);
}
for(int bad=0;bad<9;bad++){Fixture f;
if(bad==0)f.mode.voice.ready=false;
if(bad==1)f.mode.active=false;
if(bad==2)f.mode.voice.endpoint="changed";
if(bad==3)f.von.method=ECommMethod::RADIO;
if(bad==4)f.actor.fit=false;
if(bad==5)f.actor.player=true;
if(bad==6)f.actor.rpl=nullptr;
if(bad==7)f.request.m_eCommType=100;
if(bad==8){f.one.pos=100;f.two.pos=100;}
assert(!f.Speak()&&f.mode.voice.requests==0);
}
{Fixture f;f.mode.voice.idle=false;assert(!f.Speak()&&f.mode.voice.requests==0);}
{Fixture f;f.Prepared();f.c1.ready=true;f.two.pos=100;assert(f.Speak()&&f.c1.delivered==1&&f.c2.delivered==0);}
{Fixture f;f.Prepared();f.c1.ready=true;f.two.fit=false;assert(f.Speak()&&f.c1.delivered==1&&f.c2.delivered==0);}
}
''')
print('PASS: actual-callout mapping, nearby listeners, all-client readiness, native fallback, no radio hijack, rate guard, changed endpoint and failed preparation')