Integrate radio-delivered convoy cover and conservative squad ammunition reports

This commit is contained in:
2026-09-20 04:23:11 +02:00
parent 9b6596eb3e
commit 9205e2129a
88 changed files with 13934 additions and 28 deletions
+31 -3
View File
@@ -9,13 +9,14 @@ def cpp(s):
('ERR_HQWatchRules.','ERR_HQWatchRules::'),('null','nullptr'),
('report.SourceId.IsEmpty()','report.SourceId.empty()'),('report.SourceId.Length()','report.SourceId.size()')]:
s=s.replace(old,new)
for typ in ('ERR_HQStatusReport','ERR_HQWatchEntry','ERR_HQReport','ERR_HQDeliveredCasualties'):
for typ in ('ERR_HQStatusReport','ERR_HQWatchEntry','ERR_HQReport','ERR_HQDeliveredCasualties','ERR_HQAmmoSnapshot'):
s=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',s)
for var in ('entry','report','validation'):s=s.replace(var+'.',var+'->')
s=s.replace('entry->Casualties.', 'entry->Casualties->')
s=s.replace('report->CasualtySourceId.IsEmpty()', 'report->CasualtySourceId.empty()')
s=s.replace('report->CasualtySourceId.Length()', 'report->CasualtySourceId.size()')
s=s.replace('report->DeathSlots,', '&report->DeathSlots,')
s=s.replace('entry->AmmoState.', 'entry->AmmoState->').replace('report->AmmoState.', 'report->AmmoState->')
return s
helper=(ROOT/'addons/ai/Scripts/Game/ERR/ERR_HQDeliveredCasualties.c').read_text()
helper=helper.replace('class ','struct ').replace('protected ','').replace('ref ','').replace('Math.','Math::')
@@ -23,10 +24,15 @@ helper=helper.replace('array<int> slots','Array<int>* slots').replace('slots.',
helper=helper.replace('array<int>','Array<int>').replace('foreach (','for (')
helper=helper.replace(' : slots)', ' : *slots)').replace('int m_Strength;', 'int m_Strength=0;')
helper=helper.replace('\n}\n','\n};\n')
ammo_source=Path(os.environ.get('ERR_AMMO_SOURCE',ROOT/'addons/ai/Scripts/Game/ERR/ERR_HQAmmoSampler.c')).read_text()
ammo=ammo_source[:ammo_source.index('class ERR_HQAmmoSampler')]
ammo=ammo.replace('class ','struct ').replace('ERR_HQAmmoSnapshot Snapshot(', 'ERR_HQAmmoSnapshot* Snapshot(')
ammo=ammo.replace('ERR_HQAmmoSnapshot copy', 'ERR_HQAmmoSnapshot* copy').replace('copy.', 'copy->')
ammo=re.sub(r'\b(bool|int) (\w+);',r'\1 \2=0;',ammo).replace('\n}\n','\n};\n')
rules='\n'.join(cpp(body(P,s)) for s in ('static bool Valid(', 'static bool RequestDue(',
'static bool InvestigationDue(', 'static int SquadLimit('))
methods='\n'.join(cpp(body(P,s)) for s in ('int Count()', 'ERR_HQWatchEntry Find(', 'ERR_HQWatchEntry At(',
'int ConfirmedLosses()', 'int SquadLimit(int baseline)', 'protected ERR_HQWatchEntry Create(',
'int ConfirmedLosses()', 'int SquadLimit(int baseline)', 'bool ArmedSearchBlocked(', 'protected ERR_HQWatchEntry Create(',
'protected void ApplyLosses(', 'bool Ingest(', 'bool IngestBody(', 'bool RequestDue(string',
'bool MarkRequested(', 'bool InvestigationDue(string', 'bool MarkInvestigating(', 'bool FinishInvestigation(', 'void Clear('))
common='\n'.join(cpp(body(U,s)) for s in ('static bool Finite(', 'static bool PositionValid('))
@@ -64,7 +70,7 @@ template<class K,class V>struct Map:std::map<K,V>{
struct ERR_HQPursuitRules{
'''+common+r'''
};
'''+helper+r'''
'''+helper+ammo+r'''
struct ERR_HQReport{
string TravelMode="body",CasualtySourceId="patrol-1";bool Distress=false;int MessageId=99,CasualtySlot=0,CasualtyStrength=4;
vector Position{900,30,900},Velocity;float ObservedAt=1000,DetailsObservedAt=-1;
@@ -74,10 +80,12 @@ struct ERR_HQReport{
};
struct ERR_HQStatusReport{
string SourceId="patrol-1";int MessageId=1;vector Position{100,20,300};
ERR_HQAmmoSnapshot* AmmoState=nullptr;
int Strength=4,Count=4,Fit=4,ConfirmedLosses=0;float ObservedAt=0;Array<int> DeathSlots;
};
struct ERR_HQWatchEntry{
string SourceId;int MessageId=0,RequestId=0;vector Position;
ERR_HQAmmoSnapshot* AmmoState=nullptr;
int Strength=0,Count=-1,Fit=-1,ConfirmedLosses=0;
ERR_HQDeliveredCasualties* Casualties=new ERR_HQDeliveredCasualties();float LastBodyDeliveredAt=-1;
float ObservedAt=-1,LastDeliveredAt=-1,AskedAt=-1,NextAttempt=0;
@@ -110,6 +118,26 @@ int main(){
assert(R::SquadLimit(2,0)==2&&R::SquadLimit(2,1)==2&&R::SquadLimit(2,2)==3);
assert(R::SquadLimit(2,-1)==2&&R::SquadLimit(2,15)==8);
assert(R::SquadLimit(100,0)==8&&R::SquadLimit(2,2147483647)==8);
Watch ammunition;ERR_HQStatusReport ammoReport;ERR_HQAmmoSnapshot ammoState;
ammoState.Complete=true;ammoState.LauncherComplete=true;ammoState.Members=4;ammoState.SmallArmsMuzzles=4;
ammoReport.AmmoState=&ammoState;
assert(!ammunition.ArmedSearchBlocked("patrol-1",1000)); // local scan is not HQ knowledge
assert(ammunition.Ingest(&ammoReport,1000)&&ammunition.ArmedSearchBlocked("patrol-1",1000));
ammoState.SmallArmsRounds=120;assert(ammunition.ArmedSearchBlocked("patrol-1",1001)); // immutable delivered copy
assert(ammunition.ArmedSearchBlocked("patrol-1",180000)&&!ammunition.ArmedSearchBlocked("patrol-1",180001));
assert(!ammunition.ArmedSearchBlocked("patrol-1",-1)&&!ammunition.ArmedSearchBlocked("patrol-1",NAN));
ammoReport.MessageId=2;ammoReport.ObservedAt=2000;
assert(ammunition.Ingest(&ammoReport,2000)&&!ammunition.ArmedSearchBlocked("patrol-1",2000));
ammoReport.MessageId=3;ammoReport.ObservedAt=3000;ammoState.SmallArmsRounds=0;ammoState.Complete=false;
assert(ammunition.Ingest(&ammoReport,3000)&&!ammunition.ArmedSearchBlocked("patrol-1",3000)); // unknown != empty
ammoReport.MessageId=4;ammoReport.ObservedAt=4000;ammoState.Complete=true;ammoState.SmallArmsChambers=1;
assert(ammunition.Ingest(&ammoReport,4000)&&!ammunition.ArmedSearchBlocked("patrol-1",4000));
ammoReport.MessageId=5;ammoReport.ObservedAt=5000;ammoState.SmallArmsChambers=0;
assert(ammunition.Ingest(&ammoReport,5000)&&ammunition.ArmedSearchBlocked("patrol-1",5000));
ERR_HQReport casualty;casualty.MessageId=901;casualty.ObservedAt=5000;
assert(ammunition.IngestBody(&casualty,5001)&&!ammunition.ArmedSearchBlocked("patrol-1",5001)); // changed roster requires fresh inventory evidence
ammoReport.MessageId=6;ammoReport.ObservedAt=6000;ammoReport.AmmoState=nullptr;
assert(ammunition.Ingest(&ammoReport,6000)&&!ammunition.ArmedSearchBlocked("patrol-1",6000)); // missing payload clears stale ammo claim
Watch watch;ERR_HQStatusReport report;
// Until an actual delivery calls Ingest the local status produces no HQ roster or missing case.
assert(watch.Count()==0&&!watch.RequestDue(report.SourceId,999999));