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.
89 lines
9.0 KiB
Python
89 lines
9.0 KiB
Python
"""Execute production feedback queue/delivery bodies with fake engine boundaries."""
|
|
from policy_harness import ROOT, body, run
|
|
P=ROOT/'addons/core/Scripts/Game/ERR/20_Intel/ERR_IntelDiscoveryFeedback.c'
|
|
source=P.read_text()
|
|
def cpp(s):
|
|
s=s.replace('protected ', '').replace('static ', '')
|
|
for name in ('Replication','Math','ERR_Phase','ERR_LocationKind','ERR_GameMode','ERR_IntelDiscoveryText','SCR_Global','SCR_ChatComponent'):
|
|
s=s.replace(name+'.',name+'::')
|
|
s=s.replace('ERR_LocationComponent location','ERR_LocationComponent* location')
|
|
s=s.replace('location.', 'location->').replace('GetWorld().','GetWorld()->').replace('GetGame().','GetGame()->')
|
|
s=s.replace('int seen;','int seen=0;').replace('int identified;','int identified=0;').replace('int rough;','int rough=0;')
|
|
s=s.replace('foreach (ERR_LocationComponent* location, int knowledge : m_ERRIntelPending)','for (auto [location, knowledge] : m_ERRIntelPending.values)')
|
|
s=s.replace('Rpc(ERR_ReceiveDiscoveryFeedback, ', 'Rpc(')
|
|
s=s.replace('OnLocationDiscovered().Remove(ERR_QueueDiscoveryFeedback);','OnLocationDiscovered().Remove(0);')
|
|
s=s.replace('OnTick().Remove(ERR_FlushDiscoveryFeedback);','OnTick().Remove(0);')
|
|
s=s.replace('string.Format(', 'Format(')
|
|
return s
|
|
methods='\n'.join(cpp(body(P,s)) for s in ('protected void ERR_QueueDiscoveryFeedback(', 'protected void ERR_FlushDiscoveryFeedback(', 'protected void ERR_ReceiveDiscoveryFeedback(', 'protected void ERR_StopDiscoveryFeedback(', 'void ERR_IntelExhausted('))
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <string>
|
|
#include <map>
|
|
#include <vector>
|
|
struct string:std::string {using std::string::string;using std::string::operator=;string(std::string s):std::string(s){}bool IsEmpty()const{return empty();}bool Contains(const char* text)const{return find(text)!=npos;}};
|
|
string Format(string text,int identified,int rough){text.replace(text.find("%1"),2,std::to_string(identified));text.replace(text.find("%2"),2,std::to_string(rough));return text;}
|
|
struct Replication {static bool server;static bool IsServer(){return server;}};bool Replication::server=true;
|
|
struct SCR_Global {static bool edit;static bool IsEditMode(){return edit;}};bool SCR_Global::edit=false;
|
|
struct SCR_ChatComponent {static std::vector<string> messages;static void RadioProtocolMessage(string text){messages.push_back(text);}};std::vector<string> SCR_ChatComponent::messages;
|
|
struct Math {static float Max(float a,float b){return a>b?a:b;}};
|
|
namespace ERR_Phase {enum {PREPARING,PRISON,RADIO_SEARCH,EXTRACTION};}
|
|
namespace ERR_LocationKind {enum {PRISON,RADIO,EXTRACTION,OUTPOST,DEPOT,MORTAR};}
|
|
struct ERR_LocationComponent {int kind=ERR_LocationKind::OUTPOST,knowledge=1;string label="Hidden outpost";int Kind(){return kind;}int Knowledge(){return knowledge;}string Label(){return label;}string Id(){return "outpost-id";}};
|
|
template<class K,class V> struct Map {std::map<K,V> values;bool Find(K k,V& v){auto i=values.find(k);if(i==values.end())return false;v=i->second;return true;}bool Contains(K k){return values.count(k);}int Count(){return values.size();}bool IsEmpty(){return values.empty();}void Set(K k,V v){values[k]=v;}void Clear(){values.clear();}};
|
|
struct World {float time=0;float GetWorldTime(){return time;}} world;
|
|
struct Game {bool controller=true;bool GetPlayerController(){return controller;}} game;
|
|
Game* currentGame=&game;Game* GetGame(){return currentGame;}
|
|
struct Events {int removes=0;void Remove(int){removes++;}};
|
|
struct ERR_IntelDiscoveryText {static
|
|
'''+cpp(body(P,'static string Type('))+'static '+cpp(body(P,'static string Message('))+r'''
|
|
};
|
|
struct ERR_GameMode {
|
|
static ERR_GameMode* current;static ERR_GameMode* Get(){return current;}
|
|
bool active=true;int phase=ERR_Phase::RADIO_SEARCH;bool Active(){return active;}int Phase(){return phase;}World* GetWorld(){return &world;}
|
|
Map<ERR_LocationComponent*,int> m_ERRIntelSeen,m_ERRIntelPending;
|
|
float m_ERRIntelMessageAt=0;bool m_ERRIntelFeedbackStopped=false;int m_ERRIntelMessageSequence=0,m_ERRIntelReceivedSequence=0;
|
|
Events discovered,tick;Events& OnLocationDiscovered(){return discovered;}Events& OnTick(){return tick;}
|
|
int rpcCount=0;void Rpc(int sequence,string message){rpcCount++;ERR_ReceiveDiscoveryFeedback(sequence,message);}
|
|
'''+methods+r'''
|
|
};
|
|
ERR_GameMode* ERR_GameMode::current=nullptr;
|
|
int main(){
|
|
ERR_GameMode m;ERR_GameMode::current=&m;ERR_LocationComponent site;
|
|
// Do not reveal startup sites, prison or automatic helicopter disclosure.
|
|
m.phase=ERR_Phase::PREPARING;m.ERR_QueueDiscoveryFeedback(&site,0,1);assert(m.m_ERRIntelPending.IsEmpty());
|
|
m.phase=ERR_Phase::RADIO_SEARCH;for(int kind:{ERR_LocationKind::PRISON,ERR_LocationKind::EXTRACTION}){site.kind=kind;m.ERR_QueueDiscoveryFeedback(&site,0,1);}assert(m.m_ERRIntelPending.IsEmpty());
|
|
site.kind=ERR_LocationKind::OUTPOST;Replication::server=false;m.ERR_QueueDiscoveryFeedback(&site,0,1);assert(m.m_ERRIntelPending.IsEmpty());Replication::server=true;
|
|
m.ERR_QueueDiscoveryFeedback(nullptr,0,1);m.ERR_QueueDiscoveryFeedback(&site,1,1);m.ERR_QueueDiscoveryFeedback(&site,0,2);m.ERR_QueueDiscoveryFeedback(&site,-1,1);assert(m.m_ERRIntelPending.IsEmpty());
|
|
// Rough clues do not disclose labels, kinds or coordinates. Listen callbacks dedupe.
|
|
m.ERR_QueueDiscoveryFeedback(&site,0,1);m.ERR_FlushDiscoveryFeedback(1499);assert(SCR_ChatComponent::messages.empty());
|
|
m.ERR_FlushDiscoveryFeedback(1500);assert(m.rpcCount==1&&SCR_ChatComponent::messages.size()==1);assert(SCR_ChatComponent::messages.back().find("Hidden")==string::npos);assert(SCR_ChatComponent::messages.back().find("New location marked. Type unknown.")!=string::npos);
|
|
m.ERR_QueueDiscoveryFeedback(&site,0,1);assert(m.m_ERRIntelPending.IsEmpty());
|
|
site.knowledge=2;world.time=2000;m.ERR_QueueDiscoveryFeedback(&site,1,2);m.ERR_FlushDiscoveryFeedback(6499);assert(m.rpcCount==1);
|
|
m.ERR_FlushDiscoveryFeedback(6500);assert(m.rpcCount==2&&SCR_ChatComponent::messages.back()=="Hey! A outpost was identified in the intel!");
|
|
// A same-batch rough→exact update becomes one final identified location.
|
|
ERR_GameMode batch;ERR_GameMode::current=&batch;ERR_LocationComponent a,b;world.time=0;
|
|
batch.ERR_QueueDiscoveryFeedback(&a,0,1);a.knowledge=2;batch.ERR_QueueDiscoveryFeedback(&a,1,2);batch.ERR_QueueDiscoveryFeedback(&b,0,1);batch.ERR_FlushDiscoveryFeedback(1500);
|
|
assert(SCR_ChatComponent::messages.back().find("Hey! A outpost was identified in the intel!")!=string::npos && SCR_ChatComponent::messages.back().find("New location marked. Type unknown.")!=string::npos);
|
|
// End/restart cancels pending output and removes both hooks, new mode is clean.
|
|
world.time=2000;b.knowledge=2;batch.ERR_QueueDiscoveryFeedback(&b,1,2);batch.ERR_StopDiscoveryFeedback();assert(batch.m_ERRIntelSeen.IsEmpty()&&batch.m_ERRIntelPending.IsEmpty()&&batch.discovered.removes==1&&batch.tick.removes==1);
|
|
int calls=batch.rpcCount;batch.ERR_FlushDiscoveryFeedback(99999);batch.ERR_QueueDiscoveryFeedback(&b,0,2);assert(batch.rpcCount==calls&&batch.m_ERRIntelPending.IsEmpty());
|
|
ERR_GameMode next;ERR_GameMode::current=&next;auto shown=SCR_ChatComponent::messages.size();batch.ERR_ReceiveDiscoveryFeedback(999,"old round");assert(SCR_ChatComponent::messages.size()==shown);
|
|
// Remote delivery gates old sequence, absent controller, terminal and editor state.
|
|
Replication::server=false;next.ERR_ReceiveDiscoveryFeedback(1,"remote");next.ERR_ReceiveDiscoveryFeedback(1,"duplicate");assert(SCR_ChatComponent::messages.size()==shown+1);
|
|
game.controller=false;next.ERR_ReceiveDiscoveryFeedback(2,"absent");game.controller=true;next.active=false;next.ERR_ReceiveDiscoveryFeedback(2,"ended");next.active=true;SCR_Global::edit=true;next.ERR_ReceiveDiscoveryFeedback(2,"editor");SCR_Global::edit=false;assert(next.m_ERRIntelReceivedSequence==1);
|
|
currentGame=nullptr;next.ERR_ReceiveDiscoveryFeedback(2,"no game");currentGame=&game;
|
|
next.ERR_ReceiveDiscoveryFeedback(2,"new");assert(SCR_ChatComponent::messages.size()==shown+2);
|
|
// Queue is bounded even for malformed/unusually large authored scenarios.
|
|
Replication::server=true;ERR_GameMode bounded;ERR_GameMode::current=&bounded;ERR_LocationComponent many[65];for(auto& item:many)bounded.ERR_QueueDiscoveryFeedback(&item,0,1);assert(bounded.m_ERRIntelSeen.Count()==64&&bounded.m_ERRIntelPending.Count()==64);
|
|
assert(ERR_IntelDiscoveryText::Type(ERR_LocationKind::MORTAR,"any")=="mortar site");
|
|
ERR_GameMode::current=&m;m.ERR_IntelExhausted();assert(SCR_ChatComponent::messages.back()=="Every known location has already been identified. This intel doesn't help us.");
|
|
assert(ERR_IntelDiscoveryText::Message(0,0,"secret").empty());assert(ERR_IntelDiscoveryText::Message(1,0,"")=="Hey! A location was identified in the intel!");
|
|
}
|
|
''')
|
|
for required in ('OnLocationDiscovered().Insert(ERR_QueueDiscoveryFeedback)', 'OnTick().Insert(ERR_FlushDiscoveryFeedback)', 'OnStop().Insert(ERR_StopDiscoveryFeedback)', '[RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]'):
|
|
assert required in source
|
|
for forbidden in ('HideHint(', 'CallLater(', 'Discover(', 'SetKnowledge('):
|
|
assert forbidden not in source
|
|
print('PASS: production Intel feedback coalescing, rough secrecy, rate limit, bounded dedupe, local/remote delivery and round cleanup; native UI/RPC not exercised')
|