"""Execute actual bounded native-inventory inspection through deterministic native API adapters.""" from pathlib import Path import os,re from policy_harness import ROOT,run P=Path(os.environ.get('ERR_AMMO_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQAmmoSampler.c')) s=P.read_text() for forbidden in ('Spawn','SetAmmoCount','GetOrigin','GetWorld','GetAgents','TryMove','DeleteEntity'): assert forbidden not in s,forbidden code=re.sub(r'//[^\n]*','',s).replace('protected ','').replace('class ','struct ') code=code.replace('InventoryStorageManagerComponent.Cast(actor.FindComponent(InventoryStorageManagerComponent))','actor->inventory') code=code.replace('BaseWeaponManagerComponent.Cast(actor.FindComponent(BaseWeaponManagerComponent))','actor->manager') code=code.replace('BaseWeaponComponent.Cast(weaponEntity.FindComponent(BaseWeaponComponent))','weaponEntity->weapon') code=code.replace('BaseMagazineComponent.Cast(item.FindComponent(BaseMagazineComponent))','item->magazine') code=re.sub(r'\bERR_HQAmmoSnapshot (?=\w)', 'ERR_HQAmmoSnapshot* ',code) code=code.replace('SCR_MuzzleInMagComponent.Cast(muzzle)', '(muzzle->inMag ? muzzle : null)') code=code.replace('array<','Array<').replace('foreach (','for (').replace('null','nullptr') for name in ('EWeaponType','EMuzzleType','Replication','ERR_Actors'):code=code.replace(name+'.',name+'::') code=code.replace('EWeaponType kind','int kind').replace('EMuzzleType muzzleKind','int muzzleKind') for name in ('weapon','muzzle','reloadable','magazine','actor','group','result','inventory','manager','loaded','copy'): code=re.sub(r'\b'+name+r'\.',name+'->',code) code=code.replace('GetMagazineWell().','GetMagazineWell()->') code=re.sub(r'\b(bool|int) (\w+);',r'\1 \2=0;',code) code=code.replace('\n}\n','\n};\n') run(r''' #include #include #include using std::vector; templatestruct Array:vector{using vector::vector; int Count(){return this->size();}bool IsEmpty(){return this->empty();} void Insert(T n){this->push_back(n);}bool Contains(T n){return std::find(this->begin(),this->end(),n)!=this->end();}}; struct Entity;struct Muzzle;struct Magazine;struct Weapon;struct Group;struct Inventory;struct Manager; using IEntity=Entity*;using SCR_AIGroup=Group*;using BaseWeaponComponent=Weapon*; using BaseMuzzleComponent=Muzzle*;using SCR_MuzzleInMagComponent=Muzzle*;using BaseMagazineComponent=Magazine*; using InventoryStorageManagerComponent=Inventory*;using BaseWeaponManagerComponent=Manager*; struct EWeaponType{static constexpr int WT_RIFLE=1,WT_SNIPERRIFLE=2,WT_MACHINEGUN=3,WT_HANDGUN=4, WT_ROCKETLAUNCHER=5,WT_GRENADELAUNCHER=6,WT_FRAGGRENADE=7,WT_SMOKEGRENADE=8;}; struct EMuzzleType{static constexpr int MT_BaseMuzzle=0,MT_RPGMuzzle=1,MT_UGLMuzzle=2;}; struct Well{int kind=1,baseKind=0;int Type(){return kind;}bool IsInherited(int type){return kind==type||baseKind==type;}}; struct Magazine{IEntity owner=nullptr;Well* well=nullptr;int rounds=0,capacity=30; IEntity GetOwner(){return owner;}Well* GetMagazineWell(){return well;} int GetAmmoCount(){return rounds;}int GetMaxAmmoCount(){return capacity;}}; struct Muzzle{bool inMag=false,canReload=true;bool CanBeReloaded(){return canReload;}bool disposable=false;bool IsDisposable(){return disposable;}Well* well=nullptr;Magazine* magazine=nullptr;int kind=0,barrels=1;bool chamber=false; int GetMuzzleType(){return kind;}Well* GetMagazineWell(){return well;} int GetBarrelsCount(){return barrels;}bool IsBarrelChambered(int){return chamber;} Magazine* GetMagazine(){return magazine;}}; struct Weapon{int kind=1;Arraymuzzles;int GetWeaponType(){return kind;} void GetMuzzlesList(Array& out){out=muzzles;}}; struct Inventory{Arrayitems;int reads=0;void GetItems(Array& out){reads++;out=items;}}; struct Manager{Arrayweapons;void GetWeaponsList(Array& out){out=weapons;}}; struct Group{bool slave=false;int players=0;bool IsSlave(){return slave;}int GetPlayerCount(bool){return players;}}; struct Entity{Inventory* inventory=nullptr;Manager* manager=nullptr;Weapon* weapon=nullptr;Magazine* magazine=nullptr; Group* group=nullptr;bool alive=true,player=false;}; struct Replication{inline static bool server=true;static bool IsServer(){return server;}}; struct ERR_Actors{static bool Alive(IEntity a){return a&&a->alive;}static bool Player(IEntity a){return a&&a->player;} static struct Group* Group(IEntity a){return a?a->group:nullptr;}}; '''+code+r''' ERR_HQAmmoSnapshot Fresh(){ERR_HQAmmoSnapshot s;s.Complete=true;s.LauncherComplete=true;return s;} int main(){ Group group,other;Well rifleWell,rocketWell;rocketWell.kind=2; Entity actor,weaponEntity,loadedEntity,spareEntity,unrelatedEntity; Inventory inv;Manager manager;Weapon weapon;Muzzle muzzle;Magazine loaded,spare,unrelated; actor.group=&group;actor.inventory=&inv;actor.manager=&manager; weaponEntity.weapon=&weapon;manager.weapons={&weaponEntity,&weaponEntity};weapon.muzzles={&muzzle,&muzzle}; muzzle.well=&rifleWell;muzzle.magazine=&loaded;muzzle.chamber=true; loaded.owner=&loadedEntity;loaded.well=&rifleWell;loaded.rounds=30;loadedEntity.magazine=&loaded; spare.owner=&spareEntity;spare.well=&rifleWell;spare.rounds=17;spareEntity.magazine=&spare; unrelated.owner=&unrelatedEntity;unrelated.well=&rocketWell;unrelated.rounds=1;unrelated.capacity=1;unrelatedEntity.magazine=&unrelated; inv.items={&loadedEntity,&loadedEntity,&spareEntity,&unrelatedEntity}; auto full=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&full); assert(full.Valid(1)&&full.Complete&&full.Members==1&&full.SmallArmsMuzzles==1); assert(full.SmallArmsRounds==47&&full.SmallArmsMagazines==2&&full.SmallArmsChambers==1&&!full.KnownSmallArmsEmpty()); auto wire=full.Snapshot();full.SmallArmsRounds=0;assert(wire->SmallArmsRounds==47); // detached delivery value // Loaded magazine omitted from native storage enumeration is still counted exactly once. inv.items={&spareEntity};auto omitted=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&omitted); assert(omitted.SmallArmsRounds==47&&omitted.SmallArmsMagazines==2); // Multiple weapons sharing one well never multiply a single physical magazine. Entity secondEntity;Weapon second;Muzzle secondMuzzle;secondEntity.weapon=&second; second.muzzles={&secondMuzzle};secondMuzzle.well=&rifleWell;secondMuzzle.magazine=&loaded; manager.weapons.Insert(&secondEntity);auto shared=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&shared); assert(shared.SmallArmsMuzzles==2&&shared.SmallArmsRounds==47&&shared.SmallArmsMagazines==2); manager.weapons={&weaponEntity}; loaded.rounds=0;spare.rounds=0;muzzle.chamber=false;auto empty=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&empty); assert(empty.KnownSmallArmsEmpty()&&empty.SmallArmsMagazines==2); muzzle.chamber=true;auto chamber=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&chamber); assert(chamber.SmallArmsRounds==0&&chamber.SmallArmsChambers==1&&!chamber.KnownSmallArmsEmpty());muzzle.chamber=false; // Under-barrel launcher is classified by muzzle, independently of its parent rifle type. Muzzle launcher;launcher.kind=EMuzzleType::MT_UGLMuzzle;launcher.well=&rocketWell;launcher.magazine=&unrelated; weapon.muzzles.Insert(&launcher);auto mixed=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&mixed); assert(mixed.Complete&&mixed.LauncherComplete&&mixed.SmallArmsRounds==0&&mixed.LauncherRounds==1); assert(mixed.LauncherMagazines==1&&mixed.LauncherMuzzles==1&&mixed.KnownSmallArmsEmpty()); auto launcherWire=mixed.Snapshot();mixed.LauncherMuzzles=0;assert(launcherWire->LauncherMuzzles==1); launcher.magazine=nullptr;inv.items.clear();auto expended=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&expended); assert(expended.LauncherComplete&&expended.LauncherMuzzles==1&&expended.LauncherRounds==0&&expended.LauncherMagazines==0); launcher.disposable=true;auto disposable=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&disposable);assert(!disposable.LauncherComplete&&disposable.LauncherMuzzles==0);launcher.disposable=false; launcher.inMag=true;launcher.canReload=false;auto singleUse=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&singleUse);assert(!singleUse.LauncherComplete&&singleUse.LauncherMuzzles==0);launcher.inMag=false;launcher.canReload=true; auto badMuzzles=expended;badMuzzles.LauncherMuzzles=-1;assert(!badMuzzles.Valid(1));badMuzzles.LauncherMuzzles=33;assert(!badMuzzles.Valid(1));badMuzzles.LauncherMuzzles=32;assert(badMuzzles.Valid(1)); launcher.magazine=&unrelated;inv.items={&loadedEntity,&spareEntity,&unrelatedEntity}; // launcher stock is not rifle stock launcher.well=nullptr;launcher.magazine=nullptr;auto loose=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&loose); assert(loose.Complete&&!loose.LauncherComplete);weapon.muzzles={&muzzle}; // Empty/unavailable native launcher muzzles affect only launcher completeness. Entity launcherEntity;Weapon launcherWeapon;launcherEntity.weapon=&launcherWeapon; launcherWeapon.kind=EWeaponType::WT_ROCKETLAUNCHER;manager.weapons.Insert(&launcherEntity); auto unavailableLauncher=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&unavailableLauncher); assert(unavailableLauncher.Complete&&!unavailableLauncher.LauncherComplete&&unavailableLauncher.KnownSmallArmsEmpty()); launcherWeapon.muzzles={nullptr};auto nullLauncher=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&nullLauncher); assert(nullLauncher.Complete&&!nullLauncher.LauncherComplete); launcherWeapon.muzzles={nullptr,nullptr,nullptr,nullptr,nullptr}; auto cappedLauncher=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&cappedLauncher); assert(cappedLauncher.Complete&&!cappedLauncher.LauncherComplete); // A native loaded projectile with no magazine well is known to belong to the launcher. launcherWeapon.muzzles={&launcher};launcher.well=nullptr;launcher.magazine=&unrelated;unrelated.well=nullptr; auto knownProjectile=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&knownProjectile); assert(knownProjectile.Complete&&!knownProjectile.LauncherComplete&&knownProjectile.KnownSmallArmsEmpty()); // Do not waive uncertainty if that same physical magazine also occurs on a small-arms muzzle. muzzle.magazine=&unrelated;auto sharedUnknown=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&sharedUnknown); assert(!sharedUnknown.Complete&&!sharedUnknown.KnownSmallArmsEmpty());muzzle.magazine=&loaded; manager.weapons={&weaponEntity};inv.items.Insert(&unrelatedEntity); auto looseUnknown=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&looseUnknown); assert(looseUnknown.Complete&&looseUnknown.KnownSmallArmsEmpty()&&!looseUnknown.LauncherComplete); assert(!ERR_HQAmmoSampler::Compatible(&unrelated,weapon.muzzles)); // stock requires a magazine well for reload inv.items={&spareEntity};unrelated.well=&rocketWell; // Stock search accepts a derived magazine well; exact reload compatibility is // narrower. Preserve exact totals but make derived-only empty knowledge unknown. Well derivedRifle;derivedRifle.kind=3;derivedRifle.baseKind=rifleWell.kind; spare.well=&derivedRifle;spare.rounds=17; assert(!ERR_HQAmmoSampler::Compatible(&spare,weapon.muzzles)); assert(ERR_HQAmmoSampler::InheritedCandidate(&spare,weapon.muzzles)); auto derived=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&derived); assert(!derived.Complete&&!derived.KnownSmallArmsEmpty()&&derived.SmallArmsRounds==0); assert(derived.LauncherComplete); // no launcher candidate from this well spare.well=&rocketWell;auto unrelatedWell=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&unrelatedWell); assert(unrelatedWell.Complete&&unrelatedWell.KnownSmallArmsEmpty()); assert(!ERR_HQAmmoSampler::InheritedCandidate(&spare,weapon.muzzles)); // Inheritance direction matches stock: magazine derives from the muzzle well. spare.well=&rifleWell;muzzle.well=&derivedRifle;muzzle.magazine=nullptr; auto reverse=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&reverse); assert(reverse.Complete&&reverse.KnownSmallArmsEmpty()); muzzle.well=&rifleWell;muzzle.magazine=&loaded;spare.rounds=0; assert(ERR_HQAmmoSampler::Compatible(&spare,weapon.muzzles)); assert(!ERR_HQAmmoSampler::InheritedCandidate(&spare,weapon.muzzles)); auto exactEmpty=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&exactEmpty); assert(exactEmpty.Complete&&exactEmpty.KnownSmallArmsEmpty()); // The corresponding launcher category independently becomes unknown. Well derivedRocket;derivedRocket.kind=4;derivedRocket.baseKind=rocketWell.kind; unrelated.well=&derivedRocket;launcher.well=&rocketWell;launcher.magazine=nullptr; weapon.muzzles.Insert(&launcher);inv.items.Insert(&unrelatedEntity); auto derivedLauncher=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&derivedLauncher); assert(derivedLauncher.Complete&&!derivedLauncher.LauncherComplete&&derivedLauncher.LauncherRounds==0); weapon.muzzles={&muzzle};inv.items={&spareEntity};unrelated.well=&rocketWell; // Missing/unsupported/malformed native data never establishes known empty ammunition. manager.weapons.Insert(nullptr);auto vanishedWeapon=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&vanishedWeapon); assert(!vanishedWeapon.Complete&&!vanishedWeapon.KnownSmallArmsEmpty());manager.weapons={&weaponEntity}; inv.items.Insert(nullptr);auto vanishedItem=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&vanishedItem); assert(!vanishedItem.Complete&&!vanishedItem.KnownSmallArmsEmpty());inv.items={&spareEntity}; actor.manager=nullptr;auto missing=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&missing);assert(!missing.KnownSmallArmsEmpty());actor.manager=&manager; loaded.well=nullptr;auto noWell=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&noWell);assert(!noWell.Complete);loaded.well=&rifleWell; loaded.rounds=31;auto malformed=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&malformed);assert(!malformed.Complete);loaded.rounds=0; weapon.kind=99;auto unsupported=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&unsupported);assert(!unsupported.Complete);weapon.kind=1; muzzle.barrels=5;auto barrels=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&barrels);assert(!barrels.Complete);muzzle.barrels=1; inv.items.resize(129,&spareEntity);auto capped=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&capped);assert(!capped.Complete);inv.items={&spareEntity}; int before=inv.reads;actor.player=true;auto player=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&player);assert(!player.Complete&&inv.reads==before);actor.player=false; actor.group=&other;auto transferred=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&transferred);assert(!transferred.Complete&&inv.reads==before);actor.group=&group; actor.alive=false;auto dead=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&dead);assert(!dead.Complete&&inv.reads==before);actor.alive=true; Replication::server=false;auto client=Fresh();ERR_HQAmmoSampler::Inspect(&actor,&group,&client);assert(!client.Complete&&inv.reads==before);Replication::server=true; auto count=Fresh();count.Members=32;ERR_HQAmmoSampler::Inspect(&actor,&group,&count);assert(!count.Complete&&count.Members==32); assert(!empty.Valid(0));empty.SmallArmsRounds=-1;assert(!empty.Valid(1)); } ''') print('PASS: actual own-inventory ammo inspection deduplicates loaded/carried/shared wells, separates chambers/launchers, freezes scalar snapshots and treats unsupported/capped/foreign inventory as unknown')