Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
108 lines
8.1 KiB
Plaintext
108 lines
8.1 KiB
Plaintext
--- a/tests/test_hq_watch.py
|
|
+++ b/tests/test_hq_watch.py
|
|
@@ -48,6 +48,11 @@
|
|
accept=accept.replace('ERR_HQMemoryKind.', 'ERR_HQMemoryKind::').replace('vector.DistanceXZ(', 'vector::DistanceXZ(')
|
|
accept=accept.replace('previous.', 'previous->').replace('vector.Zero', 'vector{}')
|
|
accept=accept.replace('string.Format(', 'Format(')
|
|
+# Execute the real existing HQ uplink delivery branch, with the native route result doubled.
|
|
+uplink=body(Path(os.environ.get('ERR_HQ_SOURCE', ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c')), 'if (unit.Uplink)')
|
|
+uplink=cpp(uplink).replace('unit.', 'unit->')
|
|
+for field in ('Uplink','PendingSweep','Task'):
|
|
+ uplink=uplink.replace('unit->'+field+'.','unit->'+field+'->')
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <cmath>
|
|
@@ -119,7 +124,40 @@
|
|
Array<ERR_HQReport*>m_Contacts;
|
|
'''+accept+r'''
|
|
};
|
|
+struct SweepWire {int BeliefId=0,BeliefRevision=0,WatchMessageId=0,WatchRequestId=0;string WatchSourceId;bool SweepQueued=false;};
|
|
+struct RouteWire {int result=0,calls=0;int Advance(float,JournalAdapter&){calls++;return result;}};
|
|
+struct StatusUnit {RouteWire* Uplink=nullptr;ERR_HQStatusReport* PendingStatus=nullptr;ERR_HQReport* PendingReport=nullptr;SweepWire *PendingSweep=nullptr,*Task=nullptr;void* PendingSupplyStatus=nullptr;};
|
|
+struct RadioIntake {Watch m_Watch;JournalAdapter m_Journal;struct Logistics {void Receive(void*,float){assert(false);}}m_Logistics;
|
|
+ void AcceptSweep(SweepWire*,float){assert(false);}void Accept(ERR_HQReport*,float){assert(false);}
|
|
+ void Deliver(StatusUnit* unit,float now) {
|
|
+'''+uplink+r'''
|
|
+}
|
|
+};
|
|
int main(){
|
|
+ // Physical mine evidence is detached at sampling, then copied again only on delivered radio.
|
|
+ ERR_HQAmmoSnapshot original;original.Complete=true;original.Members=4;
|
|
+ original.MineComplete=true;original.MineMembers=4;original.ReadyMines=3;
|
|
+ ERR_HQStatusReport mineWire;mineWire.AmmoState=original.Snapshot();
|
|
+ original.ReadyMines=0;original.MineComplete=false;original.MineMembers=0;
|
|
+ assert(mineWire.AmmoState!=&original&&mineWire.AmmoState->ReadyMines==3&&mineWire.AmmoState->MineComplete&&mineWire.AmmoState->MineMembers==4);
|
|
+ RadioIntake radio;StatusUnit sender;RouteWire route;sender.Uplink=&route;sender.PendingStatus=&mineWire;
|
|
+ radio.Deliver(&sender,1000);assert(!radio.m_Watch.Count()&&sender.PendingStatus==&mineWire); // still travelling
|
|
+ route.result=-1;radio.Deliver(&sender,2000);assert(!radio.m_Watch.Count()&&!sender.PendingStatus&&!sender.Uplink); // interrupted route cannot deliver
|
|
+ sender.Uplink=&route;sender.PendingStatus=&mineWire;route.result=1;radio.Deliver(&sender,3000);
|
|
+ auto mines=radio.m_Watch.Find(mineWire.SourceId);assert(mines&&mines->AmmoState!=mineWire.AmmoState&&mines->AmmoState->ReadyMines==3&&mines->AmmoState->MineComplete&&mines->AmmoState->MineMembers==4);
|
|
+ mineWire.AmmoState->ReadyMines=0;mineWire.AmmoState->MineComplete=false;assert(mines->AmmoState->ReadyMines==3&&mines->AmmoState->MineComplete);
|
|
+ // Relabelled stale observations and old radio arrivals cannot overwrite the latest delivered evidence.
|
|
+ mineWire.MessageId=2;sender.Uplink=&route;sender.PendingStatus=&mineWire;radio.Deliver(&sender,4000);
|
|
+ assert(mines->MessageId==1&&mines->AmmoState->ReadyMines==3&&!sender.PendingStatus);
|
|
+ mineWire.ObservedAt=4000;sender.Uplink=&route;sender.PendingStatus=&mineWire;radio.Deliver(&sender,124001);
|
|
+ assert(mines->MessageId==1&&mines->AmmoState->MineComplete);
|
|
+ sender.Uplink=&route;sender.PendingStatus=&mineWire;radio.Deliver(&sender,5000);
|
|
+ assert(mines->MessageId==2&&!mines->AmmoState->MineComplete&&mines->AmmoState->ReadyMines==0&&mines->AmmoState->MineMembers==4);
|
|
+ mineWire.MessageId=3;mineWire.ObservedAt=5000;mineWire.AmmoState->MineComplete=true;mineWire.AmmoState->MineMembers=3;
|
|
+ assert(!radio.m_Watch.Ingest(&mineWire,6000)&&mines->MessageId==2); // false full-roster claim
|
|
+ mineWire.AmmoState->MineMembers=4;mineWire.AmmoState->ReadyMines=513;
|
|
+ assert(!radio.m_Watch.Ingest(&mineWire,6000)&&mines->MessageId==2); // impossible finite count
|
|
+ mineWire.AmmoState->ReadyMines=0;assert(radio.m_Watch.Ingest(&mineWire,6000)&&mines->AmmoState->MineComplete&&mines->AmmoState->ReadyMines==0);
|
|
// Mounted payload obeys the same delivered chronology and is deep copied.
|
|
Watch mountedWatch;ERR_HQStatusReport mountedReport;ERR_HQMountedAmmoSnapshot mountedState;
|
|
mountedState.Complete=true;mountedState.Weapons=1;mountedState.Muzzles=1;mountedState.Magazines=1;
|
|
@@ -314,4 +352,4 @@
|
|
'GetControlledEntity(','Spawn(','CallLater(','ERR_HQMemory'):
|
|
assert forbidden not in source,forbidden
|
|
assert all(len(line)<120 for line in source.splitlines())
|
|
-print('PASS: delivered-only status snapshots, two missed check-ins/reply deadline, last reported coordinates, episode/replay guards, confirmed-loss idempotence, detached/validated mounted payload replacement and 256-record cap')
|
|
+print('PASS: delivered-only status snapshots, two missed check-ins/reply deadline, last reported coordinates, episode/replay guards, confirmed-loss idempotence, detached/validated mounted payload replacement 256-record cap and detached carried-mine radio delivery/chronology')
|
|
--- a/tests/test_hq_ammo_reporting.py
|
|
+++ b/tests/test_hq_ammo_reporting.py
|
|
@@ -4,8 +4,8 @@
|
|
from policy_harness import ROOT,body,run
|
|
BASE=Path(os.environ.get('ERR_AMMO_REPORTING_ROOT',ROOT))
|
|
P=BASE/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQAmmoReporting.c'
|
|
-H=BASE/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c'
|
|
-A=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQAmmoSampler.c'
|
|
+H=Path(os.environ.get('ERR_HQ_SOURCE',BASE/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c'))
|
|
+A=Path(os.environ.get('ERR_AMMO_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQAmmoSampler.c'))
|
|
M=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQMountedAmmoSampler.c'
|
|
|
|
def cpp(s):
|
|
@@ -32,6 +32,7 @@
|
|
template<class... T>string Format(T...){return "sample";}
|
|
struct ERR_HQPursuitRules{static bool Finite(float v){return std::isfinite(v)&&v>-1e20&&v<1e20;}};
|
|
struct ERR_HQAmmoSnapshot{
|
|
+ bool MineComplete=false;int MineMembers=0,ReadyMines=0;
|
|
bool Complete=true;int Members=4,SmallArmsMuzzles=4,SmallArmsRounds=121,SmallArmsMagazines=4,SmallArmsChambers=0;
|
|
int LauncherRounds=0,LauncherMagazines=0,LauncherChambers=0;
|
|
'''+body(A,' bool Valid(')+r'''
|
|
@@ -64,6 +65,18 @@
|
|
u.Uplink=nullptr;u.PendingStatus=nullptr;u.contact=false;}
|
|
};
|
|
int main(){
|
|
+ // Mine scarcity/completeness alone never consumes the early status slot.
|
|
+ for(int state=0;state<3;state++){
|
|
+ HQ onlyMines;ERR_HQUnit squad;squad.NextStatus=180000;squad.StatusSampler.stock.MineMembers=4;
|
|
+ squad.StatusSampler.stock.ReadyMines=state==2?2:0;squad.StatusSampler.stock.MineComplete=state!=1;
|
|
+ onlyMines.QueueReports(&squad,0);assert(squad.contact&&!squad.PendingStatus&&onlyMines.m_Journal.writes==0);
|
|
+ onlyMines.Finish(squad);squad.visible=false;squad.NextStatus=1000;onlyMines.QueueReports(&squad,1000);
|
|
+ assert(squad.PendingStatus&&squad.PendingStatus->AmmoState->ReadyMines==(state==2?2:0)&&squad.PendingStatus->AmmoState->MineComplete==(state!=1)&&squad.PendingStatus->AmmoState->MineMembers==4);
|
|
+ assert(squad.Uplink->Payload.find("; carried mines=")!=string::npos);
|
|
+ if(state==1)assert(squad.Uplink->Payload.find("carried mines=unknown/incomplete")!=string::npos);
|
|
+ auto sent=squad.PendingStatus->AmmoState;squad.StatusSampler.stock.ReadyMines=77;squad.StatusSampler.stock.MineMembers=0;squad.StatusSampler.stock.MineComplete=!sent->MineComplete;
|
|
+ assert(sent->ReadyMines==(state==2?2:0)&&sent->MineComplete==(state!=1)&&sent->MineMembers==4);
|
|
+ }
|
|
// Exact inclusive threshold counts chambers, ignores launcher stock, requires the complete current living roster.
|
|
ERR_HQAmmoReporting p; p.Initialize(6,0);ERR_HQStatusReport r;ERR_HQAmmoSnapshot a;r.AmmoState=&a;
|
|
assert(!p.Urgent(&r,0));a.SmallArmsRounds=120;assert(p.Urgent(&r,0));
|
|
@@ -138,4 +151,4 @@
|
|
assert(status==10&&contacts==90&&firing.StatusSampler.samples<=21);
|
|
}
|
|
''')
|
|
-print('PASS: actual carried/mounted early ammunition policy and status-first shared uplink; complete threshold, phases, cooldown, failures and requested check-ins')
|
|
+print('PASS: actual carried/mounted early ammunition policy and status-first shared uplink; complete threshold, phases, cooldown, failures requested check-ins and non-urgent detached carried-mine status')
|