Fix convoy coordination, AI combat and spectator recovery

This commit is contained in:
2026-09-27 22:28:07 +02:00
parent 2551d171b3
commit c5629f7f4f
108 changed files with 4337 additions and 282 deletions
+58
View File
@@ -128,3 +128,61 @@ supply_state=body(battery,'ERR_MortarSupplyState SupplyState(')
assert supply_state.index('m_Transfer.Poll(now)') < supply_state.index('if (!SupplyAuthorized(now))')
assert shoot.count('super.EOnTaskSimulate(')==1 and 'battery.SupplyGunner()' not in shoot
print('PASS: actual startup/generation/ownership/timeout/callback policies and native-counter wait; integration preserves one-shot allocation and pending-crew cancellation')
# Execute the production post-shot node, including the superclass completion
# callback. A fast repeater must not start a ninth aim while HQ is still waiting
# to observe native completion; pending ammunition and duplicate ticks stay safe.
from types import SimpleNamespace
section=(r/'ERR_MortarFiring.c').read_text().split('modded class SCR_AIStaticArtilleryBehavior_ShootingLogic',1)[1]
cycle=body(SimpleNamespace(read_text=lambda:section),'override ENodeResult EOnTaskSimulate(')
cycle=cycle.replace('override ', '').replace('AIAgent owner','void* owner')
cycle=cycle.replace('ERR_MortarBattery battery;', 'ERR_MortarBattery* battery=nullptr;').replace('SCR_AIStaticArtilleryBehavior behavior;', 'SCR_AIStaticArtilleryBehavior* behavior=nullptr;')
cycle=cycle.replace('SCR_AIStaticArtilleryBehavior.Cast(m_Utility.GetExecutedAction())','m_Utility->GetExecutedAction()')
cycle=cycle.replace('ERR_MortarSystem system = ERR_MortarSystem.Get();','ERR_MortarSystem* system = &systemInstance;')
for name in ['battery','behavior','system']:cycle=cycle.replace(name+'.',name+'->')
for name in ['ERR_MortarSupplyRules','ERR_MortarSalvo','ERR_MortarSupplyState','ENodeResult']:cycle=cycle.replace(name+'.',name+'::')
cycle=cycle.replace('GetGame().GetWorld().GetWorldTime()', '1000.f').replace('super.EOnTaskSimulate(owner, dt)', 'Base::EOnTaskSimulate(owner, dt)')
gate=body(readiness,'static int CycleGate(')
run('''#include <cassert>
enum class ENodeResult {FAIL,RUNNING,SUCCESS};
enum ERR_MortarSupplyState {FAILED=-1,PENDING,READY};
struct ERR_MortarSalvo {static constexpr int ROUNDS=8;};
struct ERR_MortarSupplyRules {'''+gate+'''};
struct ERR_MortarBattery {
bool CommandOpen=true;int OrderShots=0,checks=0;ERR_MortarSupplyState supply=ERR_MortarSupplyState::READY;
ERR_MortarSupplyState SupplyState(float){checks++;return supply;}
};
struct SCR_AIStaticArtilleryBehavior {
ERR_MortarBattery* battery=nullptr;int count=0,completions=0;
int GetShotCount(){return count;}ERR_MortarBattery* ERR_GetMortarBattery(){return battery;}
};
struct Utility {SCR_AIStaticArtilleryBehavior* behavior;auto GetExecutedAction(){return behavior;}};
struct ERR_MortarSystem {int refusals=0;void Withhold(ERR_MortarBattery*,int){refusals++;}int SupplyFailure(ERR_MortarBattery*,float){return 0;}} systemInstance;
struct Base {
Utility* m_Utility=nullptr;int calls=0;bool aiming=false;static constexpr int PORT_AIMING_NEEDED=1;
ENodeResult EOnTaskSimulate(void*,float){calls++;auto b=m_Utility->behavior;if(++b->count==8)b->completions++;return ENodeResult::SUCCESS;}
void SetVariableOut(int,bool value){aiming=value;}
};
struct Node:Base {'''+cycle+'''};
int main(){
ERR_MortarBattery battery;SCR_AIStaticArtilleryBehavior behavior;behavior.battery=&battery;Utility utility{&behavior};Node n;n.m_Utility=&utility;
assert(n.EOnTaskSimulate(nullptr,0)==ENodeResult::RUNNING&&n.calls==0);
battery.OrderShots=1;battery.supply=ERR_MortarSupplyState::PENDING;
assert(n.EOnTaskSimulate(nullptr,0)==ENodeResult::RUNNING&&n.calls==0);
battery.supply=ERR_MortarSupplyState::READY;
for(int shot=1;shot<=8;shot++){
battery.OrderShots=shot;n.aiming=false;
if(shot==8)battery.supply=ERR_MortarSupplyState::FAILED; // final shot needs no ninth shell
auto result=n.EOnTaskSimulate(nullptr,0);
assert(n.calls==shot&&behavior.count==shot);
assert(result==(shot<8?ENodeResult::SUCCESS:ENodeResult::RUNNING));
assert(n.aiming==(shot<8));
}
assert(behavior.completions==1&&systemInstance.refusals==0);
for(int tick=0;tick<100;tick++)assert(n.EOnTaskSimulate(nullptr,0)==ENodeResult::RUNNING);
assert(n.calls==8&&behavior.completions==1); // no duplicate count or ninth aim
battery.CommandOpen=false;assert(n.EOnTaskSimulate(nullptr,0)==ENodeResult::FAIL);
behavior.battery=nullptr;assert(n.EOnTaskSimulate(nullptr,0)==ENodeResult::SUCCESS); // unrelated native artillery unchanged
}
''')
print('PASS: rapid mortar cycle counts eight physical rounds once and waits for native completion without a ninth aim or shell')