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
+65
View File
@@ -0,0 +1,65 @@
"""Actual native shoot adapter: void acceptance is not a physical projectile."""
import re
from policy_harness import ROOT, body, run
p=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_MortarFiring.c'
s=body(p,'modded class SCR_AIShootStaticArtillery')
s=s.replace('modded class SCR_AIShootStaticArtillery','struct Node').replace('protected ','').replace('override ','')
s=s.replace('GetGame().GetWorld().GetWorldTime()','this->now')
s=s.replace('SCR_MortarMuzzleComponent.Cast(gun.FindComponent(SCR_MortarMuzzleComponent))','gun->muzzle')
s=s.replace('SCR_CharacterControllerComponent.Cast(loader.FindComponent(SCR_CharacterControllerComponent))','loader->controller')
for typ in ['ERR_MortarBattery','ERR_MortarSystem','SCR_MortarMuzzleComponent','SCR_CharacterControllerComponent','AIAgent']:
s=re.sub(r'\b'+typ+r' (\w+)',typ+r'* \1',s)
s=s.replace('super.EOnTaskSimulate(owner, dt)','Native(owner, dt)').replace('ERR_MortarSystem.Get()','ERR_MortarSystem::Get()').replace('ERR_Actors.','ERR_Actors::').replace('ENodeResult.','ENodeResult::').replace('string.Format','string::Format')
for n in ['owner','system','battery','gun','loader','muzzle','controller']:s=s.replace(n+'.',n+'->')
s=re.sub(r'\bnull\b','nullptr',s)
# Enforce initializes locals/members; C++ needs those defaults explicitly.
s=re.sub(r'(ERR_MortarBattery\*|SCR_CharacterControllerComponent\*|IEntity) (\w+);',r'\1 \2 = nullptr;',s)
s=s.replace('IEntity m_ERRLoader, m_ERRShell;','IEntity m_ERRLoader=nullptr, m_ERRShell=nullptr;')
s=s.replace('int m_ERRLoadingOrder, m_ERRLoadingShot, m_ERRLoadAttempts;','int m_ERRLoadingOrder=0, m_ERRLoadingShot=0, m_ERRLoadAttempts=0;').replace('float m_ERRLoadStarted, m_ERRRetryAt;','float m_ERRLoadStarted=0, m_ERRRetryAt=0;')
extra='''
static constexpr int PORT_ARTILLERY_ENTITY=1,PORT_AMMO_ENTITY=2;
float now=0;int requests=0;bool startLoad=false,synchronous=false;
IEntity portGun=nullptr,portShell=nullptr;
void GetVariableIn(int p,IEntity& e){e=p==1?portGun:portShell;}
ENodeResult Native(AIAgent*,float){requests++;if(startLoad)portGun->muzzle->loading=true;if(synchronous){ERR_MortarSystem::instance->battery->OrderShots++;}return ENodeResult::SUCCESS;}
'''
s=s[:s.rfind('}')]+extra+s[s.rfind('}'):]+ ';'
run('''#include <cassert>
#include <string>
struct string:std::string{using std::string::string;template<class... T>static string Format(const char* s,T...){return s;}};
enum class ENodeResult{FAIL,RUNNING,SUCCESS};
struct Entity;using IEntity=Entity*;
struct SCR_MortarMuzzleComponent{bool loading=false;int ammo=0;bool IsBeingLoaded(){return loading;}int GetAmmoCount(){return ammo;}};
struct SCR_CharacterControllerComponent{IEntity held=nullptr;IEntity GetAttachedGadgetAtLeftHandSlot(){return held;}};
struct Entity{SCR_MortarMuzzleComponent* muzzle=nullptr;SCR_CharacterControllerComponent* controller=nullptr;bool fighting=true,player=false;};
struct AIAgent{IEntity actor=nullptr;IEntity GetControlledEntity(){return actor;}};
struct ERR_Actors{static bool CanFight(IEntity e){return e&&e->fighting;}static bool Player(IEntity e){return e&&e->player;}};
struct ERR_MortarBattery{int OrderId=1,OrderShots=0;bool CommandOpen=true,Released=false;void Trace(string,string,string){}};
struct ERR_MortarSystem{static ERR_MortarSystem* instance;ERR_MortarBattery* battery=nullptr;bool allowed=true;int rejects=0,checks=0;static ERR_MortarSystem* Get(){return instance;}ERR_MortarBattery* FindGun(IEntity){return battery;}bool AllowShot(ERR_MortarBattery*){checks++;return allowed;}void Withhold(ERR_MortarBattery*,string){rejects++;}};
ERR_MortarSystem* ERR_MortarSystem::instance=nullptr;
'''+s+'''
struct Fixture{ERR_MortarBattery battery;ERR_MortarSystem sys;SCR_MortarMuzzleComponent muzzle;SCR_CharacterControllerComponent controller;Entity gun,actor,shell;AIAgent agent;Node node;
Fixture(){sys.battery=&battery;ERR_MortarSystem::instance=&sys;gun.muzzle=&muzzle;actor.controller=&controller;controller.held=&shell;agent.actor=&actor;node.portGun=&gun;node.portShell=&shell;}
ENodeResult tick(float t=0){node.now=t;return node.EOnTaskSimulate(&agent,.02f);}};
int main(){
Fixture retry;assert(retry.tick()==ENodeResult::RUNNING&&retry.node.requests==1);assert(retry.tick(999)==ENodeResult::RUNNING&&retry.node.requests==1);
retry.node.startLoad=true;assert(retry.tick(1000)==ENodeResult::RUNNING&&retry.node.requests==2);assert(retry.tick(4000)==ENodeResult::RUNNING&&retry.node.requests==2);
retry.battery.Released=true;retry.muzzle.loading=false;assert(retry.tick(20000)==ENodeResult::RUNNING&&retry.node.requests==2);
retry.battery.OrderShots++;retry.battery.Released=false;retry.node.portShell=nullptr;assert(retry.tick(20001)==ENodeResult::SUCCESS&&retry.node.requests==2&&!retry.node.m_ERRLoadingBattery);
Fixture refused;refused.tick();refused.tick(1000);refused.tick(2000);assert(refused.tick(3000)==ENodeResult::FAIL&&refused.node.requests==3&&refused.sys.rejects==1);
Fixture interrupted;interrupted.node.startLoad=true;interrupted.tick();interrupted.muzzle.loading=false;assert(interrupted.tick(2000)==ENodeResult::RUNNING&&interrupted.node.requests==2);
Fixture stuck;stuck.node.startLoad=true;stuck.tick();assert(stuck.tick(15000)==ENodeResult::FAIL&&stuck.node.requests==1&&stuck.sys.rejects==1);
Fixture successor;successor.tick();successor.battery.OrderId++;assert(successor.tick(1000)==ENodeResult::FAIL&&!successor.sys.rejects&&successor.node.requests==1);
Fixture closed;closed.tick();closed.battery.CommandOpen=false;assert(closed.tick(1000)==ENodeResult::FAIL&&!closed.sys.rejects);
Fixture newOwner;newOwner.tick();newOwner.agent.actor=&newOwner.shell;assert(newOwner.tick(1000)==ENodeResult::FAIL&&!newOwner.sys.rejects);
Fixture changed;changed.tick();changed.node.portShell=&changed.actor;changed.controller.held=&changed.actor;assert(changed.tick(1000)==ENodeResult::FAIL&&changed.node.requests==1);
Fixture moved;moved.tick();moved.controller.held=nullptr;assert(moved.tick(1000)==ENodeResult::FAIL&&moved.node.requests==1);
Fixture unsafe;unsafe.tick();unsafe.sys.allowed=false;assert(unsafe.tick(1000)==ENodeResult::FAIL&&unsafe.node.requests==1);
Fixture chambered;chambered.muzzle.ammo=1;assert(chambered.tick()==ENodeResult::FAIL&&!chambered.node.requests);
Fixture captured;captured.actor.player=true;assert(captured.tick()==ENodeResult::FAIL&&!captured.node.requests);
Fixture dead;dead.actor.fighting=false;assert(dead.tick()==ENodeResult::FAIL&&!dead.node.requests);
Fixture instant;instant.node.synchronous=true;assert(instant.tick()==ENodeResult::RUNNING);assert(instant.tick(1)==ENodeResult::SUCCESS&&instant.node.requests==1);
Fixture native;native.sys.battery=nullptr;assert(native.tick()==ENodeResult::SUCCESS&&native.node.requests==1);
}
''')
print('PASS: shoot adapter waits for a physical round; bounded same-shell retries, interrupted/never-started loads, committed no-retry, order/owner changes and normal native delegation')