from pathlib import Path p=Path('/tmp/erri-resupply-tests-0826');s=(p/'baseline/tests/test_infantry_resupply_planning.py').read_text() s=s.replace("I=ROOT/", "A=Path(os.environ.get('ERR_INFANTRY_TRANSFER_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryAmmoTransfer.c'))\nI=ROOT/",1) s=s.replace("methods='\\n'.join(cpp(body(P,x)) for x in (", "stock_method=('protected bool CompatibleStock(',) if 'protected bool CompatibleStock(' in P.read_text() else ()\nmethods='\\n'.join(cpp(body(P,x)) for x in stock_method+(",1) # Actual compatibility method, not a boolean compatibility stand-in. needle="run(r'''\n" addition='''compatible=body(A,'static bool Compatible(') for a,b in { 'BaseMagazineComponent.Cast(exactMagazine.FindComponent(BaseMagazineComponent))':'exactMagazine->ammoComp', 'BaseWeaponManagerComponent.Cast(actor.FindComponent(BaseWeaponManagerComponent))':'actor->weapons', 'BaseWeaponComponent.Cast(rifle.FindComponent(BaseWeaponComponent))':'rifle->weapon', 'InventoryItemComponent.Cast(loaded.GetOwner().FindComponent(InventoryItemComponent))':'loaded->GetOwner()->item', 'SCR_MuzzleInMagComponent.Cast(muzzle)':'(muzzle->inMag ? muzzle : nullptr)', }.items():compatible=compatible.replace(a,b) compatible=compatible.replace('array<','Array<').replace('foreach (','for (').replace('EWeaponType kind','int kind').replace('EMuzzleType muzzleKind','int muzzleKind') for a in ['EWeaponType','EMuzzleType']:compatible=compatible.replace(a+'.',a+'::') for a in ['magazine','manager','weapon','muzzle','reloadable','loaded','loadedItem','loadedSlot']:compatible=compatible.replace(a+'.',a+'->') compatible=compatible.replace('GetMagazineWell().','GetMagazineWell()->') ''' assert needle in s;s=s.replace(needle,addition+needle,1) s=s.replace('struct Entity;using IEntity=Entity*;', '''struct Entity;using IEntity=Entity*; struct Magazine;struct Manager;struct Weapon;struct Muzzle;struct Item;struct Slot; using BaseMagazineComponent=Magazine*;using BaseWeaponManagerComponent=Manager*;using BaseWeaponComponent=Weapon*;using BaseMuzzleComponent=Muzzle*;using SCR_MuzzleInMagComponent=Muzzle*;using InventoryItemComponent=Item*;using InventoryStorageSlot=Slot*; struct EWeaponType{static const int WT_RIFLE=1,WT_SNIPERRIFLE=2,WT_MACHINEGUN=3,WT_HANDGUN=4,WT_GRENADELAUNCHER=5,WT_ROCKETLAUNCHER=6;}; struct EMuzzleType{static const int MT_BaseMuzzle=0,MT_UGLMuzzle=1,MT_RPGMuzzle=2;}; struct Well{int type=1,parent=0;int Type(){return type;}bool IsInherited(int t){return type==t||parent==t;}}; struct Magazine{IEntity owner=nullptr;Well*well=nullptr;int rounds=30,capacity=30;bool used=false;IEntity GetOwner(){return owner;}Well*GetMagazineWell(){return well;}int GetAmmoCount(){return rounds;}int GetMaxAmmoCount(){return capacity;}bool IsUsed(){return used;}}; struct Muzzle{bool inMag=true,canReload=true,disposable=false;Magazine*loaded=nullptr;Well*well=nullptr;int kind=0;bool CanBeReloaded(){return canReload;}bool IsDisposable(){return disposable;}Magazine*GetMagazine(){return loaded;}Well*GetMagazineWell(){return well;}int GetMuzzleType(){return kind;}}; struct Weapon{IEntity owner=nullptr;Arraymuzzles;int kind=1;IEntity GetOwner(){return owner;}int GetWeaponType(){return kind;}void GetMuzzlesList(Array&a){a=muzzles;}}; struct Manager{Arrayweapons;void GetWeaponsList(Array&a){a=weapons;}}; struct Slot{Muzzle*container=nullptr;IEntity attached=nullptr;Muzzle*GetParentContainer(){return container;}IEntity GetAttachedEntity(){return attached;}}; struct Item{Slot*slot=nullptr;Slot*GetParentSlot(){return slot;}};''') s=s.replace('struct Entity{Prefab prefab;', 'struct Entity{Magazine*ammoComp=nullptr;Manager*weapons=nullptr;Weapon*weapon=nullptr;Item*item=nullptr;IEntity parent=nullptr;Prefab prefab;') old='struct ERR_InfantryAmmoTransfer{static inline IEntity only=nullptr;static inline int checks=0;static inline bool compatible=true;static bool Compatible(IEntity actor,IEntity item,bool launcher){checks++;return actor&&item&&launcher&&compatible&&(!only||item==only);}};' new="struct ERR_InfantryAmmoTransfer{static bool StoredUnder(IEntity item,IEntity actor){return item&&item->parent==actor;}\n'''+compatible+r'''\n};" assert old in s;s=s.replace(old,new) s=s.replace('std::arrayitems;Entity one,two;', 'std::arrayitems;std::arraymagazines;Entity rifle,launcher;Manager weapons;Weapon rifleWeapon,launcherWeapon;Muzzle rifleMuzzle,launcherMuzzle;Well well,wrongWell{2,0};Entity one,two;') s=s.replace('vehicle.inventory=&inventory;', '''one.weapons=&weapons;two.weapons=&weapons;weapons.weapons={&rifle,&launcher};rifle.weapon=&rifleWeapon;rifleWeapon.owner=&rifle;rifleWeapon.muzzles={&rifleMuzzle};rifleMuzzle.well=&well; launcher.weapon=&launcherWeapon;launcherWeapon.owner=&launcher;launcherWeapon.kind=EWeaponType::WT_ROCKETLAUNCHER;launcherWeapon.muzzles={&launcherMuzzle};launcherMuzzle.well=&well;launcherMuzzle.kind=EMuzzleType::MT_RPGMuzzle;launcher.parent=&one; for(int i=0;i<140;i++){items[i].ammoComp=&magazines[i];magazines[i].owner=&items[i];magazines[i].well=&well;} vehicle.inventory=&inventory;''',1) s=s.replace('hq.downlinks==0);}', 'hq.downlinks==0&&!truck.SupplyHeld&&!convoy.Stopped&&!s.m_Recipient&&!s.m_Donor);}',1) s=s.replace('ERR_InfantryAmmoTransfer::only=&f.items[95];ERR_InfantryAmmoTransfer::checks=0;', 'for(int i=0;i<95;i++)f.magazines[i].well=&f.wrongWell;') s=s.replace('assert(ERR_InfantryAmmoTransfer::checks==191);ERR_InfantryAmmoTransfer::only=nullptr;', 'assert(f.s.m_LauncherTrip);') s=s.replace('ERR_InfantryAmmoTransfer::compatible=false;', 'for(auto&mag:f.magazines)mag.well=&f.wrongWell;').replace('ERR_InfantryAmmoTransfer::compatible=true;', '') needle='int main(){\n' cases='''int main(){ // Actual Compatible must reject wrong-well, empty or malformed stock before // any report/cooldown consumption, reservation, downlink or convoy hold. for(int bad=0;bad<5;bad++){Fixture f;for(auto&mag:f.magazines){if(bad==0)mag.well=&f.wrongWell;if(bad==1)mag.rounds=0;if(bad==2)mag.capacity=0;if(bad==3)mag.rounds=31;if(bad==4)mag.well=nullptr;}f.s.Stock(0);assert(!f.Plan());f.Unchanged();} {Fixture f;f.s.Stock(0);f.rifleMuzzle.well=&f.wrongWell;assert(!f.Plan());f.Unchanged();f.rifleMuzzle.well=&f.well;assert(f.Plan());} // Same delivered recipient report remains available to a later compatible // truck sharing the real per-source consumption/cooldown maps. {Fixture first,second;Map consumed;Map cooldown;second.hq.convoys.tasks={&first.convoy,&second.convoy};second.hq.convoy=&first.convoy;second.hq.m_Units={&first.recipient,&second.donor}; assert(first.s.Bind(&first.convoy,0,&consumed,&cooldown));assert(second.s.Bind(&second.convoy,1,&consumed,&cooldown)); for(auto&mag:first.magazines)mag.well=&first.wrongWell;first.s.Stock(0);second.s.Stock(0);assert(!first.Plan());first.Unchanged(); assert(second.s.TryPlan(&first.recipient,&second.donor,&first.demand,&second.donorStatus,0));assert(consumed.Get("recipient")==1&&cooldown.Get("recipient")==180000&&first.recipient.LogisticsReservation==100129&&second.hq.downlinks==1&&first.hq.downlinks==0);} // A usable compatible item must still be a surviving unspent frozen identity. {Fixture f;for(auto&mag:f.magazines)mag.rounds=0;f.magazines[48].rounds=30;f.s.Stock(0);f.inventory.items.Insert(&f.items[48]);assert(!f.Plan());f.Unchanged();f.magazines[0].rounds=30;f.s.m_Spent.Insert(&f.items[0]);assert(!f.Plan());f.Unchanged();f.s.m_Spent.Clear();assert(f.Plan());} // Hard 32 original carriers and 96 captured magazines remain fail-closed. {Fixture f;f.s.Stock(0);f.sampler.original.resize(33,&f.one);assert(!f.Plan());f.Unchanged();} {Fixture f;f.s.Stock(0);for(int i=48;i<97;i++){f.s.m_Stock.Insert(&f.items[i]);f.inventory.items.Insert(&f.items[i]);}assert(!f.Plan());f.Unchanged();} {Fixture f;for(int i=48;i<96;i++)f.inventory.items.Insert(&f.items[i]);for(int i=0;i<95;i++)f.magazines[i].well=&f.wrongWell;f.s.Stock(0);assert(f.Plan());} // Small-arms demand cannot borrow launcher compatibility, but the dedicated // launcher path still supports its real empty reloadable muzzle and rejects disposables. {Fixture f;f.weapons.weapons={&f.launcher};f.s.Stock(0);assert(!f.Plan());f.Unchanged();assert(f.s.TryPlanLauncher(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0));} {Fixture f;f.weapons.weapons={&f.launcher};f.launcherMuzzle.disposable=true;f.s.Stock(0);assert(!f.s.TryPlanLauncher(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0));f.Unchanged();} ''' assert needle in s;s=s.replace(needle,cases,1) s=s.replace('PASS: actual convoy binding,', 'PASS: actual Compatible and bounded stock preflight before report/claim consumption, later compatible truck, launcher controls, convoy binding,') (p/'tests/test_infantry_resupply_planning.py').write_text(s)