Files
EscapistsReintroduced/tests/test_enemy_loadout.py
T

87 lines
8.7 KiB
Python

"""Execute production finite launcher/fallback/mine controllers with native boundaries."""
from policy_harness import ROOT, run, body
import re
source=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyLoadout.c'
s=source.read_text();s=s[s.index('class ERR_EnemyLauncherGrant'):]
s=re.sub(r'//[^\n]*','',s).replace('protected ','').replace('ref ','').replace('class ','struct ')
s=s.replace('ResourceName','string').replace('array<IEntity>','Array<IEntity*>')
s=s.replace('InventoryStorageManagerComponent.Cast(actor.FindComponent(InventoryStorageManagerComponent))','actor->inventory').replace('BaseWeaponManagerComponent.Cast(actor.FindComponent(BaseWeaponManagerComponent))','actor->manager')
s=s.replace('item.FindComponent(SCR_AntiPersonnelMineCollisionHandlerComponent)','item->mine')
for typ in ('ERR_GameMode','SCR_AIGroup','IEntity','InventoryStorageManagerComponent','BaseWeaponManagerComponent','ERR_EnemyEquipmentCallback'):
s=re.sub(r'\b'+typ+r' (\w+)',typ+r'* \1',s)
for obj in ('actor','group','mode','inventory','manager','m_Operation','existing','item'):s=s.replace(obj+'.',obj+'->')
s=s.replace('Settings().','Settings()->').replace('GetPrefabData().','GetPrefabData()->')
for obj in ('ERR_HQ','Math','ERR_EnemyLoadoutRules','ERR_EnemyEquipmentRules','ERR_EnemyLoadoutCatalog','EStoragePurpose','string'):s=s.replace(obj+'.',obj+'::')
s=s.replace('string::Format(', 'Format(').replace('foreach (','for (').replace('null','nullptr')
s=re.sub(r'\b(bool|int|\w+\*) (\w+);',r'\1 \2 {};',s).replace('\n}\n','\n};\n')
selected=body(source,'static bool Selected(')
run(r'''
#include <cassert>
#include <algorithm>
#include <string>
#include <vector>
using string=std::string;
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;using std::vector<T>::operator=;int Count(){return this->size();}bool Contains(T x){return std::find(this->begin(),this->end(),x)!=this->end();}void Clear(){this->clear();}void Insert(T x){this->push_back(x);}};
struct ERR_EnemyEquipmentCallback{bool Finished=false,Succeeded=false;};
struct InventoryStorageManagerComponent;struct BaseWeaponManagerComponent;
struct IEntity{string prefab;bool mine=false,loaded=true,eligible=true;InventoryStorageManagerComponent* inventory=nullptr;BaseWeaponManagerComponent* manager=nullptr;
IEntity* GetPrefabData(){return this;}string GetPrefabName(){return prefab;}int GetOrigin(){return 0;}};
struct BaseWeaponManagerComponent{Array<IEntity*> weapons;void GetWeaponsList(Array<IEntity*>& out){out=weapons;}};
namespace EStoragePurpose{const int PURPOSE_WEAPON_PROXY=1,PURPOSE_DEPOSIT=2;}
struct InventoryStorageManagerComponent{Array<IEntity*> items;ERR_EnemyEquipmentCallback* pending=nullptr;bool accept=true,deleteAccept=true;int requests=0,deletes=0;string last;IEntity* deleting=nullptr;
void GetItems(Array<IEntity*>& out){out=items;}
bool TrySpawnPrefabToStorage(string prefab,void*,int,int,ERR_EnemyEquipmentCallback* op){requests++;last=prefab;pending=op;return accept;}
bool TryDeleteItem(IEntity* item,ERR_EnemyEquipmentCallback* op){assert(items.Contains(item));deletes++;deleting=item;pending=op;return deleteAccept;}
void Complete(bool ok){pending->Finished=true;pending->Succeeded=ok;}
};
struct Settings{int aa=100,mines=100;int Runtime(string key){return key=="antiAirChancePercent"?aa:mines;}};
struct ERR_GameMode{struct Settings settings;struct Settings* Settings(){return &settings;}};
struct SCR_AIGroup{int GetFaction(){return 1;}};
namespace ERR_EnemyEquipmentRules{bool Eligible(ERR_GameMode*,SCR_AIGroup*,IEntity* actor){return actor&&actor->eligible;}}
namespace Math{int roll=0;int RandomInt(int,int){return roll;}int Min(int a,int b){return std::min(a,b);}}
namespace ERR_HQ{void Event(string,string){}}
template<class... Args>string Format(string,Args...){return "audit";}
struct ERR_EnemyLoadoutRules{
'''+selected+r'''
static bool AntiAirName(string prefab){return prefab=="IGLA"||prefab=="FIM";}
};
struct ERR_EnemyLoadoutCatalog{
static inline string FIM="FIM",MINE="MINE",preferred="IGLA";static inline bool available=true;
static string Launcher(ERR_GameMode*,int){return preferred;}static bool Available(string){return available;}
static bool Loaded(IEntity* item){return item&&item->loaded;}
static IEntity* Find(string name,Array<IEntity*>& items,Array<IEntity*>& excluded){for(auto* i:items)if(i->prefab==name&&!excluded.Contains(i))return i;return nullptr;}
};
'''+s+r'''
struct Soldier{IEntity actor,rifle;InventoryStorageManagerComponent inv;BaseWeaponManagerComponent mgr;SCR_AIGroup group;
Soldier(){actor.inventory=&inv;actor.manager=&mgr;rifle.prefab="RIFLE";inv.items={&rifle};mgr.weapons={&rifle};}
void Add(IEntity& item,bool weapon){inv.items.Insert(&item);if(weapon)mgr.weapons.Insert(&item);}
};
int main(){
for(int percent=0;percent<=100;percent++){int chosen=0;for(int roll=0;roll<100;roll++)chosen+=ERR_EnemyLoadoutRules::Selected(percent,roll);assert(chosen==percent);}
assert(!ERR_EnemyLoadoutRules::Selected(-1,0)&&!ERR_EnemyLoadoutRules::Selected(101,0));
ERR_GameMode mode;Math::roll=99;
Soldier off;ERR_EnemyLauncherGrant noaa;ERR_EnemyMineGrant nomines;mode.settings.aa=mode.settings.mines=0;
noaa.Check(&mode,&off.group,&off.actor);nomines.Check(&mode,&off.group,&off.actor);assert(off.inv.requests==0);
mode.settings.aa=mode.settings.mines=100;
Soldier good;ERR_EnemyLauncherGrant aa;aa.Check(&mode,&good.group,&good.actor);assert(good.inv.last=="IGLA");
IEntity igla;igla.prefab="IGLA";good.Add(igla,true);good.inv.Complete(true);aa.Check(&mode,&good.group,&good.actor);assert(aa.m_Finished&&good.inv.requests==1);
good.mgr.weapons.clear();good.inv.items.clear();for(int n=0;n<20;n++)aa.Check(&mode,&good.group,&good.actor);assert(good.inv.requests==1);
Soldier reject;ERR_EnemyLauncherGrant fallback;reject.inv.accept=false;fallback.Check(&mode,&reject.group,&reject.actor);assert(fallback.m_Fallback);
reject.inv.accept=true;fallback.Check(&mode,&reject.group,&reject.actor);assert(reject.inv.last=="FIM"&&reject.inv.requests==2);
IEntity fim;fim.prefab="FIM";reject.Add(fim,true);reject.inv.Complete(true);fallback.Check(&mode,&reject.group,&reject.actor);assert(fallback.m_Finished);
Soldier broken;ERR_EnemyLauncherGrant bad;bad.Check(&mode,&broken.group,&broken.actor);IEntity empty;empty.prefab="IGLA";empty.loaded=false;broken.Add(empty,true);broken.inv.Complete(true);
bad.Check(&mode,&broken.group,&broken.actor);assert(broken.inv.deletes==1&&broken.inv.deleting==&empty&&broken.inv.items.Contains(&broken.rifle));
broken.inv.items={&broken.rifle};broken.mgr.weapons={&broken.rifle};broken.inv.Complete(true);bad.Check(&mode,&broken.group,&broken.actor);assert(bad.m_Fallback);
bad.Check(&mode,&broken.group,&broken.actor);assert(broken.inv.requests==2&&broken.inv.last=="FIM");
Soldier pending;ERR_EnemyLauncherGrant wait;for(int n=0;n<30;n++)wait.Check(&mode,&pending.group,&pending.actor);assert(wait.m_Finished&&pending.inv.requests==1);pending.inv.Complete(true);wait.Check(&mode,&pending.group,&pending.actor);assert(pending.inv.requests==1);
Soldier looted;ERR_EnemyLauncherGrant lost;lost.Check(&mode,&looted.group,&looted.actor);looted.inv.Complete(true);lost.Check(&mode,&looted.group,&looted.actor);assert(lost.m_Finished&&!lost.m_Fallback&&looted.inv.requests==1);
Soldier captured;ERR_EnemyLauncherGrant ownership;ownership.Check(&mode,&captured.group,&captured.actor);captured.actor.eligible=false;ownership.Check(&mode,&captured.group,&captured.actor);assert(ownership.m_Finished&&captured.inv.requests==1);
Soldier miner;ERR_EnemyMineGrant mines;mines.Check(&mode,&miner.group,&miner.actor);assert(miner.inv.requests==1);IEntity mine1,mine2;mine1.mine=mine2.mine=true;miner.Add(mine1,false);miner.inv.Complete(true);mines.Check(&mode,&miner.group,&miner.actor);assert(miner.inv.requests==2);miner.Add(mine2,false);miner.inv.Complete(true);mines.Check(&mode,&miner.group,&miner.actor);assert(mines.m_Finished);
miner.inv.items.clear();for(int n=0;n<20;n++)mines.Check(&mode,&miner.group,&miner.actor);assert(miner.inv.requests==2);
Soldier full;ERR_EnemyMineGrant existing;full.Add(mine1,false);full.Add(mine2,false);existing.Check(&mode,&full.group,&full.actor);assert(existing.m_Finished&&full.inv.requests==0);
Soldier dropped;ERR_EnemyMineGrant finite;finite.Check(&mode,&dropped.group,&dropped.actor);dropped.inv.Complete(true);finite.Check(&mode,&dropped.group,&dropped.actor);dropped.inv.Complete(true);finite.Check(&mode,&dropped.group,&dropped.actor);assert(finite.m_Finished&&dropped.inv.requests==2);
Soldier noSpace;noSpace.inv.accept=false;ERR_EnemyMineGrant noLoose;noLoose.Check(&mode,&noSpace.group,&noSpace.actor);assert(noLoose.m_Finished&&noSpace.inv.requests==1);
}
''')
print('PASS: actual percentage selection, native/preferred launcher preservation, failure/removal fallback, pending-operation quarantine, capture, finite two-mine grants and no replenishment')