"""Execute the production infantry combat state machine against waypoint doubles.""" import re from policy_harness import ROOT, body, run p=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryCombat.c' s=p.read_text() s=s.replace('class ERR_InfantryCombat', 'struct ERR_InfantryCombat') s=s.replace('protected ', '').replace('ref ', '').replace('null','nullptr') s=s.replace('EAIGroupCombatMode.FIRE_AT_WILL','1').replace('.IsEmpty()', '.empty()') for typ in ('AIWaypoint','ERR_HQTask','ERR_HQUnit'): s=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',s) s=s.replace('bool Held;', 'bool Held=false;').replace('float QuietUntil;', 'float QuietUntil=0;') s=s.replace('m_PausedWaypoint;', 'm_PausedWaypoint=nullptr;').replace('m_PausedTask;', 'm_PausedTask=nullptr;') for name in ('unit','task','Task','Group','Utility'): s=s.replace(name+'.',name+'->') s=s.rstrip()+';' run(r''' #include #include #include #include struct AIWaypoint{}; struct ERR_HQTask {int OperationId=0;bool Maneuver=false,ReleaseOnly=false,RoadIntercept=false;std::string SecuritySite;float ExpiresAt=100000;}; struct Group {std::vector queue;int adds=0,removes=0; void RemoveWaypoint(AIWaypoint* w){removes++;queue.erase(std::remove(queue.begin(),queue.end(),w),queue.end());} void AddWaypointAt(AIWaypoint* w,int slot){assert(slot==0);adds++;queue.insert(queue.begin(),w);}}; struct Utility{int mode=0,changes=0;void SetCombatMode(int m){mode=m;changes++;}}; struct ERR_HQUnit{AIWaypoint* Waypoint=nullptr;ERR_HQTask* Task=nullptr;void* Assignment=nullptr;struct Group* Group;struct Utility* Utility;}; '''+s+r''' struct Fixture {Group group;Utility utility;AIWaypoint search,home;ERR_HQTask task;ERR_HQUnit unit;ERR_InfantryCombat combat; Fixture(){unit.Group=&group;unit.Utility=&utility;unit.Task=&task;unit.Assignment=&task;unit.Waypoint=&search;group.queue={&search,&home};} }; int main(){ {Fixture f;assert(f.combat.Update(&f.unit,true,false,1000)==0);assert(f.group.removes==0); assert(f.combat.Update(&f.unit,true,true,2000)==1);assert(f.combat.Held&&f.combat.Paused()); assert(f.group.queue.size()==1&&f.group.queue[0]==&f.home&&f.utility.mode==1); assert(f.unit.Task==&f.task&&f.unit.Waypoint==&f.search&&f.unit.Assignment); assert(f.combat.Update(&f.unit,true,true,4000)==0&&f.group.removes==1&&f.utility.changes==1); assert(f.combat.Update(&f.unit,true,false,18999)==0&&f.combat.Held); assert(f.combat.Update(&f.unit,true,false,19000)==2&&!f.combat.Held&&!f.combat.Paused()); assert(f.group.adds==1&&f.group.queue.size()==2&&f.group.queue[0]==&f.search); assert(f.combat.Update(&f.unit,true,false,20000)==0&&f.group.adds==1);} // Explicit HQ flank movement survives contact; expiration returns to normal combat. {Fixture f;f.task.Maneuver=true;assert(f.combat.Update(&f.unit,true,true,2000)==0&&!f.combat.Held&&f.group.removes==0);f.task.ExpiresAt=3000;assert(f.combat.Update(&f.unit,true,true,3000)==1&&f.combat.Paused());} // Never resurrect expired, released, replaced, or newly defensive tasks. for(int invalid=0;invalid<6;invalid++){Fixture f;ERR_HQTask replacement;AIWaypoint other; f.combat.Update(&f.unit,true,true,0); if(invalid==0)f.task.ExpiresAt=15000; if(invalid==1)f.unit.Task=nullptr; if(invalid==2)f.unit.Waypoint=&other; if(invalid==3)f.unit.Task=&replacement; if(invalid==4)f.unit.Assignment=nullptr; if(invalid==5)f.task.RoadIntercept=true; assert(f.combat.Update(&f.unit,true,false,15000)==3&&f.group.adds==0);} // Existing defensive posts and recalls keep their queue and fire policy. for(int defence=0;defence<3;defence++){Fixture f; if(defence==0){f.task.SecuritySite="radio";}if(defence==1){f.task.RoadIntercept=true;}if(defence==2){f.task.ReleaseOnly=true;} assert(f.combat.Update(&f.unit,true,true,0)==1&&!f.combat.Paused()); assert(f.group.removes==0&&f.utility.changes==0); assert(f.combat.Update(&f.unit,true,false,15000)==3&&f.group.adds==0);} // Reservations/player/vehicle control cannot insert or remove waypoints. {Fixture f;assert(f.combat.Update(&f.unit,false,true,0)==0&&f.group.removes==0); f.combat.Update(&f.unit,true,true,0); assert(f.combat.Update(&f.unit,false,false,20000)==0&&f.group.adds==0); assert(f.combat.Update(&f.unit,true,false,20000)==2&&f.group.adds==1);} } ''') print('PASS: actual infantry search suspension, quiet grace, finite resume, expiry/replacement and defensive/ownership boundaries') # Guard the production integration: local pause occurs before radio delivery, and # only task downlinks are cancelled; status/vehicle messages remain independent. hq=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c' tick=body(hq,'override void Tick(') assert tick.index('UpdateInfantryCombat(unit, now)') < tick.index('unit.Downlink.Advance') assert 'unit.InfantryCombat.Held || ERR_HunterCombat.Active(unit.Group)' in body(hq,'protected bool Fighting(') assert 'if (unit.PendingTask && !unit.PendingTask.Maneuver) LostOrder(unit, now)' in body(hq,'protected void UpdateInfantryCombat(') print('PASS: infantry combat gate precedes radio delivery and preserves non-task transports') update=body(hq,'protected void UpdateInfantryCombat(').replace('protected ', '') update=update.replace('ERR_HQUnit unit','ERR_HQUnit* unit').replace('unit.', 'unit->') update=update.replace('PendingTask.', 'PendingTask->') update=update.replace('InfantryCombat.', 'InfantryCombat->').replace('Group.', 'Group->') update=update.replace('array reacting', 'std::vector reacting') update=update.replace('foreach (AIAgent actor : reacting)', 'for (AIAgent* actor : reacting)').replace('actor.', 'actor->') update=update.replace('ERR_HunterCombat.Active', 'ERR_HunterCombat::Active').replace('ERR_Actors.CanFight', 'ERR_Actors::CanFight').replace('string.Format', 'Format') run(r''' #include #include #include using string=std::string; templatestring Format(T...){return "entry";} struct AIAgent{bool fit=true;int activations=0;AIAgent* GetControlledEntity(){return this;}void ActivateAI(){activations++;}}; struct Group{int activations=0;std::vector members;void ActivateAI(){activations++;}void GetAgents(std::vector& a){a=members;}}; struct ERR_Actors{static bool CanFight(AIAgent* a){return a->fit;}}; struct ERR_HunterCombat{static bool Active(Group*){return true;}}; struct ERR_HQUnit; struct State{int change=1;bool paused=true;int Update(ERR_HQUnit*,bool eligible,bool,float){return eligible?change:0;}bool Paused(){return paused;}}; struct Task{bool Maneuver=false;}; struct ERR_HQUnit{int Id=7;string ForceSourceId="town";struct Group* Group;State* InfantryCombat;Task* PendingTask=nullptr;bool eligible=true;bool Eligible(int){return eligible;}}; struct Journal{int logs=0;void Write(string,string){logs++;}}; struct HQ{int m_Mode=1,cancelled=0;Journal m_Journal;void LostOrder(ERR_HQUnit* u,float){cancelled++;u->PendingTask=nullptr;} '''+update+r''' }; int main(){HQ h;Group g;State state;ERR_HQUnit u;u.Group=&g;u.InfantryCombat=&state; AIAgent fit,casualty;casualty.fit=false;g.members={&fit,&casualty,nullptr};Task pending;u.PendingTask=&pending; h.UpdateInfantryCombat(&u,1000);assert(g.activations==1&&fit.activations==1&&casualty.activations==0&&h.cancelled==1&&h.m_Journal.logs==1); state.change=0;h.UpdateInfantryCombat(&u,2000);assert(g.activations==1&&h.cancelled==1); state.change=1;u.PendingTask=nullptr;h.UpdateInfantryCombat(&u,3000);assert(h.cancelled==1&&fit.activations==2); u.eligible=false;h.UpdateInfantryCombat(&u,4000);assert(g.activations==2&&h.m_Journal.logs==2); u.eligible=true;pending.Maneuver=true;u.PendingTask=&pending;h.UpdateInfantryCombat(&u,4500);assert(u.PendingTask==&pending&&h.cancelled==1);state.change=2;h.UpdateInfantryCombat(&u,5000);assert(g.activations==3&&h.m_Journal.logs==4); } ''') print('PASS: actual HQ contact transition wakes only fit squad members, cancels only task orders and logs changes once')