Capture current mission, AI, map and administration development

Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
This commit is contained in:
2026-10-13 22:52:53 +02:00
parent a951a96edc
commit 6a667d8deb
8104 changed files with 2124945 additions and 2061 deletions
@@ -0,0 +1,314 @@
enum ERR_InfantryAmmoTransferState { FAILED = -1, PENDING, SUCCEEDED }
class ERR_InfantryAmmoCallback : ScriptedInventoryOperationCallback
{
ERR_InfantryAmmoTransfer Transfer;
bool Returning;
protected override void OnComplete() { if (Transfer) Transfer.Acknowledge(true, Returning); }
protected override void OnFailed() { if (Transfer) Transfer.Acknowledge(false, Returning); }
protected override void OnAllComplete() { if (Transfer) Transfer.Acknowledge(true, Returning); }
protected override void OnAllFailed() { if (Transfer) Transfer.Acknowledge(false, Returning); }
}
// One exact physical magazine movement. The caller owns HQ authorization and
// the spent-item reservation; this leaf never selects tasks or retries a move.
class ERR_InfantryAmmoTransfer
{
protected ERR_GameMode m_Mode;
protected Vehicle m_Donor;
protected SCR_AIGroup m_DonorGroup;
protected IEntity m_Recipient;
protected SCR_AIGroup m_RecipientGroup;
protected IEntity m_Magazine;
protected IEntity m_Driver;
protected ERR_ForceRecord m_DonorRecord;
protected ERR_ForceRecord m_RecipientRecord;
protected InventoryStorageManagerComponent m_Source;
protected InventoryStorageManagerComponent m_Destination;
protected BaseInventoryStorageComponent m_Storage;
protected InventoryStorageSlot m_OriginalSlot;
protected BaseInventoryStorageComponent m_OriginalStorage;
protected ref ERR_InfantryAmmoCallback m_Callback;
protected bool m_RecycleEmpty;
protected bool m_LauncherMagazine;
protected IEntity m_ReturnMagazine;
protected BaseInventoryStorageComponent m_ReturnStorage;
protected ref ERR_InfantryAmmoCallback m_ReturnCallback;
protected bool m_ReturnSubmitted;
protected bool m_ReturnAccepted;
protected bool m_ReturnDone;
protected int m_ReturnAcknowledgement;
protected float m_PhaseStarted = -1;
protected bool m_Submitted;
protected bool m_Accepted;
protected int m_Acknowledgement;
protected float m_Started = -1;
protected ERR_InfantryAmmoTransferState m_State = ERR_InfantryAmmoTransferState.PENDING;
protected string m_Reason;
void ERR_InfantryAmmoTransfer(ERR_GameMode mode, Vehicle donorVehicle, SCR_AIGroup donorGroup, IEntity recipientActor, SCR_AIGroup recipientGroup, IEntity exactMagazine, bool recycleEmpty = false, bool launcherMagazine = false)
{
m_RecycleEmpty = recycleEmpty; m_LauncherMagazine = launcherMagazine;
m_Mode = mode; m_Donor = donorVehicle; m_DonorGroup = donorGroup;
m_Recipient = recipientActor; m_RecipientGroup = recipientGroup; m_Magazine = exactMagazine;
}
static bool Compatible(IEntity actor, IEntity exactMagazine, bool launcherMagazine = false)
{
if (!actor || !exactMagazine) return false;
BaseMagazineComponent magazine = BaseMagazineComponent.Cast(exactMagazine.FindComponent(BaseMagazineComponent));
BaseWeaponManagerComponent manager = BaseWeaponManagerComponent.Cast(actor.FindComponent(BaseWeaponManagerComponent));
if (!magazine || !manager || !magazine.GetMagazineWell() || magazine.GetAmmoCount() <= 0 || magazine.GetMaxAmmoCount() <= 0 || magazine.GetAmmoCount() > magazine.GetMaxAmmoCount()) return false;
if (launcherMagazine && magazine.IsUsed()) return false;
array<IEntity> weapons = {}; manager.GetWeaponsList(weapons);
if (weapons.IsEmpty() || weapons.Count() > 8) return false;
foreach (IEntity rifle : weapons)
{
if (!rifle) continue;
BaseWeaponComponent weapon = BaseWeaponComponent.Cast(rifle.FindComponent(BaseWeaponComponent));
if (!weapon || weapon.GetOwner() != rifle) continue;
EWeaponType kind = weapon.GetWeaponType();
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;
array<BaseMuzzleComponent> muzzles = {}; weapon.GetMuzzlesList(muzzles);
if (muzzles.Count() > 4) continue;
foreach (BaseMuzzleComponent muzzle : muzzles)
{
if (!muzzle || !muzzle.GetMagazineWell()) continue;
EMuzzleType muzzleKind = muzzle.GetMuzzleType();
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;
// 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;
SCR_MuzzleInMagComponent reloadable = SCR_MuzzleInMagComponent.Cast(muzzle);
if (reloadable && !reloadable.CanBeReloaded()) 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;
}
if (magazine.GetMagazineWell().Type() != muzzle.GetMagazineWell().Type()) continue;
}
// Same direction as the stock magazine inventory predicate: a magazine's
// derived well must inherit the actual carried small-arms muzzle's well.
if (magazine.GetMagazineWell().IsInherited(muzzle.GetMagazineWell().Type())) return true;
}
}
return false;
}
static bool StoredUnder(IEntity entity, IEntity root)
{
for (int depth = 0; entity && depth < 16; depth++)
{
if (entity == root) return true;
InventoryItemComponent item = InventoryItemComponent.Cast(entity.FindComponent(InventoryItemComponent));
if (item && item.GetParentSlot())
{
BaseInventoryStorageComponent storage = item.GetParentSlot().GetStorage();
if (!storage) return false;
entity = storage.GetOwner();
}
else entity = entity.GetParent();
}
return false;
}
protected bool ManagedStorage(InventoryStorageManagerComponent inventory, BaseInventoryStorageComponent storage, IEntity owner)
{
if (!inventory || !storage || inventory.GetOwner() != owner || !StoredUnder(storage.GetOwner(), owner)) return false;
array<BaseInventoryStorageComponent> storages = {};
inventory.GetStorages(storages, EStoragePurpose.PURPOSE_DEPOSIT);
return storages.Count() <= 64 && storages.Contains(storage);
}
protected int Ownership(InventoryStorageManagerComponent inventory, IEntity exact = null)
{
if (!exact) exact = m_Magazine;
if (!inventory || !exact) return -1;
array<IEntity> items = {}; inventory.GetItems(items, EStoragePurpose.PURPOSE_DEPOSIT);
if (items.Count() > 512) return -1;
if (items.Contains(exact)) return 1;
return 0;
}
protected bool Proof(IEntity exact, InventoryStorageManagerComponent source, InventoryStorageManagerComponent destination, BaseInventoryStorageComponent storage, IEntity owner, int acknowledgement)
{
if (!exact || acknowledgement != 1 || Ownership(source, exact) != 0 || Ownership(destination, exact) != 1 || !ManagedStorage(destination, storage, owner)) return false;
InventoryItemComponent item = InventoryItemComponent.Cast(exact.FindComponent(InventoryItemComponent));
return item && item.GetParentSlot() && item.GetParentSlot().GetStorage() == storage;
}
protected bool PhysicalProof()
{
return Proof(m_Magazine, m_Source, m_Destination, m_Storage, m_Recipient, m_Acknowledgement);
}
// Terminal negative callback is settled only with exact unchanged source
// custody. Native false without a callback still retains all uncertainty.
protected bool RefusedAtSource()
{
if (!m_Submitted || m_Acknowledgement != -1 || !m_Magazine || !m_Donor || !m_Recipient) return false;
if (!m_OriginalSlot || !m_OriginalStorage) return false;
if (InventoryStorageManagerComponent.Cast(m_Donor.FindComponent(InventoryStorageManagerComponent)) != m_Source) return false;
if (InventoryStorageManagerComponent.Cast(m_Recipient.FindComponent(InventoryStorageManagerComponent)) != m_Destination) return false;
if (Ownership(m_Source) != 1 || Ownership(m_Destination) != 0) return false;
if (!ManagedStorage(m_Source, m_OriginalStorage, m_Donor)) return false;
InventoryItemComponent item = InventoryItemComponent.Cast(m_Magazine.FindComponent(InventoryItemComponent));
if (!item || item.GetParentSlot() != m_OriginalSlot) return false;
return m_OriginalSlot.GetStorage() == m_OriginalStorage && m_OriginalSlot.GetAttachedEntity() == m_Magazine;
}
protected bool ReturnProof()
{
return Proof(m_ReturnMagazine, m_Destination, m_Source, m_ReturnStorage, m_Donor, m_ReturnAcknowledgement);
}
void Acknowledge(bool success, bool returning = false)
{
if (returning)
{
if (m_ReturnAcknowledgement == 0)
{
if (success) m_ReturnAcknowledgement = 1;
else m_ReturnAcknowledgement = -1;
}
return;
}
if (m_Acknowledgement != 0) return;
if (success) m_Acknowledgement = 1;
else m_Acknowledgement = -1;
}
// Both native operations remain observable after Stop. Returning an empty
// magazine never proves delivery or grants authority for a later full one.
bool Unresolved()
{
if ((m_ReturnAccepted || (m_LauncherMagazine && m_ReturnSubmitted)) && !m_ReturnDone && m_ReturnMagazine && m_ReturnAcknowledgement >= 0 && !ReturnProof()) return true;
if (m_LauncherMagazine && m_Submitted) return m_Magazine && !PhysicalProof() && !RefusedAtSource();
if (!m_Accepted || !m_Magazine || m_Acknowledgement < 0) return false;
return !PhysicalProof();
}
// Any native submission consumes this incoming identity at the coordinator,
// even when only the empty return was attempted and delivery never started.
bool Submitted() { return m_Submitted || m_ReturnSubmitted; }
string Reason() { return m_Reason; }
protected ERR_InfantryAmmoTransferState Fail(string reason)
{
if (m_State == ERR_InfantryAmmoTransferState.PENDING) { m_State = ERR_InfantryAmmoTransferState.FAILED; m_Reason = reason; }
return m_State;
}
void Stop() { Fail("HQ authorization stopped; an accepted native move cannot be cancelled"); }
protected bool Context()
{
if (!Replication.IsServer() || SCR_Global.IsEditMode() || !m_Mode || m_Mode != ERR_GameMode.Get() || !m_Mode.Active() || !m_Mode.Forces()) return false;
if (!m_Donor || !ERR_Actors.Alive(m_Donor) || !m_DonorGroup || !m_RecipientGroup || m_DonorGroup == m_RecipientGroup) return false;
if (!ERR_EnemyEquipmentRules.Eligible(m_Mode, m_RecipientGroup, m_Recipient) || CompartmentAccessComponent.GetVehicleIn(m_Recipient)) return false;
if (m_DonorGroup.IsSlave() || m_RecipientGroup.IsSlave() || m_DonorGroup.GetPlayerCount(true) > 0 || m_DonorGroup.GetFaction() != m_Mode.EnemyFaction()) return false;
if (SCR_FactionManager.SGetFaction(m_Donor) != m_Mode.EnemyFaction() || SCR_FactionManager.SGetFaction(m_Recipient) != m_Mode.EnemyFaction()) return false;
ERR_ForceRecord donorRecord = m_Mode.Forces().FindGroup(m_DonorGroup);
ERR_ForceRecord recipientRecord = m_Mode.Forces().FindGroup(m_RecipientGroup);
if (!donorRecord || !recipientRecord || donorRecord.Group != m_DonorGroup || recipientRecord.Group != m_RecipientGroup || !donorRecord.Physical || !recipientRecord.Physical || donorRecord.Eliminated || recipientRecord.Eliminated) return false;
if (m_DonorRecord && (m_DonorRecord != donorRecord || m_RecipientRecord != recipientRecord)) return false;
SCR_AIGroupUtilityComponent utility = m_DonorGroup.GetGroupUtilityComponent();
if (!utility || !utility.IsMilitary() || !utility.m_VehicleMgr || !utility.m_VehicleMgr.FindVehicle(m_Donor)) return false;
if (ERR_ConvoyRules.Captured(m_Donor, m_DonorGroup)) return false;
SCR_AIVehicleUsageComponent usage = SCR_AIVehicleUsageComponent.Cast(m_Donor.FindComponent(SCR_AIVehicleUsageComponent));
if (!usage || !usage.GetPilotCompartmentSlot()) return false;
IEntity driver = usage.GetPilotCompartmentSlot().GetOccupant();
if (!ERR_EnemyEquipmentRules.Eligible(m_Mode, m_DonorGroup, driver) || !ERR_ConvoyRules.PartOf(CompartmentAccessComponent.GetVehicleIn(driver), m_Donor)) return false;
if (SCR_FactionManager.SGetFaction(driver) != m_Mode.EnemyFaction() || (m_Driver && driver != m_Driver)) return false;
Physics physics = m_Donor.GetPhysics();
if (!physics) return false;
float speed = physics.GetVelocity().Length();
float angular = physics.GetAngularVelocity().Length();
float distance = vector.Distance(m_Donor.GetOrigin(), m_Recipient.GetOrigin());
if (!(speed >= 0 && speed <= 0.25 && angular >= 0 && angular <= 0.1 && distance >= 0 && distance <= 15)) return false;
if (distance > 4 && !m_Donor.FindComponent(VehicleHelicopterSimulation)) return false;
if (!Compatible(m_Recipient, m_Magazine, m_LauncherMagazine)) return false;
m_Driver = driver; m_DonorRecord = donorRecord; m_RecipientRecord = recipientRecord;
return true;
}
// One optional capacity recovery, using only an unused exact empty identity.
protected bool ReturnEmpty()
{
if (!m_RecycleEmpty || m_ReturnSubmitted || m_ReturnMagazine) return false;
BaseMagazineComponent incoming = BaseMagazineComponent.Cast(m_Magazine.FindComponent(BaseMagazineComponent));
if (!incoming || !incoming.GetMagazineWell()) return false;
array<IEntity> items = {}; m_Destination.GetItems(items, EStoragePurpose.PURPOSE_DEPOSIT);
if (items.Count() > 128) return false;
foreach (IEntity candidate : items)
{
if (!candidate || candidate == m_Magazine) continue;
BaseMagazineComponent empty = BaseMagazineComponent.Cast(candidate.FindComponent(BaseMagazineComponent));
if (!empty || empty.IsUsed() || empty.GetAmmoCount() != 0 || empty.GetMaxAmmoCount() <= 0
|| !empty.GetMagazineWell() || empty.GetMagazineWell().Type() != incoming.GetMagazineWell().Type()) continue;
if (Ownership(m_Destination, candidate) != 1 || Ownership(m_Source, candidate) != 0) continue;
InventoryItemComponent item = InventoryItemComponent.Cast(candidate.FindComponent(InventoryItemComponent));
if (!item || !item.GetParentSlot() || !ManagedStorage(m_Destination, item.GetParentSlot().GetStorage(), m_Recipient)) continue;
BaseInventoryStorageComponent storage = m_Source.FindStorageForItem(candidate, EStoragePurpose.PURPOSE_DEPOSIT);
if (!ManagedStorage(m_Source, storage, m_Donor) || !m_Destination.CanMoveItemToStorage(candidate, storage, -1)) continue;
m_ReturnMagazine = candidate; m_ReturnStorage = storage;
m_ReturnCallback = new ERR_InfantryAmmoCallback(); m_ReturnCallback.Transfer = this; m_ReturnCallback.Returning = true;
m_ReturnSubmitted = true;
m_ReturnAccepted = m_Destination.TryMoveItemToStorage(candidate, storage, -1, m_ReturnCallback);
if (m_ReturnAcknowledgement == 1) m_ReturnAccepted = true;
return true;
}
return false;
}
ERR_InfantryAmmoTransferState Tick(float now)
{
if (m_State != ERR_InfantryAmmoTransferState.PENDING) return m_State;
if (!ERR_ForceRules.Finite(now) || now < 0) return Fail("invalid transaction clock");
if (m_Started < 0) { m_Started = now; m_PhaseStarted = now; }
if (now < m_Started || now < m_PhaseStarted || now - m_Started >= 16000 || now - m_PhaseStarted >= 8000) return Fail("native magazine move deadline expired");
if (!Context()) return Fail("original owned groups, vehicle, driver, recipient, proximity or compatible weapon unavailable");
if (m_ReturnSubmitted && !m_ReturnDone)
{
if (!m_ReturnAccepted || m_ReturnAcknowledgement < 0) return Fail("native empty return rejected; neither identity will be retried");
if (!ReturnProof()) return ERR_InfantryAmmoTransferState.PENDING;
m_ReturnDone = true; m_PhaseStarted = now;
// Separate slow tick allows loss of HQ authority to stop before delivery.
return ERR_InfantryAmmoTransferState.PENDING;
}
if (!m_Submitted)
{
m_Source = InventoryStorageManagerComponent.Cast(m_Donor.FindComponent(InventoryStorageManagerComponent));
m_Destination = InventoryStorageManagerComponent.Cast(m_Recipient.FindComponent(InventoryStorageManagerComponent));
if (!m_Source || !m_Destination || m_Source == m_Destination || Ownership(m_Source) != 1 || Ownership(m_Destination) != 0) return Fail("exact magazine is not exclusive donor stock");
InventoryItemComponent item = InventoryItemComponent.Cast(m_Magazine.FindComponent(InventoryItemComponent));
if (!item || !item.GetParentSlot() || !ManagedStorage(m_Source, item.GetParentSlot().GetStorage(), m_Donor)) return Fail("exact source magazine storage does not belong to original donor");
m_Storage = m_Destination.FindStorageForItem(m_Magazine, EStoragePurpose.PURPOSE_DEPOSIT);
if (m_Storage && !ManagedStorage(m_Destination, m_Storage, m_Recipient)) return Fail("recipient storage ownership unavailable");
bool capacity = m_Storage && m_Source.CanMoveItemToStorage(m_Magazine, m_Storage, -1);
if (!ERR_InfantryAmmoAccess.Clear(m_Donor, m_Recipient)) return Fail("no unobstructed physical reach to an authored donor storage access point");
if (!capacity)
{
if (!m_ReturnDone && ReturnEmpty()) return ERR_InfantryAmmoTransferState.PENDING;
return Fail("recipient has no native compatible available storage after at most one empty return");
}
if (m_LauncherMagazine)
{
m_OriginalSlot = item.GetParentSlot(); m_OriginalStorage = m_OriginalSlot.GetStorage();
if (m_OriginalSlot.GetAttachedEntity() != m_Magazine) return Fail("launcher source slot lacks reciprocal item proof");
}
m_Callback = new ERR_InfantryAmmoCallback(); m_Callback.Transfer = this;
m_Submitted = true;
m_Accepted = m_Source.TryMoveItemToStorage(m_Magazine, m_Storage, -1, m_Callback);
if (m_Acknowledgement == 1) m_Accepted = true;
if (!m_Accepted) return Fail("native magazine move rejected; exact item will not be retried");
}
// Native callbacks may synchronously cancel this leaf or revoke ownership.
if (m_State != ERR_InfantryAmmoTransferState.PENDING) return m_State;
if (!Context()) return Fail("magazine custody authority changed during native submission");
if (PhysicalProof())
{
m_State = ERR_InfantryAmmoTransferState.SUCCEEDED;
m_Reason = "native callback and exact destination storage confirm one physical magazine transferred";
return m_State;
}
if (m_Acknowledgement < 0) return Fail("native magazine move failed");
return ERR_InfantryAmmoTransferState.PENDING;
}
}
@@ -0,0 +1,10 @@
{
"addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryAmmoTransfer.c": {
"working_sha256": "463f8a355aeac40e5ee80981b09e8561fae1b44e179facf911a0fecbfc7722fe",
"index_sha256": "401a29ec31712d113c203d6975e44aed7bb3166025454f4950a4c7cb6f5ad6eb"
},
"tests/test_infantry_ammo_transfer.py": {
"working_sha256": "7929033fe661de87f9ca72bc33d64fe996ac8b8e3630a17b4210a6f498b2930f",
"index_sha256": "e1172dda6350d2f8b89166ba63995cd1590435930ba220cd38b022307103a306"
}
}
@@ -0,0 +1,314 @@
enum ERR_InfantryAmmoTransferState { FAILED = -1, PENDING, SUCCEEDED }
class ERR_InfantryAmmoCallback : ScriptedInventoryOperationCallback
{
ERR_InfantryAmmoTransfer Transfer;
bool Returning;
protected override void OnComplete() { if (Transfer) Transfer.Acknowledge(true, Returning); }
protected override void OnFailed() { if (Transfer) Transfer.Acknowledge(false, Returning); }
protected override void OnAllComplete() { if (Transfer) Transfer.Acknowledge(true, Returning); }
protected override void OnAllFailed() { if (Transfer) Transfer.Acknowledge(false, Returning); }
}
// One exact physical magazine movement. The caller owns HQ authorization and
// the spent-item reservation; this leaf never selects tasks or retries a move.
class ERR_InfantryAmmoTransfer
{
protected ERR_GameMode m_Mode;
protected Vehicle m_Donor;
protected SCR_AIGroup m_DonorGroup;
protected IEntity m_Recipient;
protected SCR_AIGroup m_RecipientGroup;
protected IEntity m_Magazine;
protected IEntity m_Driver;
protected ERR_ForceRecord m_DonorRecord;
protected ERR_ForceRecord m_RecipientRecord;
protected InventoryStorageManagerComponent m_Source;
protected InventoryStorageManagerComponent m_Destination;
protected BaseInventoryStorageComponent m_Storage;
protected InventoryStorageSlot m_OriginalSlot;
protected BaseInventoryStorageComponent m_OriginalStorage;
protected ref ERR_InfantryAmmoCallback m_Callback;
protected bool m_RecycleEmpty;
protected bool m_LauncherMagazine;
protected IEntity m_ReturnMagazine;
protected BaseInventoryStorageComponent m_ReturnStorage;
protected ref ERR_InfantryAmmoCallback m_ReturnCallback;
protected bool m_ReturnSubmitted;
protected bool m_ReturnAccepted;
protected bool m_ReturnDone;
protected int m_ReturnAcknowledgement;
protected float m_PhaseStarted = -1;
protected bool m_Submitted;
protected bool m_Accepted;
protected int m_Acknowledgement;
protected float m_Started = -1;
protected ERR_InfantryAmmoTransferState m_State = ERR_InfantryAmmoTransferState.PENDING;
protected string m_Reason;
void ERR_InfantryAmmoTransfer(ERR_GameMode mode, Vehicle donorVehicle, SCR_AIGroup donorGroup, IEntity recipientActor, SCR_AIGroup recipientGroup, IEntity exactMagazine, bool recycleEmpty = false, bool launcherMagazine = false)
{
m_RecycleEmpty = recycleEmpty; m_LauncherMagazine = launcherMagazine;
m_Mode = mode; m_Donor = donorVehicle; m_DonorGroup = donorGroup;
m_Recipient = recipientActor; m_RecipientGroup = recipientGroup; m_Magazine = exactMagazine;
}
static bool Compatible(IEntity actor, IEntity exactMagazine, bool launcherMagazine = false)
{
if (!actor || !exactMagazine) return false;
BaseMagazineComponent magazine = BaseMagazineComponent.Cast(exactMagazine.FindComponent(BaseMagazineComponent));
BaseWeaponManagerComponent manager = BaseWeaponManagerComponent.Cast(actor.FindComponent(BaseWeaponManagerComponent));
if (!magazine || !manager || !magazine.GetMagazineWell() || magazine.GetAmmoCount() <= 0 || magazine.GetMaxAmmoCount() <= 0 || magazine.GetAmmoCount() > magazine.GetMaxAmmoCount()) return false;
if (launcherMagazine && magazine.IsUsed()) return false;
array<IEntity> weapons = {}; manager.GetWeaponsList(weapons);
if (weapons.IsEmpty() || weapons.Count() > 8) return false;
foreach (IEntity rifle : weapons)
{
if (!rifle) continue;
BaseWeaponComponent weapon = BaseWeaponComponent.Cast(rifle.FindComponent(BaseWeaponComponent));
if (!weapon || weapon.GetOwner() != rifle) continue;
EWeaponType kind = weapon.GetWeaponType();
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;
array<BaseMuzzleComponent> muzzles = {}; weapon.GetMuzzlesList(muzzles);
if (muzzles.Count() > 4) continue;
foreach (BaseMuzzleComponent muzzle : muzzles)
{
if (!muzzle || !muzzle.GetMagazineWell()) continue;
EMuzzleType muzzleKind = muzzle.GetMuzzleType();
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;
// 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;
SCR_MuzzleInMagComponent reloadable = SCR_MuzzleInMagComponent.Cast(muzzle);
if (reloadable && !reloadable.CanBeReloaded()) 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;
}
if (magazine.GetMagazineWell().Type() != muzzle.GetMagazineWell().Type()) continue;
}
// Same direction as the stock magazine inventory predicate: a magazine's
// derived well must inherit the actual carried small-arms muzzle's well.
if (magazine.GetMagazineWell().IsInherited(muzzle.GetMagazineWell().Type())) return true;
}
}
return false;
}
static bool StoredUnder(IEntity entity, IEntity root)
{
for (int depth = 0; entity && depth < 16; depth++)
{
if (entity == root) return true;
InventoryItemComponent item = InventoryItemComponent.Cast(entity.FindComponent(InventoryItemComponent));
if (item && item.GetParentSlot())
{
BaseInventoryStorageComponent storage = item.GetParentSlot().GetStorage();
if (!storage) return false;
entity = storage.GetOwner();
}
else entity = entity.GetParent();
}
return false;
}
protected bool ManagedStorage(InventoryStorageManagerComponent inventory, BaseInventoryStorageComponent storage, IEntity owner)
{
if (!inventory || !storage || inventory.GetOwner() != owner || !StoredUnder(storage.GetOwner(), owner)) return false;
array<BaseInventoryStorageComponent> storages = {};
inventory.GetStorages(storages, EStoragePurpose.PURPOSE_DEPOSIT);
return storages.Count() <= 64 && storages.Contains(storage);
}
protected int Ownership(InventoryStorageManagerComponent inventory, IEntity exact = null)
{
if (!exact) exact = m_Magazine;
if (!inventory || !exact) return -1;
array<IEntity> items = {}; inventory.GetItems(items, EStoragePurpose.PURPOSE_DEPOSIT);
if (items.Count() > 512) return -1;
if (items.Contains(exact)) return 1;
return 0;
}
protected bool Proof(IEntity exact, InventoryStorageManagerComponent source, InventoryStorageManagerComponent destination, BaseInventoryStorageComponent storage, IEntity owner, int acknowledgement)
{
if (!exact || acknowledgement != 1 || Ownership(source, exact) != 0 || Ownership(destination, exact) != 1 || !ManagedStorage(destination, storage, owner)) return false;
InventoryItemComponent item = InventoryItemComponent.Cast(exact.FindComponent(InventoryItemComponent));
return item && item.GetParentSlot() && item.GetParentSlot().GetStorage() == storage;
}
protected bool PhysicalProof()
{
return Proof(m_Magazine, m_Source, m_Destination, m_Storage, m_Recipient, m_Acknowledgement);
}
// Terminal negative callback is settled only with exact unchanged source
// custody. Native false without a callback still retains all uncertainty.
protected bool RefusedAtSource()
{
if (!m_Submitted || m_Acknowledgement != -1 || !m_Magazine || !m_Donor || !m_Recipient) return false;
if (!m_OriginalSlot || !m_OriginalStorage) return false;
if (InventoryStorageManagerComponent.Cast(m_Donor.FindComponent(InventoryStorageManagerComponent)) != m_Source) return false;
if (InventoryStorageManagerComponent.Cast(m_Recipient.FindComponent(InventoryStorageManagerComponent)) != m_Destination) return false;
if (Ownership(m_Source) != 1 || Ownership(m_Destination) != 0) return false;
if (!ManagedStorage(m_Source, m_OriginalStorage, m_Donor)) return false;
InventoryItemComponent item = InventoryItemComponent.Cast(m_Magazine.FindComponent(InventoryItemComponent));
if (!item || item.GetParentSlot() != m_OriginalSlot) return false;
return m_OriginalSlot.GetStorage() == m_OriginalStorage && m_OriginalSlot.GetAttachedEntity() == m_Magazine;
}
protected bool ReturnProof()
{
return Proof(m_ReturnMagazine, m_Destination, m_Source, m_ReturnStorage, m_Donor, m_ReturnAcknowledgement);
}
void Acknowledge(bool success, bool returning = false)
{
if (returning)
{
if (m_ReturnAcknowledgement == 0)
{
if (success) m_ReturnAcknowledgement = 1;
else m_ReturnAcknowledgement = -1;
}
return;
}
if (m_Acknowledgement != 0) return;
if (success) m_Acknowledgement = 1;
else m_Acknowledgement = -1;
}
// Both native operations remain observable after Stop. Returning an empty
// magazine never proves delivery or grants authority for a later full one.
bool Unresolved()
{
if ((m_ReturnAccepted || (m_LauncherMagazine && m_ReturnSubmitted)) && !m_ReturnDone && m_ReturnMagazine && m_ReturnAcknowledgement >= 0 && !ReturnProof()) return true;
if (m_LauncherMagazine && m_Submitted) return m_Magazine && !PhysicalProof() && !RefusedAtSource();
if (!m_Accepted || !m_Magazine || m_Acknowledgement < 0) return false;
return !PhysicalProof();
}
// Any native submission consumes this incoming identity at the coordinator,
// even when only the empty return was attempted and delivery never started.
bool Submitted() { return m_Submitted || m_ReturnSubmitted; }
string Reason() { return m_Reason; }
protected ERR_InfantryAmmoTransferState Fail(string reason)
{
if (m_State == ERR_InfantryAmmoTransferState.PENDING) { m_State = ERR_InfantryAmmoTransferState.FAILED; m_Reason = reason; }
return m_State;
}
void Stop() { Fail("HQ authorization stopped; an accepted native move cannot be cancelled"); }
protected bool Context()
{
if (!Replication.IsServer() || SCR_Global.IsEditMode() || !m_Mode || m_Mode != ERR_GameMode.Get() || !m_Mode.Active() || !m_Mode.Forces()) return false;
if (!m_Donor || !ERR_Actors.Alive(m_Donor) || !m_DonorGroup || !m_RecipientGroup || m_DonorGroup == m_RecipientGroup) return false;
if (!ERR_EnemyEquipmentRules.Eligible(m_Mode, m_RecipientGroup, m_Recipient) || CompartmentAccessComponent.GetVehicleIn(m_Recipient)) return false;
if (m_DonorGroup.IsSlave() || m_RecipientGroup.IsSlave() || m_DonorGroup.GetPlayerCount(true) > 0 || m_DonorGroup.GetFaction() != m_Mode.EnemyFaction()) return false;
if (SCR_FactionManager.SGetFaction(m_Donor) != m_Mode.EnemyFaction() || SCR_FactionManager.SGetFaction(m_Recipient) != m_Mode.EnemyFaction()) return false;
ERR_ForceRecord donorRecord = m_Mode.Forces().FindGroup(m_DonorGroup);
ERR_ForceRecord recipientRecord = m_Mode.Forces().FindGroup(m_RecipientGroup);
if (!donorRecord || !recipientRecord || donorRecord.Group != m_DonorGroup || recipientRecord.Group != m_RecipientGroup || !donorRecord.Physical || !recipientRecord.Physical || donorRecord.Eliminated || recipientRecord.Eliminated) return false;
if (m_DonorRecord && (m_DonorRecord != donorRecord || m_RecipientRecord != recipientRecord)) return false;
SCR_AIGroupUtilityComponent utility = m_DonorGroup.GetGroupUtilityComponent();
if (!utility || !utility.IsMilitary() || !utility.m_VehicleMgr || !utility.m_VehicleMgr.FindVehicle(m_Donor)) return false;
if (ERR_ConvoyRules.Captured(m_Donor, m_DonorGroup)) return false;
SCR_AIVehicleUsageComponent usage = SCR_AIVehicleUsageComponent.Cast(m_Donor.FindComponent(SCR_AIVehicleUsageComponent));
if (!usage || !usage.GetPilotCompartmentSlot()) return false;
IEntity driver = usage.GetPilotCompartmentSlot().GetOccupant();
if (!ERR_EnemyEquipmentRules.Eligible(m_Mode, m_DonorGroup, driver) || !ERR_ConvoyRules.PartOf(CompartmentAccessComponent.GetVehicleIn(driver), m_Donor)) return false;
if (SCR_FactionManager.SGetFaction(driver) != m_Mode.EnemyFaction() || (m_Driver && driver != m_Driver)) return false;
Physics physics = m_Donor.GetPhysics();
if (!physics) return false;
float speed = physics.GetVelocity().Length();
float angular = physics.GetAngularVelocity().Length();
float distance = vector.Distance(m_Donor.GetOrigin(), m_Recipient.GetOrigin());
if (!(speed >= 0 && speed <= 0.25 && angular >= 0 && angular <= 0.1 && distance >= 0 && distance <= 15)) return false;
if (distance > 4 && !m_Donor.FindComponent(VehicleHelicopterSimulation)) return false;
if (!Compatible(m_Recipient, m_Magazine, m_LauncherMagazine)) return false;
m_Driver = driver; m_DonorRecord = donorRecord; m_RecipientRecord = recipientRecord;
return true;
}
// One optional capacity recovery, using only an unused exact empty identity.
protected bool ReturnEmpty()
{
if (!m_RecycleEmpty || m_ReturnSubmitted || m_ReturnMagazine) return false;
BaseMagazineComponent incoming = BaseMagazineComponent.Cast(m_Magazine.FindComponent(BaseMagazineComponent));
if (!incoming || !incoming.GetMagazineWell()) return false;
array<IEntity> items = {}; m_Destination.GetItems(items, EStoragePurpose.PURPOSE_DEPOSIT);
if (items.Count() > 128) return false;
foreach (IEntity candidate : items)
{
if (!candidate || candidate == m_Magazine) continue;
BaseMagazineComponent empty = BaseMagazineComponent.Cast(candidate.FindComponent(BaseMagazineComponent));
if (!empty || empty.IsUsed() || empty.GetAmmoCount() != 0 || empty.GetMaxAmmoCount() <= 0
|| !empty.GetMagazineWell() || empty.GetMagazineWell().Type() != incoming.GetMagazineWell().Type()) continue;
if (Ownership(m_Destination, candidate) != 1 || Ownership(m_Source, candidate) != 0) continue;
InventoryItemComponent item = InventoryItemComponent.Cast(candidate.FindComponent(InventoryItemComponent));
if (!item || !item.GetParentSlot() || !ManagedStorage(m_Destination, item.GetParentSlot().GetStorage(), m_Recipient)) continue;
BaseInventoryStorageComponent storage = m_Source.FindStorageForItem(candidate, EStoragePurpose.PURPOSE_DEPOSIT);
if (!ManagedStorage(m_Source, storage, m_Donor) || !m_Destination.CanMoveItemToStorage(candidate, storage, -1)) continue;
m_ReturnMagazine = candidate; m_ReturnStorage = storage;
m_ReturnCallback = new ERR_InfantryAmmoCallback(); m_ReturnCallback.Transfer = this; m_ReturnCallback.Returning = true;
m_ReturnSubmitted = true;
m_ReturnAccepted = m_Destination.TryMoveItemToStorage(candidate, storage, -1, m_ReturnCallback);
if (m_ReturnAcknowledgement == 1) m_ReturnAccepted = true;
return true;
}
return false;
}
ERR_InfantryAmmoTransferState Tick(float now)
{
if (m_State != ERR_InfantryAmmoTransferState.PENDING) return m_State;
if (!ERR_ForceRules.Finite(now) || now < 0) return Fail("invalid transaction clock");
if (m_Started < 0) { m_Started = now; m_PhaseStarted = now; }
if (now < m_Started || now < m_PhaseStarted || now - m_Started >= 16000 || now - m_PhaseStarted >= 8000) return Fail("native magazine move deadline expired");
if (!Context()) return Fail("original owned groups, vehicle, driver, recipient, proximity or compatible weapon unavailable");
if (m_ReturnSubmitted && !m_ReturnDone)
{
if (!m_ReturnAccepted || m_ReturnAcknowledgement < 0) return Fail("native empty return rejected; neither identity will be retried");
if (!ReturnProof()) return ERR_InfantryAmmoTransferState.PENDING;
m_ReturnDone = true; m_PhaseStarted = now;
// Separate slow tick allows loss of HQ authority to stop before delivery.
return ERR_InfantryAmmoTransferState.PENDING;
}
if (!m_Submitted)
{
m_Source = InventoryStorageManagerComponent.Cast(m_Donor.FindComponent(InventoryStorageManagerComponent));
m_Destination = InventoryStorageManagerComponent.Cast(m_Recipient.FindComponent(InventoryStorageManagerComponent));
if (!m_Source || !m_Destination || m_Source == m_Destination || Ownership(m_Source) != 1 || Ownership(m_Destination) != 0) return Fail("exact magazine is not exclusive donor stock");
InventoryItemComponent item = InventoryItemComponent.Cast(m_Magazine.FindComponent(InventoryItemComponent));
if (!item || !item.GetParentSlot() || !ManagedStorage(m_Source, item.GetParentSlot().GetStorage(), m_Donor)) return Fail("exact source magazine storage does not belong to original donor");
m_Storage = m_Destination.FindStorageForItem(m_Magazine, EStoragePurpose.PURPOSE_DEPOSIT);
if (m_Storage && !ManagedStorage(m_Destination, m_Storage, m_Recipient)) return Fail("recipient storage ownership unavailable");
bool capacity = m_Storage && m_Source.CanMoveItemToStorage(m_Magazine, m_Storage, -1);
if (!ERR_InfantryAmmoAccess.Clear(m_Donor, m_Recipient)) return Fail("no unobstructed physical reach to an authored donor storage access point");
if (!capacity)
{
if (!m_ReturnDone && ReturnEmpty()) return ERR_InfantryAmmoTransferState.PENDING;
return Fail("recipient has no native compatible available storage after at most one empty return");
}
if (m_LauncherMagazine)
{
m_OriginalSlot = item.GetParentSlot(); m_OriginalStorage = m_OriginalSlot.GetStorage();
if (m_OriginalSlot.GetAttachedEntity() != m_Magazine) return Fail("launcher source slot lacks reciprocal item proof");
}
m_Callback = new ERR_InfantryAmmoCallback(); m_Callback.Transfer = this;
m_Submitted = true;
m_Accepted = m_Source.TryMoveItemToStorage(m_Magazine, m_Storage, -1, m_Callback);
if (m_Acknowledgement == 1) m_Accepted = true;
if (!m_Accepted) return Fail("native magazine move rejected; exact item will not be retried");
}
// 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())
{
m_State = ERR_InfantryAmmoTransferState.SUCCEEDED;
m_Reason = "native callback and exact destination storage confirm one physical magazine transferred";
return m_State;
}
if (m_Acknowledgement < 0) return Fail("native magazine move failed");
return ERR_InfantryAmmoTransferState.PENDING;
}
}
@@ -0,0 +1,339 @@
"""Execute the actual finite native magazine transaction; engine behavior remains separate."""
from pathlib import Path
import os,re
from policy_harness import ROOT,run,body
from test_finite_magazine_reservation import translated_reservation
p=Path(os.environ.get('ERR_INFANTRY_TRANSFER_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryAmmoTransfer.c'))
s=p.read_text()
for forbidden in ('TrySpawn','DeleteEntity','SetOrigin','SetTransform','TryInsert','TryRemove','SetAmmoCount'):
assert forbidden not in s,forbidden
callback=s[s.index('class ERR_InfantryAmmoCallback'):s.index('// One exact physical')]
code=s[s.index('class ERR_InfantryAmmoTransfer\n'):]
code=re.sub(r'//[^\n]*','',code).replace('protected ','').replace('class ','struct ')
for src,dst in {
'm_Donor.FindComponent(VehicleHelicopterSimulation)':'m_Donor->helicopter',
'InventoryItemComponent.Cast(loaded.GetOwner().FindComponent(InventoryItemComponent))':'loaded->GetOwner()->item',
'BaseMagazineComponent.Cast(exactMagazine.FindComponent(BaseMagazineComponent))':'exactMagazine->magazine',
'BaseWeaponManagerComponent.Cast(actor.FindComponent(BaseWeaponManagerComponent))':'actor->weapons',
'BaseWeaponComponent.Cast(rifle.FindComponent(BaseWeaponComponent))':'rifle->weapon',
'InventoryItemComponent.Cast(entity.FindComponent(InventoryItemComponent))':'entity->item',
'InventoryItemComponent.Cast(m_Magazine.FindComponent(InventoryItemComponent))':'m_Magazine->item',
'InventoryItemComponent.Cast(exact.FindComponent(InventoryItemComponent))':'exact->item',
'InventoryItemComponent.Cast(candidate.FindComponent(InventoryItemComponent))':'candidate->item',
'BaseMagazineComponent.Cast(m_Magazine.FindComponent(BaseMagazineComponent))':'m_Magazine->magazine',
'BaseMagazineComponent.Cast(candidate.FindComponent(BaseMagazineComponent))':'candidate->magazine',
'SCR_AIVehicleUsageComponent.Cast(m_Donor.FindComponent(SCR_AIVehicleUsageComponent))':'m_Donor->usage',
'InventoryStorageManagerComponent.Cast(m_Donor.FindComponent(InventoryStorageManagerComponent))':'m_Donor->inventory',
'InventoryStorageManagerComponent.Cast(m_Recipient.FindComponent(InventoryStorageManagerComponent))':'m_Recipient->inventory',
}.items():code=code.replace(src,dst)
code=code.replace('SCR_MuzzleInMagComponent.Cast(muzzle)', '(muzzle->inMag ? muzzle : null)')
code=code.replace('ref ERR_InfantryAmmoCallback','ERR_InfantryAmmoCallback*').replace('array<','Array<').replace('foreach (','for (').replace('null','nullptr')
code=re.sub(r'\bERR_GameMode (?=\w)','ERR_GameMode* ',code)
code=code.replace('void ERR_InfantryAmmoTransfer(','ERR_InfantryAmmoTransfer(').replace('EWeaponType kind','int kind').replace('EMuzzleType muzzleKind','int muzzleKind')
for name in ('ERR_InfantryAmmoTransferState','ERR_InfantryAmmoAccess','ERR_GameMode','ERR_EnemyEquipmentRules','ERR_ForceRules','Replication','SCR_Global','ERR_Actors','SCR_FactionManager','ERR_ConvoyRules','CompartmentAccessComponent','EWeaponType','EMuzzleType','EStoragePurpose','vector'):
code=code.replace(name+'.',name+'::')
for name in ('m_Mode','m_Donor','m_DonorGroup','m_Recipient','m_RecipientGroup','m_Source','m_Destination','m_Storage','m_OriginalSlot','m_Callback','magazine','reloadable','loaded','loadedItem','loadedSlot','manager','weapon','muzzle','entity','item','storage','inventory','incoming','empty','source','destination','m_ReturnCallback','donorRecord','recipientRecord','utility','usage','physics'):
code=re.sub(r'\b'+name+r'\.',name+'->',code)
code=code.replace('GetMagazineWell().','GetMagazineWell()->').replace('GetParentSlot().','GetParentSlot()->').replace('Forces().','Forces()->').replace('m_VehicleMgr.','m_VehicleMgr->').replace('GetPilotCompartmentSlot().','GetPilotCompartmentSlot()->')
code=re.sub(r'\b(bool|int|Vehicle|IEntity|SCR_AIGroup|ERR_ForceRecord|InventoryStorageSlot|InventoryStorageManagerComponent|BaseInventoryStorageComponent|ERR_GameMode\*|ERR_InfantryAmmoCallback\*) (\w+);',r'\1 \2{};',code)
code=code.replace('\n}\n','\n};\n')
callback=callback.replace('class ERR_InfantryAmmoCallback : ScriptedInventoryOperationCallback','').strip()[1:-1]
callbacks=[]
for name in ('OnComplete','OnFailed','OnAllComplete','OnAllFailed'):
m=re.search(r'protected override void '+name+r'\(\) (\{[^\n]+\})',callback)
callbacks.append('void ERR_InfantryAmmoCallback::'+name+'() '+m[1].replace('Transfer.','Transfer->'))
# Integrate the actual coordinator transfer selector with this actual leaf.
coordinator_path=Path(os.environ.get('ERR_INFANTRY_RESUPPLY_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c'))
transfer=body(coordinator_path,'protected void Transfer(float now)').replace('protected ','').replace('null','nullptr').replace('foreach (','for (')
transfer=transfer.replace('ERR_HQAmmoSnapshot local','ERR_HQAmmoSnapshot* local')
for name in ('ERR_Actors','ERR_HQAmmoSampler','ERR_InfantryAmmoTransfer','vector'):
transfer=transfer.replace(name+'.',name+'::')
transfer=transfer.replace('ERR_FiniteMagazineReservation reservation', 'ERR_FiniteMagazineReservation* reservation').replace('new ERR_FiniteMagazineReservation(m_Stock, m_Spent,', 'new ERR_FiniteMagazineReservation(&m_Stock, &m_Spent,')
for name in ('m_Move','actor','m_Truck','m_Recipient','local','reservation'):
transfer=transfer.replace(name+'.',name+'->')
transfer=transfer.replace('Entity.GetOrigin()', 'Entity->GetOrigin()')
pre=r'''
#include <cassert>
#include <cmath>
#include <string>
#include <vector>
#include <algorithm>
#include <limits>
#include <functional>
using string=std::string;
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;int Count(){return this->size();}bool IsEmpty(){return this->empty();}void Insert(T t){this->push_back(t);}void RemoveItem(T t){this->erase(std::remove(this->begin(),this->end(),t),this->end());}bool Contains(T t){return std::find(this->begin(),this->end(),t)!=this->end();}};
struct vector{float x=0,y=0,z=0;float Length(){return std::sqrt(x*x+y*y+z*z);}static float DistanceXZ(vector a,vector b){return std::hypot(a.x-b.x,a.z-b.z);}static float Distance(vector a,vector b){return vector{a.x-b.x,a.y-b.y,a.z-b.z}.Length();}};
struct Entity;struct Group;struct Inventory;struct Storage;struct Item;struct Slot;struct Magazine;struct Weapon;struct Muzzle;struct Manager;struct Utility;struct Usage;struct Physics_;struct Record;
using IEntity=Entity*;using Vehicle=Entity*;using SCR_AIGroup=Group*;using ERR_ForceRecord=Record*;
using InventoryStorageSlot=Slot*;using InventoryStorageManagerComponent=Inventory*;using BaseInventoryStorageComponent=Storage*;using InventoryItemComponent=Item*;using BaseMagazineComponent=Magazine*;
using BaseWeaponManagerComponent=Manager*;using BaseWeaponComponent=Weapon*;using BaseMuzzleComponent=Muzzle*;using SCR_MuzzleInMagComponent=Muzzle*;
using SCR_AIGroupUtilityComponent=Utility*;using SCR_AIVehicleUsageComponent=Usage*;using Physics=Physics_*;
enum ERR_InfantryAmmoTransferState{FAILED=-1,PENDING,SUCCEEDED};
struct ERR_InfantryAmmoTransfer;
struct ScriptedInventoryOperationCallback{virtual void OnComplete(){}virtual void OnFailed(){}virtual void OnAllComplete(){}virtual void OnAllFailed(){}virtual ~ScriptedInventoryOperationCallback()=default;};
struct ERR_InfantryAmmoCallback:ScriptedInventoryOperationCallback{ERR_InfantryAmmoTransfer*Transfer=nullptr;bool Returning=false;void OnComplete()override;void OnFailed()override;void OnAllComplete()override;void OnAllFailed()override;};
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 EStoragePurpose{static const int PURPOSE_DEPOSIT=1;};
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;IEntity GetOwner(){return owner;}Well*well=nullptr;int rounds=30,capacity=30;bool used=false;bool IsUsed(){return used;}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;}Magazine*loaded=nullptr;Magazine*GetMagazine(){return loaded;}Well*well=nullptr;int kind=0;Well*GetMagazineWell(){return well;}int GetMuzzleType(){return kind;}};
struct Weapon{IEntity owner=nullptr;Array<Muzzle*>muzzles;int kind=1;IEntity GetOwner(){return owner;}int GetWeaponType(){return kind;}void GetMuzzlesList(Array<Muzzle*>&a){a=muzzles;}};
struct Manager{Array<IEntity>weapons;void GetWeaponsList(Array<IEntity>&a){a=weapons;}};
struct Storage{IEntity owner=nullptr;IEntity GetOwner(){return owner;}};
struct Slot{Storage*storage=nullptr;IEntity occupant=nullptr;Muzzle*container=nullptr;IEntity attached=nullptr;Muzzle*GetParentContainer(){return container;}IEntity GetAttachedEntity(){return attached;}Storage*GetStorage(){return storage;}IEntity GetOccupant(){return occupant;}};
struct Item{Slot*slot=nullptr;Slot*GetParentSlot(){return slot;}};
struct Physics_{vector velocity,angular;vector GetVelocity(){return velocity;}vector GetAngularVelocity(){return angular;}};
struct Usage{Slot*pilot=nullptr;Slot*GetPilotCompartmentSlot(){return pilot;}};
struct VehicleManager{IEntity vehicle=nullptr;IEntity FindVehicle(IEntity v){return vehicle==v?v:nullptr;}};
struct Utility{VehicleManager*m_VehicleMgr=nullptr;bool military=true;bool IsMilitary(){return military;}};
struct Group{int faction=1,players=0;bool slave=false;Utility*utility=nullptr;int GetPlayerCount(bool){return players;}int GetFaction(){return faction;}bool IsSlave(){return slave;}Utility*GetGroupUtilityComponent(){return utility;}};
struct Entity{bool helicopter=false;bool alive=true,awake=true,player=false,captured=false;int faction=1;Group*group=nullptr;IEntity vehicle=nullptr,parent=nullptr;
Inventory*inventory=nullptr;Magazine*magazine=nullptr;Manager*weapons=nullptr;Weapon*weapon=nullptr;Item*item=nullptr;Usage*usage=nullptr;Physics_*physics=nullptr;vector position;
IEntity GetParent(){return parent;}Physics_*GetPhysics(){return physics;}vector GetOrigin(){return position;}};
struct Record{SCR_AIGroup Group=nullptr;bool Physical=true,Eliminated=false;};
struct Ledger{Array<Record*>records;Record*FindGroup(SCR_AIGroup g){for(auto*r:records)if(r->Group==g)return r;return nullptr;}};
struct ERR_GameMode{inline static ERR_GameMode*current=nullptr;bool active=true;Ledger*ledger=nullptr;static ERR_GameMode*Get(){return current;}bool Active(){return active;}Ledger*Forces(){return ledger;}int EnemyFaction(){return 1;}};
struct Replication{inline static bool server=true;static bool IsServer(){return server;}};
struct SCR_Global{inline static bool editor=false;static bool IsEditMode(){return editor;}};
struct ERR_Actors{static bool CanFight(IEntity a){return a&&a->alive&&a->awake;}static bool Alive(IEntity a){return a&&a->alive;}};
struct SCR_FactionManager{static int SGetFaction(IEntity a){return a?a->faction:-1;}};
struct CompartmentAccessComponent{static IEntity GetVehicleIn(IEntity a){return a?a->vehicle:nullptr;}};
struct ERR_ConvoyRules{static bool Captured(Vehicle v,SCR_AIGroup g){return v->captured||g->players>0;}static bool PartOf(IEntity a,IEntity root){return a==root;}};
struct ERR_EnemyEquipmentRules{static bool Eligible(ERR_GameMode*m,SCR_AIGroup g,IEntity a){return m&&g&&a&&a->alive&&a->awake&&!a->player&&a->group==g&&g->players==0&&g->faction==m->EnemyFaction()&&g->utility&&g->utility->military;}};
struct ERR_ForceRules{static bool Finite(float n){return std::isfinite(n);}};
struct ERR_InfantryAmmoAccess{static inline bool clear=true;static inline int checks=0;static bool Clear(Vehicle donor,IEntity recipient){assert(donor&&recipient);checks++;return clear;}};
struct Inventory{IEntity owner=nullptr;Array<IEntity>items;Array<Storage*>storages;Storage*selected=nullptr;bool allow=true,accept=true;int calls=0,callback=0;ScriptedInventoryOperationCallback*last=nullptr;std::function<void()> effect;
IEntity GetOwner(){return owner;}void GetItems(Array<IEntity>&a,int purpose){assert(purpose==1);a=items;}void GetStorages(Array<Storage*>&a,int purpose){assert(purpose==1);a=storages;}
Storage*FindStorageForItem(IEntity,int purpose){assert(purpose==1);return selected;}
bool CanMoveItemToStorage(IEntity,Storage*,int slot){assert(slot==-1);return allow;}
bool TryMoveItemToStorage(IEntity,Storage*,int slot,ScriptedInventoryOperationCallback*cb){assert(slot==-1);calls++;last=cb;if(effect)effect();if(callback==1){cb->OnComplete();}if(callback==2){cb->OnFailed();}return accept;}};
'''
main=r'''
struct Fixture{ERR_GameMode mode;Ledger ledger;Group donorGroup,recipientGroup,otherGroup;Record donorRecord,recipientRecord;Entity truck,recipient,driver,spareDriver,magEntity,rifle,foreign;
VehicleManager vehicleManager;Utility donorUtility,recipientUtility;Physics_ physics;Usage usage;Slot pilot,fromSlot,toSlot;Storage sourceStorage,targetStorage;Inventory source,destination;Item item;Magazine magazine;Manager weapons;Weapon weapon;Muzzle muzzle;Well well;
Fixture(){ERR_GameMode::current=&mode;mode.ledger=&ledger;donorRecord.Group=&donorGroup;recipientRecord.Group=&recipientGroup;ledger.records={&donorRecord,&recipientRecord};
donorGroup.utility=&donorUtility;recipientGroup.utility=&recipientUtility;donorUtility.m_VehicleMgr=&vehicleManager;vehicleManager.vehicle=&truck;
truck.physics=&physics;truck.usage=&usage;usage.pilot=&pilot;pilot.occupant=&driver;driver.group=&donorGroup;driver.vehicle=&truck;spareDriver.group=&donorGroup;spareDriver.vehicle=&truck;
recipient.group=&recipientGroup;recipient.position.x=4;truck.inventory=&source;source.owner=&truck;recipient.inventory=&destination;destination.owner=&recipient;
sourceStorage.owner=&truck;targetStorage.owner=&recipient;source.storages={&sourceStorage};destination.storages={&targetStorage};destination.selected=&targetStorage;
source.items={&magEntity};magEntity.item=&item;item.slot=&fromSlot;fromSlot.storage=&sourceStorage;fromSlot.attached=&magEntity;toSlot.storage=&targetStorage;magEntity.magazine=&magazine;magazine.well=&well;
recipient.weapons=&weapons;weapons.weapons={&rifle};rifle.weapon=&weapon;weapon.owner=&rifle;weapon.muzzles={&muzzle};muzzle.well=&well;}
ERR_InfantryAmmoTransfer Job(){return ERR_InfantryAmmoTransfer(&mode,&truck,&donorGroup,&recipient,&recipientGroup,&magEntity,true);}
void Move(){source.items.RemoveItem(&magEntity);destination.items.Insert(&magEntity);item.slot=&toSlot;}
};
struct EmptyFixture:Fixture{
Entity empty,secondEmpty;Magazine emptyMag;Item emptyItem,secondItem;Slot emptyFrom,emptyTo;
EmptyFixture(){emptyMag.well=&well;emptyMag.rounds=0;empty.magazine=&emptyMag;empty.item=&emptyItem;
emptyFrom.storage=&targetStorage;emptyTo.storage=&sourceStorage;emptyItem.slot=&emptyFrom;
secondEmpty.magazine=&emptyMag;secondEmpty.item=&secondItem;secondItem.slot=&emptyFrom;
destination.items={&empty,&secondEmpty};destination.selected=nullptr;source.selected=&sourceStorage;}
void Return(bool capacity=true){destination.items.RemoveItem(&empty);source.items.Insert(&empty);emptyItem.slot=&emptyTo;if(capacity)destination.selected=&targetStorage;}
};
struct ERR_HQAmmoSnapshot{bool Complete=true,LauncherComplete=true;int Members=1,LauncherMuzzles=1,LauncherMagazines=1,LauncherRounds=0,LauncherChambers=0;bool Valid(int n){return n==Members;}int SmallArmsMuzzles=1,SmallArmsRounds=0,SmallArmsChambers=0;};
struct ERR_HQAmmoSampler{static void Inspect(IEntity actor,SCR_AIGroup group,ERR_HQAmmoSnapshot* stock){assert(actor&&actor->group==group&&stock);}};
struct SupplyTruck{IEntity Entity;SCR_AIGroup Group;};
struct SupplyRecipient{SCR_AIGroup Group;};
struct Coordinator{bool m_LauncherTrip=false;ERR_GameMode* m_Mode;SupplyTruck* m_Truck;SupplyRecipient* m_Recipient;
ERR_InfantryAmmoTransfer* m_Move=nullptr;Array<IEntity>m_Members,m_Stock,m_Spent,m_Served;IEntity m_Receiving=nullptr;
int m_Delivered=0,m_TripDelivered=0,finishes=0,audits=0;
void Finish(string){finishes++;if(m_Move)m_Move->Stop();}
void Audit(string){audits++;}
bool Contains(IEntity owner,IEntity item){return owner&&owner->inventory&&owner->inventory->items.Count()<=128&&owner->inventory->items.Contains(item);}
'''+transfer+r'''
};
struct SupplyFixture:Fixture{SupplyTruck supplyTruck;SupplyRecipient supplyRecipient;Coordinator c;
SupplyFixture():supplyTruck{&truck,&donorGroup},supplyRecipient{&recipientGroup}{
c.m_Mode=&mode;c.m_Truck=&supplyTruck;c.m_Recipient=&supplyRecipient;c.m_Members={&recipient};c.m_Stock={&magEntity};}
};
struct LauncherFixture:SupplyFixture{Entity loaded;Magazine empty;Item loadedItem;Slot loadedSlot;
LauncherFixture(){c.m_LauncherTrip=true;weapon.kind=EWeaponType::WT_GRENADELAUNCHER;muzzle.kind=EMuzzleType::MT_UGLMuzzle;
rifle.parent=&recipient;loaded.parent=&rifle;loaded.magazine=&empty;empty.owner=&loaded;empty.well=&well;empty.rounds=0;muzzle.loaded=&empty;loaded.item=&loadedItem;loadedItem.slot=&loadedSlot;loadedSlot.container=&muzzle;loadedSlot.attached=&loaded;}
};
int main(){
// Failed callback settles only exact original unchanged custody; unknown never does.
{LauncherFixture f;f.source.callback=2;f.c.Transfer(0);assert(f.c.m_Move->Submitted()&&!f.c.m_Move->Unresolved()&&f.c.m_Spent.Count()==1);f.c.Transfer(1000);assert(f.source.calls==1&&f.c.m_Spent.Count()==1);}
for(int bad=0;bad<7;bad++){LauncherFixture f;f.c.Transfer(0);assert(f.c.m_Move->Unresolved());
if(bad!=0)f.source.last->OnFailed();
if(bad==1)f.item.slot=nullptr;
if(bad==2)f.fromSlot.attached=nullptr;
if(bad==3)f.sourceStorage.owner=&f.foreign;
if(bad==4)f.destination.items.Insert(&f.magEntity);
if(bad==5)f.truck.inventory=&f.destination;
if(bad==6)f.source.items.RemoveItem(&f.magEntity);
assert(f.c.m_Move->Unresolved());f.c.m_Move->Stop();assert(f.c.m_Move->Unresolved());}
{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());}
{LauncherFixture f;assert(!ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity));assert(ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity,true));
f.c.Transfer(0);assert(f.c.m_Move&&f.c.m_Move->Submitted()&&f.c.m_Spent.Count()==1&&f.source.calls==1);
f.Move();f.source.last->OnComplete();f.c.Transfer(1000);f.c.Transfer(2000);assert(f.c.m_Delivered==1&&f.source.calls==1&&f.c.m_Served.Count()==1);}
for(int bad=0;bad<8;bad++){LauncherFixture f;if(bad==0)f.magazine.used=true;if(bad==1)f.muzzle.well=nullptr;if(bad==2)f.muzzle.disposable=true;if(bad==3)f.empty.well=nullptr;if(bad==4)f.loadedSlot.attached=nullptr;if(bad==5)f.rifle.parent=nullptr;if(bad==6)f.empty.capacity=0;if(bad==7){f.muzzle.inMag=true;f.muzzle.canReload=false;}
f.c.Transfer(0);assert(!f.c.m_Move&&f.source.calls==0&&f.c.m_Spent.IsEmpty());}
{LauncherFixture f;f.source.accept=false;f.c.Transfer(0);assert(f.c.m_Move&&f.c.m_Move->Submitted()&&f.c.m_Move->Unresolved()&&f.c.m_Spent.Count()==1);f.c.Transfer(1000);assert(f.source.calls==1);f.Move();f.source.last->OnComplete();assert(!f.c.m_Move->Unresolved());}
{LauncherFixture f;auto t=ERR_InfantryAmmoTransfer(&f.mode,&f.truck,&f.donorGroup,&f.recipient,&f.recipientGroup,&f.magEntity,false,true);
f.source.effect=[&](){t.Stop();f.Move();};f.source.callback=1;assert(t.Tick(0)==FAILED&&!t.Unresolved()&&f.source.calls==1);assert(t.Tick(1)==FAILED);}
// A capacity recovery uses one exact unused empty, separately proving both native operations.
{Fixture f;Entity empty;Magazine emptyMag;Item emptyItem;Slot emptyFrom{&f.targetStorage},emptyTo{&f.sourceStorage};
emptyMag.well=&f.well;emptyMag.rounds=0;empty.magazine=&emptyMag;empty.item=&emptyItem;emptyItem.slot=&emptyFrom;
f.destination.items={&empty};f.destination.selected=nullptr;f.source.selected=&f.sourceStorage;
auto t=f.Job();assert(t.Tick(0)==PENDING&&t.Submitted()&&t.Unresolved()&&f.destination.calls==1&&f.source.calls==0);
auto returning=f.destination.last;returning->OnComplete();assert(t.Tick(1)==PENDING&&f.source.calls==0&&t.Unresolved());
f.destination.items.clear();f.source.items.Insert(&empty);emptyItem.slot=&emptyTo;f.destination.selected=&f.targetStorage;
assert(t.Tick(2)==PENDING&&!t.Unresolved()&&f.source.calls==0);assert(t.Tick(3)==PENDING&&f.source.calls==1&&t.Unresolved());
returning->OnAllComplete();returning->OnFailed();assert(t.Tick(4)==PENDING&&t.Unresolved());
f.Move();assert(t.Tick(5)==PENDING);f.source.last->OnComplete();assert(t.Tick(6)==SUCCEEDED&&!t.Unresolved());
assert(f.destination.calls==1&&f.source.calls==1);}
// Ground coordinator reserves the incoming identity as soon as reverse native movement is submitted.
// A failed/ambiguous return never releases it to try a second empty or another incoming magazine.
for(int outcome=0;outcome<3;outcome++){EmptyFixture f;SupplyTruck truck{&f.truck,&f.donorGroup};SupplyRecipient recipient{&f.recipientGroup};Coordinator c;
c.m_Mode=&f.mode;c.m_Truck=&truck;c.m_Recipient=&recipient;c.m_Members={&f.recipient};c.m_Stock={&f.magEntity};
if(outcome==0){f.destination.accept=false;}
c.Transfer(0);assert(c.m_Move&&c.m_Move->Submitted()&&c.m_Spent.Count()==1&&c.m_Receiving==&f.recipient&&c.m_Delivered==0);
if(outcome==1){f.Return(false);f.destination.last->OnComplete();c.Transfer(1000);}
c.Transfer(outcome==2?8000:2000);assert(c.finishes==1&&c.m_Spent.Contains(&f.magEntity)&&c.m_Delivered==0&&f.destination.calls==1&&f.source.calls==0);
assert(c.m_Move->Unresolved()==(outcome==2));}
{EmptyFixture f;SupplyTruck truck{&f.truck,&f.donorGroup};SupplyRecipient recipient{&f.recipientGroup};Coordinator c;
c.m_Mode=&f.mode;c.m_Truck=&truck;c.m_Recipient=&recipient;c.m_Members={&f.recipient};c.m_Stock={&f.magEntity};
c.Transfer(0);f.Return();f.destination.last->OnComplete();c.Transfer(1000);assert(c.m_Delivered==0&&f.source.calls==0);
c.Transfer(2000);assert(c.m_Delivered==0&&f.source.calls==1);f.Move();f.source.last->OnComplete();c.Transfer(3000);c.Transfer(4000);
assert(c.m_Delivered==1&&c.m_Served.Count()==1&&c.m_Spent.Count()==1&&f.source.calls==1&&f.destination.calls==1&&f.source.items.Contains(&f.empty));}
// Default callers (including air delivery) retain the original one-way behavior.
{EmptyFixture f;ERR_InfantryAmmoTransfer t(&f.mode,&f.truck,&f.donorGroup,&f.recipient,&f.recipientGroup,&f.magEntity);
assert(t.Tick(0)==FAILED&&!t.Submitted()&&f.destination.calls==0&&f.source.calls==0);}
// No return when capacity already exists, even with carried empties.
{EmptyFixture f;f.destination.selected=&f.targetStorage;auto t=f.Job();assert(t.Tick(0)==PENDING&&f.source.calls==1&&f.destination.calls==0);}
// Unused, zero-round, exact-well, exclusive recipient storage ownership and donor space are required.
for(int invalid=0;invalid<12;invalid++){EmptyFixture f;f.destination.items={&f.empty};Well other;other.type=2;
if(invalid==0)f.emptyMag.used=true;if(invalid==1)f.emptyMag.rounds=1;if(invalid==2)f.emptyMag.rounds=-1;
if(invalid==3)f.emptyMag.capacity=0;if(invalid==4)f.emptyMag.well=&other;if(invalid==5)f.emptyMag.well=nullptr;
if(invalid==6)f.emptyItem.slot=&f.emptyTo;if(invalid==7)f.source.items.Insert(&f.empty);
if(invalid==8)f.source.selected=nullptr;if(invalid==9)f.destination.allow=false;
if(invalid==10){for(int i=0;i<129;i++)f.destination.items.Insert(&f.empty);}if(invalid==11)ERR_InfantryAmmoAccess::clear=false;
auto t=f.Job();assert(t.Tick(0)==FAILED&&!t.Submitted()&&!t.Unresolved()&&f.destination.calls==0&&f.source.calls==0);
ERR_InfantryAmmoAccess::clear=true;}
// Return proof needs exact destination slot as well as callback; no second candidate is ever moved.
{EmptyFixture f;auto t=f.Job();t.Tick(0);f.Return();f.emptyItem.slot=&f.emptyFrom;f.destination.last->OnComplete();
assert(t.Tick(1)==PENDING&&t.Unresolved()&&f.source.calls==0&&f.destination.calls==1);
f.emptyItem.slot=&f.emptyTo;assert(t.Tick(2)==PENDING&&!t.Unresolved());assert(t.Tick(3)==PENDING&&f.source.calls==1&&f.destination.calls==1);}
// Inline success callbacks override a false API result for the reverse operation, but require physical proof.
{EmptyFixture f;f.destination.accept=false;f.destination.callback=1;f.destination.effect=[&](){f.Return();};auto t=f.Job();
assert(t.Tick(0)==PENDING&&t.Submitted()&&!t.Unresolved());assert(t.Tick(1)==PENDING&&f.source.calls==0);
f.source.accept=false;f.source.callback=1;f.source.effect=[&](){f.Move();};assert(t.Tick(2)==SUCCEEDED&&f.source.calls==1&&f.destination.calls==1);}
// Every attempted reverse move burns the incoming identity, including synchronous rejection.
for(int failure=0;failure<3;failure++){EmptyFixture f;if(failure==0)f.destination.accept=false;if(failure==1)f.destination.callback=2;
auto t=f.Job();assert(t.Tick(0)==PENDING&&t.Submitted()&&f.destination.calls==1&&f.source.calls==0);
assert(t.Tick(failure==2?8000:1)==FAILED);assert(t.Unresolved()==(failure==2));
for(int i=0;i<3;i++){t.Tick(9000+i);}assert(f.destination.calls==1&&f.source.calls==0);}
// Stop before, during, or after confirmed return can never launch the incoming move.
for(int stopAt=0;stopAt<3;stopAt++){EmptyFixture f;auto t=f.Job();if(stopAt==0){t.Stop();assert(t.Tick(0)==FAILED&&!t.Submitted());continue;}
t.Tick(0);if(stopAt==2){f.Return();f.destination.last->OnComplete();assert(t.Tick(1)==PENDING&&!t.Unresolved());}
t.Stop();assert(t.Tick(2)==FAILED&&t.Submitted()&&f.source.calls==0);if(stopAt==1){assert(t.Unresolved());f.Return();f.destination.last->OnAllComplete();}
assert(!t.Unresolved()&&t.Tick(3)==FAILED&&f.source.calls==0&&f.destination.calls==1);}
// Confirmed return cannot trigger a second empty or bypass revalidated incoming custody/capacity/access.
for(int changed=0;changed<6;changed++){EmptyFixture f;auto t=f.Job();t.Tick(0);f.Return(changed!=0);f.destination.last->OnComplete();assert(t.Tick(1)==PENDING);
if(changed==1)f.source.items.RemoveItem(&f.magEntity);if(changed==2)f.destination.items.Insert(&f.magEntity);
if(changed==3)f.truck.captured=true;if(changed==4)f.recipient.player=true;if(changed==5)ERR_InfantryAmmoAccess::clear=false;
assert(t.Tick(2)==FAILED&&t.Submitted()&&!t.Unresolved()&&f.source.calls==0&&f.destination.calls==1);
assert(t.Tick(3)==FAILED&&f.destination.calls==1);ERR_InfantryAmmoAccess::clear=true;}
// Original owner/driver/ledger loss while a return is accepted quarantines that exact operation.
for(int changed=0;changed<4;changed++){EmptyFixture f;auto t=f.Job();t.Tick(0);
if(changed==0)f.truck.captured=true;if(changed==1)f.recipient.player=true;if(changed==2)f.pilot.occupant=&f.spareDriver;if(changed==3)f.donorRecord.Eliminated=true;
assert(t.Tick(1)==FAILED&&t.Unresolved());f.destination.last->OnFailed();assert(!t.Unresolved()&&f.source.calls==0&&f.destination.calls==1);}
// Both native phases are bounded independently, with no renewal while waiting for callbacks.
{EmptyFixture f;auto t=f.Job();t.Tick(0);f.Return();f.destination.last->OnComplete();assert(t.Tick(7999)==PENDING);
assert(t.Tick(8000)==PENDING&&f.source.calls==1);assert(t.Tick(15999)==FAILED&&t.Unresolved());}
// XZ approach can precede valid vertical reach, stopped state, capacity or clear access.
// None of those no-submission outcomes consumes cargo or ends the trip; the same exact item can later submit once.
for(int obstruction=0;obstruction<5;obstruction++){SupplyFixture f;
if(obstruction==0){f.recipient.position.y=3;}if(obstruction==1){f.physics.velocity.x=1;}
if(obstruction==2){f.destination.selected=nullptr;}if(obstruction==3){f.source.allow=false;}
if(obstruction==4){ERR_InfantryAmmoAccess::clear=false;}
for(int tick=0;tick<3;tick++){f.c.Transfer(tick*1000);assert(f.source.calls==0&&f.c.m_Spent.IsEmpty()&&!f.c.m_Move&&!f.c.m_Receiving&&f.c.finishes==0);}
f.recipient.position.y=0;f.physics.velocity.x=0;f.destination.selected=&f.targetStorage;f.source.allow=true;ERR_InfantryAmmoAccess::clear=true;
f.c.Transfer(3000);assert(f.source.calls==1&&f.c.m_Spent.Count()==1&&f.c.m_Move&&f.c.m_Move->Submitted());
f.Move();f.source.last->OnComplete();f.c.Transfer(4000);assert(f.c.m_Delivered==1&&f.c.m_Served.Contains(&f.recipient)&&f.source.calls==1&&f.c.m_Spent.Count()==1);
}
// A capacity-blocked first member cannot prevent another original member from receiving this magazine.
{SupplyFixture f;Fixture second;ERR_GameMode::current=&f.mode;
second.recipient.group=&f.recipientGroup;second.recipientRecord.Group=&f.recipientGroup;
f.c.m_Members.Insert(&second.recipient);f.destination.selected=nullptr;
f.c.Transfer(0);assert(f.source.calls==1&&f.c.m_Receiving==&second.recipient&&f.c.m_Spent.Count()==1&&f.c.finishes==0);
f.source.items.clear();second.destination.items={&f.magEntity};f.item.slot=&second.toSlot;f.source.last->OnComplete();
f.c.Transfer(1000);assert(f.c.m_Delivered==1&&f.c.m_Served.Contains(&second.recipient)&&!f.c.m_Served.Contains(&f.recipient));}
// A native API may complete both physical movement and callback inline. Account once on the next slow tick.
{SupplyFixture f;f.source.callback=1;f.source.effect=[&](){f.Move();};f.c.Transfer(0);
assert(f.source.calls==1&&f.c.m_Move&&f.c.m_Move->Submitted()&&f.c.m_Delivered==0&&f.c.m_Spent.Count()==1);
f.c.Transfer(1000);f.c.Transfer(2000);assert(f.c.m_Delivered==1&&f.c.m_Served.Count()==1&&f.source.calls==1&&f.c.finishes==0);}
// Crossing the native submission boundary burns the exact identity even if native refusal is synchronous.
for(int refusal=0;refusal<2;refusal++){SupplyFixture f;if(refusal==0){f.source.accept=false;}else{f.source.callback=2;}
f.c.Transfer(0);assert(f.source.calls==1&&f.c.m_Spent.Count()==1&&f.c.m_Move->Submitted());
f.c.Transfer(1000);assert(f.c.finishes==1&&f.c.m_Spent.Count()==1&&f.source.calls==1);
}
// Accepted but unresolved loss stays retained/spent; changing geometry cannot cause a second native move.
{SupplyFixture f;f.c.Transfer(0);f.recipient.position.x=8;f.c.Transfer(1000);
assert(f.c.finishes==1&&f.c.m_Move&&f.c.m_Move->Unresolved()&&f.c.m_Spent.Count()==1&&f.source.calls==1);}
// Several equivalent magazines do not multiply a blocked actor's expensive preflight work on one slow tick.
{SupplyFixture f;Entity another=f.magEntity;f.source.items.Insert(&another);f.c.m_Stock.Insert(&another);
ERR_InfantryAmmoAccess::clear=false;ERR_InfantryAmmoAccess::checks=0;f.c.Transfer(0);
assert(ERR_InfantryAmmoAccess::checks==1&&f.source.calls==0&&f.c.m_Spent.IsEmpty());ERR_InfantryAmmoAccess::clear=true;}
// A blocked native access check happens before submission and is terminal.
{Fixture f;auto t=f.Job();ERR_InfantryAmmoAccess::clear=false;ERR_InfantryAmmoAccess::checks=0;
assert(t.Tick(0)==FAILED&&!t.Submitted()&&!t.Unresolved()&&f.source.calls==0&&ERR_InfantryAmmoAccess::checks==1);
ERR_InfantryAmmoAccess::clear=true;assert(t.Tick(1)==FAILED&&f.source.calls==0&&ERR_InfantryAmmoAccess::checks==1);}
// Once native move is accepted, later geometry cannot cause a second operation or pretend cancellation.
{Fixture f;auto t=f.Job();ERR_InfantryAmmoAccess::checks=0;assert(t.Tick(0)==PENDING&&ERR_InfantryAmmoAccess::checks==1);
ERR_InfantryAmmoAccess::clear=false;assert(t.Tick(1)==PENDING&&t.Unresolved()&&f.source.calls==1&&ERR_InfantryAmmoAccess::checks==1);
f.Move();f.source.last->OnComplete();assert(t.Tick(2)==SUCCEEDED);ERR_InfantryAmmoAccess::clear=true;}
// Read-only selection follows the native inherited-well direction, excluding launcher muzzles and weapons.
{Fixture f;assert(ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity));Well derived;derived.type=2;derived.parent=1;f.magazine.well=&derived;assert(ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity));
f.magazine.well=&f.well;f.muzzle.well=&derived;assert(!ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity));
f.muzzle.well=&f.well;f.muzzle.kind=EMuzzleType::MT_UGLMuzzle;assert(!ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity));
f.muzzle.kind=0;f.weapon.kind=EWeaponType::WT_ROCKETLAUNCHER;assert(!ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity));}
// Exactly one source move; neither a callback alone nor physical movement alone is a successful transaction.
{Fixture f;auto t=f.Job();assert(t.Tick(100)==PENDING&&f.source.calls==1&&t.Unresolved());f.source.last->OnComplete();assert(t.Tick(101)==PENDING&&t.Unresolved());
f.Move();assert(t.Tick(102)==SUCCEEDED&&!t.Unresolved());t.Stop();assert(t.Tick(103)==SUCCEEDED&&f.source.calls==1);}
{Fixture f;auto t=f.Job();t.Tick(100);f.Move();assert(t.Tick(101)==PENDING&&t.Unresolved());f.source.last->OnAllComplete();assert(t.Tick(102)==SUCCEEDED);}
// Exact destination slot and known donor absence are mandatory even after success callback.
{Fixture f;auto t=f.Job();t.Tick(100);f.Move();f.item.slot=&f.fromSlot;f.source.last->OnComplete();assert(t.Tick(101)==PENDING&&t.Unresolved());
f.item.slot=&f.toSlot;for(int i=0;i<513;i++)f.source.items.Insert(&f.foreign);assert(t.Tick(102)==PENDING&&t.Unresolved());}
// Late callbacks resolve retained reservations after Stop without Tick or further operations; the stopped task stays failed.
{Fixture f;auto t=f.Job();t.Tick(0);t.Stop();assert(t.Unresolved());f.Move();f.source.last->OnAllComplete();assert(!t.Unresolved());assert(t.Tick(2)==FAILED&&f.source.calls==1);}
{Fixture f;auto t=f.Job();t.Tick(0);t.Stop();f.source.last->OnAllFailed();assert(!t.Unresolved());assert(t.Tick(1)==FAILED&&f.source.calls==1);}
{Fixture f;auto t=f.Job();t.Tick(0);f.source.last->OnComplete();f.source.last->OnFailed();assert(t.Unresolved());f.Move();assert(!t.Unresolved()&&t.Tick(1)==SUCCEEDED);}
// Failure/rejection and the 8s deadline never submit a second move.
for(int failure=0;failure<3;failure++){Fixture f;if(failure==0)f.source.accept=false;if(failure==1)f.source.callback=2;auto t=f.Job();t.Tick(100);
if(failure==2){assert(t.Tick(8100)==FAILED&&t.Unresolved());}else assert(t.Tick(101)==FAILED&&!t.Unresolved());
for(int i=0;i<10;i++)t.Tick(8200+i);assert(f.source.calls==1);}
// Invalid physical state before submission spends no native action; the terminal object cannot revive.
for(int invalid=0;invalid<25;invalid++){Fixture f;auto t=f.Job();
if(invalid==0)Replication::server=false;if(invalid==1)SCR_Global::editor=true;if(invalid==2)f.mode.active=false;
if(invalid==3)f.truck.alive=false;if(invalid==4)f.recipient.alive=false;if(invalid==5)f.recipient.awake=false;if(invalid==6)f.recipient.player=true;
if(invalid==7)f.recipient.vehicle=&f.truck;if(invalid==8)f.truck.captured=true;if(invalid==9)f.donorGroup.players=1;
if(invalid==10)f.recipient.group=&f.otherGroup;if(invalid==11)f.donorRecord.Physical=false;if(invalid==12)f.recipientRecord.Eliminated=true;
if(invalid==13)f.vehicleManager.vehicle=&f.foreign;if(invalid==14)f.driver.alive=false;if(invalid==15)f.driver.vehicle=nullptr;
if(invalid==16)f.physics.velocity.x=.251f;if(invalid==17)f.physics.angular.x=.101f;if(invalid==18)f.recipient.position.x=4.001f;
if(invalid==19)f.truck.faction=2;if(invalid==20)f.driver.faction=2;if(invalid==21)f.recipientGroup.slave=true;
if(invalid==22)f.magazine.rounds=0;if(invalid==23)f.magazine.rounds=31;if(invalid==24)f.physics.velocity.x=std::numeric_limits<float>::quiet_NaN();
assert(t.Tick(100)==FAILED&&f.source.calls==0&&!t.Unresolved());Replication::server=true;SCR_Global::editor=false;assert(t.Tick(200)==FAILED&&f.source.calls==0);}
// Native stock/storage identity and capacity guards reject shared, foreign, oversized and inaccessible inventory.
for(int invalid=0;invalid<9;invalid++){Fixture f;auto t=f.Job();if(invalid==0)f.source.items.clear();if(invalid==1)f.destination.items={&f.magEntity};
if(invalid==2)f.fromSlot.storage=&f.targetStorage;if(invalid==3)f.sourceStorage.owner=&f.foreign;if(invalid==4)f.destination.selected=nullptr;
if(invalid==5)f.targetStorage.owner=&f.foreign;if(invalid==6)f.source.allow=false;if(invalid==7)f.destination.storages.clear();if(invalid==8){for(int i=0;i<65;i++)f.destination.storages.Insert(&f.targetStorage);}
assert(t.Tick(0)==FAILED&&f.source.calls==0);}
// Conditions and original native driver/ledger identities remain strict while an accepted move is pending.
for(int changed=0;changed<4;changed++){Fixture f;auto t=f.Job();t.Tick(100);Record replacement=f.recipientRecord;
if(changed==0)f.pilot.occupant=&f.spareDriver;if(changed==1)f.ledger.records[1]=&replacement;if(changed==2)f.physics.velocity.x=1;if(changed==3)f.recipient.position.x=8;
assert(t.Tick(101)==FAILED&&t.Unresolved());f.source.last->OnFailed();assert(!t.Unresolved()&&f.source.calls==1);}
{Fixture f;auto t=f.Job();t.Tick(100);assert(t.Tick(99)==FAILED&&t.Unresolved());}
// Clothing storage ownership follows native inventory slots and bounds cycles.
{Fixture f;Entity vest;Item vestItem;Slot vestSlot;Storage body;body.owner=&f.recipient;vestSlot.storage=&body;vestItem.slot=&vestSlot;vest.item=&vestItem;f.targetStorage.owner=&vest;
auto t=f.Job();assert(t.Tick(0)==PENDING);f.Move();f.source.last->OnComplete();assert(t.Tick(1)==SUCCEEDED);
vestSlot.storage=&f.targetStorage;assert(!ERR_InfantryAmmoTransfer::StoredUnder(&vest,&f.recipient));}
}
'''
# Brace tiny fixture statements to retain -Werror without suppressing useful compiler diagnostics.
main=re.sub(r'if\((invalid|changed|failure)==(\d+)\)([^;{}]+);',r'if(\1==\2){\3;}',main)
main=main.replace('for(int i=0;i<10;i++)t.Tick(8200+i);','for(int i=0;i<10;i++){t.Tick(8200+i);}')
run(pre+translated_reservation()+code+'\n'.join(callbacks)+main)
print('PASS: actual bounded empty-return and finite delivery; phase-isolated callbacks, exact ownership proof, stop quarantine and original actor/vehicle boundaries')
@@ -0,0 +1,314 @@
enum ERR_InfantryAmmoTransferState { FAILED = -1, PENDING, SUCCEEDED }
class ERR_InfantryAmmoCallback : ScriptedInventoryOperationCallback
{
ERR_InfantryAmmoTransfer Transfer;
bool Returning;
protected override void OnComplete() { if (Transfer) Transfer.Acknowledge(true, Returning); }
protected override void OnFailed() { if (Transfer) Transfer.Acknowledge(false, Returning); }
protected override void OnAllComplete() { if (Transfer) Transfer.Acknowledge(true, Returning); }
protected override void OnAllFailed() { if (Transfer) Transfer.Acknowledge(false, Returning); }
}
// One exact physical magazine movement. The caller owns HQ authorization and
// the spent-item reservation; this leaf never selects tasks or retries a move.
class ERR_InfantryAmmoTransfer
{
protected ERR_GameMode m_Mode;
protected Vehicle m_Donor;
protected SCR_AIGroup m_DonorGroup;
protected IEntity m_Recipient;
protected SCR_AIGroup m_RecipientGroup;
protected IEntity m_Magazine;
protected IEntity m_Driver;
protected ERR_ForceRecord m_DonorRecord;
protected ERR_ForceRecord m_RecipientRecord;
protected InventoryStorageManagerComponent m_Source;
protected InventoryStorageManagerComponent m_Destination;
protected BaseInventoryStorageComponent m_Storage;
protected InventoryStorageSlot m_OriginalSlot;
protected BaseInventoryStorageComponent m_OriginalStorage;
protected ref ERR_InfantryAmmoCallback m_Callback;
protected bool m_RecycleEmpty;
protected bool m_LauncherMagazine;
protected IEntity m_ReturnMagazine;
protected BaseInventoryStorageComponent m_ReturnStorage;
protected ref ERR_InfantryAmmoCallback m_ReturnCallback;
protected bool m_ReturnSubmitted;
protected bool m_ReturnAccepted;
protected bool m_ReturnDone;
protected int m_ReturnAcknowledgement;
protected float m_PhaseStarted = -1;
protected bool m_Submitted;
protected bool m_Accepted;
protected int m_Acknowledgement;
protected float m_Started = -1;
protected ERR_InfantryAmmoTransferState m_State = ERR_InfantryAmmoTransferState.PENDING;
protected string m_Reason;
void ERR_InfantryAmmoTransfer(ERR_GameMode mode, Vehicle donorVehicle, SCR_AIGroup donorGroup, IEntity recipientActor, SCR_AIGroup recipientGroup, IEntity exactMagazine, bool recycleEmpty = false, bool launcherMagazine = false)
{
m_RecycleEmpty = recycleEmpty; m_LauncherMagazine = launcherMagazine;
m_Mode = mode; m_Donor = donorVehicle; m_DonorGroup = donorGroup;
m_Recipient = recipientActor; m_RecipientGroup = recipientGroup; m_Magazine = exactMagazine;
}
static bool Compatible(IEntity actor, IEntity exactMagazine, bool launcherMagazine = false)
{
if (!actor || !exactMagazine) return false;
BaseMagazineComponent magazine = BaseMagazineComponent.Cast(exactMagazine.FindComponent(BaseMagazineComponent));
BaseWeaponManagerComponent manager = BaseWeaponManagerComponent.Cast(actor.FindComponent(BaseWeaponManagerComponent));
if (!magazine || !manager || !magazine.GetMagazineWell() || magazine.GetAmmoCount() <= 0 || magazine.GetMaxAmmoCount() <= 0 || magazine.GetAmmoCount() > magazine.GetMaxAmmoCount()) return false;
if (launcherMagazine && magazine.IsUsed()) return false;
array<IEntity> weapons = {}; manager.GetWeaponsList(weapons);
if (weapons.IsEmpty() || weapons.Count() > 8) return false;
foreach (IEntity rifle : weapons)
{
if (!rifle) continue;
BaseWeaponComponent weapon = BaseWeaponComponent.Cast(rifle.FindComponent(BaseWeaponComponent));
if (!weapon || weapon.GetOwner() != rifle) continue;
EWeaponType kind = weapon.GetWeaponType();
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;
array<BaseMuzzleComponent> muzzles = {}; weapon.GetMuzzlesList(muzzles);
if (muzzles.Count() > 4) continue;
foreach (BaseMuzzleComponent muzzle : muzzles)
{
if (!muzzle || !muzzle.GetMagazineWell()) continue;
EMuzzleType muzzleKind = muzzle.GetMuzzleType();
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;
// 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;
SCR_MuzzleInMagComponent reloadable = SCR_MuzzleInMagComponent.Cast(muzzle);
if (reloadable && !reloadable.CanBeReloaded()) 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;
}
if (magazine.GetMagazineWell().Type() != muzzle.GetMagazineWell().Type()) continue;
}
// Same direction as the stock magazine inventory predicate: a magazine's
// derived well must inherit the actual carried small-arms muzzle's well.
if (magazine.GetMagazineWell().IsInherited(muzzle.GetMagazineWell().Type())) return true;
}
}
return false;
}
static bool StoredUnder(IEntity entity, IEntity root)
{
for (int depth = 0; entity && depth < 16; depth++)
{
if (entity == root) return true;
InventoryItemComponent item = InventoryItemComponent.Cast(entity.FindComponent(InventoryItemComponent));
if (item && item.GetParentSlot())
{
BaseInventoryStorageComponent storage = item.GetParentSlot().GetStorage();
if (!storage) return false;
entity = storage.GetOwner();
}
else entity = entity.GetParent();
}
return false;
}
protected bool ManagedStorage(InventoryStorageManagerComponent inventory, BaseInventoryStorageComponent storage, IEntity owner)
{
if (!inventory || !storage || inventory.GetOwner() != owner || !StoredUnder(storage.GetOwner(), owner)) return false;
array<BaseInventoryStorageComponent> storages = {};
inventory.GetStorages(storages, EStoragePurpose.PURPOSE_DEPOSIT);
return storages.Count() <= 64 && storages.Contains(storage);
}
protected int Ownership(InventoryStorageManagerComponent inventory, IEntity exact = null)
{
if (!exact) exact = m_Magazine;
if (!inventory || !exact) return -1;
array<IEntity> items = {}; inventory.GetItems(items, EStoragePurpose.PURPOSE_DEPOSIT);
if (items.Count() > 512) return -1;
if (items.Contains(exact)) return 1;
return 0;
}
protected bool Proof(IEntity exact, InventoryStorageManagerComponent source, InventoryStorageManagerComponent destination, BaseInventoryStorageComponent storage, IEntity owner, int acknowledgement)
{
if (!exact || acknowledgement != 1 || Ownership(source, exact) != 0 || Ownership(destination, exact) != 1 || !ManagedStorage(destination, storage, owner)) return false;
InventoryItemComponent item = InventoryItemComponent.Cast(exact.FindComponent(InventoryItemComponent));
return item && item.GetParentSlot() && item.GetParentSlot().GetStorage() == storage;
}
protected bool PhysicalProof()
{
return Proof(m_Magazine, m_Source, m_Destination, m_Storage, m_Recipient, m_Acknowledgement);
}
// Terminal negative callback is settled only with exact unchanged source
// custody. Native false without a callback still retains all uncertainty.
protected bool RefusedAtSource()
{
if (!m_Submitted || m_Acknowledgement != -1 || !m_Magazine || !m_Donor || !m_Recipient) return false;
if (!m_OriginalSlot || !m_OriginalStorage) return false;
if (InventoryStorageManagerComponent.Cast(m_Donor.FindComponent(InventoryStorageManagerComponent)) != m_Source) return false;
if (InventoryStorageManagerComponent.Cast(m_Recipient.FindComponent(InventoryStorageManagerComponent)) != m_Destination) return false;
if (Ownership(m_Source) != 1 || Ownership(m_Destination) != 0) return false;
if (!ManagedStorage(m_Source, m_OriginalStorage, m_Donor)) return false;
InventoryItemComponent item = InventoryItemComponent.Cast(m_Magazine.FindComponent(InventoryItemComponent));
if (!item || item.GetParentSlot() != m_OriginalSlot) return false;
return m_OriginalSlot.GetStorage() == m_OriginalStorage && m_OriginalSlot.GetAttachedEntity() == m_Magazine;
}
protected bool ReturnProof()
{
return Proof(m_ReturnMagazine, m_Destination, m_Source, m_ReturnStorage, m_Donor, m_ReturnAcknowledgement);
}
void Acknowledge(bool success, bool returning = false)
{
if (returning)
{
if (m_ReturnAcknowledgement == 0)
{
if (success) m_ReturnAcknowledgement = 1;
else m_ReturnAcknowledgement = -1;
}
return;
}
if (m_Acknowledgement != 0) return;
if (success) m_Acknowledgement = 1;
else m_Acknowledgement = -1;
}
// Both native operations remain observable after Stop. Returning an empty
// magazine never proves delivery or grants authority for a later full one.
bool Unresolved()
{
if ((m_ReturnAccepted || (m_LauncherMagazine && m_ReturnSubmitted)) && !m_ReturnDone && m_ReturnMagazine && m_ReturnAcknowledgement >= 0 && !ReturnProof()) return true;
if (m_LauncherMagazine && m_Submitted) return m_Magazine && !PhysicalProof() && !RefusedAtSource();
if (!m_Accepted || !m_Magazine || m_Acknowledgement < 0) return false;
return !PhysicalProof();
}
// Any native submission consumes this incoming identity at the coordinator,
// even when only the empty return was attempted and delivery never started.
bool Submitted() { return m_Submitted || m_ReturnSubmitted; }
string Reason() { return m_Reason; }
protected ERR_InfantryAmmoTransferState Fail(string reason)
{
if (m_State == ERR_InfantryAmmoTransferState.PENDING) { m_State = ERR_InfantryAmmoTransferState.FAILED; m_Reason = reason; }
return m_State;
}
void Stop() { Fail("HQ authorization stopped; an accepted native move cannot be cancelled"); }
protected bool Context()
{
if (!Replication.IsServer() || SCR_Global.IsEditMode() || !m_Mode || m_Mode != ERR_GameMode.Get() || !m_Mode.Active() || !m_Mode.Forces()) return false;
if (!m_Donor || !ERR_Actors.Alive(m_Donor) || !m_DonorGroup || !m_RecipientGroup || m_DonorGroup == m_RecipientGroup) return false;
if (!ERR_EnemyEquipmentRules.Eligible(m_Mode, m_RecipientGroup, m_Recipient) || CompartmentAccessComponent.GetVehicleIn(m_Recipient)) return false;
if (m_DonorGroup.IsSlave() || m_RecipientGroup.IsSlave() || m_DonorGroup.GetPlayerCount(true) > 0 || m_DonorGroup.GetFaction() != m_Mode.EnemyFaction()) return false;
if (SCR_FactionManager.SGetFaction(m_Donor) != m_Mode.EnemyFaction() || SCR_FactionManager.SGetFaction(m_Recipient) != m_Mode.EnemyFaction()) return false;
ERR_ForceRecord donorRecord = m_Mode.Forces().FindGroup(m_DonorGroup);
ERR_ForceRecord recipientRecord = m_Mode.Forces().FindGroup(m_RecipientGroup);
if (!donorRecord || !recipientRecord || donorRecord.Group != m_DonorGroup || recipientRecord.Group != m_RecipientGroup || !donorRecord.Physical || !recipientRecord.Physical || donorRecord.Eliminated || recipientRecord.Eliminated) return false;
if (m_DonorRecord && (m_DonorRecord != donorRecord || m_RecipientRecord != recipientRecord)) return false;
SCR_AIGroupUtilityComponent utility = m_DonorGroup.GetGroupUtilityComponent();
if (!utility || !utility.IsMilitary() || !utility.m_VehicleMgr || !utility.m_VehicleMgr.FindVehicle(m_Donor)) return false;
if (ERR_ConvoyRules.Captured(m_Donor, m_DonorGroup)) return false;
SCR_AIVehicleUsageComponent usage = SCR_AIVehicleUsageComponent.Cast(m_Donor.FindComponent(SCR_AIVehicleUsageComponent));
if (!usage || !usage.GetPilotCompartmentSlot()) return false;
IEntity driver = usage.GetPilotCompartmentSlot().GetOccupant();
if (!ERR_EnemyEquipmentRules.Eligible(m_Mode, m_DonorGroup, driver) || !ERR_ConvoyRules.PartOf(CompartmentAccessComponent.GetVehicleIn(driver), m_Donor)) return false;
if (SCR_FactionManager.SGetFaction(driver) != m_Mode.EnemyFaction() || (m_Driver && driver != m_Driver)) return false;
Physics physics = m_Donor.GetPhysics();
if (!physics) return false;
float speed = physics.GetVelocity().Length();
float angular = physics.GetAngularVelocity().Length();
float distance = vector.Distance(m_Donor.GetOrigin(), m_Recipient.GetOrigin());
if (!(speed >= 0 && speed <= 0.25 && angular >= 0 && angular <= 0.1 && distance >= 0 && distance <= 15)) return false;
if (distance > 4 && !m_Donor.FindComponent(VehicleHelicopterSimulation)) return false;
if (!Compatible(m_Recipient, m_Magazine, m_LauncherMagazine)) return false;
m_Driver = driver; m_DonorRecord = donorRecord; m_RecipientRecord = recipientRecord;
return true;
}
// One optional capacity recovery, using only an unused exact empty identity.
protected bool ReturnEmpty()
{
if (!m_RecycleEmpty || m_ReturnSubmitted || m_ReturnMagazine) return false;
BaseMagazineComponent incoming = BaseMagazineComponent.Cast(m_Magazine.FindComponent(BaseMagazineComponent));
if (!incoming || !incoming.GetMagazineWell()) return false;
array<IEntity> items = {}; m_Destination.GetItems(items, EStoragePurpose.PURPOSE_DEPOSIT);
if (items.Count() > 128) return false;
foreach (IEntity candidate : items)
{
if (!candidate || candidate == m_Magazine) continue;
BaseMagazineComponent empty = BaseMagazineComponent.Cast(candidate.FindComponent(BaseMagazineComponent));
if (!empty || empty.IsUsed() || empty.GetAmmoCount() != 0 || empty.GetMaxAmmoCount() <= 0
|| !empty.GetMagazineWell() || empty.GetMagazineWell().Type() != incoming.GetMagazineWell().Type()) continue;
if (Ownership(m_Destination, candidate) != 1 || Ownership(m_Source, candidate) != 0) continue;
InventoryItemComponent item = InventoryItemComponent.Cast(candidate.FindComponent(InventoryItemComponent));
if (!item || !item.GetParentSlot() || !ManagedStorage(m_Destination, item.GetParentSlot().GetStorage(), m_Recipient)) continue;
BaseInventoryStorageComponent storage = m_Source.FindStorageForItem(candidate, EStoragePurpose.PURPOSE_DEPOSIT);
if (!ManagedStorage(m_Source, storage, m_Donor) || !m_Destination.CanMoveItemToStorage(candidate, storage, -1)) continue;
m_ReturnMagazine = candidate; m_ReturnStorage = storage;
m_ReturnCallback = new ERR_InfantryAmmoCallback(); m_ReturnCallback.Transfer = this; m_ReturnCallback.Returning = true;
m_ReturnSubmitted = true;
m_ReturnAccepted = m_Destination.TryMoveItemToStorage(candidate, storage, -1, m_ReturnCallback);
if (m_ReturnAcknowledgement == 1) m_ReturnAccepted = true;
return true;
}
return false;
}
ERR_InfantryAmmoTransferState Tick(float now)
{
if (m_State != ERR_InfantryAmmoTransferState.PENDING) return m_State;
if (!ERR_ForceRules.Finite(now) || now < 0) return Fail("invalid transaction clock");
if (m_Started < 0) { m_Started = now; m_PhaseStarted = now; }
if (now < m_Started || now < m_PhaseStarted || now - m_Started >= 16000 || now - m_PhaseStarted >= 8000) return Fail("native magazine move deadline expired");
if (!Context()) return Fail("original owned groups, vehicle, driver, recipient, proximity or compatible weapon unavailable");
if (m_ReturnSubmitted && !m_ReturnDone)
{
if (!m_ReturnAccepted || m_ReturnAcknowledgement < 0) return Fail("native empty return rejected; neither identity will be retried");
if (!ReturnProof()) return ERR_InfantryAmmoTransferState.PENDING;
m_ReturnDone = true; m_PhaseStarted = now;
// Separate slow tick allows loss of HQ authority to stop before delivery.
return ERR_InfantryAmmoTransferState.PENDING;
}
if (!m_Submitted)
{
m_Source = InventoryStorageManagerComponent.Cast(m_Donor.FindComponent(InventoryStorageManagerComponent));
m_Destination = InventoryStorageManagerComponent.Cast(m_Recipient.FindComponent(InventoryStorageManagerComponent));
if (!m_Source || !m_Destination || m_Source == m_Destination || Ownership(m_Source) != 1 || Ownership(m_Destination) != 0) return Fail("exact magazine is not exclusive donor stock");
InventoryItemComponent item = InventoryItemComponent.Cast(m_Magazine.FindComponent(InventoryItemComponent));
if (!item || !item.GetParentSlot() || !ManagedStorage(m_Source, item.GetParentSlot().GetStorage(), m_Donor)) return Fail("exact source magazine storage does not belong to original donor");
m_Storage = m_Destination.FindStorageForItem(m_Magazine, EStoragePurpose.PURPOSE_DEPOSIT);
if (m_Storage && !ManagedStorage(m_Destination, m_Storage, m_Recipient)) return Fail("recipient storage ownership unavailable");
bool capacity = m_Storage && m_Source.CanMoveItemToStorage(m_Magazine, m_Storage, -1);
if (!ERR_InfantryAmmoAccess.Clear(m_Donor, m_Recipient)) return Fail("no unobstructed physical reach to an authored donor storage access point");
if (!capacity)
{
if (!m_ReturnDone && ReturnEmpty()) return ERR_InfantryAmmoTransferState.PENDING;
return Fail("recipient has no native compatible available storage after at most one empty return");
}
if (m_LauncherMagazine)
{
m_OriginalSlot = item.GetParentSlot(); m_OriginalStorage = m_OriginalSlot.GetStorage();
if (m_OriginalSlot.GetAttachedEntity() != m_Magazine) return Fail("launcher source slot lacks reciprocal item proof");
}
m_Callback = new ERR_InfantryAmmoCallback(); m_Callback.Transfer = this;
m_Submitted = true;
m_Accepted = m_Source.TryMoveItemToStorage(m_Magazine, m_Storage, -1, m_Callback);
if (m_Acknowledgement == 1) m_Accepted = true;
if (!m_Accepted) return Fail("native magazine move rejected; exact item will not be retried");
}
// Native callbacks may synchronously cancel this leaf or revoke ownership.
if (m_State != ERR_InfantryAmmoTransferState.PENDING) return m_State;
if (m_LauncherMagazine && !Context()) return Fail("magazine custody authority changed during native submission");
if (PhysicalProof())
{
m_State = ERR_InfantryAmmoTransferState.SUCCEEDED;
m_Reason = "native callback and exact destination storage confirm one physical magazine transferred";
return m_State;
}
if (m_Acknowledgement < 0) return Fail("native magazine move failed");
return ERR_InfantryAmmoTransferState.PENDING;
}
}
@@ -0,0 +1,12 @@
check: /tmp/err-policy-psipyug_/check.cpp:498: int main(): Assertion `t.Tick(0)==FAILED&&t.Submitted()&&!t.Unresolved()&&f.source.calls==1' failed.
Traceback (most recent call last):
File "/tmp/erri-supply-gameplay-1552/tests/test_infantry_ammo_transfer.py", line 352, in <module>
run(pre+translated_reservation()+code+'\n'.join(callbacks)+main)
~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jakub/devel/escapistsrereintroduced/tests/policy_harness.py", line 14, in run
subprocess.run([str(p/'check')],check=True)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.14/subprocess.py", line 578, in run
raise CalledProcessError(retcode, process.args,
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['/tmp/err-policy-psipyug_/check']' died with <Signals.SIGABRT: 6>.
@@ -0,0 +1,17 @@
{
"finding": "Ordinary rifle-ammunition Tick can overwrite FAILED with SUCCEEDED after synchronous native callback processing cancels the leaf; the launcher-only post-native guards also allow captured donor/inactive mission/changed recipient ownership to pass the native success boundary.",
"trigger": "Initial Context valid, TryMoveItemToStorage enters native boundary; Stop or ownership invalidation occurs inline while exact item moves and callback completes; current rifle branch skips both guards and PhysicalProof overwrites state. Actual coordinator accounts this as delivery on its next transfer tick.",
"fix": "Apply already-existing post-native terminal state and Context checks to rifle mode as well. Two-line production change. No changes to callback acceptance, uncertainty policy, finite stock or retries.",
"tests": {
"baseline": "Full actual-method harness compiles and assertion-fails; red.log",
"proposal": "Full existing plus added rifle cases pass; green.log",
"capture_guard_mutation": {
"exit": 1,
"compiled_runtime_assertion": true,
"log": "capture-guard-mutation.log"
}
},
"apply_check_exit": 0,
"limits": "Native inventory boundary doubled; no engine run. Source/test proposals only; original and successor not edited. Existing pre- and post-submission callbacks/late proof remain readable and identities remain spent.",
"integration": "Apply fix.patch (one production file and existing test). No new test registration."
}
@@ -0,0 +1,36 @@
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryAmmoTransfer.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryAmmoTransfer.c
@@ -300,8 +300,8 @@
if (!m_Accepted) return Fail("native magazine move rejected; exact item will not be retried");
}
// 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 (m_State != ERR_InfantryAmmoTransferState.PENDING) return m_State;
+ if (!Context()) return Fail("magazine custody authority changed during native submission");
if (PhysicalProof())
{
m_State = ERR_InfantryAmmoTransferState.SUCCEEDED;
--- a/tests/test_infantry_ammo_transfer.py
+++ b/tests/test_infantry_ammo_transfer.py
@@ -150,6 +150,20 @@
rifle.parent=&recipient;loaded.parent=&rifle;loaded.magazine=&empty;empty.owner=&loaded;empty.well=&well;empty.rounds=0;muzzle.loaded=&empty;loaded.item=&loadedItem;loadedItem.slot=&loadedSlot;loadedSlot.container=&muzzle;loadedSlot.attached=&loaded;}
};
int main(){
+ // Rifle moves must obey the same irreversible cancellation boundary as launchers.
+ // Native inventory processing may synchronously invoke mission/ownership events.
+ for(int loss=0;loss<4;loss++){Fixture f;auto t=f.Job();
+ f.source.effect=[&](){if(loss==0)t.Stop();if(loss==1)f.truck.captured=true;if(loss==2)f.mode.active=false;if(loss==3)f.recipient.group=&f.otherGroup;f.Move();};
+ f.source.callback=1;assert(t.Tick(0)==FAILED&&t.Submitted()&&!t.Unresolved()&&f.source.calls==1);
+ assert(t.Tick(1)==FAILED&&f.source.calls==1);}
+ // Callback without custody after cancellation retains uncertainty until the
+ // original physical identity arrives, without resurrecting success or retry.
+ {Fixture f;auto t=f.Job();f.source.effect=[&](){t.Stop();};f.source.callback=1;
+ assert(t.Tick(0)==FAILED&&t.Submitted()&&t.Unresolved());f.Move();assert(!t.Unresolved());assert(t.Tick(1)==FAILED&&f.source.calls==1);}
+ // Coordinator must never book a cancelled native rifle transfer as delivery.
+ {SupplyFixture f;f.source.effect=[&](){f.c.m_Move->Stop();f.Move();};f.source.callback=1;
+ f.c.Transfer(0);assert(f.c.m_Spent.Count()==1&&f.c.m_Delivered==0);f.c.Transfer(1000);
+ assert(f.c.finishes==1&&f.c.m_Delivered==0&&f.c.m_Spent.Count()==1&&f.source.calls==1);}
// Failed callback settles only exact original unchanged custody; unknown never does.
{LauncherFixture f;f.source.callback=2;f.c.Transfer(0);assert(f.c.m_Move->Submitted()&&!f.c.m_Move->Unresolved()&&f.c.m_Spent.Count()==1);f.c.Transfer(1000);assert(f.source.calls==1&&f.c.m_Spent.Count()==1);}
for(int bad=0;bad<7;bad++){LauncherFixture f;f.c.Transfer(0);assert(f.c.m_Move->Unresolved());
@@ -0,0 +1 @@
PASS: actual bounded empty-return and finite delivery; phase-isolated callbacks, exact ownership proof, stop quarantine and original actor/vehicle boundaries
@@ -0,0 +1,12 @@
check: /tmp/err-policy-ao8r4a81/check.cpp:498: int main(): Assertion `t.Tick(0)==FAILED&&t.Submitted()&&!t.Unresolved()&&f.source.calls==1' failed.
Traceback (most recent call last):
File "/tmp/erri-supply-gameplay-1552/tests/test_infantry_ammo_transfer.py", line 352, in <module>
run(pre+translated_reservation()+code+'\n'.join(callbacks)+main)
~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jakub/devel/escapistsrereintroduced/tests/policy_harness.py", line 14, in run
subprocess.run([str(p/'check')],check=True)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.14/subprocess.py", line 578, in run
raise CalledProcessError(retcode, process.args,
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['/tmp/err-policy-ao8r4a81/check']' died with <Signals.SIGABRT: 6>.
@@ -0,0 +1,12 @@
check: /tmp/err-policy-f7degjbp/check.cpp:498: int main(): Assertion `t.Tick(0)==FAILED&&t.Submitted()&&!t.Unresolved()&&f.source.calls==1' failed.
Traceback (most recent call last):
File "/tmp/erri-supply-gameplay-1552/tests/test_infantry_ammo_transfer.py", line 352, in <module>
run(pre+translated_reservation()+code+'\n'.join(callbacks)+main)
~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jakub/devel/escapistsrereintroduced/tests/policy_harness.py", line 14, in run
subprocess.run([str(p/'check')],check=True)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.14/subprocess.py", line 578, in run
raise CalledProcessError(retcode, process.args,
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['/tmp/err-policy-f7degjbp/check']' died with <Signals.SIGABRT: 6>.
@@ -0,0 +1 @@
PASS: actual bounded empty-return and finite delivery; phase-isolated callbacks, exact ownership proof, stop quarantine and original actor/vehicle boundaries
@@ -0,0 +1,31 @@
{
"decision": "Approved: terminal-state and current authority checks correctly cover ordinary rifle transfers after synchronous native submission.",
"source_sha256": "99fa26beb0c0869d0cd694500f3bfc0705c2c1cf674dea90d98fb93a59570ef7",
"test_sha256": "0d2ebe4d5c18a4187745784013d07df621b8495967d0c01f39bd5a68ae574d26",
"patch_sha256": "6b35a464a3411feff535995fd4c7ac4694f7b1b212b364b1395062ee6b0dc667",
"tests": {
"baseline": {
"exit": 1,
"compiled_runtime_assertion": true
},
"proposal": {
"exit": 0,
"compiled_runtime_assertion": false
},
"capture-mutation": {
"exit": 1,
"compiled_runtime_assertion": true
}
},
"reviewed_invariants": [
"Stop remains irreversible when the exact physical item arrives inline; success cannot overwrite FAILED.",
"Captured donor, inactive mission and changed recipient ownership cannot qualify delivery after native call.",
"Actual coordinator counts zero deliveries for cancelled transfer while retaining one spent identity and one native submission.",
"Accepted unresolved move remains observable after Stop; delayed exact physical arrival settles uncertainty without resurrecting success.",
"Launcher behavior, native rejection policy, stock/refund/retry policy unchanged."
],
"limits": [
"Native inventory boundary is modeled; no physical transfer or multiplayer qualification claimed."
],
"target_or_native_changes": false
}
@@ -0,0 +1,12 @@
check: /tmp/err-policy-sn_6aky6/check.cpp:498: int main(): Assertion `t.Tick(0)==FAILED&&t.Submitted()&&!t.Unresolved()&&f.source.calls==1' failed.
Traceback (most recent call last):
File "/tmp/erri-supply-gameplay-1552/tests/test_infantry_ammo_transfer.py", line 352, in <module>
run(pre+translated_reservation()+code+'\n'.join(callbacks)+main)
~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jakub/devel/escapistsrereintroduced/tests/policy_harness.py", line 14, in run
subprocess.run([str(p/'check')],check=True)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.14/subprocess.py", line 578, in run
raise CalledProcessError(retcode, process.args,
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['/tmp/err-policy-sn_6aky6/check']' died with <Signals.SIGABRT: 6>.
@@ -0,0 +1,5 @@
{
"addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryAmmoTransfer.c": "99fa26beb0c0869d0cd694500f3bfc0705c2c1cf674dea90d98fb93a59570ef7",
"tests/test_infantry_ammo_transfer.py": "0d2ebe4d5c18a4187745784013d07df621b8495967d0c01f39bd5a68ae574d26",
"fix.patch": "6b35a464a3411feff535995fd4c7ac4694f7b1b212b364b1395062ee6b0dc667"
}
@@ -0,0 +1,353 @@
"""Execute the actual finite native magazine transaction; engine behavior remains separate."""
from pathlib import Path
import os,re
from policy_harness import ROOT,run,body
from test_finite_magazine_reservation import translated_reservation
p=Path(os.environ.get('ERR_INFANTRY_TRANSFER_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryAmmoTransfer.c'))
s=p.read_text()
for forbidden in ('TrySpawn','DeleteEntity','SetOrigin','SetTransform','TryInsert','TryRemove','SetAmmoCount'):
assert forbidden not in s,forbidden
callback=s[s.index('class ERR_InfantryAmmoCallback'):s.index('// One exact physical')]
code=s[s.index('class ERR_InfantryAmmoTransfer\n'):]
code=re.sub(r'//[^\n]*','',code).replace('protected ','').replace('class ','struct ')
for src,dst in {
'm_Donor.FindComponent(VehicleHelicopterSimulation)':'m_Donor->helicopter',
'InventoryItemComponent.Cast(loaded.GetOwner().FindComponent(InventoryItemComponent))':'loaded->GetOwner()->item',
'BaseMagazineComponent.Cast(exactMagazine.FindComponent(BaseMagazineComponent))':'exactMagazine->magazine',
'BaseWeaponManagerComponent.Cast(actor.FindComponent(BaseWeaponManagerComponent))':'actor->weapons',
'BaseWeaponComponent.Cast(rifle.FindComponent(BaseWeaponComponent))':'rifle->weapon',
'InventoryItemComponent.Cast(entity.FindComponent(InventoryItemComponent))':'entity->item',
'InventoryItemComponent.Cast(m_Magazine.FindComponent(InventoryItemComponent))':'m_Magazine->item',
'InventoryItemComponent.Cast(exact.FindComponent(InventoryItemComponent))':'exact->item',
'InventoryItemComponent.Cast(candidate.FindComponent(InventoryItemComponent))':'candidate->item',
'BaseMagazineComponent.Cast(m_Magazine.FindComponent(BaseMagazineComponent))':'m_Magazine->magazine',
'BaseMagazineComponent.Cast(candidate.FindComponent(BaseMagazineComponent))':'candidate->magazine',
'SCR_AIVehicleUsageComponent.Cast(m_Donor.FindComponent(SCR_AIVehicleUsageComponent))':'m_Donor->usage',
'InventoryStorageManagerComponent.Cast(m_Donor.FindComponent(InventoryStorageManagerComponent))':'m_Donor->inventory',
'InventoryStorageManagerComponent.Cast(m_Recipient.FindComponent(InventoryStorageManagerComponent))':'m_Recipient->inventory',
}.items():code=code.replace(src,dst)
code=code.replace('SCR_MuzzleInMagComponent.Cast(muzzle)', '(muzzle->inMag ? muzzle : null)')
code=code.replace('ref ERR_InfantryAmmoCallback','ERR_InfantryAmmoCallback*').replace('array<','Array<').replace('foreach (','for (').replace('null','nullptr')
code=re.sub(r'\bERR_GameMode (?=\w)','ERR_GameMode* ',code)
code=code.replace('void ERR_InfantryAmmoTransfer(','ERR_InfantryAmmoTransfer(').replace('EWeaponType kind','int kind').replace('EMuzzleType muzzleKind','int muzzleKind')
for name in ('ERR_InfantryAmmoTransferState','ERR_InfantryAmmoAccess','ERR_GameMode','ERR_EnemyEquipmentRules','ERR_ForceRules','Replication','SCR_Global','ERR_Actors','SCR_FactionManager','ERR_ConvoyRules','CompartmentAccessComponent','EWeaponType','EMuzzleType','EStoragePurpose','vector'):
code=code.replace(name+'.',name+'::')
for name in ('m_Mode','m_Donor','m_DonorGroup','m_Recipient','m_RecipientGroup','m_Source','m_Destination','m_Storage','m_OriginalSlot','m_Callback','magazine','reloadable','loaded','loadedItem','loadedSlot','manager','weapon','muzzle','entity','item','storage','inventory','incoming','empty','source','destination','m_ReturnCallback','donorRecord','recipientRecord','utility','usage','physics'):
code=re.sub(r'\b'+name+r'\.',name+'->',code)
code=code.replace('GetMagazineWell().','GetMagazineWell()->').replace('GetParentSlot().','GetParentSlot()->').replace('Forces().','Forces()->').replace('m_VehicleMgr.','m_VehicleMgr->').replace('GetPilotCompartmentSlot().','GetPilotCompartmentSlot()->')
code=re.sub(r'\b(bool|int|Vehicle|IEntity|SCR_AIGroup|ERR_ForceRecord|InventoryStorageSlot|InventoryStorageManagerComponent|BaseInventoryStorageComponent|ERR_GameMode\*|ERR_InfantryAmmoCallback\*) (\w+);',r'\1 \2{};',code)
code=code.replace('\n}\n','\n};\n')
callback=callback.replace('class ERR_InfantryAmmoCallback : ScriptedInventoryOperationCallback','').strip()[1:-1]
callbacks=[]
for name in ('OnComplete','OnFailed','OnAllComplete','OnAllFailed'):
m=re.search(r'protected override void '+name+r'\(\) (\{[^\n]+\})',callback)
callbacks.append('void ERR_InfantryAmmoCallback::'+name+'() '+m[1].replace('Transfer.','Transfer->'))
# Integrate the actual coordinator transfer selector with this actual leaf.
coordinator_path=Path(os.environ.get('ERR_INFANTRY_RESUPPLY_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c'))
transfer=body(coordinator_path,'protected void Transfer(float now)').replace('protected ','').replace('null','nullptr').replace('foreach (','for (')
transfer=transfer.replace('ERR_HQAmmoSnapshot local','ERR_HQAmmoSnapshot* local')
for name in ('ERR_Actors','ERR_HQAmmoSampler','ERR_InfantryAmmoTransfer','vector'):
transfer=transfer.replace(name+'.',name+'::')
transfer=transfer.replace('ERR_FiniteMagazineReservation reservation', 'ERR_FiniteMagazineReservation* reservation').replace('new ERR_FiniteMagazineReservation(m_Stock, m_Spent,', 'new ERR_FiniteMagazineReservation(&m_Stock, &m_Spent,')
for name in ('m_Move','actor','m_Truck','m_Recipient','local','reservation'):
transfer=transfer.replace(name+'.',name+'->')
transfer=transfer.replace('Entity.GetOrigin()', 'Entity->GetOrigin()')
pre=r'''
#include <cassert>
#include <cmath>
#include <string>
#include <vector>
#include <algorithm>
#include <limits>
#include <functional>
using string=std::string;
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;int Count(){return this->size();}bool IsEmpty(){return this->empty();}void Insert(T t){this->push_back(t);}void RemoveItem(T t){this->erase(std::remove(this->begin(),this->end(),t),this->end());}bool Contains(T t){return std::find(this->begin(),this->end(),t)!=this->end();}};
struct vector{float x=0,y=0,z=0;float Length(){return std::sqrt(x*x+y*y+z*z);}static float DistanceXZ(vector a,vector b){return std::hypot(a.x-b.x,a.z-b.z);}static float Distance(vector a,vector b){return vector{a.x-b.x,a.y-b.y,a.z-b.z}.Length();}};
struct Entity;struct Group;struct Inventory;struct Storage;struct Item;struct Slot;struct Magazine;struct Weapon;struct Muzzle;struct Manager;struct Utility;struct Usage;struct Physics_;struct Record;
using IEntity=Entity*;using Vehicle=Entity*;using SCR_AIGroup=Group*;using ERR_ForceRecord=Record*;
using InventoryStorageSlot=Slot*;using InventoryStorageManagerComponent=Inventory*;using BaseInventoryStorageComponent=Storage*;using InventoryItemComponent=Item*;using BaseMagazineComponent=Magazine*;
using BaseWeaponManagerComponent=Manager*;using BaseWeaponComponent=Weapon*;using BaseMuzzleComponent=Muzzle*;using SCR_MuzzleInMagComponent=Muzzle*;
using SCR_AIGroupUtilityComponent=Utility*;using SCR_AIVehicleUsageComponent=Usage*;using Physics=Physics_*;
enum ERR_InfantryAmmoTransferState{FAILED=-1,PENDING,SUCCEEDED};
struct ERR_InfantryAmmoTransfer;
struct ScriptedInventoryOperationCallback{virtual void OnComplete(){}virtual void OnFailed(){}virtual void OnAllComplete(){}virtual void OnAllFailed(){}virtual ~ScriptedInventoryOperationCallback()=default;};
struct ERR_InfantryAmmoCallback:ScriptedInventoryOperationCallback{ERR_InfantryAmmoTransfer*Transfer=nullptr;bool Returning=false;void OnComplete()override;void OnFailed()override;void OnAllComplete()override;void OnAllFailed()override;};
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 EStoragePurpose{static const int PURPOSE_DEPOSIT=1;};
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;IEntity GetOwner(){return owner;}Well*well=nullptr;int rounds=30,capacity=30;bool used=false;bool IsUsed(){return used;}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;}Magazine*loaded=nullptr;Magazine*GetMagazine(){return loaded;}Well*well=nullptr;int kind=0;Well*GetMagazineWell(){return well;}int GetMuzzleType(){return kind;}};
struct Weapon{IEntity owner=nullptr;Array<Muzzle*>muzzles;int kind=1;IEntity GetOwner(){return owner;}int GetWeaponType(){return kind;}void GetMuzzlesList(Array<Muzzle*>&a){a=muzzles;}};
struct Manager{Array<IEntity>weapons;void GetWeaponsList(Array<IEntity>&a){a=weapons;}};
struct Storage{IEntity owner=nullptr;IEntity GetOwner(){return owner;}};
struct Slot{Storage*storage=nullptr;IEntity occupant=nullptr;Muzzle*container=nullptr;IEntity attached=nullptr;Muzzle*GetParentContainer(){return container;}IEntity GetAttachedEntity(){return attached;}Storage*GetStorage(){return storage;}IEntity GetOccupant(){return occupant;}};
struct Item{Slot*slot=nullptr;Slot*GetParentSlot(){return slot;}};
struct Physics_{vector velocity,angular;vector GetVelocity(){return velocity;}vector GetAngularVelocity(){return angular;}};
struct Usage{Slot*pilot=nullptr;Slot*GetPilotCompartmentSlot(){return pilot;}};
struct VehicleManager{IEntity vehicle=nullptr;IEntity FindVehicle(IEntity v){return vehicle==v?v:nullptr;}};
struct Utility{VehicleManager*m_VehicleMgr=nullptr;bool military=true;bool IsMilitary(){return military;}};
struct Group{int faction=1,players=0;bool slave=false;Utility*utility=nullptr;int GetPlayerCount(bool){return players;}int GetFaction(){return faction;}bool IsSlave(){return slave;}Utility*GetGroupUtilityComponent(){return utility;}};
struct Entity{bool helicopter=false;bool alive=true,awake=true,player=false,captured=false;int faction=1;Group*group=nullptr;IEntity vehicle=nullptr,parent=nullptr;
Inventory*inventory=nullptr;Magazine*magazine=nullptr;Manager*weapons=nullptr;Weapon*weapon=nullptr;Item*item=nullptr;Usage*usage=nullptr;Physics_*physics=nullptr;vector position;
IEntity GetParent(){return parent;}Physics_*GetPhysics(){return physics;}vector GetOrigin(){return position;}};
struct Record{SCR_AIGroup Group=nullptr;bool Physical=true,Eliminated=false;};
struct Ledger{Array<Record*>records;Record*FindGroup(SCR_AIGroup g){for(auto*r:records)if(r->Group==g)return r;return nullptr;}};
struct ERR_GameMode{inline static ERR_GameMode*current=nullptr;bool active=true;Ledger*ledger=nullptr;static ERR_GameMode*Get(){return current;}bool Active(){return active;}Ledger*Forces(){return ledger;}int EnemyFaction(){return 1;}};
struct Replication{inline static bool server=true;static bool IsServer(){return server;}};
struct SCR_Global{inline static bool editor=false;static bool IsEditMode(){return editor;}};
struct ERR_Actors{static bool CanFight(IEntity a){return a&&a->alive&&a->awake;}static bool Alive(IEntity a){return a&&a->alive;}};
struct SCR_FactionManager{static int SGetFaction(IEntity a){return a?a->faction:-1;}};
struct CompartmentAccessComponent{static IEntity GetVehicleIn(IEntity a){return a?a->vehicle:nullptr;}};
struct ERR_ConvoyRules{static bool Captured(Vehicle v,SCR_AIGroup g){return v->captured||g->players>0;}static bool PartOf(IEntity a,IEntity root){return a==root;}};
struct ERR_EnemyEquipmentRules{static bool Eligible(ERR_GameMode*m,SCR_AIGroup g,IEntity a){return m&&g&&a&&a->alive&&a->awake&&!a->player&&a->group==g&&g->players==0&&g->faction==m->EnemyFaction()&&g->utility&&g->utility->military;}};
struct ERR_ForceRules{static bool Finite(float n){return std::isfinite(n);}};
struct ERR_InfantryAmmoAccess{static inline bool clear=true;static inline int checks=0;static bool Clear(Vehicle donor,IEntity recipient){assert(donor&&recipient);checks++;return clear;}};
struct Inventory{IEntity owner=nullptr;Array<IEntity>items;Array<Storage*>storages;Storage*selected=nullptr;bool allow=true,accept=true;int calls=0,callback=0;ScriptedInventoryOperationCallback*last=nullptr;std::function<void()> effect;
IEntity GetOwner(){return owner;}void GetItems(Array<IEntity>&a,int purpose){assert(purpose==1);a=items;}void GetStorages(Array<Storage*>&a,int purpose){assert(purpose==1);a=storages;}
Storage*FindStorageForItem(IEntity,int purpose){assert(purpose==1);return selected;}
bool CanMoveItemToStorage(IEntity,Storage*,int slot){assert(slot==-1);return allow;}
bool TryMoveItemToStorage(IEntity,Storage*,int slot,ScriptedInventoryOperationCallback*cb){assert(slot==-1);calls++;last=cb;if(effect)effect();if(callback==1){cb->OnComplete();}if(callback==2){cb->OnFailed();}return accept;}};
'''
main=r'''
struct Fixture{ERR_GameMode mode;Ledger ledger;Group donorGroup,recipientGroup,otherGroup;Record donorRecord,recipientRecord;Entity truck,recipient,driver,spareDriver,magEntity,rifle,foreign;
VehicleManager vehicleManager;Utility donorUtility,recipientUtility;Physics_ physics;Usage usage;Slot pilot,fromSlot,toSlot;Storage sourceStorage,targetStorage;Inventory source,destination;Item item;Magazine magazine;Manager weapons;Weapon weapon;Muzzle muzzle;Well well;
Fixture(){ERR_GameMode::current=&mode;mode.ledger=&ledger;donorRecord.Group=&donorGroup;recipientRecord.Group=&recipientGroup;ledger.records={&donorRecord,&recipientRecord};
donorGroup.utility=&donorUtility;recipientGroup.utility=&recipientUtility;donorUtility.m_VehicleMgr=&vehicleManager;vehicleManager.vehicle=&truck;
truck.physics=&physics;truck.usage=&usage;usage.pilot=&pilot;pilot.occupant=&driver;driver.group=&donorGroup;driver.vehicle=&truck;spareDriver.group=&donorGroup;spareDriver.vehicle=&truck;
recipient.group=&recipientGroup;recipient.position.x=4;truck.inventory=&source;source.owner=&truck;recipient.inventory=&destination;destination.owner=&recipient;
sourceStorage.owner=&truck;targetStorage.owner=&recipient;source.storages={&sourceStorage};destination.storages={&targetStorage};destination.selected=&targetStorage;
source.items={&magEntity};magEntity.item=&item;item.slot=&fromSlot;fromSlot.storage=&sourceStorage;fromSlot.attached=&magEntity;toSlot.storage=&targetStorage;magEntity.magazine=&magazine;magazine.well=&well;
recipient.weapons=&weapons;weapons.weapons={&rifle};rifle.weapon=&weapon;weapon.owner=&rifle;weapon.muzzles={&muzzle};muzzle.well=&well;}
ERR_InfantryAmmoTransfer Job(){return ERR_InfantryAmmoTransfer(&mode,&truck,&donorGroup,&recipient,&recipientGroup,&magEntity,true);}
void Move(){source.items.RemoveItem(&magEntity);destination.items.Insert(&magEntity);item.slot=&toSlot;}
};
struct EmptyFixture:Fixture{
Entity empty,secondEmpty;Magazine emptyMag;Item emptyItem,secondItem;Slot emptyFrom,emptyTo;
EmptyFixture(){emptyMag.well=&well;emptyMag.rounds=0;empty.magazine=&emptyMag;empty.item=&emptyItem;
emptyFrom.storage=&targetStorage;emptyTo.storage=&sourceStorage;emptyItem.slot=&emptyFrom;
secondEmpty.magazine=&emptyMag;secondEmpty.item=&secondItem;secondItem.slot=&emptyFrom;
destination.items={&empty,&secondEmpty};destination.selected=nullptr;source.selected=&sourceStorage;}
void Return(bool capacity=true){destination.items.RemoveItem(&empty);source.items.Insert(&empty);emptyItem.slot=&emptyTo;if(capacity)destination.selected=&targetStorage;}
};
struct ERR_HQAmmoSnapshot{bool Complete=true,LauncherComplete=true;int Members=1,LauncherMuzzles=1,LauncherMagazines=1,LauncherRounds=0,LauncherChambers=0;bool Valid(int n){return n==Members;}int SmallArmsMuzzles=1,SmallArmsRounds=0,SmallArmsChambers=0;};
struct ERR_HQAmmoSampler{static void Inspect(IEntity actor,SCR_AIGroup group,ERR_HQAmmoSnapshot* stock){assert(actor&&actor->group==group&&stock);}};
struct SupplyTruck{IEntity Entity;SCR_AIGroup Group;};
struct SupplyRecipient{SCR_AIGroup Group;};
struct Coordinator{bool m_LauncherTrip=false;ERR_GameMode* m_Mode;SupplyTruck* m_Truck;SupplyRecipient* m_Recipient;
ERR_InfantryAmmoTransfer* m_Move=nullptr;Array<IEntity>m_Members,m_Stock,m_Spent,m_Served;IEntity m_Receiving=nullptr;
int m_Delivered=0,m_TripDelivered=0,finishes=0,audits=0;
void Finish(string){finishes++;if(m_Move)m_Move->Stop();}
void Audit(string){audits++;}
bool Contains(IEntity owner,IEntity item){return owner&&owner->inventory&&owner->inventory->items.Count()<=128&&owner->inventory->items.Contains(item);}
'''+transfer+r'''
};
struct SupplyFixture:Fixture{SupplyTruck supplyTruck;SupplyRecipient supplyRecipient;Coordinator c;
SupplyFixture():supplyTruck{&truck,&donorGroup},supplyRecipient{&recipientGroup}{
c.m_Mode=&mode;c.m_Truck=&supplyTruck;c.m_Recipient=&supplyRecipient;c.m_Members={&recipient};c.m_Stock={&magEntity};}
};
struct LauncherFixture:SupplyFixture{Entity loaded;Magazine empty;Item loadedItem;Slot loadedSlot;
LauncherFixture(){c.m_LauncherTrip=true;weapon.kind=EWeaponType::WT_GRENADELAUNCHER;muzzle.kind=EMuzzleType::MT_UGLMuzzle;
rifle.parent=&recipient;loaded.parent=&rifle;loaded.magazine=&empty;empty.owner=&loaded;empty.well=&well;empty.rounds=0;muzzle.loaded=&empty;loaded.item=&loadedItem;loadedItem.slot=&loadedSlot;loadedSlot.container=&muzzle;loadedSlot.attached=&loaded;}
};
int main(){
// Rifle moves must obey the same irreversible cancellation boundary as launchers.
// Native inventory processing may synchronously invoke mission/ownership events.
for(int loss=0;loss<4;loss++){Fixture f;auto t=f.Job();
f.source.effect=[&](){if(loss==0)t.Stop();if(loss==1)f.truck.captured=true;if(loss==2)f.mode.active=false;if(loss==3)f.recipient.group=&f.otherGroup;f.Move();};
f.source.callback=1;assert(t.Tick(0)==FAILED&&t.Submitted()&&!t.Unresolved()&&f.source.calls==1);
assert(t.Tick(1)==FAILED&&f.source.calls==1);}
// Callback without custody after cancellation retains uncertainty until the
// original physical identity arrives, without resurrecting success or retry.
{Fixture f;auto t=f.Job();f.source.effect=[&](){t.Stop();};f.source.callback=1;
assert(t.Tick(0)==FAILED&&t.Submitted()&&t.Unresolved());f.Move();assert(!t.Unresolved());assert(t.Tick(1)==FAILED&&f.source.calls==1);}
// Coordinator must never book a cancelled native rifle transfer as delivery.
{SupplyFixture f;f.source.effect=[&](){f.c.m_Move->Stop();f.Move();};f.source.callback=1;
f.c.Transfer(0);assert(f.c.m_Spent.Count()==1&&f.c.m_Delivered==0);f.c.Transfer(1000);
assert(f.c.finishes==1&&f.c.m_Delivered==0&&f.c.m_Spent.Count()==1&&f.source.calls==1);}
// Failed callback settles only exact original unchanged custody; unknown never does.
{LauncherFixture f;f.source.callback=2;f.c.Transfer(0);assert(f.c.m_Move->Submitted()&&!f.c.m_Move->Unresolved()&&f.c.m_Spent.Count()==1);f.c.Transfer(1000);assert(f.source.calls==1&&f.c.m_Spent.Count()==1);}
for(int bad=0;bad<7;bad++){LauncherFixture f;f.c.Transfer(0);assert(f.c.m_Move->Unresolved());
if(bad!=0)f.source.last->OnFailed();
if(bad==1)f.item.slot=nullptr;
if(bad==2)f.fromSlot.attached=nullptr;
if(bad==3)f.sourceStorage.owner=&f.foreign;
if(bad==4)f.destination.items.Insert(&f.magEntity);
if(bad==5)f.truck.inventory=&f.destination;
if(bad==6)f.source.items.RemoveItem(&f.magEntity);
assert(f.c.m_Move->Unresolved());f.c.m_Move->Stop();assert(f.c.m_Move->Unresolved());}
{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());}
{LauncherFixture f;assert(!ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity));assert(ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity,true));
f.c.Transfer(0);assert(f.c.m_Move&&f.c.m_Move->Submitted()&&f.c.m_Spent.Count()==1&&f.source.calls==1);
f.Move();f.source.last->OnComplete();f.c.Transfer(1000);f.c.Transfer(2000);assert(f.c.m_Delivered==1&&f.source.calls==1&&f.c.m_Served.Count()==1);}
for(int bad=0;bad<8;bad++){LauncherFixture f;if(bad==0)f.magazine.used=true;if(bad==1)f.muzzle.well=nullptr;if(bad==2)f.muzzle.disposable=true;if(bad==3)f.empty.well=nullptr;if(bad==4)f.loadedSlot.attached=nullptr;if(bad==5)f.rifle.parent=nullptr;if(bad==6)f.empty.capacity=0;if(bad==7){f.muzzle.inMag=true;f.muzzle.canReload=false;}
f.c.Transfer(0);assert(!f.c.m_Move&&f.source.calls==0&&f.c.m_Spent.IsEmpty());}
{LauncherFixture f;f.source.accept=false;f.c.Transfer(0);assert(f.c.m_Move&&f.c.m_Move->Submitted()&&f.c.m_Move->Unresolved()&&f.c.m_Spent.Count()==1);f.c.Transfer(1000);assert(f.source.calls==1);f.Move();f.source.last->OnComplete();assert(!f.c.m_Move->Unresolved());}
{LauncherFixture f;auto t=ERR_InfantryAmmoTransfer(&f.mode,&f.truck,&f.donorGroup,&f.recipient,&f.recipientGroup,&f.magEntity,false,true);
f.source.effect=[&](){t.Stop();f.Move();};f.source.callback=1;assert(t.Tick(0)==FAILED&&!t.Unresolved()&&f.source.calls==1);assert(t.Tick(1)==FAILED);}
// A capacity recovery uses one exact unused empty, separately proving both native operations.
{Fixture f;Entity empty;Magazine emptyMag;Item emptyItem;Slot emptyFrom{&f.targetStorage},emptyTo{&f.sourceStorage};
emptyMag.well=&f.well;emptyMag.rounds=0;empty.magazine=&emptyMag;empty.item=&emptyItem;emptyItem.slot=&emptyFrom;
f.destination.items={&empty};f.destination.selected=nullptr;f.source.selected=&f.sourceStorage;
auto t=f.Job();assert(t.Tick(0)==PENDING&&t.Submitted()&&t.Unresolved()&&f.destination.calls==1&&f.source.calls==0);
auto returning=f.destination.last;returning->OnComplete();assert(t.Tick(1)==PENDING&&f.source.calls==0&&t.Unresolved());
f.destination.items.clear();f.source.items.Insert(&empty);emptyItem.slot=&emptyTo;f.destination.selected=&f.targetStorage;
assert(t.Tick(2)==PENDING&&!t.Unresolved()&&f.source.calls==0);assert(t.Tick(3)==PENDING&&f.source.calls==1&&t.Unresolved());
returning->OnAllComplete();returning->OnFailed();assert(t.Tick(4)==PENDING&&t.Unresolved());
f.Move();assert(t.Tick(5)==PENDING);f.source.last->OnComplete();assert(t.Tick(6)==SUCCEEDED&&!t.Unresolved());
assert(f.destination.calls==1&&f.source.calls==1);}
// Ground coordinator reserves the incoming identity as soon as reverse native movement is submitted.
// A failed/ambiguous return never releases it to try a second empty or another incoming magazine.
for(int outcome=0;outcome<3;outcome++){EmptyFixture f;SupplyTruck truck{&f.truck,&f.donorGroup};SupplyRecipient recipient{&f.recipientGroup};Coordinator c;
c.m_Mode=&f.mode;c.m_Truck=&truck;c.m_Recipient=&recipient;c.m_Members={&f.recipient};c.m_Stock={&f.magEntity};
if(outcome==0){f.destination.accept=false;}
c.Transfer(0);assert(c.m_Move&&c.m_Move->Submitted()&&c.m_Spent.Count()==1&&c.m_Receiving==&f.recipient&&c.m_Delivered==0);
if(outcome==1){f.Return(false);f.destination.last->OnComplete();c.Transfer(1000);}
c.Transfer(outcome==2?8000:2000);assert(c.finishes==1&&c.m_Spent.Contains(&f.magEntity)&&c.m_Delivered==0&&f.destination.calls==1&&f.source.calls==0);
assert(c.m_Move->Unresolved()==(outcome==2));}
{EmptyFixture f;SupplyTruck truck{&f.truck,&f.donorGroup};SupplyRecipient recipient{&f.recipientGroup};Coordinator c;
c.m_Mode=&f.mode;c.m_Truck=&truck;c.m_Recipient=&recipient;c.m_Members={&f.recipient};c.m_Stock={&f.magEntity};
c.Transfer(0);f.Return();f.destination.last->OnComplete();c.Transfer(1000);assert(c.m_Delivered==0&&f.source.calls==0);
c.Transfer(2000);assert(c.m_Delivered==0&&f.source.calls==1);f.Move();f.source.last->OnComplete();c.Transfer(3000);c.Transfer(4000);
assert(c.m_Delivered==1&&c.m_Served.Count()==1&&c.m_Spent.Count()==1&&f.source.calls==1&&f.destination.calls==1&&f.source.items.Contains(&f.empty));}
// Default callers (including air delivery) retain the original one-way behavior.
{EmptyFixture f;ERR_InfantryAmmoTransfer t(&f.mode,&f.truck,&f.donorGroup,&f.recipient,&f.recipientGroup,&f.magEntity);
assert(t.Tick(0)==FAILED&&!t.Submitted()&&f.destination.calls==0&&f.source.calls==0);}
// No return when capacity already exists, even with carried empties.
{EmptyFixture f;f.destination.selected=&f.targetStorage;auto t=f.Job();assert(t.Tick(0)==PENDING&&f.source.calls==1&&f.destination.calls==0);}
// Unused, zero-round, exact-well, exclusive recipient storage ownership and donor space are required.
for(int invalid=0;invalid<12;invalid++){EmptyFixture f;f.destination.items={&f.empty};Well other;other.type=2;
if(invalid==0)f.emptyMag.used=true;if(invalid==1)f.emptyMag.rounds=1;if(invalid==2)f.emptyMag.rounds=-1;
if(invalid==3)f.emptyMag.capacity=0;if(invalid==4)f.emptyMag.well=&other;if(invalid==5)f.emptyMag.well=nullptr;
if(invalid==6)f.emptyItem.slot=&f.emptyTo;if(invalid==7)f.source.items.Insert(&f.empty);
if(invalid==8)f.source.selected=nullptr;if(invalid==9)f.destination.allow=false;
if(invalid==10){for(int i=0;i<129;i++)f.destination.items.Insert(&f.empty);}if(invalid==11)ERR_InfantryAmmoAccess::clear=false;
auto t=f.Job();assert(t.Tick(0)==FAILED&&!t.Submitted()&&!t.Unresolved()&&f.destination.calls==0&&f.source.calls==0);
ERR_InfantryAmmoAccess::clear=true;}
// Return proof needs exact destination slot as well as callback; no second candidate is ever moved.
{EmptyFixture f;auto t=f.Job();t.Tick(0);f.Return();f.emptyItem.slot=&f.emptyFrom;f.destination.last->OnComplete();
assert(t.Tick(1)==PENDING&&t.Unresolved()&&f.source.calls==0&&f.destination.calls==1);
f.emptyItem.slot=&f.emptyTo;assert(t.Tick(2)==PENDING&&!t.Unresolved());assert(t.Tick(3)==PENDING&&f.source.calls==1&&f.destination.calls==1);}
// Inline success callbacks override a false API result for the reverse operation, but require physical proof.
{EmptyFixture f;f.destination.accept=false;f.destination.callback=1;f.destination.effect=[&](){f.Return();};auto t=f.Job();
assert(t.Tick(0)==PENDING&&t.Submitted()&&!t.Unresolved());assert(t.Tick(1)==PENDING&&f.source.calls==0);
f.source.accept=false;f.source.callback=1;f.source.effect=[&](){f.Move();};assert(t.Tick(2)==SUCCEEDED&&f.source.calls==1&&f.destination.calls==1);}
// Every attempted reverse move burns the incoming identity, including synchronous rejection.
for(int failure=0;failure<3;failure++){EmptyFixture f;if(failure==0)f.destination.accept=false;if(failure==1)f.destination.callback=2;
auto t=f.Job();assert(t.Tick(0)==PENDING&&t.Submitted()&&f.destination.calls==1&&f.source.calls==0);
assert(t.Tick(failure==2?8000:1)==FAILED);assert(t.Unresolved()==(failure==2));
for(int i=0;i<3;i++){t.Tick(9000+i);}assert(f.destination.calls==1&&f.source.calls==0);}
// Stop before, during, or after confirmed return can never launch the incoming move.
for(int stopAt=0;stopAt<3;stopAt++){EmptyFixture f;auto t=f.Job();if(stopAt==0){t.Stop();assert(t.Tick(0)==FAILED&&!t.Submitted());continue;}
t.Tick(0);if(stopAt==2){f.Return();f.destination.last->OnComplete();assert(t.Tick(1)==PENDING&&!t.Unresolved());}
t.Stop();assert(t.Tick(2)==FAILED&&t.Submitted()&&f.source.calls==0);if(stopAt==1){assert(t.Unresolved());f.Return();f.destination.last->OnAllComplete();}
assert(!t.Unresolved()&&t.Tick(3)==FAILED&&f.source.calls==0&&f.destination.calls==1);}
// Confirmed return cannot trigger a second empty or bypass revalidated incoming custody/capacity/access.
for(int changed=0;changed<6;changed++){EmptyFixture f;auto t=f.Job();t.Tick(0);f.Return(changed!=0);f.destination.last->OnComplete();assert(t.Tick(1)==PENDING);
if(changed==1)f.source.items.RemoveItem(&f.magEntity);if(changed==2)f.destination.items.Insert(&f.magEntity);
if(changed==3)f.truck.captured=true;if(changed==4)f.recipient.player=true;if(changed==5)ERR_InfantryAmmoAccess::clear=false;
assert(t.Tick(2)==FAILED&&t.Submitted()&&!t.Unresolved()&&f.source.calls==0&&f.destination.calls==1);
assert(t.Tick(3)==FAILED&&f.destination.calls==1);ERR_InfantryAmmoAccess::clear=true;}
// Original owner/driver/ledger loss while a return is accepted quarantines that exact operation.
for(int changed=0;changed<4;changed++){EmptyFixture f;auto t=f.Job();t.Tick(0);
if(changed==0)f.truck.captured=true;if(changed==1)f.recipient.player=true;if(changed==2)f.pilot.occupant=&f.spareDriver;if(changed==3)f.donorRecord.Eliminated=true;
assert(t.Tick(1)==FAILED&&t.Unresolved());f.destination.last->OnFailed();assert(!t.Unresolved()&&f.source.calls==0&&f.destination.calls==1);}
// Both native phases are bounded independently, with no renewal while waiting for callbacks.
{EmptyFixture f;auto t=f.Job();t.Tick(0);f.Return();f.destination.last->OnComplete();assert(t.Tick(7999)==PENDING);
assert(t.Tick(8000)==PENDING&&f.source.calls==1);assert(t.Tick(15999)==FAILED&&t.Unresolved());}
// XZ approach can precede valid vertical reach, stopped state, capacity or clear access.
// None of those no-submission outcomes consumes cargo or ends the trip; the same exact item can later submit once.
for(int obstruction=0;obstruction<5;obstruction++){SupplyFixture f;
if(obstruction==0){f.recipient.position.y=3;}if(obstruction==1){f.physics.velocity.x=1;}
if(obstruction==2){f.destination.selected=nullptr;}if(obstruction==3){f.source.allow=false;}
if(obstruction==4){ERR_InfantryAmmoAccess::clear=false;}
for(int tick=0;tick<3;tick++){f.c.Transfer(tick*1000);assert(f.source.calls==0&&f.c.m_Spent.IsEmpty()&&!f.c.m_Move&&!f.c.m_Receiving&&f.c.finishes==0);}
f.recipient.position.y=0;f.physics.velocity.x=0;f.destination.selected=&f.targetStorage;f.source.allow=true;ERR_InfantryAmmoAccess::clear=true;
f.c.Transfer(3000);assert(f.source.calls==1&&f.c.m_Spent.Count()==1&&f.c.m_Move&&f.c.m_Move->Submitted());
f.Move();f.source.last->OnComplete();f.c.Transfer(4000);assert(f.c.m_Delivered==1&&f.c.m_Served.Contains(&f.recipient)&&f.source.calls==1&&f.c.m_Spent.Count()==1);
}
// A capacity-blocked first member cannot prevent another original member from receiving this magazine.
{SupplyFixture f;Fixture second;ERR_GameMode::current=&f.mode;
second.recipient.group=&f.recipientGroup;second.recipientRecord.Group=&f.recipientGroup;
f.c.m_Members.Insert(&second.recipient);f.destination.selected=nullptr;
f.c.Transfer(0);assert(f.source.calls==1&&f.c.m_Receiving==&second.recipient&&f.c.m_Spent.Count()==1&&f.c.finishes==0);
f.source.items.clear();second.destination.items={&f.magEntity};f.item.slot=&second.toSlot;f.source.last->OnComplete();
f.c.Transfer(1000);assert(f.c.m_Delivered==1&&f.c.m_Served.Contains(&second.recipient)&&!f.c.m_Served.Contains(&f.recipient));}
// A native API may complete both physical movement and callback inline. Account once on the next slow tick.
{SupplyFixture f;f.source.callback=1;f.source.effect=[&](){f.Move();};f.c.Transfer(0);
assert(f.source.calls==1&&f.c.m_Move&&f.c.m_Move->Submitted()&&f.c.m_Delivered==0&&f.c.m_Spent.Count()==1);
f.c.Transfer(1000);f.c.Transfer(2000);assert(f.c.m_Delivered==1&&f.c.m_Served.Count()==1&&f.source.calls==1&&f.c.finishes==0);}
// Crossing the native submission boundary burns the exact identity even if native refusal is synchronous.
for(int refusal=0;refusal<2;refusal++){SupplyFixture f;if(refusal==0){f.source.accept=false;}else{f.source.callback=2;}
f.c.Transfer(0);assert(f.source.calls==1&&f.c.m_Spent.Count()==1&&f.c.m_Move->Submitted());
f.c.Transfer(1000);assert(f.c.finishes==1&&f.c.m_Spent.Count()==1&&f.source.calls==1);
}
// Accepted but unresolved loss stays retained/spent; changing geometry cannot cause a second native move.
{SupplyFixture f;f.c.Transfer(0);f.recipient.position.x=8;f.c.Transfer(1000);
assert(f.c.finishes==1&&f.c.m_Move&&f.c.m_Move->Unresolved()&&f.c.m_Spent.Count()==1&&f.source.calls==1);}
// Several equivalent magazines do not multiply a blocked actor's expensive preflight work on one slow tick.
{SupplyFixture f;Entity another=f.magEntity;f.source.items.Insert(&another);f.c.m_Stock.Insert(&another);
ERR_InfantryAmmoAccess::clear=false;ERR_InfantryAmmoAccess::checks=0;f.c.Transfer(0);
assert(ERR_InfantryAmmoAccess::checks==1&&f.source.calls==0&&f.c.m_Spent.IsEmpty());ERR_InfantryAmmoAccess::clear=true;}
// A blocked native access check happens before submission and is terminal.
{Fixture f;auto t=f.Job();ERR_InfantryAmmoAccess::clear=false;ERR_InfantryAmmoAccess::checks=0;
assert(t.Tick(0)==FAILED&&!t.Submitted()&&!t.Unresolved()&&f.source.calls==0&&ERR_InfantryAmmoAccess::checks==1);
ERR_InfantryAmmoAccess::clear=true;assert(t.Tick(1)==FAILED&&f.source.calls==0&&ERR_InfantryAmmoAccess::checks==1);}
// Once native move is accepted, later geometry cannot cause a second operation or pretend cancellation.
{Fixture f;auto t=f.Job();ERR_InfantryAmmoAccess::checks=0;assert(t.Tick(0)==PENDING&&ERR_InfantryAmmoAccess::checks==1);
ERR_InfantryAmmoAccess::clear=false;assert(t.Tick(1)==PENDING&&t.Unresolved()&&f.source.calls==1&&ERR_InfantryAmmoAccess::checks==1);
f.Move();f.source.last->OnComplete();assert(t.Tick(2)==SUCCEEDED);ERR_InfantryAmmoAccess::clear=true;}
// Read-only selection follows the native inherited-well direction, excluding launcher muzzles and weapons.
{Fixture f;assert(ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity));Well derived;derived.type=2;derived.parent=1;f.magazine.well=&derived;assert(ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity));
f.magazine.well=&f.well;f.muzzle.well=&derived;assert(!ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity));
f.muzzle.well=&f.well;f.muzzle.kind=EMuzzleType::MT_UGLMuzzle;assert(!ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity));
f.muzzle.kind=0;f.weapon.kind=EWeaponType::WT_ROCKETLAUNCHER;assert(!ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity));}
// Exactly one source move; neither a callback alone nor physical movement alone is a successful transaction.
{Fixture f;auto t=f.Job();assert(t.Tick(100)==PENDING&&f.source.calls==1&&t.Unresolved());f.source.last->OnComplete();assert(t.Tick(101)==PENDING&&t.Unresolved());
f.Move();assert(t.Tick(102)==SUCCEEDED&&!t.Unresolved());t.Stop();assert(t.Tick(103)==SUCCEEDED&&f.source.calls==1);}
{Fixture f;auto t=f.Job();t.Tick(100);f.Move();assert(t.Tick(101)==PENDING&&t.Unresolved());f.source.last->OnAllComplete();assert(t.Tick(102)==SUCCEEDED);}
// Exact destination slot and known donor absence are mandatory even after success callback.
{Fixture f;auto t=f.Job();t.Tick(100);f.Move();f.item.slot=&f.fromSlot;f.source.last->OnComplete();assert(t.Tick(101)==PENDING&&t.Unresolved());
f.item.slot=&f.toSlot;for(int i=0;i<513;i++)f.source.items.Insert(&f.foreign);assert(t.Tick(102)==PENDING&&t.Unresolved());}
// Late callbacks resolve retained reservations after Stop without Tick or further operations; the stopped task stays failed.
{Fixture f;auto t=f.Job();t.Tick(0);t.Stop();assert(t.Unresolved());f.Move();f.source.last->OnAllComplete();assert(!t.Unresolved());assert(t.Tick(2)==FAILED&&f.source.calls==1);}
{Fixture f;auto t=f.Job();t.Tick(0);t.Stop();f.source.last->OnAllFailed();assert(!t.Unresolved());assert(t.Tick(1)==FAILED&&f.source.calls==1);}
{Fixture f;auto t=f.Job();t.Tick(0);f.source.last->OnComplete();f.source.last->OnFailed();assert(t.Unresolved());f.Move();assert(!t.Unresolved()&&t.Tick(1)==SUCCEEDED);}
// Failure/rejection and the 8s deadline never submit a second move.
for(int failure=0;failure<3;failure++){Fixture f;if(failure==0)f.source.accept=false;if(failure==1)f.source.callback=2;auto t=f.Job();t.Tick(100);
if(failure==2){assert(t.Tick(8100)==FAILED&&t.Unresolved());}else assert(t.Tick(101)==FAILED&&!t.Unresolved());
for(int i=0;i<10;i++)t.Tick(8200+i);assert(f.source.calls==1);}
// Invalid physical state before submission spends no native action; the terminal object cannot revive.
for(int invalid=0;invalid<25;invalid++){Fixture f;auto t=f.Job();
if(invalid==0)Replication::server=false;if(invalid==1)SCR_Global::editor=true;if(invalid==2)f.mode.active=false;
if(invalid==3)f.truck.alive=false;if(invalid==4)f.recipient.alive=false;if(invalid==5)f.recipient.awake=false;if(invalid==6)f.recipient.player=true;
if(invalid==7)f.recipient.vehicle=&f.truck;if(invalid==8)f.truck.captured=true;if(invalid==9)f.donorGroup.players=1;
if(invalid==10)f.recipient.group=&f.otherGroup;if(invalid==11)f.donorRecord.Physical=false;if(invalid==12)f.recipientRecord.Eliminated=true;
if(invalid==13)f.vehicleManager.vehicle=&f.foreign;if(invalid==14)f.driver.alive=false;if(invalid==15)f.driver.vehicle=nullptr;
if(invalid==16)f.physics.velocity.x=.251f;if(invalid==17)f.physics.angular.x=.101f;if(invalid==18)f.recipient.position.x=4.001f;
if(invalid==19)f.truck.faction=2;if(invalid==20)f.driver.faction=2;if(invalid==21)f.recipientGroup.slave=true;
if(invalid==22)f.magazine.rounds=0;if(invalid==23)f.magazine.rounds=31;if(invalid==24)f.physics.velocity.x=std::numeric_limits<float>::quiet_NaN();
assert(t.Tick(100)==FAILED&&f.source.calls==0&&!t.Unresolved());Replication::server=true;SCR_Global::editor=false;assert(t.Tick(200)==FAILED&&f.source.calls==0);}
// Native stock/storage identity and capacity guards reject shared, foreign, oversized and inaccessible inventory.
for(int invalid=0;invalid<9;invalid++){Fixture f;auto t=f.Job();if(invalid==0)f.source.items.clear();if(invalid==1)f.destination.items={&f.magEntity};
if(invalid==2)f.fromSlot.storage=&f.targetStorage;if(invalid==3)f.sourceStorage.owner=&f.foreign;if(invalid==4)f.destination.selected=nullptr;
if(invalid==5)f.targetStorage.owner=&f.foreign;if(invalid==6)f.source.allow=false;if(invalid==7)f.destination.storages.clear();if(invalid==8){for(int i=0;i<65;i++)f.destination.storages.Insert(&f.targetStorage);}
assert(t.Tick(0)==FAILED&&f.source.calls==0);}
// Conditions and original native driver/ledger identities remain strict while an accepted move is pending.
for(int changed=0;changed<4;changed++){Fixture f;auto t=f.Job();t.Tick(100);Record replacement=f.recipientRecord;
if(changed==0)f.pilot.occupant=&f.spareDriver;if(changed==1)f.ledger.records[1]=&replacement;if(changed==2)f.physics.velocity.x=1;if(changed==3)f.recipient.position.x=8;
assert(t.Tick(101)==FAILED&&t.Unresolved());f.source.last->OnFailed();assert(!t.Unresolved()&&f.source.calls==1);}
{Fixture f;auto t=f.Job();t.Tick(100);assert(t.Tick(99)==FAILED&&t.Unresolved());}
// Clothing storage ownership follows native inventory slots and bounds cycles.
{Fixture f;Entity vest;Item vestItem;Slot vestSlot;Storage body;body.owner=&f.recipient;vestSlot.storage=&body;vestItem.slot=&vestSlot;vest.item=&vestItem;f.targetStorage.owner=&vest;
auto t=f.Job();assert(t.Tick(0)==PENDING);f.Move();f.source.last->OnComplete();assert(t.Tick(1)==SUCCEEDED);
vestSlot.storage=&f.targetStorage;assert(!ERR_InfantryAmmoTransfer::StoredUnder(&vest,&f.recipient));}
}
'''
# Brace tiny fixture statements to retain -Werror without suppressing useful compiler diagnostics.
main=re.sub(r'if\((invalid|changed|failure)==(\d+)\)([^;{}]+);',r'if(\1==\2){\3;}',main)
main=main.replace('for(int i=0;i<10;i++)t.Tick(8200+i);','for(int i=0;i<10;i++){t.Tick(8200+i);}')
run(pre+translated_reservation()+code+'\n'.join(callbacks)+main)
print('PASS: actual bounded empty-return and finite delivery; phase-isolated callbacks, exact ownership proof, stop quarantine and original actor/vehicle boundaries')