Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
90 lines
8.0 KiB
Python
90 lines
8.0 KiB
Python
from pathlib import Path
|
|
import hashlib,json,subprocess
|
|
R=Path('/home/jakub/devel/escapistsrereintroduced'); P=Path('/tmp/erri-launcher-resupply-1140'); names=['ERR_InfantryResupply.c','ERR_InfantryAmmoTransfer.c','ERR_HQ.c'];baseline={}
|
|
for name in names:
|
|
rel='addons/core/Scripts/Game/ERR/10_AI/'+name;src=R/rel
|
|
for dest in (P/rel,P/'baseline'/rel):dest.parent.mkdir(parents=True,exist_ok=True);dest.write_bytes(src.read_bytes())
|
|
baseline[rel]={'working_sha256':hashlib.sha256(src.read_bytes()).hexdigest(),'index_sha256':hashlib.sha256(subprocess.check_output(['git','show',':'+rel],cwd=R)).hexdigest()}
|
|
(P/'baseline.json').write_text(json.dumps(baseline,indent=2)+'\n')
|
|
p=P/'addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryAmmoTransfer.c';s=p.read_text();s=s.replace('protected bool m_RecycleEmpty;','protected bool m_RecycleEmpty;\n protected bool m_LauncherMagazine;')
|
|
s=s.replace('bool recycleEmpty = false)','bool recycleEmpty = false, bool launcherMagazine = false)').replace('m_RecycleEmpty = recycleEmpty;','m_RecycleEmpty = recycleEmpty; m_LauncherMagazine = launcherMagazine;')
|
|
s=s.replace('static bool Compatible(IEntity actor, IEntity exactMagazine)','static bool Compatible(IEntity actor, IEntity exactMagazine, bool launcherMagazine = false)')
|
|
s=s.replace('array<IEntity> weapons = {}; manager.GetWeaponsList(weapons);','if (launcherMagazine && magazine.IsUsed()) return false;\n array<IEntity> weapons = {}; manager.GetWeaponsList(weapons);')
|
|
s=s.replace('if (kind != EWeaponType.WT_RIFLE && kind != EWeaponType.WT_SNIPERRIFLE && kind != EWeaponType.WT_MACHINEGUN && kind != EWeaponType.WT_HANDGUN) continue;', '''bool smallWeapon = kind == EWeaponType.WT_RIFLE || kind == EWeaponType.WT_SNIPERRIFLE || kind == EWeaponType.WT_MACHINEGUN || kind == EWeaponType.WT_HANDGUN;
|
|
if (!launcherMagazine && !smallWeapon) continue;
|
|
if (launcherMagazine && !smallWeapon && kind != EWeaponType.WT_ROCKETLAUNCHER && kind != EWeaponType.WT_GRENADELAUNCHER) continue;
|
|
if (launcherMagazine && !StoredUnder(rifle, actor)) continue;''')
|
|
s=s.replace('if (muzzleKind != EMuzzleType.MT_BaseMuzzle) continue;', '''if (!launcherMagazine && muzzleKind != EMuzzleType.MT_BaseMuzzle) continue;
|
|
if (launcherMagazine)
|
|
{
|
|
if (muzzleKind != EMuzzleType.MT_RPGMuzzle && muzzleKind != EMuzzleType.MT_UGLMuzzle
|
|
&& kind != EWeaponType.WT_ROCKETLAUNCHER && kind != EWeaponType.WT_GRENADELAUNCHER) continue;
|
|
// 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;
|
|
if (!StoredUnder(loaded.GetOwner(), rifle)) continue;
|
|
if (loaded.GetMagazineWell().Type() != muzzle.GetMagazineWell().Type()) continue;
|
|
if (magazine.GetMagazineWell().Type() != muzzle.GetMagazineWell().Type()) continue;
|
|
}''')
|
|
s=s.replace('if (!Compatible(m_Recipient, m_Magazine))','if (!Compatible(m_Recipient, m_Magazine, m_LauncherMagazine))')
|
|
s=s.replace('if (m_ReturnAccepted &&', 'if ((m_ReturnAccepted || (m_LauncherMagazine && m_ReturnSubmitted)) &&').replace('if (!m_Accepted || !m_Magazine || m_Acknowledgement < 0) return false;', 'if (m_LauncherMagazine && m_Submitted) return m_Magazine && !PhysicalProof();\n if (!m_Accepted || !m_Magazine || m_Acknowledgement < 0) return false;')
|
|
s=s.replace('if (PhysicalProof())\n {', '''// Native callbacks may synchronously cancel this leaf or revoke ownership.
|
|
if (m_LauncherMagazine && m_State != ERR_InfantryAmmoTransferState.PENDING) return m_State;
|
|
if (m_LauncherMagazine && !Context()) return Fail("launcher custody authority changed during native submission");
|
|
if (PhysicalProof())
|
|
{''')
|
|
p.write_text(s)
|
|
p=P/'addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c';s=p.read_text();anchor=' static bool MinePair('
|
|
s=s.replace(anchor,''' // A zero aggregate without a supported physical magazine is not launcher evidence.
|
|
static bool LauncherNeed(ERR_HQWatchEntry entry, float now)
|
|
{
|
|
return Fresh(entry, now) && entry.AmmoState && entry.AmmoState.Valid(entry.Count)
|
|
&& entry.AmmoState.LauncherComplete && entry.AmmoState.Members == entry.Count
|
|
&& entry.AmmoState.LauncherMagazines > 0
|
|
&& entry.AmmoState.LauncherRounds == 0 && entry.AmmoState.LauncherChambers == 0;
|
|
}
|
|
static bool LauncherPair(ERR_HQWatchEntry recipient, ERR_HQWatchEntry donor, float now)
|
|
{
|
|
return LauncherNeed(recipient, now) && Fresh(donor, now) && recipient.SourceId != donor.SourceId
|
|
&& vector.DistanceXZ(recipient.Position, donor.Position) <= 200;
|
|
}
|
|
'''+anchor)
|
|
s=s.replace('protected bool m_MineTrip;','protected bool m_MineTrip;\n protected bool m_LauncherTrip;')
|
|
s=s.replace('if (m_State <= 1 && m_Move && !m_Move.Unresolved()) m_Move = null;', '''if (m_Move && !m_Move.Unresolved() && (m_State <= 1 || (m_State == 7 && m_LauncherTrip)))
|
|
{
|
|
m_Move = null;
|
|
if (m_State == 7) Finish("launcher custody settled; original claims released without retry or refund");
|
|
}''')
|
|
s=s.replace('if (m_MineTrip) reason = "mine top-up: " + reason;', 'if (m_MineTrip) reason = "mine top-up: " + reason;\n if (m_LauncherTrip) reason = "depleted launcher magazine: " + reason;')
|
|
s=s.replace('float now, bool mineTrip = false)','float now, bool mineTrip = false, bool launcherTrip = false)')
|
|
s=s.replace('if (m_Truck.Combat.Held || m_Truck.SupplyToken > 0 || !recipient || !donor)', 'if ((mineTrip && launcherTrip) || m_Truck.Combat.Held || m_Truck.SupplyToken > 0 || !recipient || !donor)')
|
|
s=s.replace('else if (!ERR_InfantryResupplyRules.Pair(demand, donorStatus, now)) return false;', '''else if (launcherTrip)
|
|
{
|
|
if (!ERR_InfantryResupplyRules.LauncherPair(demand, donorStatus, now)) return false;
|
|
}
|
|
else if (!ERR_InfantryResupplyRules.Pair(demand, donorStatus, now)) return false;''')
|
|
s=s.replace('m_MineTrip = mineTrip; m_MineRecipientSlot', 'm_MineTrip = mineTrip; m_LauncherTrip = launcherTrip; m_MineRecipientSlot')
|
|
s=s.replace('if (mineTrip) purpose += "; one finite PMN4 top-up for an existing carrier";', 'if (mineTrip) purpose += "; one finite PMN4 top-up for an existing carrier";\n if (launcherTrip) purpose += "; finite compatible magazine for a reported depleted launcher";')
|
|
s=s.replace(' bool TryPlanMines(',''' bool TryPlanLauncher(ERR_HQUnit recipient, ERR_HQUnit donor, ERR_HQWatchEntry demand, ERR_HQWatchEntry donorStatus, float now)
|
|
{
|
|
return TryPlan(recipient, donor, demand, donorStatus, now, false, true);
|
|
}
|
|
bool TryPlanMines(''')
|
|
s=s.replace('if (m_MineMove && m_MineMove.Unresolved()) { m_State = 7; return; }', 'if ((m_MineMove && m_MineMove.Unresolved()) || (m_LauncherTrip && m_Move && m_Move.Unresolved())) { m_State = 7; return; }')
|
|
s=s.replace('m_State = 1; m_MineTrip = false;', 'm_State = 1; m_MineTrip = false; m_LauncherTrip = false;')
|
|
s=s.replace('if (!local.Complete || local.SmallArmsMuzzles <= 0 || local.SmallArmsRounds + local.SmallArmsChambers > 30) continue;', '''if (m_LauncherTrip)
|
|
{
|
|
if (!local.Valid(1) || !local.LauncherComplete || local.Members != 1 || local.LauncherMagazines <= 0
|
|
|| local.LauncherRounds != 0 || local.LauncherChambers != 0) continue;
|
|
}
|
|
else if (!local.Complete || local.SmallArmsMuzzles <= 0 || local.SmallArmsRounds + local.SmallArmsChambers > 30) continue;''')
|
|
s=s.replace('ERR_InfantryAmmoTransfer.Compatible(actor, magazine)', 'ERR_InfantryAmmoTransfer.Compatible(actor, magazine, m_LauncherTrip)')
|
|
s=s.replace('actor, m_Recipient.Group, magazine, true);','actor, m_Recipient.Group, magazine, true, m_LauncherTrip);')
|
|
p.write_text(s)
|
|
p=P/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c';s=p.read_text().replace(' // Optional finite mines are last:', ''' // Supported depleted launcher magazines follow rifle/mounted demand and
|
|
// share the same finite stock, radio replay and physical rendezvous service.
|
|
foreach (ERR_HQUnit launcherRecipient : m_Units)
|
|
if (supply.TryPlanLauncher(launcherRecipient, donor, m_Watch.Find(launcherRecipient.ForceSourceId), donorStatus, now)) return true;
|
|
// Optional finite mines are last:''');p.write_text(s)
|