Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
62 lines
4.9 KiB
Python
62 lines
4.9 KiB
Python
"""Execute gunner retention eligibility and mixed-crew completion boundaries."""
|
|
import re
|
|
from policy_harness import ROOT,body,run
|
|
p=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HunterGunTeam.c'
|
|
s=body(p,'static IEntity LoadedGunner(').replace('foreach (','for (')
|
|
for t in ['IEntity','ERR_HunterVehicle','SCR_VehicleDamageManagerComponent','ERR_HQMountedAmmoSnapshot','BaseCompartmentSlot']:
|
|
s=re.sub(r'\b'+t+r' (?=\w)',t+'* ',s)
|
|
s=s.replace('static IEntity* LoadedGunner', 'static IEntity* LoadedGunner')
|
|
s=s.replace('SCR_VehicleDamageManagerComponent.Cast(unit.Entity.FindComponent(SCR_VehicleDamageManagerComponent))','unit.Entity.damage')
|
|
s=s.replace('ERR_HQMountedAmmoSampler.Inspect(unit.Entity)','unit.Entity.ammo')
|
|
s=s.replace('unit.Entity.damage','unit->Entity->damage').replace('unit.Entity.ammo','unit->Entity->ammo')
|
|
for n in ['unit','damage','ammo','seat']:s=s.replace(n+'.',n+'->')
|
|
for cls in ['ERR_Actors','ERR_ConvoyRules','EDamageState','ECompartmentType']:s=s.replace(cls+'.',cls+'::')
|
|
s=s.replace('null','nullptr')
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <vector>
|
|
struct Group{};
|
|
struct SCR_VehicleDamageManagerComponent{int state=0;float health=1;bool engine=true,gear=true;int GetState(){return state;}float GetHealthScaled(){return health;}bool GetEngineFunctional(){return engine;}bool GetGearboxFunctional(){return gear;}};
|
|
struct ERR_HQMountedAmmoSnapshot{bool Complete=true;int Rounds=1,Chambers=0;};
|
|
struct IEntity{bool fit=true,player=false;Group*group=nullptr;};
|
|
struct Vehicle{SCR_VehicleDamageManagerComponent*damage=nullptr;ERR_HQMountedAmmoSnapshot*ammo=nullptr;};
|
|
struct BaseCompartmentSlot{int type=1;IEntity*occupant=nullptr;int GetType(){return type;}IEntity*GetOccupant(){return occupant;}};
|
|
struct Members{IEntity*original=nullptr;bool Contains(IEntity*a){return a&&a==original;}};
|
|
struct ERR_HunterVehicle{Vehicle*Entity=nullptr;struct Group*Group=nullptr;bool Released=false;struct Members Members;std::vector<BaseCompartmentSlot*>Seats;};
|
|
namespace ERR_Actors{bool CanFight(IEntity*a){return a&&a->fit;}bool Player(IEntity*a){return a&&a->player;}struct Group*Group(IEntity*a){return a?a->group:nullptr;}}
|
|
namespace ERR_ConvoyRules{bool captured=false;bool Captured(Vehicle*,Group*){return captured;}}
|
|
namespace EDamageState{enum{DESTROYED=1};}namespace ECompartmentType{enum{TURRET=1};}
|
|
'''+s+r'''
|
|
int main(){
|
|
Group group,other;IEntity gunner;gunner.group=&group;SCR_VehicleDamageManagerComponent damage;ERR_HQMountedAmmoSnapshot ammo;Vehicle car;car.damage=&damage;car.ammo=&ammo;
|
|
ERR_HunterVehicle unit;unit.Entity=&car;unit.Group=&group;unit.Members.original=&gunner;BaseCompartmentSlot seat;seat.occupant=&gunner;unit.Seats={&seat};
|
|
assert(LoadedGunner(&unit)==&gunner); // No driver-seated prerequisite: driver is assault infantry.
|
|
for(int fault=0;fault<13;fault++){
|
|
if(fault==0)unit.Released=true;if(fault==1)ERR_ConvoyRules::captured=true;if(fault==2)damage.state=1;
|
|
if(fault==3)damage.health=.25;if(fault==4)damage.engine=false;if(fault==5)damage.gear=false;
|
|
if(fault==6)ammo.Complete=false;if(fault==7)ammo.Rounds=0;if(fault==8)gunner.fit=false;
|
|
if(fault==9)gunner.player=true;if(fault==10)gunner.group=&other;if(fault==11)unit.Members.original=nullptr;if(fault==12)seat.type=0;
|
|
assert(!LoadedGunner(&unit));unit.Released=false;ERR_ConvoyRules::captured=false;damage={};ammo={};gunner.fit=true;gunner.player=false;gunner.group=&group;unit.Members.original=&gunner;seat.type=1;
|
|
}
|
|
ammo.Rounds=0;ammo.Chambers=1;assert(LoadedGunner(&unit)==&gunner);
|
|
unit.Seats.clear();assert(!LoadedGunner(&unit));assert(!LoadedGunner(nullptr));
|
|
}
|
|
'''.replace(';if(', ';\n if('))
|
|
q=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HunterTactics.c'
|
|
s=body(q,'bool Aboard(').replace('foreach (','for (').replace('IEntity member','IEntity* member').replace('Unit.','Unit->').replace('m_GunTeam.','m_GunTeam->')
|
|
for cls in ['ERR_Actors','CompartmentAccessComponent']:s=s.replace(cls+'.',cls+'::')
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <vector>
|
|
struct IEntity{bool fit=true,player=false;int group=1,vehicle=1;};
|
|
namespace ERR_Actors{bool CanFight(IEntity*a){return a&&a->fit;}bool Player(IEntity*a){return a&&a->player;}int Group(IEntity*a){return a?a->group:0;}}
|
|
namespace CompartmentAccessComponent{int GetVehicleIn(IEntity*a){return a?a->vehicle:0;}}
|
|
struct UnitType{int Group=1,Entity=1;std::vector<IEntity*>Members;};
|
|
struct Team{IEntity*gunner;bool Retained(IEntity*a){return a==gunner&&a->vehicle==1;}};
|
|
struct Tactics{UnitType*Unit;Team*m_GunTeam;
|
|
'''+s+r'''
|
|
};
|
|
int main(){IEntity driver,gunner,passenger;UnitType unit;unit.Members={&driver,&gunner,&passenger};Team team{&gunner};Tactics t{&unit,&team};assert(t.Aboard(true)&&!t.Aboard(false));driver.vehicle=passenger.vehicle=0;assert(t.Aboard(false)&&!t.Aboard(true));t.m_GunTeam=nullptr;assert(!t.Aboard(false));gunner.vehicle=0;assert(t.Aboard(false));}
|
|
''')
|
|
print('PASS: only original fit loaded healthy uncaptured gunners stay mounted; driver can leave; infantry readiness excludes the retained gunner but regroup requires all survivors')
|