Files
EscapistsReintroduced/docs/evidence/recovery-launcher-2026-10-02/launcher/presence.py
T
jakub 6a667d8deb Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
2026-10-13 22:52:53 +02:00

56 lines
5.4 KiB
Python

from pathlib import Path
import hashlib,json,subprocess
P=Path('/tmp/erri-launcher-resupply-1140');R=Path('/home/jakub/devel/escapistsrereintroduced');D=P/'addons/core/Scripts/Game/ERR/10_AI';rel='addons/core/Scripts/Game/ERR/10_AI/ERR_HQAmmoSampler.c';s=(R/rel).read_text();(P/'baseline'/rel).write_text(s)
b=json.loads((P/'baseline.json').read_text());b[rel]={'working_sha256':hashlib.sha256((R/rel).read_bytes()).hexdigest(),'index_sha256':hashlib.sha256(subprocess.check_output(['git','show',':'+rel],cwd=R)).hexdigest()};(P/'baseline.json').write_text(json.dumps(b,indent=2)+'\n')
s=s.replace(' int LauncherRounds;',' int LauncherMuzzles;\n int LauncherRounds;').replace('copy.LauncherRounds = LauncherRounds;', 'copy.LauncherMuzzles = LauncherMuzzles; copy.LauncherRounds = LauncherRounds;')
s=s.replace(' if (LauncherRounds < 0', ' if (LauncherMuzzles < 0 || LauncherMuzzles > Members * 32) return false;\n if (LauncherRounds < 0')
s=s.replace('else launchers.Insert(muzzle);', '''else
{
launchers.Insert(muzzle);
if (muzzle.GetMagazineWell() && !muzzle.IsDisposable()) result.LauncherMuzzles++;
else result.LauncherComplete = false;
}''');(P/rel).write_text(s)
p=D/'ERR_InfantryResupply.c';s=p.read_text().replace('A zero aggregate without a supported physical magazine is not launcher evidence.', 'Only positively sampled reloadable magazine-well muzzles establish launcher demand.')
s=s.replace('entry.AmmoState.LauncherMagazines > 0','entry.AmmoState.LauncherMuzzles > 0').replace('local.LauncherMagazines <= 0','local.LauncherMuzzles <= 0');p.write_text(s)
p=D/'ERR_InfantryAmmoTransfer.c';s=p.read_text();s=s.replace(''' // Projectile-only/disposable and entirely missing magazine configurations
// are outside this finite magazine slice; never synthesize a projectile.
BaseMagazineComponent loaded = muzzle.GetMagazine();
if (!loaded || !loaded.GetOwner() || !loaded.GetMagazineWell() || loaded.GetMaxAmmoCount() <= 0) continue;
InventoryItemComponent loadedItem = InventoryItemComponent.Cast(loaded.GetOwner().FindComponent(InventoryItemComponent));
if (!loadedItem || !loadedItem.GetParentSlot()) continue;
InventoryStorageSlot loadedSlot = loadedItem.GetParentSlot();
if (loadedSlot.GetParentContainer() != muzzle || loadedSlot.GetAttachedEntity() != loaded.GetOwner()) continue;
if (loaded.GetMagazineWell().Type() != muzzle.GetMagazineWell().Type()) continue;''', ''' // An expended single-round item may no longer exist. The real reloadable
// muzzle/well proves capability; existing loaded items need exact custody.
if (muzzle.IsDisposable()) continue;
BaseMagazineComponent loaded = muzzle.GetMagazine();
if (loaded)
{
if (!loaded.GetOwner() || !loaded.GetMagazineWell() || loaded.GetMaxAmmoCount() <= 0) continue;
InventoryItemComponent loadedItem = InventoryItemComponent.Cast(loaded.GetOwner().FindComponent(InventoryItemComponent));
if (!loadedItem || !loadedItem.GetParentSlot()) continue;
InventoryStorageSlot loadedSlot = loadedItem.GetParentSlot();
if (loadedSlot.GetParentContainer() != muzzle || loadedSlot.GetAttachedEntity() != loaded.GetOwner()) continue;
if (loaded.GetMagazineWell().Type() != muzzle.GetMagazineWell().Type()) continue;
}''');p.write_text(s)
for name in ('test_infantry_ammo_transfer.py','test_infantry_resupply_planning.py'):
p=P/'tests'/name;s=p.read_text().replace('LauncherMagazines=1,','LauncherMuzzles=1,LauncherMagazines=1,')
if name=='test_infantry_ammo_transfer.py':
s=s.replace('struct Muzzle{Magazine*', 'struct Muzzle{bool disposable=false;bool IsDisposable(){return disposable;}Magazine*')
s=s.replace('if(bad==2)f.muzzle.loaded=nullptr;', 'if(bad==2)f.muzzle.disposable=true;')
s=s.replace('int main(){', '''int main(){
{LauncherFixture f;f.muzzle.loaded=nullptr;assert(ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity,true));f.c.Transfer(0);assert(f.source.calls==1&&f.c.m_Move->Submitted());}
''',1)
else:s=s.replace('if(bad==1)f.ammo.LauncherMagazines=0;', 'if(bad==1)f.ammo.LauncherMuzzles=0;').replace('f.ammo.SmallArmsRounds=999;assert(!f.Plan());', 'f.ammo.SmallArmsRounds=999;f.ammo.LauncherMagazines=0;assert(!f.Plan());')
p.write_text(s)
name='test_hq_ammo_sampler.py';s=(R/'tests'/name).read_text();(P/'baseline/tests'/name).write_text(s)
s=s.replace('struct Muzzle{Well*', 'struct Muzzle{bool disposable=false;bool IsDisposable(){return disposable;}Well*')
s=s.replace('assert(mixed.LauncherMagazines==1&&mixed.KnownSmallArmsEmpty());', '''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.magazine=&unrelated;inv.items={&loadedEntity,&spareEntity,&unrelatedEntity};''')
# names above depend on existing exact fixture names; use original variable spelling.
print([l for l in s.splitlines() if 'Entity actor' in l or 'Entity loaded' in l]);(P/'tests'/name).write_text(s)