Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
88 lines
7.9 KiB
Python
88 lines
7.9 KiB
Python
"""Actual bounded carried PMN4 sampler and detached counts; no native qualification."""
|
|
from pathlib import Path
|
|
import os,re
|
|
from policy_harness import ROOT,body,run
|
|
BASE=Path(os.environ.get('ERR_MINE_STATUS_ROOT',ROOT))
|
|
P=BASE/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQMineSampler.c'
|
|
A=BASE/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQAmmoSampler.c'
|
|
s=P.read_text()
|
|
for forbidden in ('TrySpawn','TryMove','SetOrigin','GetOrigin','ActivateTrigger','GetAgents','GetWorld','SetAmmoCount'):
|
|
assert forbidden not in s,forbidden
|
|
snapshot=A.read_text().split('class ERR_HQAmmoSampler')[0]
|
|
owned=body(ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryAmmoTransfer.c',' static bool StoredUnder(')
|
|
def cpp(s):
|
|
s=re.sub(r'//[^\n]*','',s).replace('class ','struct ').replace('protected ','').replace('foreach (','for (').replace('array<','Array<').replace('null','nullptr')
|
|
s=s.replace('InventoryStorageManagerComponent.Cast(actor.FindComponent(InventoryStorageManagerComponent))','actor->inventory')
|
|
for entity in ('item','entity'):
|
|
s=s.replace(f'InventoryItemComponent.Cast({entity}.FindComponent(InventoryItemComponent))',f'{entity}->carried')
|
|
s=s.replace('SCR_PressureTriggerComponent.Cast(item.FindComponent(SCR_PressureTriggerComponent))','item->trigger')
|
|
s=s.replace('DamageManagerComponent.Cast(item.FindComponent(DamageManagerComponent))','item->hasDamage ? &item->damage : nullptr')
|
|
s=s.replace('item.FindComponent(SCR_AntiPersonnelMineCollisionHandlerComponent)','item->collision')
|
|
for name in ('Replication','ERR_Actors','ERR_EnemyLoadoutCatalog','ERR_InfantryAmmoTransfer'):
|
|
s=s.replace(name+'.',name+'::')
|
|
s=re.sub(r'\bERR_HQAmmoSnapshot (?=\w)','ERR_HQAmmoSnapshot* ',s)
|
|
for name in ('actor','group','result','inventory','item','entity','carried','trigger','copy','storage','damage'):
|
|
s=re.sub(r'\b'+name+r'\.',name+'->',s)
|
|
s=s.replace('GetPrefabData().','GetPrefabData()->').replace('GetParentSlot().','GetParentSlot()->')
|
|
s=re.sub(r'\b(bool|int) (\w+);',r'\1 \2=0;',s)
|
|
return s.replace('\n}\n','\n};\n')
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <algorithm>
|
|
using string=std::string;
|
|
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;int Count(){return this->size();}void Insert(T v){this->push_back(v);}bool Contains(T v){return std::find(this->begin(),this->end(),v)!=this->end();}};
|
|
struct Entity;struct Group;struct Inventory;using IEntity=Entity*;using SCR_AIGroup=Group*;
|
|
struct Prefab{string name="PMN4";string GetPrefabName(){return name;}};
|
|
struct Trigger{bool active=false,byDefault=false;bool IsActivated(){return active;}bool IsActivatedByDefault(){return byDefault;}};using SCR_PressureTriggerComponent=Trigger*;
|
|
struct Storage{Entity*owner=nullptr;Entity*GetOwner(){return owner;}};using BaseInventoryStorageComponent=Storage*;
|
|
struct Slot{Entity*attached=nullptr;Storage*storage=nullptr;Entity*GetAttachedEntity(){return attached;}Storage*GetStorage(){return storage;}};
|
|
struct Carried{Slot*slot=nullptr;Slot*GetParentSlot(){return slot;}};using InventoryItemComponent=Carried*;
|
|
struct Damage{bool dead=false;bool IsDestroyed(){return dead;}};using DamageManagerComponent=Damage*;
|
|
struct Entity{Group*group=nullptr;Inventory*inventory=nullptr;Prefab*prefab=nullptr;Carried*carried=nullptr;Trigger*trigger=nullptr;Entity*parent=nullptr;
|
|
Damage damage;bool hasDamage=true;bool alive=true,player=false,collision=true;Prefab*GetPrefabData(){return prefab;}Entity*GetParent(){return parent;}};
|
|
struct Group{bool slave=false;int players=0;bool IsSlave(){return slave;}int GetPlayerCount(bool){return players;}};
|
|
struct Inventory{Entity*owner=nullptr;Array<IEntity>items;int reads=0;IEntity GetOwner(){return owner;}void GetItems(Array<IEntity>&a){reads++;a=items;}};using InventoryStorageManagerComponent=Inventory*;
|
|
struct Replication{inline static bool server=true;static bool IsServer(){return server;}};
|
|
struct ERR_EnemyLoadoutCatalog{inline static string MINE="PMN4";};
|
|
struct ERR_Actors{static bool Alive(IEntity e){return e&&e->alive;}static bool Player(IEntity e){return e&&e->player;}static struct Group*Group(IEntity e){return e?e->group:nullptr;}};
|
|
struct ERR_InfantryAmmoTransfer{
|
|
'''+cpp(owned)+r'''
|
|
};
|
|
'''+cpp(snapshot)+cpp(s)+r'''
|
|
struct Fixture{Entity actor,mine,other,bag;Group group;Inventory inventory;Prefab prefab,otherPrefab;Carried carried,bagCarried;Slot slot,bagSlot;Storage storage,bagStorage;Trigger trigger;
|
|
Fixture(){actor.group=&group;actor.inventory=&inventory;inventory.owner=&actor;inventory.items={&mine};mine.prefab=&prefab;mine.carried=&carried;mine.trigger=&trigger;carried.slot=&slot;slot.attached=&mine;slot.storage=&storage;storage.owner=&actor;otherPrefab.name="rifle";other.prefab=&otherPrefab;}
|
|
ERR_HQAmmoSnapshot Sample(){ERR_HQAmmoSnapshot s;s.MineComplete=true;s.Complete=true;s.LauncherComplete=true;s.Members=1;ERR_HQMineSampler::Inspect(&actor,&group,&s);return s;}
|
|
};
|
|
int main(){
|
|
{Fixture f;f.inventory.items={&f.mine,&f.other,&f.mine};auto s=f.Sample();assert(s.MineComplete&&s.ReadyMines==1&&s.MineMembers==1&&s.Valid(1));
|
|
auto frozen=s.Snapshot();f.inventory.items.clear();s.ReadyMines=0;assert(frozen->ReadyMines==1&&frozen->MineMembers==1&&frozen->MineComplete);}
|
|
{Fixture f;f.inventory.items.clear();auto s=f.Sample();assert(s.MineComplete&&s.ReadyMines==0&&s.Valid(1));}
|
|
// Existing nested carried storage is accepted; no weapon manager is required.
|
|
{Fixture f;f.storage.owner=&f.bag;f.bag.carried=&f.bagCarried;f.bagCarried.slot=&f.bagSlot;f.bagSlot.attached=&f.bag;f.bagSlot.storage=&f.bagStorage;f.bagStorage.owner=&f.actor;
|
|
auto s=f.Sample();assert(s.MineComplete&&s.ReadyMines==1);}
|
|
// Known armed/default-armed/damaged items are not available unarmed mines.
|
|
for(int k=0;k<3;k++){Fixture f;if(k==0)f.trigger.active=true;if(k==1)f.trigger.byDefault=true;if(k==2)f.mine.damage.dead=true;
|
|
auto s=f.Sample();assert(s.MineComplete&&s.ReadyMines==0&&s.Complete&&s.LauncherComplete);}
|
|
// Unsupported/broken inventory or PMN4 custody never means known empty.
|
|
for(int k=0;k<13;k++){Fixture f;switch(k){case 0:f.actor.inventory=nullptr;break;case 1:f.inventory.owner=&f.other;break;
|
|
case 2:f.inventory.items.resize(129,&f.mine);break;case 3:f.inventory.items={nullptr};break;case 4:f.mine.prefab=nullptr;break;
|
|
case 5:f.mine.carried=nullptr;break;case 6:f.carried.slot=nullptr;break;case 7:f.slot.attached=&f.other;break;
|
|
case 8:f.storage.owner=&f.other;break;case 9:f.mine.collision=false;break;case 10:f.mine.trigger=nullptr;break;case 11:f.slot.storage=nullptr;break;case 12:f.mine.hasDamage=false;break;}
|
|
auto s=f.Sample();assert(!s.MineComplete&&s.ReadyMines==0&&s.Complete&&s.LauncherComplete&&s.Valid(1));}
|
|
// Cyclic ownership is bounded rather than accepted as carried stock.
|
|
{Fixture f;f.storage.owner=&f.mine;auto s=f.Sample();assert(!s.MineComplete&&s.ReadyMines==0);}
|
|
// Foreign/dead/player/oversized roster rejection does not query inventory.
|
|
for(int k=0;k<6;k++){Fixture f;Group other;if(k==0)f.actor.group=&other;if(k==1)f.actor.alive=false;if(k==2)f.actor.player=true;
|
|
if(k==3){f.group.players=1;}if(k==4){f.group.slave=true;}if(k==5){Replication::server=false;}
|
|
auto s=f.Sample();assert(!s.MineComplete&&s.MineMembers==0&&f.inventory.reads==0);Replication::server=true;}
|
|
{Fixture f;ERR_HQAmmoSnapshot s;s.MineComplete=true;s.MineMembers=32;ERR_HQMineSampler::Inspect(&f.actor,&f.group,&s);assert(!s.MineComplete&&s.MineMembers==32&&f.inventory.reads==0);}
|
|
{Fixture f;f.inventory.items.resize(128,&f.mine);auto s=f.Sample();assert(s.MineComplete&&s.ReadyMines==1);}
|
|
// Impossible wire counts and incomplete coverage are rejected independently.
|
|
{ERR_HQAmmoSnapshot s;assert(s.Valid(0)&&!s.MineComplete);s.ReadyMines=1;assert(!s.Valid(0));s.ReadyMines=0;s.MineMembers=-1;assert(!s.Valid(1));
|
|
s.MineMembers=1;s.MineComplete=true;assert(s.Valid(1)&&!s.Valid(2));s.ReadyMines=129;assert(!s.Valid(1));s.ReadyMines=-1;assert(!s.Valid(1));}
|
|
}
|
|
''')
|
|
print('PASS: actual carried PMN4 inventory, original-owner guards, bounded deduplication, independent unknown state and detached counts')
|