Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
67 lines
4.5 KiB
Python
67 lines
4.5 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'
|
|
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_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_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;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);}
|
|
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')
|