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 group radio startup preservation, finite native grants, callback quarantine, frozen selection/actor ownership and bounded observation
@@ -0,0 +1,25 @@
/tmp/err-policy-g4ak9_pz/check.cpp: In member function ‘bool ERR_EnemyEquipment::MineSupplyReady(SCR_AIGroup, IEntity)’:
/tmp/err-policy-g4ak9_pz/check.cpp:252:39: error: ‘struct ERR_EnemyEquipmentEntry’ has no member named ‘Mines’
252 | return !entry->Released && entry->Mines && entry->Mines.ReadyForResupply();
| ^~~~~
/tmp/err-policy-g4ak9_pz/check.cpp:252:55: error: ‘struct ERR_EnemyEquipmentEntry’ has no member named ‘Mines’
252 | return !entry->Released && entry->Mines && entry->Mines.ReadyForResupply();
| ^~~~~
Traceback (most recent call last):
File "/home/jakub/devel/escapistsrereintroduced/tests/test_enemy_radio.py", line 45, in <module>
run(r'''
~~~^^^^^
#include <cassert>
^^^^^^^^^^^^^^^^^^
...<97 lines>...
}
^
''')
^^^^
File "/home/jakub/devel/escapistsrereintroduced/tests/policy_harness.py", line 13, in run
subprocess.run(['g++','-std=c++17','-Wall','-Werror',str(p/'check.cpp'),'-o',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 '['g++', '-std=c++17', '-Wall', '-Werror', '/tmp/err-policy-g4ak9_pz/check.cpp', '-o', '/tmp/err-policy-g4ak9_pz/check']' returned non-zero exit status 1.
@@ -0,0 +1,94 @@
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyLoadout.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyLoadout.c
@@ -158,6 +158,9 @@
protected int m_Checks;
protected int m_Remaining;
protected ref ERR_EnemyEquipmentCallback m_Operation;
+ // A terminal grant may still retain an accepted native insertion after timeout.
+ // Resupply cannot race that callback or restart the initial allowance.
+ bool ReadyForResupply() { return m_Finished && (!m_Operation || m_Operation.Finished); }
void Check(ERR_GameMode mode, SCR_AIGroup group, IEntity actor)
{
if (m_Finished) return;
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyEquipment.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyEquipment.c
@@ -138,6 +138,14 @@
if (state.Radio) state.Radio.Stop();
}
foreach (ERR_EnemyEquipmentEntry entry : m_Entries) if (entry.Group == group) entry.Stop(m_Mode);
+ }
+ bool MineSupplyReady(SCR_AIGroup group, IEntity actor)
+ {
+ if (m_Stopped || m_Entries.Count() > 2048 || !ERR_EnemyEquipmentRules.Owned(m_Mode, group, actor)) return false;
+ foreach (ERR_EnemyEquipmentEntry entry : m_Entries)
+ if (entry && entry.Actor == actor && entry.Group == group)
+ return !entry.Released && entry.Mines && entry.Mines.ReadyForResupply();
+ return false;
}
protected bool Known(IEntity actor)
{
--- a/tests/test_enemy_loadout.py
+++ b/tests/test_enemy_loadout.py
@@ -76,11 +76,25 @@
Soldier pending;ERR_EnemyLauncherGrant wait;for(int n=0;n<30;n++)wait.Check(&mode,&pending.group,&pending.actor);assert(wait.m_Finished&&pending.inv.requests==1);pending.inv.Complete(true);wait.Check(&mode,&pending.group,&pending.actor);assert(pending.inv.requests==1);
Soldier looted;ERR_EnemyLauncherGrant lost;lost.Check(&mode,&looted.group,&looted.actor);looted.inv.Complete(true);lost.Check(&mode,&looted.group,&looted.actor);assert(lost.m_Finished&&!lost.m_Fallback&&looted.inv.requests==1);
Soldier captured;ERR_EnemyLauncherGrant ownership;ownership.Check(&mode,&captured.group,&captured.actor);captured.actor.eligible=false;ownership.Check(&mode,&captured.group,&captured.actor);assert(ownership.m_Finished&&captured.inv.requests==1);
- Soldier miner;ERR_EnemyMineGrant mines;mines.Check(&mode,&miner.group,&miner.actor);assert(miner.inv.requests==1);IEntity mine1,mine2;mine1.mine=mine2.mine=true;miner.Add(mine1,false);miner.inv.Complete(true);mines.Check(&mode,&miner.group,&miner.actor);assert(miner.inv.requests==2);miner.Add(mine2,false);miner.inv.Complete(true);mines.Check(&mode,&miner.group,&miner.actor);assert(mines.m_Finished);
+ // Startup insertion must settle before a physical mine top-up is eligible.
+ ERR_EnemyMineGrant readiness;ERR_EnemyEquipmentCallback pendingMineGrant;
+ assert(!readiness.ReadyForResupply());readiness.m_Finished=true;
+ assert(readiness.ReadyForResupply());readiness.m_Operation=&pendingMineGrant;
+ assert(!readiness.ReadyForResupply());pendingMineGrant.Finished=true;
+ assert(readiness.ReadyForResupply());readiness.m_Finished=false;
+ assert(!readiness.ReadyForResupply());
+ Soldier miner;ERR_EnemyMineGrant mines;mines.Check(&mode,&miner.group,&miner.actor);assert(miner.inv.requests==1);IEntity mine1,mine2;mine1.mine=mine2.mine=true;miner.Add(mine1,false);miner.inv.Complete(true);mines.Check(&mode,&miner.group,&miner.actor);assert(miner.inv.requests==2&&!mines.ReadyForResupply());miner.Add(mine2,false);miner.inv.Complete(true);mines.Check(&mode,&miner.group,&miner.actor);assert(mines.m_Finished&&mines.ReadyForResupply());
miner.inv.items.clear();for(int n=0;n<20;n++)mines.Check(&mode,&miner.group,&miner.actor);assert(miner.inv.requests==2);
Soldier full;ERR_EnemyMineGrant existing;full.Add(mine1,false);full.Add(mine2,false);existing.Check(&mode,&full.group,&full.actor);assert(existing.m_Finished&&full.inv.requests==0);
Soldier dropped;ERR_EnemyMineGrant finite;finite.Check(&mode,&dropped.group,&dropped.actor);dropped.inv.Complete(true);finite.Check(&mode,&dropped.group,&dropped.actor);dropped.inv.Complete(true);finite.Check(&mode,&dropped.group,&dropped.actor);assert(finite.m_Finished&&dropped.inv.requests==2);
Soldier noSpace;noSpace.inv.accept=false;ERR_EnemyMineGrant noLoose;noLoose.Check(&mode,&noSpace.group,&noSpace.actor);assert(noLoose.m_Finished&&noSpace.inv.requests==1);
+ Soldier timeoutActor;ERR_EnemyMineGrant timeoutGrant;
+ timeoutGrant.Check(&mode,&timeoutActor.group,&timeoutActor.actor);
+ for(int i=0;i<12;i++)timeoutGrant.Check(&mode,&timeoutActor.group,&timeoutActor.actor);
+ assert(timeoutGrant.m_Finished&&!timeoutGrant.ReadyForResupply()&&timeoutActor.inv.requests==1);
+ timeoutActor.inv.Complete(true);assert(timeoutGrant.ReadyForResupply());
+ timeoutGrant.Check(&mode,&timeoutActor.group,&timeoutActor.actor);assert(timeoutActor.inv.requests==1);
+
}
''')
print('PASS: actual percentage selection, native/preferred launcher preservation, failure/removal fallback, pending-operation quarantine, capture, finite two-mine grants and no replenishment')
--- a/tests/test_enemy_equipment.py
+++ b/tests/test_enemy_equipment.py
@@ -11,7 +11,7 @@
coordinator=re.sub(r'\b'+cls+r' ([a-zA-Z_]\w*)',cls+r'* \1',coordinator)
for obj in ('m_Mode','existing','state','group','agent','entry','actor'):
coordinator=coordinator.replace(obj+'.',obj+'->')
-coordinator=coordinator.replace('Group.GetAgents','Group->GetAgents').replace('Combat.ERR_','Combat->ERR_').replace('Optics.Check','Optics->Check').replace('Radio.Check','Radio->Check').replace('Radio.Stop','Radio->Stop').replace('Radio.Register','Radio->Register')
+coordinator=coordinator.replace('Group.GetAgents','Group->GetAgents').replace('Combat.ERR_','Combat->ERR_').replace('Mines.ReadyForResupply','Mines->ReadyForResupply').replace('Optics.Check','Optics->Check').replace('Radio.Check','Radio->Check').replace('Radio.Stop','Radio->Stop').replace('Radio.Register','Radio->Register')
for cls in ('Replication','ERR_GameMode','ERR_EnemyEquipmentRules'):coordinator=coordinator.replace(cls+'.',cls+'::')
coordinator=coordinator.replace('void ERR_EnemyEquipment(', 'ERR_EnemyEquipment(')
coordinator=coordinator.replace('GetGame().GetWorld().GetWorldTime()','worldTime').replace('foreach (','for (').replace('null','nullptr')
@@ -36,7 +36,9 @@
void Check(ERR_GameMode*){checks++;}void Stop(){stops++;}
};
struct ERR_EnemyRadio{ERR_EnemyRadio(SCR_AIGroup*,int){}void Register(IEntity*){}void Check(ERR_GameMode*){}void Stop(){}};
+struct MineGrant{bool settled=false;bool ReadyForResupply(){return settled;}};
struct ERR_EnemyEquipmentEntry{
+ MineGrant* Mines=nullptr;
ERR_EnemyOptics* Optics=nullptr;bool Complete=false;
IEntity* Actor=nullptr;SCR_AIGroup* Group=nullptr;SCR_AICombatComponent* Combat=nullptr;bool Released=false;int SkillChecks=0,checks=0,stops=0;
void Check(ERR_GameMode*){checks++;Complete=true;}void Stop(ERR_GameMode*){stops++;Released=true;if(Optics)Optics->Stop();Combat=nullptr;}
@@ -49,6 +51,14 @@
service.Tick(0);assert(service.m_Entries.Count()==0);service.Tick(3000);assert(service.m_Entries.Count()==1&&combat.binds==1&&combat.refreshes==1);
auto entry=service.m_Entries[0];assert(entry->Optics&&entry->Optics->actor==&soldier&&entry->Optics->group==&group);
assert(entry->Complete&&entry->Optics->checks==1&&combat.refreshes==1); // Stinger is already complete; optics has its own startup completion.
+ MineGrant grant;entry->Mines=&grant;IEntity foreign;SCR_AIGroup otherGroup;
+ assert(!service.MineSupplyReady(&group,&soldier));grant.settled=true;
+ assert(service.MineSupplyReady(&group,&soldier));assert(!service.MineSupplyReady(&otherGroup,&soldier));assert(!service.MineSupplyReady(&group,&foreign));
+ soldier.eligible=false;assert(!service.MineSupplyReady(&group,&soldier));soldier.eligible=true;
+ entry->Released=true;assert(!service.MineSupplyReady(&group,&soldier));entry->Released=false;
+ entry->Mines=nullptr;assert(!service.MineSupplyReady(&group,&soldier));entry->Mines=&grant;
+ service.m_Stopped=true;assert(!service.MineSupplyReady(&group,&soldier));service.m_Stopped=false;
+ service.m_Entries.resize(2049,nullptr);assert(!service.MineSupplyReady(&group,&soldier));service.m_Entries.resize(1);
service.Tick(3001);assert(entry->checks==1&&entry->Optics->checks==1&&combat.refreshes==1);
service.Tick(6000);assert(entry->checks==2&&entry->Optics->checks==2&&combat.binds==1&&combat.refreshes==2);
service.UnregisterGroup(&group);service.Tick(9000);assert(service.m_Entries[0]->Released&&service.m_Entries[0]->checks==2&&entry->Optics->checks==2&&entry->Optics->stops==1);
@@ -0,0 +1,21 @@
--- a/tests/test_enemy_radio.py
+++ b/tests/test_enemy_radio.py
@@ -30,7 +30,7 @@
coordinator=re.sub(r'\b'+cls+r' ([a-zA-Z_]\w*)',cls+r'* \1',coordinator)
for obj in ('m_Mode','existing','state','group','agent','entry','actor'):
coordinator=coordinator.replace(obj+'.',obj+'->')
-coordinator=coordinator.replace('Group.GetAgents','Group->GetAgents').replace('Combat.ERR_','Combat->ERR_').replace('Optics.Check','Optics->Check').replace('Radio.Stop','Radio->Stop').replace('Radio.Check','Radio->Check').replace('Radio.Register','Radio->Register')
+coordinator=coordinator.replace('Group.GetAgents','Group->GetAgents').replace('Combat.ERR_','Combat->ERR_').replace('Mines.ReadyForResupply','Mines->ReadyForResupply').replace('Optics.Check','Optics->Check').replace('Radio.Stop','Radio->Stop').replace('Radio.Check','Radio->Check').replace('Radio.Register','Radio->Register')
for cls in ('Replication','ERR_GameMode','ERR_EnemyEquipmentRules'):coordinator=coordinator.replace(cls+'.',cls+'::')
coordinator=coordinator.replace('void ERR_EnemyEquipment(', 'ERR_EnemyEquipment(')
coordinator=coordinator.replace('GetGame().GetWorld().GetWorldTime()','worldTime').replace('foreach (','for (').replace('null','nullptr')
@@ -39,7 +39,8 @@
float worldTime=0;
struct SCR_AICombatComponent{void ERR_BindEnemySkill(ERR_GameMode*,Group*){} void ERR_RefreshEnemySkill(){}};
struct ERR_EnemyOptics{ERR_EnemyOptics(IEntity,Group*){}void Check(ERR_GameMode*){}};
-struct ERR_EnemyEquipmentEntry{IEntity Actor=nullptr;struct Group*Group=nullptr;SCR_AICombatComponent*Combat=nullptr;ERR_EnemyOptics*Optics=nullptr;bool Released=false;int SkillChecks=0;
+struct MineGrant{bool ReadyForResupply(){return false;}};
+struct ERR_EnemyEquipmentEntry{MineGrant*Mines=nullptr;IEntity Actor=nullptr;struct Group*Group=nullptr;SCR_AICombatComponent*Combat=nullptr;ERR_EnemyOptics*Optics=nullptr;bool Released=false;int SkillChecks=0;
void Check(ERR_GameMode*){}void Stop(ERR_GameMode*){Released=true;}};
'''
run(r'''
@@ -0,0 +1,4 @@
{
"tests/test_enemy_loadout.py": 0,
"tests/test_enemy_equipment.py": 0
}
@@ -0,0 +1 @@
PASS: actual equipment coordinator registration dedupe, startup delay, five member scans, finite component retries, unregister/stop terminal behavior and 256-group bound
@@ -0,0 +1 @@
PASS: actual percentage selection, native/preferred launcher preservation, failure/removal fallback, pending-operation quarantine, capture, finite two-mine grants and no replenishment