79 lines
5.3 KiB
Python
79 lines
5.3 KiB
Python
"""Execute the hunter fleet and actual seat allocation for UAZs and troop trucks."""
|
|
import re
|
|
from policy_harness import ROOT,body,run
|
|
p=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HunterService.c'
|
|
sarka_rules=body(p.parent/'ERR_HunterSarkaRules.c','static bool Seats(')
|
|
bike_rules=body(p.parent/'ERR_OptionalMotorbikes.c','static bool Seats(')
|
|
rules=body(p,'static bool Transport(')+body(p,'static bool Seats(')
|
|
s=body(p,'override protected bool FillCrew(').replace('override protected ','')
|
|
s=s.replace('array<BaseCompartmentSlot> all = {}, cargo = {}, turrets = {}, drivers = {};','Array<BaseCompartmentSlot*> all,cargo,turrets,drivers;')
|
|
s=s.replace('ERR_HunterVehicle.Cast(unit)','unit')
|
|
s=s.replace('foreach (','for (').replace('null','nullptr')
|
|
for typ in ['ERR_ConvoyVehicle','ERR_HunterVehicle','BaseCompartmentSlot','AIGroup','IEntity']:
|
|
s=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',s)
|
|
for obj in ['unit','seat','hunter','m_Mode']:
|
|
s=s.replace(obj+'.',obj+'->')
|
|
s=s.replace('unit->Seats[i].','unit->Seats[i]->')
|
|
for cls in ['ERR_EnemyMedicalCatalog','ERR_EnemyLoadoutCatalog','ERR_ConvoyRules','ERR_HunterFleetRules','ERR_MotorbikeRules','ERR_SarkaRules','ERR_ConvoyResources','ERR_HQ','ECompartmentType','string']:
|
|
s=s.replace(cls+'.',cls+'::')
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <vector>
|
|
#include <string>
|
|
using ResourceName=int;
|
|
struct string{template<class...T>static int Format(T...){return 0;}};
|
|
template<class T>struct Array:std::vector<T>{using std::vector<T>::operator=;void Insert(T x){this->push_back(x);}int Count(){return this->size();}};
|
|
struct ERR_HunterFleetRules{
|
|
'''+rules+r'''
|
|
};
|
|
struct ERR_SarkaRules{
|
|
'''+sarka_rules+r'''
|
|
};
|
|
struct ERR_MotorbikeRules{
|
|
'''+bike_rules+r'''
|
|
};
|
|
namespace ECompartmentType{enum{PILOT,TURRET,CARGO};}
|
|
struct IEntity{int prefab;};struct AIGroup{};
|
|
struct BaseCompartmentSlot{int type;bool available=true,fail=false;IEntity*occupant=nullptr;bool IsCompartmentAccessible(){return available;}IEntity*GetOccupant(){return occupant;}int GetType(){return type;}
|
|
IEntity*SpawnCharacterInCompartment(int prefab,AIGroup*&,int){if(fail)return nullptr;occupant=new IEntity{prefab};return occupant;}};
|
|
struct Vehicle{Array<BaseCompartmentSlot*> seats;};
|
|
struct ERR_ConvoyVehicle{Vehicle*Entity;AIGroup*Group;bool TroopTransport=false;bool Motorbike=false;bool Sarka=false;Array<BaseCompartmentSlot*>Seats;Array<IEntity*>Members;IEntity*Driver=nullptr;};using ERR_HunterVehicle=ERR_ConvoyVehicle;
|
|
struct Mode{int owned=0;void Own(IEntity*){owned++;}};
|
|
namespace ERR_EnemyMedicalCatalog{int Crew(Mode*,bool medic){return medic?2:1;}}
|
|
namespace ERR_EnemyLoadoutCatalog{bool Available(int p){return p>0;}}
|
|
namespace ERR_ConvoyRules{bool CollectSeats(Vehicle*v,Array<BaseCompartmentSlot*>&out){out=v->seats;return true;}}
|
|
namespace ERR_ConvoyResources{int GroupPrefab(){return 1;}}
|
|
namespace ERR_HQ{void Event(const char*,int){}void Event(const char*,const char*){}}
|
|
struct Service{Mode*m_Mode;
|
|
'''+s+r'''
|
|
};
|
|
int main(){
|
|
int trucks=0;for(int i=0;i<15;i++){if(ERR_HunterFleetRules::Transport(i))trucks++;assert(ERR_HunterFleetRules::Transport(i)==(i%3==2));}assert(trucks==5);
|
|
assert(!ERR_HunterFleetRules::Transport(-1)&&!ERR_HunterFleetRules::Transport(15));
|
|
for(int transport=0;transport<2;transport++){
|
|
Mode mode;Service service{&mode};Vehicle vehicle;AIGroup group;ERR_HunterVehicle unit{&vehicle,&group};unit.TroopTransport=transport;
|
|
BaseCompartmentSlot driver{ECompartmentType::PILOT},gunner{ECompartmentType::TURRET};vehicle.seats.Insert(&driver);
|
|
if(!transport)vehicle.seats.Insert(&gunner);
|
|
BaseCompartmentSlot cargo[14];for(int i=0;i<(transport?14:3);i++){cargo[i].type=ECompartmentType::CARGO;vehicle.seats.Insert(&cargo[i]);}
|
|
assert(service.FillCrew(&unit));assert(unit.Members.Count()==(transport?15:5)&&unit.Driver==driver.occupant&&mode.owned==unit.Members.Count());
|
|
int medics=0;for(auto member:unit.Members)if(member->prefab==2)medics++;assert(medics==1&&cargo[0].occupant->prefab==2);
|
|
}
|
|
{Mode mode;Service service{&mode};Vehicle vehicle;AIGroup group;ERR_HunterVehicle bike{&vehicle,&group};bike.Motorbike=true;
|
|
BaseCompartmentSlot driver{ECompartmentType::PILOT},passenger{ECompartmentType::CARGO};vehicle.seats={};vehicle.seats.Insert(&driver);vehicle.seats.Insert(&passenger);
|
|
assert(service.FillCrew(&bike)&&bike.Members.Count()==2&&bike.Driver==driver.occupant);
|
|
assert(driver.occupant->prefab==1&&passenger.occupant->prefab==2);}
|
|
for(int passengers=2;passengers<=4;passengers++){
|
|
Mode mode;Service service{&mode};Vehicle vehicle;AIGroup group;ERR_HunterVehicle car{&vehicle,&group};car.Sarka=true;car.TroopTransport=true;
|
|
BaseCompartmentSlot driver{ECompartmentType::PILOT},cargo[4];vehicle.seats.Insert(&driver);
|
|
for(int i=0;i<passengers;i++){cargo[i].type=ECompartmentType::CARGO;vehicle.seats.Insert(&cargo[i]);}
|
|
assert(service.FillCrew(&car)==(passengers==3));
|
|
if(passengers==3){assert(car.Members.Count()==4&&car.Driver==driver.occupant);int medics=0;for(auto member:car.Members)if(member->prefab==2)medics++;assert(medics==1);}
|
|
else assert(mode.owned==0);
|
|
}
|
|
assert(!ERR_HunterFleetRules::Seats(true,1,0,13,14)); // no silent population shrink
|
|
assert(!ERR_HunterFleetRules::Seats(false,1,0,14,15)); // transport cannot pass as armed UAZ
|
|
assert(!ERR_HunterFleetRules::Seats(true,2,0,14,16));
|
|
}
|
|
''')
|
|
print('PASS: finite 2-UAZ/1-truck rotation, exact physical crew seats, one medic, and no smaller/armed substitute for transport')
|