Merge owned systems into one addon while preserving resource identities. Gate player admission on the replicated prison scene, restore the original framed door, and hold enemy AI until escape. Correct scenario branding and author metadata, include publishing assets, and retain regression and native verification evidence.
106 lines
8.2 KiB
Python
106 lines
8.2 KiB
Python
"""Execute production HQ convoy planning/delivery with detached reports and radio-boundary doubles."""
|
|
from policy_harness import ROOT, body, run
|
|
import re
|
|
H = ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c'
|
|
|
|
def cpp(s):
|
|
s=s.replace('protected ', '').replace('null','nullptr').replace('foreach (','for (')
|
|
for x in ('ERR_HQPursuitRules','vector','string'): s=s.replace(x+'.',x+'::')
|
|
for typ in ('ERR_HQUnit','ERR_HQReport','ERR_HQWatchEntry','ERR_HQRoute','ERR_ConvoyCombatOrder','ERR_ConvoyVehicle'):
|
|
s=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',s)
|
|
for x in ('entry','unit','best','report','vehicle','contact','route','order','m_ConvoyTask'):
|
|
s=s.replace(x+'.',x+'->')
|
|
s=s.replace('->Combat.', '->Combat->').replace('->Receipt.', '->Receipt->')
|
|
s=s.replace('unit->Downlink.', 'unit->Downlink->')
|
|
s=re.sub(r'(ERR_\w+\* \w+);',r'\1 = nullptr;',s)
|
|
s=s.replace('bool accepted;', 'bool accepted = false;')
|
|
return s
|
|
methods='\n'.join(cpp(body(H,s)) for s in ('protected bool ConvoyPositionKnown(',
|
|
'protected ERR_HQReport ConvoyContact(', 'protected void PlanConvoy(', 'protected void DeliverConvoyOrder('))
|
|
downlink=cpp(body(H,'if (unit.Downlink)\n'))
|
|
cancel=cpp(body(H,'void CancelPending('))
|
|
src=H.read_text()
|
|
tick=body(H,'override void Tick(')
|
|
assert tick.index('PlanConvoy(now);') < tick.index('PlanResupply(now);') < tick.index('Assign(now);')
|
|
assert 'm_Contacts' in body(H,'protected ERR_HQReport ConvoyContact(')
|
|
assert 'GetOrigin' not in methods and 'Target' not in methods
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <cmath>
|
|
#include <map>
|
|
struct string:std::string {using std::string::string; template<class...T>static string Format(T...){return "order";}};
|
|
struct vector {float x=0,z=0;static float DistanceXZ(vector a,vector b){return std::hypot(a.x-b.x,a.z-b.z);}};
|
|
struct ERR_HQPursuitRules {static bool Finite(float n){return std::isfinite(n);}};
|
|
struct SCR_AIGroup{};
|
|
struct ERR_HQReport {int MessageId=0;float ObservedAt=-1;vector Position;};
|
|
struct ERR_HQWatchEntry {bool Closed=false;int Count=2;float ObservedAt=-1;vector Position;};
|
|
struct Watch {std::map<string,ERR_HQWatchEntry*> entries;ERR_HQWatchEntry* Find(string key){return entries.count(key)?entries[key]:nullptr;}};
|
|
struct Journal {int seq=100;int Message(){return ++seq;}void Write(string,string,int){}};
|
|
struct ERR_HQRoute {int MessageId=0;string Payload;int result=0;int Advance(float,Journal&){return result;}};
|
|
struct Network {bool available=true;int calls=0;ERR_HQRoute* Route(void*,bool down){assert(down);calls++;return available?new ERR_HQRoute:nullptr;}};
|
|
struct ERR_ConvoyCombatOrder {int CommandId=0,ReportId=0,VehicleIndex=0;string SourceId;SCR_AIGroup* Group=nullptr;bool Hold=false;float ObservedAt=-1,IssuedAt=0,ExpiresAt=0;};
|
|
struct CombatState {bool Held=false;int LastCommandId=0,ReportId=0;float ObservedAt=-1;};
|
|
struct ERR_ConvoyVehicle {int SupplyToken=0,Index=0;SCR_AIGroup* Group=nullptr;bool Released=false,Arrived=false,Registered=true;CombatState state;CombatState* Combat=&state;};
|
|
struct Receipt {string SourceId="convoy-1";};
|
|
struct ConvoyTask {bool Stopped=false,Ready=true;struct Receipt receipt;struct Receipt* Receipt=&receipt;std::vector<ERR_ConvoyVehicle*> Vehicles;};
|
|
struct Service {int calls=0;bool valid=true;ERR_ConvoyCombatOrder* last=nullptr;
|
|
bool ApplyCombatOrder(ConvoyTask* task,ERR_ConvoyCombatOrder* o,float){calls++;last=o;if(!task||!valid)return false;
|
|
auto s=task->Vehicles.at(o->VehicleIndex)->Combat;s->Held=o->Hold;s->LastCommandId=o->CommandId;s->ReportId=o->ReportId;s->ObservedAt=o->ObservedAt;return true;}};
|
|
struct ERR_HQUnit {SCR_AIGroup* Group=nullptr;bool ReservedForSupport=true;void* Radio=this;string ForceSourceId="vehicle-0";
|
|
ERR_HQRoute* Downlink=nullptr;ERR_ConvoyCombatOrder* PendingConvoyOrder=nullptr;void* PendingOrder=nullptr;void* PendingTask=nullptr;
|
|
bool PendingCheckin=false;float NextStatus=0;
|
|
'''+cancel+r'''
|
|
};
|
|
struct HQ {Watch m_Watch;Network m_Network;Journal m_Journal;Service m_Convoys;ConvoyTask* m_ConvoyTask=nullptr;
|
|
std::vector<ERR_HQUnit*> m_Units;std::vector<ERR_HQReport*> m_Contacts;int ordinary=0,lost=0;
|
|
void DeliverOrder(ERR_HQUnit*,float){ordinary++;}void LostOrder(ERR_HQUnit* u,float){lost++;u->CancelPending();}
|
|
'''+methods+r'''
|
|
void TickDownlink(ERR_HQUnit* unit,float now){
|
|
'''+downlink+r'''
|
|
}
|
|
};
|
|
int main(){
|
|
HQ h;SCR_AIGroup g;ERR_HQUnit u;u.Group=&g;h.m_Units={&u};ConvoyTask t;h.m_ConvoyTask=&t;
|
|
ERR_ConvoyVehicle v;v.Group=&g;t.Vehicles={&v};ERR_HQWatchEntry e;e.ObservedAt=1000;e.Position={0,0};
|
|
ERR_HQReport r;r.MessageId=1;r.ObservedAt=2000;r.Position={200,0};
|
|
h.PlanConvoy(3000);assert(!u.Downlink); // no delivered own status or target report
|
|
h.m_Watch.entries[u.ForceSourceId]=&e;h.PlanConvoy(3000);assert(!u.Downlink);
|
|
h.m_Contacts={&r};v.SupplyToken=100001;h.PlanConvoy(3000);assert(!u.Downlink);v.SupplyToken=0;
|
|
h.m_Contacts={&r};h.m_Network.available=false;h.PlanConvoy(3000);assert(!u.Downlink&&!v.Combat->Held);
|
|
h.m_Network.available=true;h.PlanConvoy(3000);assert(u.Downlink&&u.PendingConvoyOrder->Hold);
|
|
auto pending=u.PendingConvoyOrder;int calls=h.m_Network.calls;h.PlanConvoy(4000);assert(h.m_Network.calls==calls&&u.PendingConvoyOrder==pending);
|
|
h.TickDownlink(&u,4000);assert(!v.Combat->Held&&h.m_Convoys.calls==0); // transmitting, no application
|
|
u.Downlink->result=-1;h.TickDownlink(&u,5000);assert(!u.Downlink&&!u.PendingConvoyOrder&&!v.Combat->Held);
|
|
h.PlanConvoy(6000);u.Downlink->result=1;h.TickDownlink(&u,8000);assert(v.Combat->Held&&h.m_Convoys.calls==1&&h.ordinary==0);
|
|
h.PlanConvoy(9000);assert(!u.Downlink); // same delivered contact never duplicates a HOLD
|
|
ERR_HQReport newer=r;newer.MessageId=2;newer.ObservedAt=12000;h.m_Contacts={&newer};h.PlanConvoy(13000);
|
|
assert(u.PendingConvoyOrder->ReportId==2);u.Downlink->result=1;h.TickDownlink(&u,15000);assert(v.Combat->ReportId==2);
|
|
h.PlanConvoy(71000);assert(!u.Downlink); // less than60s quiet
|
|
h.PlanConvoy(72000);assert(u.Downlink&&!u.PendingConvoyOrder->Hold);
|
|
newer.MessageId=3;newer.ObservedAt=73000;u.Downlink->result=1;h.TickDownlink(&u,74000);
|
|
assert(v.Combat->Held&&h.m_Convoys.calls==2&&!u.Downlink); // new report rejects in-flight RESUME
|
|
h.m_Network.available=false;h.PlanConvoy(75000);assert(!u.Downlink&&v.Combat->Held);
|
|
h.m_Network.available=true;h.PlanConvoy(76000);assert(u.PendingConvoyOrder->Hold);u.Downlink->result=1;
|
|
newer.MessageId=4;newer.ObservedAt=77000;h.TickDownlink(&u,78000);assert(h.m_Convoys.calls==3&&v.Combat->ReportId==3); // a newer nearby report cannot starve HOLD delivery
|
|
h.PlanConvoy(79000);u.Downlink->result=1;h.TickDownlink(&u,80000);assert(v.Combat->ReportId==4);
|
|
h.PlanConvoy(137000);assert(u.Downlink&&!u.PendingConvoyOrder->Hold);u.Downlink->result=1;h.TickDownlink(&u,139000);
|
|
assert(!v.Combat->Held&&h.m_Convoys.calls==5);h.PlanConvoy(140000);assert(!u.Downlink);
|
|
// Report proximity uses detached Watch coordinates only; no live entity reads exist.
|
|
newer.ObservedAt=150000;newer.MessageId=5;newer.Position={401,0};h.PlanConvoy(151000);assert(!u.Downlink);
|
|
newer.Position={400,0};h.PlanConvoy(151000);assert(u.Downlink);u.CancelPending();
|
|
e.Closed=true;h.PlanConvoy(151000);assert(!u.Downlink);e.Closed=false;e.Count=0;h.PlanConvoy(151000);assert(!u.Downlink);e.Count=2;
|
|
e.ObservedAt=160000;h.PlanConvoy(151000);assert(!u.Downlink);e.ObservedAt=0;h.PlanConvoy(181000);assert(!u.Downlink);
|
|
e.ObservedAt=150000;v.Released=true;h.PlanConvoy(151000);assert(!u.Downlink);v.Released=false;v.Arrived=true;
|
|
h.PlanConvoy(151000);assert(!u.Downlink);v.Arrived=false;t.Ready=false;h.PlanConvoy(151000);assert(!u.Downlink);t.Ready=true;
|
|
u.ReservedForSupport=false;h.PlanConvoy(151000);assert(!u.Downlink);u.ReservedForSupport=true;
|
|
h.PlanConvoy(151000);assert(u.Downlink);h.m_Convoys.valid=false;u.Downlink->result=1;h.TickDownlink(&u,152000);
|
|
assert(!v.Combat->Held&&!u.Downlink&&h.ordinary==0); // service capture/death/canonical guards are authoritative
|
|
// A routine check-in shares the channel and cannot be displaced by convoy planning.
|
|
u.PendingCheckin=true;u.Downlink=new ERR_HQRoute;calls=h.m_Network.calls;h.PlanConvoy(153000);assert(h.m_Network.calls==calls);
|
|
u.Downlink->result=1;h.TickDownlink(&u,154000);assert(u.NextStatus==154000&&!u.Downlink&&h.ordinary==0);
|
|
}
|
|
''')
|
|
print('HQ convoy delivered-report planning and downlink regressions passed')
|