Add mission customization, HQ support and aggression-driven AI

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.
This commit is contained in:
2026-09-23 15:07:14 +02:00
parent 04bd19327b
commit 1b1a8b5173
242 changed files with 12153 additions and 875 deletions
+11 -9
View File
@@ -15,28 +15,28 @@ def cpp(s):
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('))
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();}};
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};}
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;}};
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 Message('))+r'''
'''+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;}
@@ -57,14 +57,14 @@ int main(){
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("500 m")!=string::npos);
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().find("Hidden outpost")!=string::npos);
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("1 identified locations and 1 search areas")!=string::npos);
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());
@@ -76,7 +76,9 @@ int main(){
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::Message(0,0,"secret").empty());assert(ERR_IntelDiscoveryText::Message(1,0,"")=="A location identified. Check your field map.");
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)]'):