Capture current mission, AI, map and administration development

Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
This commit is contained in:
2026-10-13 22:52:53 +02:00
parent a951a96edc
commit 6a667d8deb
8104 changed files with 2124945 additions and 2061 deletions
@@ -0,0 +1 @@
PASS: actual engineer native group-envelope admission and phase cancellation, preparation/deadline, original custody/identity, once-only callback removal, cancellation quarantine, withdrawal/clearance, native refusal and owned-action cleanup; native execution untested
@@ -0,0 +1,81 @@
// Mission-owned finite attempt history. Registration and assignment release
// never reset it. Values are copied before native work; no live enemy queries.
class ERR_EngineerMinePost
{
ref ERR_ForceRecord Record;
SCR_AIGroup Group;
string Source;
int Strength;
ref ERR_HQTask Task;
ref ERR_HQReport Assignment;
int MessageId;
int BeliefId;
int Revision;
float QueuedAt;
float ObservedAt;
vector Post;
bool HasSite;
vector Site;
}
class ERR_EngineerMinePosts
{
protected ref array<ref ERR_EngineerMinePost> m_Posts = {};
int Count() { return m_Posts.Count(); }
bool Eligible(ERR_ForceRecord record, string source, ERR_HQTask task, ERR_HQReport assignment, float now)
{
if (!record || !record.Group || !record.Physical || record.Eliminated || source.IsEmpty() || record.SourceId != source) return false;
if (record.InitialStrength < 1 || record.InitialStrength > 32 || !task || !assignment || m_Posts.Count() >= 32) return false;
if (!task.RoadIntercept || task.ReleaseOnly || task.Investigation() || !task.SecuritySite.IsEmpty()) return false;
if (task.BeliefId <= 0 || task.BeliefRevision <= 0 || assignment.MessageId <= 0) return false;
if (!ERR_ForceRules.Finite(now) || !ERR_ForceRules.Finite(task.QueuedAt) || !ERR_ForceRules.Finite(task.ExpiresAt)
|| !ERR_ForceRules.Finite(assignment.ObservedAt) || assignment.ObservedAt < 0 || task.QueuedAt < assignment.ObservedAt
|| task.QueuedAt > now || now >= task.ExpiresAt) return false;
if (!ERR_ForceRules.FiniteVector(task.Destination) || !ERR_ForceRules.FiniteVector(assignment.Position)) return false;
int used;
foreach (ERR_EngineerMinePost previous : m_Posts)
{
if (!previous) return false;
float separation = vector.DistanceXZ(task.Destination, previous.Post);
if (!ERR_ForceRules.Finite(separation) || separation <= 160) return false;
if (previous.Task == task || previous.Assignment == assignment) return false;
// Same source or physical group under another record cannot acquire quota.
if (previous.Record != record)
{
if (previous.Source == source || previous.Group == record.Group) return false;
continue;
}
if (previous.Source != source || previous.Group != record.Group || previous.Strength != record.InitialStrength) return false;
used++;
if (used >= 5 || task.QueuedAt <= previous.QueuedAt || assignment.ObservedAt <= previous.ObservedAt
|| assignment.MessageId == previous.MessageId) return false;
if (task.BeliefId == previous.BeliefId && task.BeliefRevision <= previous.Revision) return false;
}
return true;
}
// Returns immutable entry index or -1; a refused site/item still consumes it.
int Consume(ERR_ForceRecord record, string source, ERR_HQTask task, ERR_HQReport assignment, float now)
{
if (!Eligible(record, source, task, assignment, now)) return -1;
ERR_EngineerMinePost entry = new ERR_EngineerMinePost();
entry.Record = record; entry.Group = record.Group; entry.Source = source; entry.Strength = record.InitialStrength;
entry.Task = task; entry.Assignment = assignment; entry.MessageId = assignment.MessageId;
entry.BeliefId = task.BeliefId; entry.Revision = task.BeliefRevision;
entry.QueuedAt = task.QueuedAt; entry.ObservedAt = assignment.ObservedAt; entry.Post = task.Destination;
m_Posts.Insert(entry);
return m_Posts.Count() - 1;
}
bool RecordSite(int index, vector site)
{
if (index < 0 || index >= m_Posts.Count() || !ERR_ForceRules.FiniteVector(site)) return false;
ERR_EngineerMinePost entry = m_Posts[index];
if (!entry || entry.HasSite || !ERR_EngineerMineSite.InBand(entry.Post, site)) return false;
entry.Site = site; entry.HasSite = true;
return true;
}
void PriorSites(out array<vector> sites)
{
sites.Clear();
foreach (ERR_EngineerMinePost entry : m_Posts)
if (entry.HasSite) sites.Insert(entry.Site);
}
}
@@ -0,0 +1,251 @@
--- a/tests/test_engineer_mine.py
+++ b/tests/test_engineer_mine.py
@@ -9,7 +9,9 @@
s=s.replace('protected override ','').replace('protected ','').replace('null','nullptr').replace('ref ','')
s=s.replace('array<','Array<').replace(' = {};',';').replace('foreach (','for (')
s=s.replace('array<IEntity> attempted','Array<IEntity>* attempted')
- for typ in ['SCR_AIGroupUtilityComponent','ERR_GameMode','ERR_HQUnit','ERR_HQTask','SCR_ChimeraAIAgent','SCR_InventoryStorageManagerComponent','SCR_PressureTriggerComponent','InventoryItemComponent','DamageManagerComponent','FactionAffiliationComponent','Faction','Physics']:
+ s=s.replace('Array<vector> priorSites','Array<vector>* priorSites').replace('priorSites.','priorSites->').replace(': priorSites)',': *priorSites)')
+ s=s.replace(', m_PriorSites)',', &m_PriorSites)').replace('Math.Clamp','Math::Clamp')
+ for typ in ['ERR_ForceRecord','SCR_AIGroupUtilityComponent','ERR_GameMode','ERR_HQUnit','ERR_HQTask','SCR_ChimeraAIAgent','SCR_InventoryStorageManagerComponent','SCR_PressureTriggerComponent','InventoryItemComponent','DamageManagerComponent','FactionAffiliationComponent','Faction','Physics']:
s=re.sub(r'\b'+typ+r' (\w+)',typ+r'* \1',s)
for ns in ['ERR_ForceRules','Replication','ERR_GameMode','ERR_HunterCombat','ERR_Actors','CompartmentAccessComponent','SCR_FactionManager','SCR_ChimeraAIAgent','SCR_AIUtils','ERR_EnemyLoadoutCatalog','ERR_Inventory','ERR_InfantryAmmoTransfer','InventoryItemComponent','DamageManagerComponent','FactionAffiliationComponent','ChimeraCharacter','Vehicle','SCR_InventoryStorageManagerComponent','SCR_PressureTriggerComponent','ERR_EngineerMineRules','ERR_HQ','vector']:
s=s.replace(ns+'.',ns+'::')
@@ -25,9 +27,9 @@
s=s.replace('ERR_EngineerMine(Array<IEntity> attempted)','ERR_EngineerMine(Array<IEntity>* attempted)')
s=s.replace('Faction* faction;','Faction* faction=nullptr;').replace('Array<AIActionBase>', 'Array<AIActionBase*>').replace('void ERR_EngineerMine(', 'ERR_EngineerMine(')
return s
-policies='\n'.join(body(P,x) for x in ['static bool Within(','static float PrepareSince(','static bool Prepared(','static bool MayArm('])
+policies='\n'.join(body(P,x) for x in ['static bool CorridorClear(','static bool WithinWorkArea(','static bool Within(','static float PrepareSince(','static bool Prepared(','static bool MayArm('])
callback='\n'.join(body(P,x) for x in ['protected void Finish(','protected override void OnComplete(','protected override void OnAllComplete(','protected override void OnFailed(','protected override void OnAllFailed('])
-methods='\n'.join(body(P,x) for x in ['void ERR_EngineerMine(','bool Finished(','protected bool Refuse(','protected bool Quiet(','protected bool Owned(','protected bool MineValid(','protected bool Carried(','protected bool Grounded(','protected bool Placed(','protected void ClearAction(','protected void Move(','protected void WaitAtMine(','protected bool CheckFriendly(','protected bool SelectActor(','bool Begin(','void Stop(','void Tick('])
+methods='\n'.join(body(P,x) for x in ['void ERR_EngineerMine(','bool Finished(','bool BlocksNextPost(','protected bool Refuse(','protected bool Quiet(','protected bool Owned(','protected bool MineValid(','protected bool Carried(','protected bool Grounded(','protected bool Placed(','protected void ClearAction(','protected void Move(','protected void WaitAtMine(','protected bool CheckFriendly(','protected bool SelectActor(','bool Begin(','void Stop(','void Tick('])
run(r'''
#include <cassert>
#include <cmath>
@@ -36,10 +38,11 @@
#include <algorithm>
#include <functional>
struct string:std::string {using std::string::string;using std::string::operator=;bool IsEmpty(){return empty();}};
-struct vector {float x=0,y=0,z=0;float LengthSq(){return x*x+y*y+z*z;}
+struct vector {float x=0,y=0,z=0;float operator[](int i){return i==0?x:i==1?y:z;}float LengthSq(){return x*x+y*y+z*z;}
static float Distance(vector a,vector b){return std::sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));}
static float DistanceXZ(vector a,vector b){a.y=b.y=0;return Distance(a,b);}};
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;using std::vector<T>::operator=;int Count(){return this->size();}bool Contains(T t){return std::find(this->begin(),this->end(),t)!=this->end();}void Insert(T t){this->push_back(t);}};
+namespace Math{float Clamp(float v,float a,float b){return std::max(a,std::min(v,b));}}
struct Entity;using IEntity=Entity*;struct SCR_AIGroup;struct SCR_ChimeraAIAgent;struct SCR_InventoryStorageManagerComponent;struct ERR_EngineerMineRemoval;
struct Faction {bool enemy=false;bool IsFactionEnemy(Faction* f){return f&&f->enemy;}};
struct Physics {vector velocity;vector GetVelocity(){return velocity;}};
@@ -101,7 +104,7 @@
'''+cpp(callback)+r'''};
bool SCR_InventoryStorageManagerComponent::TryRemoveItemFromInventory(IEntity,void*,ERR_EngineerMineRemoval* cb){calls++;callback=cb;if(inlineResult>0)cb->OnComplete();if(inlineResult<0)cb->OnFailed();return accepted;}
struct ERR_EngineerMine {
- ERR_GameMode* m_Mode=nullptr;ERR_HQUnit* m_Unit=nullptr;SCR_AIGroup* m_Group=nullptr;ERR_HQTask* m_Task=nullptr;ERR_HQReport* m_Assignment=nullptr;ERR_ForceRecord* m_Record=nullptr;string m_SourceId;int m_Strength=0;IEntity m_Actor=nullptr;int m_Slot=0;IEntity m_Mine=nullptr;SCR_PressureTriggerComponent* m_Trigger=nullptr;SCR_InventoryStorageManagerComponent* m_Inventory=nullptr;SCR_AIUtilityComponent* m_Utility=nullptr;SCR_AIBehaviorBase* m_Action=nullptr;ERR_EngineerMineRemoval* m_Removal=nullptr;Array<IEntity>* m_Attempted=nullptr;Array<IEntity> m_IntelFolders;vector m_Position,m_Rally;float m_Started=-1,m_LastTick=-1,m_PreparedAt=-1,m_NextClearance=0;int m_State=0,m_Checked=0;bool m_FriendlyNear=false,m_Begun=false,m_Stopped=false;
+ ERR_GameMode* m_Mode=nullptr;ERR_HQUnit* m_Unit=nullptr;SCR_AIGroup* m_Group=nullptr;ERR_HQTask* m_Task=nullptr;ERR_HQReport* m_Assignment=nullptr;ERR_ForceRecord* m_Record=nullptr;string m_SourceId;int m_Strength=0;IEntity m_Actor=nullptr;int m_Slot=0;IEntity m_Mine=nullptr;SCR_PressureTriggerComponent* m_Trigger=nullptr;SCR_InventoryStorageManagerComponent* m_Inventory=nullptr;SCR_AIUtilityComponent* m_Utility=nullptr;SCR_AIBehaviorBase* m_Action=nullptr;ERR_EngineerMineRemoval* m_Removal=nullptr;Array<IEntity>* m_Attempted=nullptr;Array<IEntity> m_IntelFolders;Array<vector> m_PriorSites;vector m_Position,m_Rally;float m_Started=-1,m_LastTick=-1,m_PreparedAt=-1,m_NextClearance=0;int m_State=0,m_Checked=0;bool m_FriendlyNear=false,m_Begun=false,m_Stopped=false;
'''+cpp(methods)+r'''};
struct Fixture {Faction faction,hostile{true};SCR_AIGroup group;SCR_AIGroupUtilityComponent groupUtility;SCR_AIUtilityComponent utility;SCR_ChimeraAIAgent agent;SCR_InventoryStorageManagerComponent inventory;Entity actor,mine;Prefab prefab;Physics physics;InventoryItemComponent item;SCR_PressureTriggerComponent trigger;Sampler roster;ERR_HQTask task;ERR_HQReport report;ERR_ForceRecord record;Ledger ledger;ERR_GameMode mode;ERR_HQUnit unit;Array<IEntity> attempted;ERR_EngineerMine controller{&attempted};
Fixture(){ERR_GameMode::current=&mode;Replication::server=true;ERR_HunterCombat::engaged=false;currentGame=&game;game.world.query.clear();game.world.queries=0;
@@ -114,6 +117,22 @@
void Withdraw(){Prepare();Drop();inventory.callback->OnComplete();controller.Tick(12001);assert(controller.m_State==3&&mine.moves==1&&utility.additions==3);}
};
int main(){
+ // History geometry uses bounded detached coordinates, not live mine entities.
+ Array<vector> hazards;hazards.Insert({20,0,20});
+ assert(!ERR_EngineerMineRules::CorridorClear({0,0,0},{40,0,0},&hazards));hazards[0].z=20.001f;
+ assert(ERR_EngineerMineRules::CorridorClear({0,0,0},{40,0,0},&hazards));
+ hazards[0]={20,0,0};assert(!ERR_EngineerMineRules::CorridorClear({0,0,0},{0,0,0},&hazards));
+ assert(ERR_EngineerMineRules::WithinWorkArea({0,0,0},{60,100,0}));assert(!ERR_EngineerMineRules::WithinWorkArea({0,0,0},{60.001f,0,0}));
+ for(int invalid=0;invalid<3;invalid++){Fixture f;Array<vector> prior;if(invalid==0)prior.Insert({20,0,5});if(invalid==1)prior.resize(33);if(invalid==2)prior.Insert({NAN,0,0});f.actor.position={-20,0,0};assert(!f.controller.Begin(&f.mode,&f.unit,&f.task,{60,0,0},1000,&prior));assert(f.attempted.empty()&&!f.utility.additions&&!f.inventory.calls);}
+ {Fixture f;Array<vector> prior;prior.Insert({100,0,0});assert(f.controller.Begin(&f.mode,&f.unit,&f.task,{30,0,0},1000,&prior));prior[0]={30,0,0};f.controller.Tick(2000);assert(!f.controller.Finished()&&f.controller.m_PriorSites[0].x==100);f.actor.position={60,0,0};f.controller.Tick(3000);assert(!f.controller.Finished());auto action=f.controller.m_Action;f.actor.position={60.001f,0,0};f.controller.Tick(4000);assert(f.controller.Finished()&&action->completed==1&&!f.inventory.calls);}
+ {Fixture f;Array<vector> prior;prior.Insert({50,0,30});assert(f.controller.Begin(&f.mode,&f.unit,&f.task,{30,0,0},1000,&prior));f.actor.position={50,0,11};f.controller.Tick(2000);assert(f.controller.Finished()&&!f.inventory.calls&&!f.trigger.calls);}
+ // Active job blocks its exact original record. Stopped pending native removal
+ // remains blocking even if its surviving item is destroyed; callbacks only settle.
+ {Fixture f;ERR_ForceRecord other;f.Begin();assert(f.controller.BlocksNextPost(&f.record)&&!f.controller.BlocksNextPost(&other)&&!f.controller.BlocksNextPost(nullptr));f.controller.Stop();assert(!f.controller.BlocksNextPost(&f.record));}
+ for(int completion=0;completion<3;completion++){Fixture f;f.Prepare();f.controller.Stop();auto callback=f.inventory.callback;assert(f.controller.BlocksNextPost(&f.record));f.mine.alive=false;assert(f.controller.BlocksNextPost(&f.record));if(completion==0)callback->OnComplete();if(completion==1)callback->OnFailed();if(completion==2)f.controller.m_Mine=nullptr;assert(!f.controller.BlocksNextPost(&f.record));f.Drop();callback->OnComplete();f.controller.Tick(14000);assert(f.inventory.calls==1&&!f.mine.moves&&!f.trigger.calls&&f.attempted.Count()==1);}
+ // An exact different physical mine can be used at a distinct post, while the
+ // first exact entity remains in the shared lifetime tombstone.
+ {Fixture f;f.Begin();f.controller.Stop();Entity second=f.mine;SCR_PressureTriggerComponent secondTrigger;InventoryItemComponent secondItem;second.trigger=&secondTrigger;second.item=&secondItem;secondItem.slot=&f.inventory;f.inventory.items={&f.mine,&second};ERR_HQTask next;ERR_HQReport nextReport;next.Destination={200,0,0};f.unit.Task=&next;f.unit.Assignment=&nextReport;f.actor.position={200,0,0};f.group.center={200,0,0};Array<vector> prior;prior.Insert({30,0,0});ERR_EngineerMine job(&f.attempted);assert(job.Begin(&f.mode,&f.unit,&next,{230,0,0},2000,&prior));assert(job.m_Mine==&second&&f.attempted.Count()==2&&f.attempted[0]==&f.mine);}
// Current native defend radius is strict and three dimensional: the broad
// 25..60 m mission band does not authorize bypassing the group's own envelope.
for(int fault=0;fault<4;fault++){Fixture f;SCR_DefendWaypoint defend;f.group.waypoint=&defend;vector point={30,0,0};
--- a/tests/test_engineer_mine_hq.py
+++ b/tests/test_engineer_mine_hq.py
@@ -1,80 +1,56 @@
-"""Execute actual HQ engineer admission/maintenance; native movement is separate."""
+"""Actual HQ admission with actual finite post ledger; native jobs are boundary doubles."""
from pathlib import Path
import os,re
from policy_harness import ROOT,body,run
+from test_engineer_mine_posts import BOUNDARY,ledger_cpp
p=Path(os.environ.get('ERR_HQ_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c'))
s=body(p,'protected void MaintainEngineerMines(')
-s=s.replace('protected ','').replace('int active;','int active=0;').replace('vector position;','vector position=0;')
-s=s.replace('foreach (ERR_HQUnit unit : m_Units)','for (ERR_HQUnit* unit : m_Units)').replace('foreach (ERR_EngineerMine job : m_EngineerMineJobs)','for (ERR_EngineerMine* job : m_EngineerMineJobs)')
-for name in ('Replication','vector','ERR_EngineerMineSite','ERR_GameMode'):s=s.replace(name+'.',name+'::')
-for name in ('unit','m_Mode','job'):s=s.replace(name+'.',name+'->')
-s=s.replace('->EngineerMine.','->EngineerMine->').replace('->Task.','->Task->').replace('->Group.','->Group->')
-s=s.replace('vector::DistanceXZ','DistanceXZ')
-run(r'''
-#include <cassert>
-#include <vector>
-#include <cmath>
-using vector=float;
-float DistanceXZ(float a,float b){return std::fabs(a-b);}
-struct Replication{static bool server;static bool IsServer(){return server;}};bool Replication::server=true;
-struct ERR_GameMode{static ERR_GameMode* current;static ERR_GameMode* Get(){return current;}bool active=true;bool Active(){return active;}};ERR_GameMode* ERR_GameMode::current=nullptr;
-struct Task{bool RoadIntercept=true;float ExpiresAt=100000,Destination=0;};
-struct Group{float origin=0;float GetOrigin(){return origin;}};
-struct AttemptArray:std::vector<int>{int Count(){return size();}};
+s=s.replace('protected ','').replace('int active;','int active=0;').replace('bool blocked;','bool blocked=false;').replace('vector position;','vector position;')
+s=s.replace('array<vector> priorSites = {};','Array<vector> priorSites;')
+for typ in ['ERR_HQUnit','ERR_EngineerMine','ERR_ForceRecord']:s=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',s)
+s=s.replace('foreach (','for (')
+for name in ('ERR_ForceRules','Replication','vector','ERR_EngineerMineSite','ERR_GameMode'):s=s.replace(name+'.',name+'::')
+for name in ('unit','m_Mode','job','previous','record'):s=re.sub(r'\b'+name+r'\.',name+'->',s)
+for name in ['EngineerMine','Task','Group','StatusSampler']:s=s.replace('->'+name+'.','->'+name+'->')
+s=s.replace('Forces().','Forces()->')
+run(BOUNDARY+ledger_cpp()+r'''
+struct Ledger{Array<ERR_ForceRecord*> records;ERR_ForceRecord* FindGroup(SCR_AIGroup* group){for(auto r:records)if(r->Group==group)return r;return nullptr;}};
+struct Replication{static inline bool server=true;static bool IsServer(){return server;}};
+struct ERR_GameMode{static inline ERR_GameMode* current=nullptr;static ERR_GameMode* Get(){return current;}bool active=true;Ledger ledger;bool Active(){return active;}Ledger* Forces(){return &ledger;}};
+using AttemptArray=Array<int>;
+struct Sampler{bool owns=true;int strength=2;bool OwnsRoster(SCR_AIGroup*){return owns;}int InitialStrength(){return strength;}};
struct ERR_HQUnit;
-struct ERR_EngineerMine {
- int ticks=0,stops=0;bool finished=false;
- AttemptArray& attempts;
- ERR_EngineerMine(AttemptArray& a):attempts(a){}
- bool Begin(ERR_GameMode*,ERR_HQUnit*,Task*,float,float);
- void Tick(float){ticks++;}bool Finished(){return finished;}void Stop(){stops++;finished=true;}
-};
-struct ERR_HQUnit{
- ERR_EngineerMine* EngineerMine=nullptr;bool EngineerMineAttempted=false;
- struct Task* Task=nullptr;void* Assignment=nullptr;void* PendingTask=nullptr;void* PendingOrder=nullptr;void* Downlink=nullptr;
- struct Group* Group=nullptr;bool eligible=true,fighting=false,begin=true;
- bool Eligible(ERR_GameMode*){return eligible;}
-};
-bool ERR_EngineerMine::Begin(ERR_GameMode*,ERR_HQUnit* unit,struct Task*,float,float){attempts.push_back(1);return unit->begin;}
-struct ERR_EngineerMineSite{static bool valid;static int calls;static bool Select(float,float& p){calls++;p=30;return valid;}};
-bool ERR_EngineerMineSite::valid=true;int ERR_EngineerMineSite::calls=0;
-struct Jobs:std::vector<ERR_EngineerMine*>{int Count(){return size();}void Insert(ERR_EngineerMine* j){push_back(j);}};
-struct HQ{
- Jobs m_EngineerMineJobs;
- bool m_Stopping=false;ERR_GameMode* m_Mode=nullptr;AttemptArray m_EngineerMineAttempts;std::vector<ERR_HQUnit*> m_Units;
- bool Fighting(ERR_HQUnit* unit){return unit->fighting;}
-'''+s+r'''
-};
-struct Fixture{
- ERR_GameMode mode;HQ h;Task tasks[4];Group groups[4];ERR_HQUnit units[4];int report;
- Fixture(){ERR_GameMode::current=&mode;h.m_Mode=&mode;for(int i=0;i<4;i++){units[i].Task=&tasks[i];units[i].Group=&groups[i];units[i].Assignment=&report;h.m_Units.push_back(&units[i]);}}
+struct ERR_EngineerMine{int ticks=0,stops=0;bool finished=false,unresolved=false;ERR_ForceRecord* record=nullptr;Array<vector> prior;AttemptArray& attempts;
+ ERR_EngineerMine(AttemptArray& a):attempts(a){} bool Begin(ERR_GameMode*,ERR_HQUnit*,ERR_HQTask*,vector,float,Array<vector> sites={});
+ void Tick(float){ticks++;}bool Finished(){return finished;}void Stop(){stops++;finished=true;}bool BlocksNextPost(ERR_ForceRecord* r){return r==record&&(!finished||unresolved);}};
+struct ERR_HQUnit{ERR_EngineerMine* EngineerMine=nullptr;bool EngineerMineAttempted=false;ERR_HQTask* Task=nullptr;ERR_HQReport* Assignment=nullptr;void* PendingTask=nullptr;void* PendingOrder=nullptr;void* Downlink=nullptr;SCR_AIGroup* Group=nullptr;Sampler* StatusSampler=nullptr;string ForceSourceId;bool eligible=true,fighting=false,begin=true;bool Eligible(ERR_GameMode*){return eligible;}};
+bool ERR_EngineerMine::Begin(ERR_GameMode* mode,ERR_HQUnit*unit,ERR_HQTask*,vector,float,Array<vector> sites){record=mode->Forces()->FindGroup(unit->Group);prior=sites;if(unit->begin)attempts.Insert(1);return unit->begin;}
+struct HQ{Array<ERR_EngineerMine*> m_EngineerMineJobs;ERR_EngineerMinePosts m_EngineerMinePosts;bool m_Stopping=false;ERR_GameMode* m_Mode=nullptr;AttemptArray m_EngineerMineAttempts;Array<ERR_HQUnit*> m_Units;bool Fighting(ERR_HQUnit*u){return u->fighting;}
+'''+s+r'''};
+struct Fixture{ERR_GameMode mode;HQ h;ERR_HQTask tasks[40];ERR_HQReport reports[40];SCR_AIGroup groups[4];ERR_ForceRecord records[4];Sampler samplers[4];ERR_HQUnit units[4];
+ Fixture(){ERR_GameMode::current=&mode;h.m_Mode=&mode;ERR_EngineerMineSite::valid=true;for(int i=0;i<40;i++){tasks[i].Destination={float(i*1000),0,0};tasks[i].QueuedAt=10+i*10;tasks[i].BeliefRevision=i+1;reports[i].ObservedAt=5+i*10;reports[i].MessageId=i+1;}for(int i=0;i<4;i++){records[i].Group=&groups[i];records[i].SourceId=std::to_string(i);units[i].ForceSourceId=records[i].SourceId;units[i].Group=&groups[i];units[i].StatusSampler=&samplers[i];mode.ledger.records.Insert(&records[i]);NewPost(i,i);h.m_Units.Insert(&units[i]);}}
+ void NewPost(int u,int n){units[u].Task=&tasks[n];units[u].Assignment=&reports[n];groups[u].position=tasks[n].Destination;}
+ void Tick(){h.MaintainEngineerMines(1000);}
~Fixture(){for(auto j:h.m_EngineerMineJobs)delete j;}
};
int main(){
- {Fixture f;f.h.MaintainEngineerMines(1);assert(f.h.m_EngineerMineAttempts.Count()==1);assert(!f.units[1].EngineerMineAttempted);
- f.h.MaintainEngineerMines(2);assert(f.units[0].EngineerMine->ticks==1&&f.h.m_EngineerMineAttempts.Count()==2);
- f.h.MaintainEngineerMines(3);assert(!f.units[2].EngineerMineAttempted);
- f.units[0].EngineerMine->Stop();f.h.MaintainEngineerMines(4);assert(f.units[2].EngineerMineAttempted&&!f.units[3].EngineerMineAttempted);assert(f.h.m_EngineerMineAttempts.Count()==3);
- f.units[1].EngineerMine->Stop();f.units[2].EngineerMine->Stop();f.h.MaintainEngineerMines(5);f.units[3].EngineerMine->Stop();f.h.MaintainEngineerMines(6);assert(f.h.m_EngineerMineAttempts.Count()==4);}
- {Fixture f;f.h.MaintainEngineerMines(1);auto job=f.units[0].EngineerMine;job->Stop();f.h.m_Units.clear();f.h.MaintainEngineerMines(2);assert(f.h.m_EngineerMineJobs.Count()==1&&job->ticks==1);}
- {Fixture f;for(int i=0;i<32;i++){auto job=new ERR_EngineerMine(f.h.m_EngineerMineAttempts);job->Stop();f.h.m_EngineerMineJobs.Insert(job);}int before=ERR_EngineerMineSite::calls;f.h.MaintainEngineerMines(1);assert(!f.units[0].EngineerMineAttempted&&ERR_EngineerMineSite::calls==before);}
- {Fixture f;for(auto& u:f.units)u.begin=false;int before=ERR_EngineerMineSite::calls;f.h.MaintainEngineerMines(1);assert(ERR_EngineerMineSite::calls==before+1&&!f.units[1].EngineerMineAttempted);f.h.MaintainEngineerMines(2);assert(ERR_EngineerMineSite::calls==before+2&&f.units[0].EngineerMine->ticks==1);}
- {Fixture f;ERR_EngineerMineSite::valid=false;int before=ERR_EngineerMineSite::calls;f.h.MaintainEngineerMines(1);assert(ERR_EngineerMineSite::calls==before+1&&!f.units[1].EngineerMineAttempted);f.h.MaintainEngineerMines(2);assert(ERR_EngineerMineSite::calls==before+2);ERR_EngineerMineSite::valid=true;}
- for(int invalid=0;invalid<9;invalid++) {Fixture f;f.h.m_Units.resize(1);auto& u=f.units[0];
- if(invalid==0){u.Assignment=nullptr;}if(invalid==1){u.Task=nullptr;}if(invalid==2){f.tasks[0].RoadIntercept=false;}
- if(invalid==3){u.PendingTask=&f.report;}if(invalid==4){u.PendingOrder=&f.report;}if(invalid==5){u.Downlink=&f.report;}
- if(invalid==6){u.eligible=false;}if(invalid==7){u.fighting=true;}if(invalid==8){f.groups[0].origin=21;}
- int before=ERR_EngineerMineSite::calls;f.h.MaintainEngineerMines(1);assert(!u.EngineerMineAttempted&&!u.EngineerMine&&ERR_EngineerMineSite::calls==before);}
- {Fixture f;f.h.m_Units.resize(1);f.tasks[0].ExpiresAt=1;f.h.MaintainEngineerMines(1);assert(!f.units[0].EngineerMineAttempted);}
- {Fixture f;f.h.m_Units.resize(1);ERR_EngineerMineSite::valid=false;int before=ERR_EngineerMineSite::calls;f.h.MaintainEngineerMines(1);f.h.MaintainEngineerMines(2);assert(f.units[0].EngineerMineAttempted&&!f.units[0].EngineerMine&&ERR_EngineerMineSite::calls==before+1);ERR_EngineerMineSite::valid=true;}
- {Fixture f;f.h.m_Units.resize(1);f.units[0].begin=false;f.h.MaintainEngineerMines(1);f.h.MaintainEngineerMines(2);assert(f.units[0].EngineerMine->stops==1&&f.h.m_EngineerMineAttempts.Count()==1);}
- {Fixture f;f.h.MaintainEngineerMines(1);f.h.m_EngineerMineAttempts.resize(256);f.h.MaintainEngineerMines(2);assert(f.units[0].EngineerMine->ticks==1&&!f.units[2].EngineerMineAttempted);}
- {Fixture f;Replication::server=false;f.h.MaintainEngineerMines(1);Replication::server=true;f.h.m_Stopping=true;f.h.MaintainEngineerMines(1);f.h.m_Stopping=false;f.mode.active=false;f.h.MaintainEngineerMines(1);f.mode.active=true;ERR_GameMode::current=nullptr;f.h.MaintainEngineerMines(1);assert(f.h.m_EngineerMineAttempts.empty());}
+ // Genuine second delivered post succeeds; no timer retry or replenishment at old post.
+ {Fixture f;f.h.m_Units.resize(1);f.Tick();auto first=f.units[0].EngineerMine;first->Stop();f.Tick();assert(f.h.m_EngineerMineJobs.Count()==1);f.NewPost(0,4);f.Tick();assert(f.h.m_EngineerMineJobs.Count()==2&&f.h.m_EngineerMineAttempts.Count()==2);assert(f.units[0].EngineerMine!=first&&f.units[0].EngineerMine->prior.size()==1&&f.units[0].EngineerMine->prior[0].x==30);}
+ {Fixture f;f.Tick();f.Tick();assert(f.h.m_EngineerMineJobs.Count()==2);int calls=ERR_EngineerMineSite::calls;f.Tick();assert(ERR_EngineerMineSite::calls==calls);f.units[0].EngineerMine->Stop();f.Tick();assert(f.h.m_EngineerMineJobs.Count()==3);}
+ // Settled old controller retained even after unit pointer disappears; pending receipt blocks exact original force.
+ {Fixture f;f.h.m_Units.resize(1);f.Tick();auto first=f.units[0].EngineerMine;first->Stop();first->unresolved=true;f.NewPost(0,4);f.units[0].EngineerMine=nullptr;int calls=ERR_EngineerMineSite::calls;f.Tick();assert(ERR_EngineerMineSite::calls==calls&&first->ticks==1);first->unresolved=false;f.Tick();assert(f.h.m_EngineerMineJobs.Count()==2);f.h.m_Units.Clear();f.Tick();assert(first->ticks==3);}
+ // Four consumed attempts survive current unit replacement. No-item Begin failures are still consumed.
+ {Fixture f;f.h.m_Units.resize(1);f.units[0].begin=false;for(int i=0;i<5;i++){f.NewPost(0,i*4);int before=ERR_EngineerMineSite::calls;f.Tick();assert(ERR_EngineerMineSite::calls==before+(i<4));}assert(f.h.m_EngineerMinePosts.Count()==4&&f.h.m_EngineerMineJobs.Count()==4&&f.h.m_EngineerMineAttempts.empty());ERR_HQUnit replacement=f.units[0];f.h.m_Units[0]=&replacement;replacement.Task=&f.tasks[24];replacement.Assignment=&f.reports[24];f.groups[0].position=f.tasks[24].Destination;f.Tick();assert(f.h.m_EngineerMinePosts.Count()==4);}
+ {Fixture f;ERR_EngineerMineSite::valid=false;int calls=ERR_EngineerMineSite::calls;f.Tick();assert(ERR_EngineerMineSite::calls==calls+1&&f.h.m_EngineerMinePosts.Count()==1&&f.h.m_EngineerMineJobs.empty());f.Tick();assert(ERR_EngineerMineSite::calls==calls+2&&f.h.m_EngineerMinePosts.Count()==2);}
+ {Fixture f;f.h.m_Units.resize(1);ERR_EngineerMineSite::valid=false;f.Tick();int calls=ERR_EngineerMineSite::calls;ERR_EngineerMineSite::valid=true;f.Tick();assert(ERR_EngineerMineSite::calls==calls&&f.h.m_EngineerMinePosts.Count()==1);}
+ {Fixture f;for(int i=0;i<32;i++){auto j=new ERR_EngineerMine(f.h.m_EngineerMineAttempts);j->Stop();f.h.m_EngineerMineJobs.Insert(j);}int calls=ERR_EngineerMineSite::calls;f.Tick();assert(ERR_EngineerMineSite::calls==calls);}
+ {Fixture f;f.Tick();f.h.m_EngineerMineAttempts.resize(256);int calls=ERR_EngineerMineSite::calls;f.Tick();assert(ERR_EngineerMineSite::calls==calls&&f.units[0].EngineerMine->ticks==1);}
+ for(int fault=0;fault<12;fault++){Fixture f;f.h.m_Units.resize(1);auto&u=f.units[0];if(fault==0)u.Assignment=nullptr;if(fault==1)u.Task=nullptr;if(fault==2)f.tasks[0].RoadIntercept=false;if(fault==3)u.PendingTask=&f;if(fault==4)u.PendingOrder=&f;if(fault==5)u.Downlink=&f;if(fault==6)u.eligible=false;if(fault==7)u.fighting=true;if(fault==8)f.groups[0].position.x=21;if(fault==9)f.samplers[0].owns=false;if(fault==10)f.samplers[0].strength=1;if(fault==11)f.tasks[0].ExpiresAt=1000;int calls=ERR_EngineerMineSite::calls;f.Tick();assert(ERR_EngineerMineSite::calls==calls&&f.h.m_EngineerMinePosts.Count()==0&&!u.EngineerMine);}
+ {Fixture f;Replication::server=false;f.Tick();Replication::server=true;f.h.m_Stopping=true;f.Tick();f.h.m_Stopping=false;f.mode.active=false;f.Tick();f.mode.active=true;ERR_GameMode::current=nullptr;f.Tick();assert(f.h.m_EngineerMinePosts.Count()==0);}
}
-'''.replace('Task* Task=', 'struct Task* Task=').replace('Group* Group=', 'struct Group* Group='))
-source=p.read_text()
+''')
assert 'if (EngineerMine) EngineerMine.Stop();' in body(p,'void ReleaseAssignment(')
assert 'MaintainEngineerMines(now);' in body(p,'override void Tick(')
assert 'm_EngineerMineAttempts.Clear();' in body(p,'override void Stop(')
assert 'foreach (ERR_EngineerMine job : m_EngineerMineJobs) job.Stop();' in body(p,'override void Stop(')
-print('PASS: actual HQ mine admission, one site query per tick, maximum two active/32 retained jobs, delivered-only original post, once-per-unit refusal latch, 256-item ceiling and maintenance/teardown ownership')
+print('PASS: actual HQ and post ledger: second delivered post, one query/tick,2 active/32 jobs,4 consumed lifetime attempts, no-stock/query refusal consumption, pending old job and original identity constraints')
--- /dev/null
+++ b/tests/test_engineer_mine_posts.py
@@ -0,0 +1,53 @@
+"""Actual finite post ledger; engine identity/vector boundaries are doubles."""
+import os,re
+from pathlib import Path
+from policy_harness import ROOT,run
+P=Path(os.environ.get('ERR_ENGINEER_MINE_POSTS_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_EngineerMinePosts.c'))
+BOUNDARY=r'''
+#include <cassert>
+#include <cmath>
+#include <string>
+#include <vector>
+#include <algorithm>
+struct string:std::string{using std::string::string;using std::string::operator=;string(std::string s):std::string(s){} bool IsEmpty(){return empty();}};
+struct vector{float x=0,y=0,z=0;static float DistanceXZ(vector a,vector b){return std::hypot(a.x-b.x,a.z-b.z);}};
+template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;int Count(){return this->size();}void Insert(T p){this->push_back(p);}void Clear(){this->clear();}};
+struct SCR_AIGroup{vector position;vector GetOrigin(){return position;}};
+struct ERR_ForceRecord{SCR_AIGroup* Group=nullptr;bool Physical=true,Eliminated=false;string SourceId="s";int InitialStrength=2;};
+struct ERR_HQTask{bool RoadIntercept=true,ReleaseOnly=false,investigation=false;string SecuritySite;int BeliefId=1,BeliefRevision=1;vector Destination;float QueuedAt=10,ExpiresAt=100000;bool Investigation(){return investigation;}};
+struct ERR_HQReport{int MessageId=1;float ObservedAt=5;vector Position;};
+namespace ERR_ForceRules{bool Finite(float f){return std::isfinite(f);}bool FiniteVector(vector p){return Finite(p.x)&&Finite(p.y)&&Finite(p.z);}}
+struct ERR_EngineerMineSite{static inline bool valid=true;static inline int calls=0;static bool InBand(vector a,vector b){float d=vector::DistanceXZ(a,b);return d>=25&&d<=60;}static bool Select(vector post,vector&site){calls++;site={post.x+30,post.y,post.z};return valid;}};
+'''
+def ledger_cpp(path=P):
+ s=path.read_text().replace('class ','struct ').replace('protected ','').replace('ref ','').replace('out array<vector> sites','Array<vector>& sites').replace('array<','Array<').replace(' = {};',';')
+ for typ in ['ERR_EngineerMinePost','ERR_ForceRecord','SCR_AIGroup','ERR_HQTask','ERR_HQReport']:
+ s=re.sub(r'\b'+typ+r'(?=\s+\w)',typ+'*',s);s=s.replace('Array<'+typ+'>','Array<'+typ+'*>')
+ for ns in ['ERR_ForceRules','vector','ERR_EngineerMineSite']:s=s.replace(ns+'.',ns+'::')
+ for obj in ['record','task','assignment','previous','entry']:s=re.sub(r'\b'+obj+r'\.',obj+'->',s)
+ s=s.replace('foreach (','for (').replace('int used;','int used=0;')
+ # Enforce initializes scalar fields to zero; C++ aggregate members must match.
+ s=re.sub(r'^( (?:bool|int|float|ERR_ForceRecord\*|SCR_AIGroup\*|ERR_HQTask\*|ERR_HQReport\*) \w+);$',r'\1{};',s,flags=re.M)
+ s=s.replace('\n}\n','\n};\n')
+ return s
+TEST=r'''
+struct Fixture{SCR_AIGroup group;ERR_ForceRecord record;ERR_HQTask tasks[40];ERR_HQReport reports[40];ERR_EngineerMinePosts posts;
+ Fixture(){record.Group=&group;for(int i=0;i<40;i++){tasks[i].Destination={float(i*200),0,0};tasks[i].QueuedAt=10+i*10;tasks[i].BeliefRevision=1+i;reports[i].ObservedAt=5+i*10;reports[i].MessageId=i+1;}}
+ int Take(int i){return posts.Consume(&record,"s",&tasks[i],&reports[i],1000);}};
+int main(){
+ {Fixture f;assert(f.Take(0)==0);assert(f.Take(1)==1);assert(f.Take(2)==2);assert(f.Take(3)==3);assert(f.Take(4)==-1&&f.posts.Count()==4);}
+ {Fixture f;assert(f.Take(0)==0);assert(f.Take(0)==-1);f.tasks[0].Destination={900,0,0};f.tasks[0].QueuedAt=900;f.reports[0].ObservedAt=800;f.reports[0].MessageId=999;f.tasks[0].BeliefRevision=999;
+ assert(f.Take(1)==1);assert(f.posts.RecordSite(0,{30,0,0}));assert(!f.posts.RecordSite(0,{31,0,0}));Array<vector> sites;f.posts.PriorSites(sites);assert(sites.size()==1&&sites[0].x==30);sites[0].x=900;f.posts.PriorSites(sites);assert(sites[0].x==30);}
+ for(int fault=0;fault<8;fault++){Fixture f;f.Take(0);if(fault==0)f.tasks[1].Destination={160,0,0};if(fault==1)f.tasks[1].QueuedAt=10;if(fault==2)f.reports[1].ObservedAt=5;if(fault==3)f.reports[1].MessageId=1;if(fault==4)f.tasks[1].BeliefRevision=1;if(fault==5)f.tasks[1].BeliefRevision=0;if(fault==6)f.tasks[1].ExpiresAt=1000;if(fault==7)f.tasks[1].Destination.x=NAN;assert(f.Take(1)==-1&&f.posts.Count()==1);}
+ {Fixture f;f.Take(0);f.tasks[1].Destination.x=160.001f;assert(f.Take(1)==1);}
+ {Fixture f;f.Take(0);f.tasks[1].BeliefId=2;f.tasks[1].BeliefRevision=1;assert(f.Take(1)==1);}
+ {Fixture f;f.Take(0);ERR_ForceRecord alias=f.record;assert(f.posts.Consume(&alias,"s",&f.tasks[1],&f.reports[1],1000)==-1);alias.SourceId="alias";assert(f.posts.Consume(&alias,"alias",&f.tasks[1],&f.reports[1],1000)==-1);f.record.SourceId="changed";assert(f.posts.Consume(&f.record,"changed",&f.tasks[1],&f.reports[1],1000)==-1);}
+ {Fixture f;f.Take(0);SCR_AIGroup replacement;f.record.Group=&replacement;assert(f.Take(1)==-1);f.record.Group=&f.group;f.record.InitialStrength=3;assert(f.Take(1)==-1);}
+ {Fixture f;assert(!f.posts.RecordSite(-1,{30,0,0}));assert(f.Take(0)==0);assert(!f.posts.RecordSite(0,{24,0,0}));assert(!f.posts.RecordSite(0,{61,0,0}));assert(!f.posts.RecordSite(0,{30,NAN,0}));Array<vector> empty;f.posts.PriorSites(empty);assert(empty.empty());assert(f.posts.RecordSite(0,{25,0,0}));}
+ {Fixture f;ERR_ForceRecord records[33];SCR_AIGroup groups[33];for(int i=0;i<33;i++){records[i].Group=&groups[i];records[i].SourceId=std::to_string(i);int result=f.posts.Consume(&records[i],records[i].SourceId,&f.tasks[i],&f.reports[i],1000);assert(result==(i<32?i:-1));}assert(f.posts.Count()==32);}
+ for(int fault=0;fault<10;fault++){Fixture f;if(fault==0)f.record.Physical=false;if(fault==1)f.record.Eliminated=true;if(fault==2)f.record.InitialStrength=33;if(fault==3)f.record.Group=nullptr;if(fault==4)f.tasks[0].ReleaseOnly=true;if(fault==5)f.tasks[0].investigation=true;if(fault==6)f.tasks[0].SecuritySite="site";if(fault==7)f.tasks[0].QueuedAt=1001;if(fault==8)f.reports[0].ObservedAt=11;if(fault==9)f.reports[0].Position.x=NAN;assert(f.Take(0)==-1&&f.posts.Count()==0);}
+}
+'''
+if __name__=='__main__':
+ run(BOUNDARY+ledger_cpp()+TEST)
+ print('PASS: actual mine post ledger immutable history, strict chronology/evidence, aliases, >160m separation, detached sites and finite4/32 consumed attempts')
@@ -0,0 +1 @@
PASS: actual HQ and post ledger: second delivered post, one query/tick,2 active/32 jobs,4 consumed lifetime attempts, no-stock/query refusal consumption, pending old job and original identity constraints
@@ -0,0 +1,333 @@
// Decisions consumed by the finite original-item controller below.
class ERR_EngineerMineRules
{
// Straight XZ corridors are conservative local checks, not native route proofs.
static bool CorridorClear(vector start, vector end, array<vector> priorSites)
{
if (!ERR_ForceRules.FiniteVector(start) || !ERR_ForceRules.FiniteVector(end)) return false;
if (!priorSites) return true;
if (priorSites.Count() > 32) return false;
float dx = end[0] - start[0], dz = end[2] - start[2];
float squared = dx * dx + dz * dz;
if (!ERR_ForceRules.Finite(squared)) return false;
foreach (vector site : priorSites)
{
if (!ERR_ForceRules.FiniteVector(site)) return false;
float along = 0;
if (squared > 0)
{
float projection = (site[0] - start[0]) * dx + (site[2] - start[2]) * dz;
if (!ERR_ForceRules.Finite(projection)) return false;
along = projection / squared;
if (!ERR_ForceRules.Finite(along)) return false;
along = Math.Clamp(along, 0, 1);
}
float x = start[0] + along * dx - site[0], z = start[2] + along * dz - site[2];
float clearance = x * x + z * z;
if (!ERR_ForceRules.Finite(clearance) || clearance <= 400) return false;
}
return true;
}
static bool WithinWorkArea(vector post, vector actor)
{
if (!ERR_ForceRules.FiniteVector(post) || !ERR_ForceRules.FiniteVector(actor)) return false;
float distance = vector.DistanceXZ(post, actor);
return ERR_ForceRules.Finite(distance) && distance <= 60;
}
static bool Within(float started, float previous, float now)
{
return ERR_ForceRules.Finite(started) && ERR_ForceRules.Finite(previous) && ERR_ForceRules.Finite(now)
&& started >= 0 && previous >= started && now >= previous && now - started < 180000;
}
static float PrepareSince(float since, float now, bool near, bool engaged)
{
if (!ERR_ForceRules.Finite(now) || now < 0 || !near || engaged) return -1;
if (!ERR_ForceRules.Finite(since) || since < 0 || since > now) return now;
return since;
}
static bool Prepared(float since, float now)
{
return ERR_ForceRules.Finite(since) && ERR_ForceRules.Finite(now) && since >= 0 && now - since >= 10000;
}
static bool MayArm(bool grounded, bool ownerValid, float distance, bool friendlyNear)
{
return grounded && ownerValid && ERR_ForceRules.Finite(distance) && distance >= 20 && !friendlyNear;
}
}
// Callback records only terminal evidence. It never moves, arms or retries items.
class ERR_EngineerMineRemoval : ScriptedInventoryOperationCallback
{
int Result;
protected void Finish(int result) { if (!Result) Result = result; }
protected override void OnComplete() { Finish(1); }
protected override void OnAllComplete() { Finish(1); }
protected override void OnFailed() { Finish(-1); }
protected override void OnAllFailed() { Finish(-1); }
}
// One original soldier, one carried PMN4, one accepted HQ road-interception post.
// Loose cancelled items remain ordinary unarmed loot. Armed mines retain native
// lifetime; a round's world reset owns their destruction, not this controller.
class ERR_EngineerMine
{
protected ERR_GameMode m_Mode;
protected ERR_HQUnit m_Unit;
protected SCR_AIGroup m_Group;
protected ref ERR_HQTask m_Task;
protected ref ERR_HQReport m_Assignment;
protected ref ERR_ForceRecord m_Record;
protected string m_SourceId;
protected int m_Strength;
protected IEntity m_Actor;
protected int m_Slot;
protected IEntity m_Mine;
protected SCR_PressureTriggerComponent m_Trigger;
protected SCR_InventoryStorageManagerComponent m_Inventory;
protected SCR_AIUtilityComponent m_Utility;
protected ref SCR_AIBehaviorBase m_Action;
protected ref ERR_EngineerMineRemoval m_Removal;
protected ref array<IEntity> m_Attempted;
protected ref array<IEntity> m_IntelFolders = {};
protected ref array<vector> m_PriorSites = {};
protected vector m_Position;
protected vector m_Rally;
protected float m_Started = -1;
protected float m_LastTick = -1;
protected float m_PreparedAt = -1;
protected float m_NextClearance;
protected int m_State; // 0 approach,1 continuous preparation,2 removal pending,3 withdraw,4 finished
protected int m_Checked;
protected bool m_FriendlyNear;
protected bool m_Begun;
protected bool m_Stopped;
void ERR_EngineerMine(array<IEntity> attempted) { m_Attempted = attempted; }
bool Finished() { return m_Stopped || m_State == 4; }
bool BlocksNextPost(ERR_ForceRecord record)
{
if (!record || record != m_Record) return false;
if (m_Begun && !Finished()) return true;
// A late terminal callback or exact deletion settles only the old job.
// Destroyed but surviving items and uncertain native refusals remain pinned.
return false;
}
protected bool Refuse() { Stop(); return false; }
protected bool Quiet()
{
return m_Unit && !m_Unit.InfantryCombat.Held && !ERR_HunterCombat.Active(m_Group);
}
protected bool Owned()
{
if (m_Stopped || !Replication.IsServer() || !m_Mode || m_Mode != ERR_GameMode.Get() || !m_Mode.Active()) return false;
if (!m_Unit || !m_Group || m_Unit.Group != m_Group || !m_Task || m_Unit.Task != m_Task) return false;
if (!m_Assignment || m_Unit.Assignment != m_Assignment || !m_Task.RoadIntercept || m_Task.ReleaseOnly) return false;
if (m_Task.Investigation() || !m_Task.SecuritySite.IsEmpty() || m_Unit.PendingTask || m_Unit.PendingOrder) return false;
if (m_Unit.ReservedForSupport || m_Unit.LogisticsReservation > 0 || m_Group.IsSlave() || m_Group.GetPlayerCount(true) > 0) return false;
SCR_AIGroupUtilityComponent groupUtility = m_Group.GetGroupUtilityComponent();
if (!groupUtility || !groupUtility.IsPositionAllowed(m_Position)) return false;
if (!m_Mode.EnemyFaction() || m_Group.GetFaction() != m_Mode.EnemyFaction() || !m_Unit.StatusSampler) return false;
if (vector.Distance(m_Task.Destination, m_Rally) > 0.01 || !m_Mode.Forces() || !m_Record) return false;
if (m_Mode.Forces().FindGroup(m_Group) != m_Record || m_Record.Group != m_Group || !m_Record.Physical || m_Record.Eliminated) return false;
if (m_Record.SourceId != m_SourceId || m_Unit.ForceSourceId != m_SourceId || m_Record.InitialStrength != m_Strength) return false;
if (!m_Unit.StatusSampler.OwnsRoster(m_Group) || m_Unit.StatusSampler.InitialStrength() != m_Strength) return false;
if (!m_Actor || !ERR_Actors.CanFight(m_Actor) || ERR_Actors.Player(m_Actor) || CompartmentAccessComponent.GetVehicleIn(m_Actor)) return false;
vector actorPosition = m_Actor.GetOrigin();
if (!ERR_EngineerMineRules.WithinWorkArea(m_Rally, actorPosition) || !ERR_EngineerMineRules.WithinWorkArea(m_Rally, m_Position)) return false;
if (!ERR_EngineerMineRules.CorridorClear(actorPosition, actorPosition, m_PriorSites)
|| !ERR_EngineerMineRules.CorridorClear(m_Position, m_Position, m_PriorSites)
|| !ERR_EngineerMineRules.CorridorClear(m_Rally, m_Rally, m_PriorSites)) return false;
if (!m_Unit.StatusSampler.CanAttribute(m_Slot, m_Actor, false) || ERR_Actors.Group(m_Actor) != m_Group) return false;
if (SCR_FactionManager.SGetFaction(m_Actor) != m_Mode.EnemyFaction()) return false;
SCR_ChimeraAIAgent agent = SCR_ChimeraAIAgent.Cast(SCR_AIUtils.GetAIAgent(m_Actor));
if (!agent || !m_Utility || agent.m_UtilityComponent != m_Utility || !m_Utility.m_AIInfo) return false;
if (!m_Inventory || m_Actor.FindComponent(SCR_InventoryStorageManagerComponent) != m_Inventory) return false;
return true;
}
protected bool MineValid()
{
if (!ERR_Actors.Alive(m_Mine) || !m_Mine.GetPrefabData() || m_Mine.GetPrefabData().GetPrefabName() != ERR_EnemyLoadoutCatalog.MINE) return false;
if (!m_Trigger || m_Mine.FindComponent(SCR_PressureTriggerComponent) != m_Trigger || m_Trigger.IsActivated()) return false;
return m_Attempted && m_Attempted.Contains(m_Mine);
}
protected bool Carried()
{
if (!MineValid() || !m_Inventory) return false;
array<IEntity> items = {}; ERR_Inventory.Items(m_Inventory, items);
return items.Count() <= 128 && items.Contains(m_Mine) && ERR_InfantryAmmoTransfer.StoredUnder(m_Mine, m_Actor);
}
protected bool Grounded()
{
if (!MineValid() || !m_Inventory) return false;
InventoryItemComponent item = InventoryItemComponent.Cast(m_Mine.FindComponent(InventoryItemComponent));
if (!item || item.GetParentSlot() || m_Mine.GetParent()) return false;
array<IEntity> items = {}; ERR_Inventory.Items(m_Inventory, items);
return items.Count() <= 128 && !items.Contains(m_Mine);
}
protected bool Placed()
{
if (!Grounded() || vector.Distance(m_Mine.GetOrigin(), m_Position) > 1) return false;
Physics physics = m_Mine.GetPhysics();
return physics && physics.GetVelocity().LengthSq() <= 0.25;
}
protected void ClearAction()
{
if (m_Action && m_Actor && m_Utility && m_Utility.m_AIInfo)
{
SCR_ChimeraAIAgent agent = SCR_ChimeraAIAgent.Cast(SCR_AIUtils.GetAIAgent(m_Actor));
if (agent && agent.m_UtilityComponent == m_Utility)
{
array<ref AIActionBase> actions = {}; m_Utility.GetActions(actions);
if (actions.Count() <= 128 && actions.Contains(m_Action)) m_Action.Complete();
}
}
m_Action = null;
}
protected void Move(vector destination)
{
ClearAction();
m_Action = new SCR_AIMoveIndividuallyBehavior(m_Utility, null, destination, 1000, 0, null, 1);
m_Utility.AddAction(m_Action);
}
protected void WaitAtMine()
{
ClearAction();
m_Action = new SCR_AIWaitBehavior(m_Utility, null);
m_Action.SetPriority(1000);
m_Utility.AddAction(m_Action);
}
protected bool CheckFriendly(IEntity entity)
{
if (++m_Checked > 256) { m_FriendlyNear = true; return false; }
if (!entity || (!ChimeraCharacter.Cast(entity) && !Vehicle.Cast(entity))) return true;
DamageManagerComponent damage = DamageManagerComponent.Cast(entity.FindComponent(DamageManagerComponent));
if (damage && damage.IsDestroyed()) return true;
FactionAffiliationComponent affiliation = FactionAffiliationComponent.Cast(entity.FindComponent(FactionAffiliationComponent));
Faction faction;
if (affiliation) faction = affiliation.GetAffiliatedFaction();
if (faction && m_Mode.EnemyFaction().IsFactionEnemy(faction)) return true;
// Unconscious allies and unknown affiliations both prevent arming.
m_FriendlyNear = true;
return false;
}
protected bool SelectActor()
{
if (!m_Unit.StatusSampler.OwnsRoster(m_Group) || m_Strength < 1 || m_Strength > 32) return false;
for (int slot = 0; slot < m_Strength; slot++)
{
IEntity actor = m_Unit.StatusSampler.InitialMember(slot);
if (!actor || !ERR_Actors.CanFight(actor) || ERR_Actors.Player(actor) || CompartmentAccessComponent.GetVehicleIn(actor)) continue;
if (!m_Unit.StatusSampler.CanAttribute(slot, actor, false) || vector.DistanceXZ(actor.GetOrigin(), m_Rally) > 20) continue;
SCR_ChimeraAIAgent agent = SCR_ChimeraAIAgent.Cast(SCR_AIUtils.GetAIAgent(actor));
if (!agent || !agent.m_UtilityComponent || !agent.m_UtilityComponent.m_AIInfo) continue;
SCR_InventoryStorageManagerComponent inventory = SCR_InventoryStorageManagerComponent.Cast(actor.FindComponent(SCR_InventoryStorageManagerComponent));
if (!inventory) continue;
array<IEntity> items = {}; ERR_Inventory.Items(inventory, items);
if (items.Count() > 128) continue;
foreach (IEntity mine : items)
{
if (!ERR_Actors.Alive(mine) || m_Attempted.Contains(mine) || !mine.GetPrefabData() || mine.GetPrefabData().GetPrefabName() != ERR_EnemyLoadoutCatalog.MINE) continue;
if (!ERR_InfantryAmmoTransfer.StoredUnder(mine, actor)) continue;
SCR_PressureTriggerComponent trigger = SCR_PressureTriggerComponent.Cast(mine.FindComponent(SCR_PressureTriggerComponent));
if (!trigger || trigger.IsActivated() || trigger.IsActivatedByDefault()) continue;
m_Actor = actor; m_Slot = slot; m_Mine = mine; m_Trigger = trigger;
m_Inventory = inventory; m_Utility = agent.m_UtilityComponent;
return true;
}
}
return false;
}
bool Begin(ERR_GameMode mode, ERR_HQUnit unit, ERR_HQTask task, vector minePosition, float now, array<vector> priorSites = null)
{
if (m_Begun || m_Stopped) return false;
m_Begun = true;
if (!Replication.IsServer() || !mode || mode != ERR_GameMode.Get() || !mode.Active()) return Refuse();
if (!unit || !unit.Group || !unit.StatusSampler || !task || unit.Task != task || !unit.Assignment) return Refuse();
if (!ERR_ForceRules.Finite(now) || now < 0 || !ERR_ForceRules.FiniteVector(minePosition)) return Refuse();
if (!m_Attempted || m_Attempted.Count() >= 256 || !mode.Forces()) return Refuse();
m_Mode = mode; m_Unit = unit; m_Task = task; m_Assignment = unit.Assignment; m_Group = unit.Group;
m_SourceId = unit.ForceSourceId; m_Strength = unit.StatusSampler.InitialStrength(); m_Record = mode.Forces().FindGroup(m_Group);
m_Position = minePosition; m_Rally = task.Destination;
float distance = vector.DistanceXZ(m_Position, m_Rally);
if (!ERR_ForceRules.FiniteVector(m_Rally) || distance < 25 || distance > 60 || now >= task.ExpiresAt) return Refuse();
if (priorSites)
{
if (priorSites.Count() > 32) return Refuse();
foreach (vector prior : priorSites)
{
if (!ERR_ForceRules.FiniteVector(prior)) return Refuse();
m_PriorSites.Insert(prior);
}
}
if (!SelectActor() || !Owned() || !Quiet()) return Refuse();
if (!ERR_EngineerMineRules.CorridorClear(m_Actor.GetOrigin(), m_Position, m_PriorSites)
|| !ERR_EngineerMineRules.CorridorClear(m_Position, m_Rally, m_PriorSites)) return Refuse();
// The shared exact-item tombstone survives pickup, transfer and late callbacks.
m_Attempted.Insert(m_Mine);
if (!Carried()) return Refuse();
if (m_Mode.MineIntel()) m_Mode.MineIntel().Candidates(m_Actor, m_IntelFolders);
m_Started = now; m_LastTick = now; m_State = 0;
Move(m_Position);
ERR_HQ.Event("ENGINEER MINE", "original carried PMN4 assigned; one approach and finite 180s deadline");
return true;
}
void Stop()
{
if (m_Stopped) return;
m_Stopped = true; m_State = 4;
ClearAction();
// Retain removal evidence and shared item quarantine. No late completion can arm.
}
void Tick(float now)
{
if (!m_Begun || Finished()) return;
if (!ERR_EngineerMineRules.Within(m_Started, m_LastTick, now)) { Stop(); return; }
m_LastTick = now;
if (!Owned() || !Quiet() || !MineValid() || now >= m_Task.ExpiresAt) { Stop(); return; }
if (m_State <= 1)
{
if (!Carried()) { Stop(); return; }
bool near = vector.DistanceXZ(m_Actor.GetOrigin(), m_Position) <= 3;
m_PreparedAt = ERR_EngineerMineRules.PrepareSince(m_PreparedAt, now, near, false);
if (m_State == 0 && near) { m_State = 1; WaitAtMine(); }
if (!ERR_EngineerMineRules.Prepared(m_PreparedAt, now)) return;
// Set state and callback BEFORE native submission; never repeat the removal.
m_Removal = new ERR_EngineerMineRemoval(); m_State = 2;
bool accepted = m_Inventory.TryRemoveItemFromInventory(m_Mine, null, m_Removal);
if (!accepted && !m_Removal.Result) { Stop(); return; }
return;
}
if (m_State == 2)
{
if (!m_Removal || m_Removal.Result < 0) { Stop(); return; }
// Observed transfer ends our custody; a later nearby drop cannot restore it.
// Original inventory ownership may still settle after the native callback.
InventoryItemComponent item = InventoryItemComponent.Cast(m_Mine.FindComponent(InventoryItemComponent));
bool attached = m_Mine.GetParent() || (item && item.GetParentSlot());
if (attached && !ERR_InfantryAmmoTransfer.StoredUnder(m_Mine, m_Actor)) { Stop(); return; }
if (!m_Removal.Result || !Grounded()) return;
if (vector.DistanceXZ(m_Actor.GetOrigin(), m_Position) > 3) { Stop(); return; }
// A callback proves removal, not continued custody of a distant loose item.
if (vector.Distance(m_Mine.GetOrigin(), m_Actor.GetOrigin()) > 3
|| vector.Distance(m_Mine.GetOrigin(), m_Position) > 3) { Stop(); return; }
m_Mine.SetOrigin(m_Position);
m_State = 3; Move(m_Rally);
return;
}
if (m_State != 3 || !Grounded() || vector.Distance(m_Mine.GetOrigin(), m_Position) > 1) { Stop(); return; }
if (now < m_NextClearance || vector.DistanceXZ(m_Actor.GetOrigin(), m_Position) < 20) return;
m_NextClearance = now + 1000;
m_Checked = 0; m_FriendlyNear = false;
if (!GetGame() || !GetGame().GetWorld()) { Stop(); return; }
GetGame().GetWorld().QueryEntitiesBySphere(m_Position, 20, CheckFriendly, queryFlags: EQueryEntitiesFlags.ALL);
if (!ERR_EngineerMineRules.MayArm(Placed(), Owned() && Quiet(), vector.DistanceXZ(m_Actor.GetOrigin(), m_Position), m_FriendlyNear)) return;
m_Trigger.SetUser(m_Actor);
m_Trigger.ActivateTrigger();
if (m_Trigger.IsActivated())
{
if (m_Mode.MineIntel()) m_Mode.MineIntel().Seal(m_Actor, m_Mine, m_Position, m_IntelFolders);
ERR_HQ.Event("ENGINEER MINE", "original PMN4 armed after engineer withdrawal and physical friendly clearance");
}
Stop();
}
}
@@ -0,0 +1,19 @@
check: /tmp/erri-engineer-posts-tests-0725/scratch/err-policy-62a5yjin/check.cpp:146: int main(): Assertion `f.h.m_EngineerMineJobs.Count()==2&&f.h.m_EngineerMineAttempts.Count()==2' failed.
Traceback (most recent call last):
File "/tmp/erri-engineer-posts-tests-0725/tests/test_engineer_mine_hq.py", line 16, in <module>
run(BOUNDARY+ledger_cpp()+r'''
~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^
struct Ledger{Array<ERR_ForceRecord*> records;ERR_ForceRecord* FindGroup(SCR_AIGroup* group){for(auto r:records)if(r->Group==group)return r;return nullptr;}};
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...<32 lines>...
}
^
''')
^^^^
File "/home/jakub/devel/escapistsrereintroduced/tests/policy_harness.py", line 14, in run
subprocess.run([str(p/'check')],check=True)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.14/subprocess.py", line 578, in run
raise CalledProcessError(retcode, process.args,
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['/tmp/erri-engineer-posts-tests-0725/scratch/err-policy-62a5yjin/check']' died with <Signals.SIGABRT: 6>.
@@ -0,0 +1,19 @@
check: /tmp/erri-engineer-posts-tests-0725/scratch/err-policy-1673sbz9/check.cpp:381: int main(): Assertion `f.controller.BlocksNextPost(&f.record)' failed.
Traceback (most recent call last):
File "/tmp/erri-engineer-posts-tests-0725/tests/test_engineer_mine.py", line 33, in <module>
run(r'''
~~~^^^^^
#include <cassert>
^^^^^^^^^^^^^^^^^^
...<198 lines>...
}
^
''')
^^^^
File "/home/jakub/devel/escapistsrereintroduced/tests/policy_harness.py", line 14, in run
subprocess.run([str(p/'check')],check=True)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.14/subprocess.py", line 578, in run
raise CalledProcessError(retcode, process.args,
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['/tmp/erri-engineer-posts-tests-0725/scratch/err-policy-1673sbz9/check']' died with <Signals.SIGABRT: 6>.
@@ -0,0 +1 @@
PASS: actual mine post ledger immutable history, strict chronology/evidence, aliases, >160m separation, detached sites and finite4/32 consumed attempts
@@ -0,0 +1,12 @@
check: /tmp/erri-engineer-posts-tests-0725/scratch/err-policy-nzidhxwh/check.cpp:102: int main(): Assertion `f.Take(4)==-1&&f.posts.Count()==4' failed.
Traceback (most recent call last):
File "/tmp/erri-engineer-posts-tests-0725/tests/test_engineer_mine_posts.py", line 52, in <module>
run(BOUNDARY+ledger_cpp()+TEST)
~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jakub/devel/escapistsrereintroduced/tests/policy_harness.py", line 14, in run
subprocess.run([str(p/'check')],check=True)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.14/subprocess.py", line 578, in run
raise CalledProcessError(retcode, process.args,
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['/tmp/erri-engineer-posts-tests-0725/scratch/err-policy-nzidhxwh/check']' died with <Signals.SIGABRT: 6>.
@@ -0,0 +1,67 @@
{
"status": "pass",
"source_hashes": {
"/tmp/erri-engineer-posts-source-0725/addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c": "d873b5b689e918f8c907918d673304a184ac2e4a4cb6face527b37a67821ee9f",
"/tmp/erri-engineer-posts-source-0725/addons/core/Scripts/Game/ERR/10_AI/ERR_EngineerMinePosts.c": "ab0171a510d46fad6df5b477ae27cb03016b894700e4111e9a5b99d8061fe3a6",
"/tmp/erri-engineer-controller-source-0725/addons/core/Scripts/Game/ERR/10_AI/ERR_EngineerMine.c": "defb12924ea5dfe68f86c2bbb4b07362f016ffee810f720d13f01d81881b3cfc"
},
"tests_sha256": {
"tests/test_engineer_mine.py": "300023f165be86f681d3980e844823c73e37b98c5855a2142d9d373fdce3e9ab",
"tests/test_engineer_mine_hq.py": "991666e1218282ce5ffca4ad3f47cf25ec436c6dc091951cc4db395e0cde01a6",
"tests/test_engineer_mine_posts.py": "6185b5ab6576948fd8b47bb945fbe40107ec6944eafde2d04dd60ff49d5ec89c"
},
"baseline_test_sha256": {
"tests/test_engineer_mine.py": "244ab49155000e5939a6c9d9e8f01fe8b14115119d548ba423ceffdd33ca9946",
"tests/test_engineer_mine_hq.py": "822f50e363be3ba03bc5d12748cfc93d83e93552d66797b01afc6460e7ab7d5d"
},
"patch_sha256": "a19a3a602cb2611bd20855fbcaad38dc31cbe09e3f6d343c05e946557d2dbd1d",
"results": [
{
"name": "old-hq-red",
"exit_code": 1,
"compiled": true,
"log_sha256": "5aa1cf3182bbf0484faeb37ea19822b5245f80bb05cc2d1f186f68e735e48808"
},
{
"name": "quota-mutation-red",
"exit_code": 1,
"compiled": true,
"log_sha256": "8447dfb3819fe13be2c138e57302932a7c922856b6f52bbc0114a7a8ca3debaa"
},
{
"name": "pending-mutation-red",
"exit_code": 1,
"compiled": true,
"log_sha256": "e7e41260b0d7b64c4f4935179c30b66b873ee708b5923344cf601ffc3a1dab6c"
},
{
"name": "posts-green",
"exit_code": 0,
"compiled": true,
"log_sha256": "ed2ac192287b36c0d58605c307521f380f75d0683cbf4a74f0a0033e83c67c9b"
},
{
"name": "hq-green",
"exit_code": 0,
"compiled": true,
"log_sha256": "382fe94c8093ce1d364f354d9fde23fb251543c27eecb954f5b8f25017aab135"
},
{
"name": "controller-green",
"exit_code": 0,
"compiled": true,
"log_sha256": "42ed037cc22a9224478449736d1f43e57cc89fa06ddc7e0f09f37771f3ca0b2a"
}
],
"coverage": [
"Actual ledger classes: immutable consumed post/evidence values, task/report alias refusal, strict160m separation, chronological replay refusal,4-per-force/32-total attempts, record/source/group aliases and detached once-recorded sites.",
"Actual HQ MaintainEngineerMines calls actual ledger. First completed job then second spatially separate delivered post succeeds. Old retained pending job blocks admission even after unit.EngineerMine pointer clears. Two-active/32-retained/256-item caps and one query/tick preserved. No-site/no-item refusals remain consumed; replacement HQUnit cannot reset original-force quota.",
"Entire existing actual controller harness retained and extended. Copied prior hazard positions, invalid/overcap input, strict20m corridors and60m work area, active history exclusion, second distinct original item and shared tombstone all checked. BlocksNextPost uses exact force record: active blocks, stopped pending survives destroyed nonnull item, terminal callback or exact deletion settles without late movement/arming.",
"Baseline actual HQ compiles then fails new-post success. Two targeted source mutations compile then fail: fifth lifetime allowance and omitted stopped pending-removal blocker."
],
"limits": [
"C++ source-method translation with vector/native job/inventory/AI boundary doubles. No Enforce compiler, native navigation, physical two-post mining, networking or path-safety guarantee.",
"No target/original mutations or full suite. New test_engineer_mine_hq imports shared ledger harness definitions from adjacent test_engineer_mine_posts; main guard prevents duplicate execution."
],
"all_compilers_finished": true
}
@@ -0,0 +1,21 @@
from pathlib import Path
import subprocess,os,json,hashlib,difflib
r=Path('/home/jakub/devel/escapistsrereintroduced');o=Path('/tmp/erri-engineer-posts-tests-0725');a=Path('/tmp/erri-engineer-posts-source-0725');c=Path('/tmp/erri-engineer-controller-source-0725');rel='addons/core/Scripts/Game/ERR/10_AI/'
hq=a/rel/'ERR_HQ.c';posts=a/rel/'ERR_EngineerMinePosts.c';controller=c/rel/'ERR_EngineerMine.c'
env=dict(os.environ,PYTHONPATH=str(r/'tests'),TMPDIR=str(o/'scratch'),ERR_HQ_SOURCE=str(hq),ERR_ENGINEER_MINE_POSTS_SOURCE=str(posts),ERR_ENGINEER_MINE_SOURCE=str(controller))
mutationPosts=o/'five-attempts.c';s=posts.read_text();assert 'used >= 4' in s;mutationPosts.write_text(s.replace('used >= 4','used >= 5',1))
mutationController=o/'ignore-pending.c';s=controller.read_text();old='return m_Removal && !m_Removal.Result && m_Mine;';assert old in s;mutationController.write_text(s.replace(old,'return false;',1))
cases=[('old-hq-red','test_engineer_mine_hq.py',{'ERR_HQ_SOURCE':str(a/'baseline'/rel/'ERR_HQ.c')},False),('quota-mutation-red','test_engineer_mine_posts.py',{'ERR_ENGINEER_MINE_POSTS_SOURCE':str(mutationPosts)},False),('pending-mutation-red','test_engineer_mine.py',{'ERR_ENGINEER_MINE_SOURCE':str(mutationController)},False),('posts-green','test_engineer_mine_posts.py',{},True),('hq-green','test_engineer_mine_hq.py',{},True),('controller-green','test_engineer_mine.py',{},True)]
results=[]
for name,test,overrides,positive in cases:
active=env|overrides
with (o/(name+'.log')).open('w') as log:res=subprocess.run(['python3',str(o/'tests'/test)],env=active,stdout=log,stderr=subprocess.STDOUT)
text=(o/(name+'.log')).read_text();assert (res.returncode==0)==positive,(name,text)
if not positive:assert 'Assertion' in text and 'failed.' in text and "Command '['g++'" not in text,(name,text)
results.append({'name':name,'exit_code':res.returncode,'compiled':True,'log_sha256':hashlib.sha256((o/(name+'.log')).read_bytes()).hexdigest()})
patch=''
for name in ['test_engineer_mine.py','test_engineer_mine_hq.py','test_engineer_mine_posts.py']:
before=r/'tests'/name;old=before.read_text() if before.exists() else '';new=(o/'tests'/name).read_text();patch+=''.join(difflib.unified_diff(old.splitlines(True),new.splitlines(True),fromfile='a/tests/'+name if before.exists() else '/dev/null',tofile='b/tests/'+name))
(o/'fix.patch').write_text(patch)
review={'status':'pass','source_hashes':{str(p):hashlib.sha256(p.read_bytes()).hexdigest() for p in [hq,posts,controller]},'tests_sha256':{str(p.relative_to(o)):hashlib.sha256(p.read_bytes()).hexdigest() for p in sorted((o/'tests').glob('*.py'))},'baseline_test_sha256':{str(p.relative_to(o/'baseline')):hashlib.sha256(p.read_bytes()).hexdigest() for p in sorted((o/'baseline/tests').glob('*.py'))},'patch_sha256':hashlib.sha256((o/'fix.patch').read_bytes()).hexdigest(),'results':results,'coverage':['Actual ledger classes: immutable consumed post/evidence values, task/report alias refusal, strict160m separation, chronological replay refusal,4-per-force/32-total attempts, record/source/group aliases and detached once-recorded sites.','Actual HQ MaintainEngineerMines calls actual ledger. First completed job then second spatially separate delivered post succeeds. Old retained pending job blocks admission even after unit.EngineerMine pointer clears. Two-active/32-retained/256-item caps and one query/tick preserved. No-site/no-item refusals remain consumed; replacement HQUnit cannot reset original-force quota.','Entire existing actual controller harness retained and extended. Copied prior hazard positions, invalid/overcap input, strict20m corridors and60m work area, active history exclusion, second distinct original item and shared tombstone all checked. BlocksNextPost uses exact force record: active blocks, stopped pending survives destroyed nonnull item, terminal callback or exact deletion settles without late movement/arming.','Baseline actual HQ compiles then fails new-post success. Two targeted source mutations compile then fail: fifth lifetime allowance and omitted stopped pending-removal blocker.'],'limits':['C++ source-method translation with vector/native job/inventory/AI boundary doubles. No Enforce compiler, native navigation, physical two-post mining, networking or path-safety guarantee.','No target/original mutations or full suite. New test_engineer_mine_hq imports shared ledger harness definitions from adjacent test_engineer_mine_posts; main guard prevents duplicate execution.'],'all_compilers_finished':True}
(o/'review.json').write_text(json.dumps(review,indent=2)+'\n');print(json.dumps(review,indent=2))