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,57 @@
{
"test_hq_ammo_sampler.py": {
"exit_code": 0,
"test_sha256": "e9c2d147eb1547be471df7459f1be815cdc9ef0a64c9a4ae4ef58936424dbc55",
"log": "/tmp/erri-launcher-review-1130/final-test_hq_ammo_sampler.py.log"
},
"test_infantry_ammo_transfer.py": {
"exit_code": 0,
"test_sha256": "7929033fe661de87f9ca72bc33d64fe996ac8b8e3630a17b4210a6f498b2930f",
"log": "/tmp/erri-launcher-review-1130/final-test_infantry_ammo_transfer.py.log"
},
"test_infantry_resupply_planning.py": {
"exit_code": 0,
"test_sha256": "9929ee0f5a3272c348ee329355b6ba67fdcb745fc9ce1f377f6c989eae91bf9f",
"log": "/tmp/erri-launcher-review-1130/final-test_infantry_resupply_planning.py.log"
},
"test_infantry_resupply_coordinator.py": {
"exit_code": 0,
"test_sha256": "f9cf7d583a25a12cec9f5f80539b2590449560331f4ef34e33ea2db283c5d54a",
"log": "/tmp/erri-launcher-review-1130/final-test_infantry_resupply_coordinator.py.log"
},
"test_mounted_supply_integration.py": {
"exit_code": 0,
"test_sha256": "f52acf7a16c9d5273894eb5a4afe7657a7c97de2ed72ba06671d25e163eea5d7",
"log": "/tmp/erri-launcher-review-1130/final-test_mounted_supply_integration.py.log"
},
"test_hq_multiconvoy_supply.py": {
"exit_code": 0,
"test_sha256": "e58d1d6eba48b2c5c6073d54c76302b2a7250ad22176fa94c386e1bc2ce65588",
"log": "/tmp/erri-launcher-review-1130/final-test_hq_multiconvoy_supply.py.log"
},
"test_hq_ammo_reporting.py": {
"exit_code": 0,
"test_sha256": "bdd4a30440501a2861aca80c951098c48cf999023eaaabd9a2eef82174f8173d",
"log": "/tmp/erri-launcher-review-1130/final-test_hq_ammo_reporting.py.log"
},
"test_hq_watch.py": {
"exit_code": 0,
"test_sha256": "51d5a728b0be83ff957c0df61289d14f90aba109b49365e457c9b200d5ede36a",
"log": "/tmp/erri-launcher-review-1130/final-test_hq_watch.py.log"
},
"test_infantry_mine_coordinator.py": {
"exit_code": 0,
"test_sha256": "2fdb2138685a329cef11557973450bd5189d3d7d657dac5cc51b3df2530fab6b",
"log": "/tmp/erri-launcher-review-1130/final-test_infantry_mine_coordinator.py.log"
},
"test_infantry_supply_pool.py": {
"exit_code": 0,
"test_sha256": "6fc0053337bbb889021c6eba608796ef946ff4d30a1b01cf678927293218d9d2",
"log": "/tmp/erri-launcher-review-1130/final-test_infantry_supply_pool.py.log"
},
"test_hq_status_sampler.py": {
"exit_code": 0,
"test_sha256": "b8582fb71b7ac77a23158ccd6230e8124ef0432831204e4b8992dee3a9c10062",
"log": "/tmp/erri-launcher-review-1130/final-test_hq_status_sampler.py.log"
}
}
@@ -0,0 +1 @@
PASS: actual carried/mounted early ammunition policy and status-first shared uplink; complete threshold, phases, cooldown, failures requested check-ins and non-urgent detached carried-mine status
@@ -0,0 +1 @@
PASS: actual own-inventory ammo inspection deduplicates loaded/carried/shared wells, separates chambers/launchers, freezes scalar snapshots and treats unsupported/capped/foreign inventory as unknown
@@ -0,0 +1 @@
PASS: actual HQ rotates finite donors, skips unavailable/contacted trucks, uses delivered reports and dispatches at most one trip per planning cadence
@@ -0,0 +1 @@
PASS: own initial roster, explicit retained corpse evidence, unconscious/missing/ref transfer guards, fixed strength, no replacement adoption, detached samples, surviving radio sender, immutable convoy root/ledger, mounted capture release and lost-root unknown
@@ -0,0 +1 @@
PASS: delivered-only status snapshots, two missed check-ins/reply deadline, last reported coordinates, episode/replay guards, confirmed-loss idempotence, detached/validated mounted payload replacement 256-record cap and detached carried-mine radio delivery/chronology
@@ -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,2 @@
PASS: actual designated zero/one original carrier selection, settled startup, shared finite claims, no refund after submission
PASS: actual HQ startup grant readiness forwarding is authoritative and fail-closed
@@ -0,0 +1,2 @@
infantry resupply actual rules, radio transitions, owned cleanup and unresolved-operation tests passed
PASS: actual resupply Roster preserves permanent release, original slot mapping and bounded ownership
@@ -0,0 +1 @@
PASS: actual convoy binding, finite identities, disjoint tokens, shared report replay/cooldown, original recipients and HQ cleanup
@@ -0,0 +1 @@
PASS: actual finite convoy supply pool; immutable slots, shared demand state, sparse/capped discovery and permanent shutdown
@@ -0,0 +1 @@
PASS: actual shared mounted stock/history ownership, atomic exact HQ pair claim, and bounded stopped-owner proof polling
@@ -0,0 +1,68 @@
{
"decision": "Approved source proposal; no remaining concrete blocker found after two review corrections.",
"producer": "/tmp/erri-launcher-resupply-1140",
"source_sha256": {
"addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c": "52e601a0571b4942c9d4d0ce820727df8498febd6726efbd7da0c5eff7041b58",
"addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c": "db2c0f1bfb0e0a8accabe45abd7eb93e6a13d6a8205ea427b12e28a7b4b28f40",
"addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryAmmoTransfer.c": "e6f0896dd7bdfea89877cc7150928c94f36ac8aeb3af9a383bc665d5df801267"
},
"independent_tests": {
"test_infantry_ammo_transfer.py": {
"exit_code": 0,
"test_sha256": "382b7a567d4a99fa6521160aac5d7eb332eeae20e14bf64eb603d59fdd0bb1a8",
"log": "/tmp/erri-launcher-review-1130/test_infantry_ammo_transfer.py.log"
},
"test_infantry_resupply_planning.py": {
"exit_code": 0,
"test_sha256": "7bcb3461cc16b04a0247e928c1c831a3fb0944f48edcb9125cf7ef463a07168a",
"log": "/tmp/erri-launcher-review-1130/test_infantry_resupply_planning.py.log"
},
"test_infantry_resupply_coordinator.py": {
"exit_code": 0,
"test_sha256": "f9cf7d583a25a12cec9f5f80539b2590449560331f4ef34e33ea2db283c5d54a",
"log": "/tmp/erri-launcher-review-1130/test_infantry_resupply_coordinator.py.log"
},
"test_mounted_supply_integration.py": {
"exit_code": 0,
"test_sha256": "f52acf7a16c9d5273894eb5a4afe7657a7c97de2ed72ba06671d25e163eea5d7",
"log": "/tmp/erri-launcher-review-1130/test_mounted_supply_integration.py.log"
},
"test_hq_multiconvoy_supply.py": {
"exit_code": 0,
"test_sha256": "e58d1d6eba48b2c5c6073d54c76302b2a7250ad22176fa94c386e1bc2ce65588",
"log": "/tmp/erri-launcher-review-1130/test_hq_multiconvoy_supply.py.log"
},
"test_infantry_mine_coordinator.py": {
"exit_code": 0,
"test_sha256": "2fdb2138685a329cef11557973450bd5189d3d7d657dac5cc51b3df2530fab6b",
"log": "/tmp/erri-launcher-review-1130/test_infantry_mine_coordinator.py.log"
},
"test_infantry_supply_pool.py": {
"exit_code": 0,
"test_sha256": "6fc0053337bbb889021c6eba608796ef946ff4d30a1b01cf678927293218d9d2",
"log": "/tmp/erri-launcher-review-1130/test_infantry_supply_pool.py.log"
}
},
"resolved_findings": [
"LauncherStock now rejects rifle-only, spent, absent and incompatible original stock before claims, message consumption or cooldown. It checks only attributable own roster/weapon custody, capped at128 compatibility pairs.",
"Loaded launcher magazine binding now requires exact current muzzle parent-container and attached entity identity, accepting a native loaded slot with no ordinary deposit storage. Incoming cargo storage validation remains unchanged."
],
"reviewed_constraints": [
"Fresh delivered LauncherComplete with full reported member count, a supported magazine and zero rounds/chambers; no inference from unknown, missing-magazine or projectile-only launchers.",
"HQ ordering remains rifle, mounted, launcher, mine; same stock/spent and report replay/cooldown state are reused. Launcher mode flows through trip, Compatible, transfer constructor and Context.",
"Original stock reserved before native submission via shared spent array; any submission remains spent. Incoming IsUsed is refused. Exact physical proof and callback are both required.",
"Uncertain submitted launcher operations retain donor/recipient claims in state7 through Stop; no native retry or stock refund. Terminal proof/deletion releases only owned claims."
],
"mutation": {
"path": "/tmp/erri-launcher-review-1130/native-slot-mutation-red.log",
"result": "Compiled then failed actual Compatible assertion when exact muzzle-slot proof was replaced by old StoredUnder loaded-magazine check.",
"setup_failure_preserved": "/tmp/erri-launcher-review-1130/native-slot-mutation-compile-only.log",
"setup_note": "First mutation left an unused local and failed compilation; declaration removed before the behavioral red run. Only final runtime assertion is counted as mutation evidence."
},
"limits": [
"No native compile or inventory transfer was run by reviewer. Carried launcher catalogue coverage, animation/reload and actual engine movement remain unqualified.",
"This slice delivers a compatible finite magazine to carried inventory; it does not guarantee an automatic reload or support projectile-only/disposable configurations.",
"All seven focused tests execute modeled actual methods; they do not establish network/native callback behavior."
],
"writes": "Only review notes/logs and isolated mutation under /tmp; no producer, target or reference source changes."
}
@@ -0,0 +1,16 @@
/tmp/erri-launcher-review-1130/scratch/err-policy-4s_8qyg4/check.cpp: In static member function ‘static bool ERR_InfantryAmmoTransfer::Compatible(IEntity, IEntity, bool)’:
/tmp/erri-launcher-review-1130/scratch/err-policy-4s_8qyg4/check.cpp:184:27: error: unused variable ‘loadedSlot’ [-Werror=unused-variable]
184 | InventoryStorageSlot loadedSlot = loadedItem->GetParentSlot();
| ^~~~~~~~~~
cc1plus: all warnings being treated as errors
Traceback (most recent call last):
File "/tmp/erri-launcher-resupply-1140/tests/test_infantry_ammo_transfer.py", line 323, in <module>
run(pre+translated_reservation()+code+'\n'.join(callbacks)+main)
~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jakub/devel/escapistsrereintroduced/tests/policy_harness.py", line 13, in run
subprocess.run(['g++','-std=c++17','-Wall','-Werror',str(p/'check.cpp'),'-o',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 '['g++', '-std=c++17', '-Wall', '-Werror', '/tmp/erri-launcher-review-1130/scratch/err-policy-4s_8qyg4/check.cpp', '-o', '/tmp/erri-launcher-review-1130/scratch/err-policy-4s_8qyg4/check']' returned non-zero exit status 1.
@@ -0,0 +1,12 @@
check: /tmp/erri-launcher-review-1130/scratch/err-policy-ses6lcfs/check.cpp:466: int main(): Assertion `ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity,true)' failed.
Traceback (most recent call last):
File "/tmp/erri-launcher-resupply-1140/tests/test_infantry_ammo_transfer.py", line 323, 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/erri-launcher-review-1130/scratch/err-policy-ses6lcfs/check']' died with <Signals.SIGABRT: 6>.
@@ -0,0 +1,286 @@
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 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;
// Projectile-only/disposable and entirely missing magazine configurations
// are outside this finite magazine slice; never synthesize a projectile.
BaseMagazineComponent loaded = muzzle.GetMagazine();
if (!loaded || !loaded.GetOwner() || !loaded.GetMagazineWell() || loaded.GetMaxAmmoCount() <= 0) continue;
InventoryItemComponent loadedItem = InventoryItemComponent.Cast(loaded.GetOwner().FindComponent(InventoryItemComponent));
if (!loadedItem || !loadedItem.GetParentSlot()) continue;
if (!StoredUnder(loaded.GetOwner(), rifle)) 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);
}
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();
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");
}
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,86 @@
{
"decision": "FINAL APPROVAL: no remaining concrete source blocker found in frozen revised proposal. Supersedes intermediate loaded-empty-only review.",
"producer": "/tmp/erri-launcher-resupply-1140",
"patch_sha256": "cfc9de0cbda4f5fb151ac5ad3c7f2b04685feecb83805fc91a8ba32d6ab86d6e",
"source_sha256": {
"addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c": "52e601a0571b4942c9d4d0ce820727df8498febd6726efbd7da0c5eff7041b58",
"addons/core/Scripts/Game/ERR/10_AI/ERR_HQAmmoSampler.c": "8bb0ec1422887feaa340307d3c60bae50fc82d424fce6390876e580ff8b75e8b",
"addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c": "76f95e3526d971e123f546afbdcb5d225253954250d06bf48f997369d7c43fdc",
"addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryAmmoTransfer.c": "72e7505fd5d303c77183a32da7fdfea68675860783219b5c7e7cc773ce0e8861"
},
"independent_tests": {
"test_hq_ammo_sampler.py": {
"exit_code": 0,
"test_sha256": "e9c2d147eb1547be471df7459f1be815cdc9ef0a64c9a4ae4ef58936424dbc55",
"log": "/tmp/erri-launcher-review-1130/final-test_hq_ammo_sampler.py.log"
},
"test_infantry_ammo_transfer.py": {
"exit_code": 0,
"test_sha256": "8ec5687714c6f6a193e6ba3e764599a8dba1fc198d98b90ea2dc6bda1083b566",
"log": "/tmp/erri-launcher-review-1130/final-test_infantry_ammo_transfer.py.log"
},
"test_infantry_resupply_planning.py": {
"exit_code": 0,
"test_sha256": "9929ee0f5a3272c348ee329355b6ba67fdcb745fc9ce1f377f6c989eae91bf9f",
"log": "/tmp/erri-launcher-review-1130/final-test_infantry_resupply_planning.py.log"
},
"test_infantry_resupply_coordinator.py": {
"exit_code": 0,
"test_sha256": "f9cf7d583a25a12cec9f5f80539b2590449560331f4ef34e33ea2db283c5d54a",
"log": "/tmp/erri-launcher-review-1130/final-test_infantry_resupply_coordinator.py.log"
},
"test_mounted_supply_integration.py": {
"exit_code": 0,
"test_sha256": "f52acf7a16c9d5273894eb5a4afe7657a7c97de2ed72ba06671d25e163eea5d7",
"log": "/tmp/erri-launcher-review-1130/final-test_mounted_supply_integration.py.log"
},
"test_hq_multiconvoy_supply.py": {
"exit_code": 0,
"test_sha256": "e58d1d6eba48b2c5c6073d54c76302b2a7250ad22176fa94c386e1bc2ce65588",
"log": "/tmp/erri-launcher-review-1130/final-test_hq_multiconvoy_supply.py.log"
},
"test_hq_ammo_reporting.py": {
"exit_code": 0,
"test_sha256": "bdd4a30440501a2861aca80c951098c48cf999023eaaabd9a2eef82174f8173d",
"log": "/tmp/erri-launcher-review-1130/final-test_hq_ammo_reporting.py.log"
},
"test_hq_watch.py": {
"exit_code": 0,
"test_sha256": "51d5a728b0be83ff957c0df61289d14f90aba109b49365e457c9b200d5ede36a",
"log": "/tmp/erri-launcher-review-1130/final-test_hq_watch.py.log"
},
"test_infantry_mine_coordinator.py": {
"exit_code": 0,
"test_sha256": "2fdb2138685a329cef11557973450bd5189d3d7d657dac5cc51b3df2530fab6b",
"log": "/tmp/erri-launcher-review-1130/final-test_infantry_mine_coordinator.py.log"
},
"test_infantry_supply_pool.py": {
"exit_code": 0,
"test_sha256": "6fc0053337bbb889021c6eba608796ef946ff4d30a1b01cf678927293218d9d2",
"log": "/tmp/erri-launcher-review-1130/final-test_infantry_supply_pool.py.log"
},
"test_hq_status_sampler.py": {
"exit_code": 0,
"test_sha256": "b8582fb71b7ac77a23158ccd6230e8124ef0432831204e4b8992dee3a9c10062",
"log": "/tmp/erri-launcher-review-1130/final-test_hq_status_sampler.py.log"
}
},
"reviewed_final_changes": [
"LauncherMuzzles is detached in Snapshot and validated from0 through32 per sampled member. Inspect counts only non-disposable well-bearing supported launcher muzzles; SCR_MuzzleInMagComponent.CanBeReloaded refusal makes launcher state incomplete. Native APIs verified in installed generated BaseMuzzleComponent and SCR_MuzzleInMagComponent.",
"Delivered demand requires fresh complete original living roster counts and at least one supported launcher muzzle with zero rounds and chambers. Empty GetMagazine is allowed, matching consumed one-round projectile-magazines; no absent-weapon inference.",
"Optional existing loaded magazine retains exact current muzzle-container/attached-entity/well proof. Incoming positive unused finite compatible magazine and original carried weapon custody still required.",
"LauncherStock scans the full bounded96x32 original stock/roster pair space, preventing permanent prefix starvation. No claims/replay/cooldown consumed for rifle-only or incompatible stock.",
"Rifle/mounted requests keep priority; launcher precedes mines. Same spent ledger, original finite inventory, report replay and recipient cooldown remain.",
"Mode reaches physical per-actor sampler, Compatible, transfer constructor and Context. Launcher empty-return is disabled; no speculative recycling of fired projectile identities.",
"Native uncertainty retains exact donor/recipient claims, no retry/refund after submission; stopped polling continues only proof/cleanup."
],
"mutation": {
"log": "/tmp/erri-launcher-review-1130/prefix-starvation-mutation-red.log",
"result": "Restoring128 pair cap compiled then failed actual TryPlanLauncher late-compatible-stock assertion. Full96x32 frozen source passes."
},
"limitations": [
"No native compile/run by reviewer. Actual RPG/VOG inventory transfer, reload, animations and multiplayer remain unqualified; tests use modeled native boundaries.",
"Does not manufacture ammunition or guarantee automatic reload. Disposable/unsupported no-well configurations remain refused."
],
"source_changes_by_reviewer": "None; only isolated review artifacts/logs/mutation under /tmp."
}
@@ -0,0 +1,19 @@
check: /tmp/erri-launcher-review-1130/scratch/err-policy-74xaaa7p/check.cpp:378: int main(): Assertion `f.s.TryPlanLauncher(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0)' failed.
Traceback (most recent call last):
File "/tmp/erri-launcher-resupply-1140/tests/test_infantry_resupply_planning.py", line 34, in <module>
run(r'''
~~~^^^^^
#include <cassert>
^^^^^^^^^^^^^^^^^^
...<202 lines>...
}
^
''')
^^^^
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/erri-launcher-review-1130/scratch/err-policy-74xaaa7p/check']' died with <Signals.SIGABRT: 6>.
@@ -0,0 +1,561 @@
// HQ-owned ground rendezvous using delivered positions and existing finite cargo.
class ERR_InfantryResupplyRules
{
static bool Fresh(ERR_HQWatchEntry entry, float now)
{
return entry && !entry.Closed && entry.Count > 0 && entry.Fit > 0
&& ERR_ForceRules.Finite(now) && entry.ObservedAt >= 0 && now >= entry.ObservedAt
&& now - entry.ObservedAt <= 120000 && ERR_ForceRules.FiniteVector(entry.Position);
}
static bool Need(ERR_HQWatchEntry entry, float now)
{
return Fresh(entry, now) && entry.AmmoState && entry.AmmoState.Valid(entry.Count)
&& entry.AmmoState.Complete && entry.AmmoState.Members == entry.Count
&& entry.AmmoState.SmallArmsMuzzles > 0
&& entry.AmmoState.SmallArmsRounds + entry.AmmoState.SmallArmsChambers <= entry.Count * 30;
}
// Only positively sampled reloadable magazine-well muzzles establish launcher demand.
static bool LauncherNeed(ERR_HQWatchEntry entry, float now)
{
return Fresh(entry, now) && entry.AmmoState && entry.AmmoState.Valid(entry.Count)
&& entry.AmmoState.LauncherComplete && entry.AmmoState.Members == entry.Count
&& entry.AmmoState.LauncherMuzzles > 0
&& entry.AmmoState.LauncherRounds == 0 && entry.AmmoState.LauncherChambers == 0;
}
static bool LauncherPair(ERR_HQWatchEntry recipient, ERR_HQWatchEntry donor, float now)
{
return LauncherNeed(recipient, now) && Fresh(donor, now) && recipient.SourceId != donor.SourceId
&& vector.DistanceXZ(recipient.Position, donor.Position) <= 200;
}
static bool MinePair(ERR_HQWatchEntry recipient, ERR_HQWatchEntry donor, float now)
{
return Fresh(recipient, now) && Fresh(donor, now) && recipient.SourceId != donor.SourceId
&& recipient.AmmoState && recipient.AmmoState.Valid(recipient.Count)
&& recipient.AmmoState.MineComplete && recipient.AmmoState.MineMembers == recipient.Count
&& recipient.AmmoState.ReadyMines <= 1 && vector.DistanceXZ(recipient.Position, donor.Position) <= 200;
}
static bool Pair(ERR_HQWatchEntry recipient, ERR_HQWatchEntry donor, float now)
{
return Need(recipient, now) && Fresh(donor, now) && recipient.SourceId != donor.SourceId
&& vector.DistanceXZ(recipient.Position, donor.Position) <= 200;
}
}
class ERR_InfantryResupply
{
protected ERR_HQ m_HQ;
protected ERR_GameMode m_Mode;
protected ERR_ConvoyTask m_Convoy;
protected int m_ConvoySlot = -1;
protected ERR_ConvoyVehicle m_Truck;
protected ERR_HQUnit m_Recipient;
protected ERR_HQUnit m_Donor;
protected ref array<IEntity> m_Stock = {};
protected ref array<IEntity> m_Spent = {};
protected ref array<IEntity> m_MineStock = {};
protected ref ERR_ConvoyMineStock m_MineBatch;
protected bool m_MineStockCaptured;
protected bool m_MineTrip;
protected bool m_LauncherTrip;
protected int m_MineRecipientSlot = -1;
protected bool m_EmptyMineReplacement;
protected int m_MinesDelivered;
protected ref array<IEntity> m_Members = {};
protected ref array<IEntity> m_Served = {};
protected ref map<string, int> m_Consumed = new map<string, int>();
protected ref map<string, float> m_NextAttempt = new map<string, float>();
protected ref ERR_HQRoute m_Order;
protected ref ERR_InfantryAmmoTransfer m_Move;
protected ref ERR_InfantryMineTransfer m_MineMove;
protected ref ERR_MountedResupply m_Mounted;
protected IEntity m_Receiving;
protected AIWaypoint m_MoveWaypoint;
protected AIWaypoint m_HoldWaypoint;
protected vector m_Rendezvous;
protected int m_State; // 0 stock startup, 1 idle, 2 truck downlink, 3 stopped status, 4 infantry downlink, 5 transfer, 7 unresolved mine custody
protected int m_Trip;
protected int m_Token;
protected int m_ReportId;
protected int m_Delivered;
protected int m_TripDelivered;
protected float m_Deadline;
protected float m_StockDeadline = -1;
protected float m_HeldAt;
protected float m_NextTick;
protected bool m_Stopped;
protected string m_Reason;
void ERR_InfantryResupply(ERR_HQ hq, ERR_GameMode mode) { m_HQ = hq; m_Mode = mode; }
// Bind before startup; every coordinator retains its exact finite convoy identity.
bool Bind(ERR_ConvoyTask task, int slot, map<string, int> consumed, map<string, float> nextAttempt)
{
if (!task || !consumed || !nextAttempt || !m_HQ || !m_HQ.Convoys() || slot < 0 || slot >= 20
|| slot >= m_HQ.Convoys().Count() || m_HQ.Convoys().At(slot) != task) return false;
if (m_Stopped || m_Convoy || m_ConvoySlot >= 0 || m_State != 0 || m_Trip != 0 || m_NextTick != 0
|| m_StockDeadline >= 0 || !m_Stock.IsEmpty() || m_Move || m_Truck) return false;
m_Convoy = task; m_ConvoySlot = slot; m_Consumed = consumed; m_NextAttempt = nextAttempt;
return true;
}
ERR_ConvoyTask Task() { return m_Convoy; }
int State()
{
if (m_Mounted && m_Mounted.Busy()) return 6;
return m_State;
}
ERR_MountedResupply Mounted() { return m_Mounted; }
bool Unresolved() { return (m_Move && m_Move.Unresolved()) || (m_MineMove && m_MineMove.Unresolved()) || (m_Mounted && m_Mounted.Unresolved()); }
protected void PollTransfers(float now)
{
if (m_Mounted) m_Mounted.Tick(now);
if (m_Move && !m_Move.Unresolved() && (m_State <= 1 || (m_State == 7 && m_LauncherTrip)))
{
m_Move = null;
if (m_State == 7) Finish("launcher custody settled; original claims released without retry or refund");
}
if (m_MineMove && !m_MineMove.Unresolved() && (m_State <= 1 || m_State == 7))
{
m_MineMove = null;
if (m_State == 7) Finish("mine custody settled; original claims released without retry or refund");
}
}
int Delivered() { return m_Delivered; }
int MinesDelivered() { return m_MinesDelivered; }
int StockCount() { return m_Stock.Count(); }
// Admission uses the existing array object so an already-created mounted
// coordinator observes this exact finite addition without a replacement ledger.
protected int m_DepotImports;
bool DepotCurrent()
{
return Current() && m_State == 1 && !m_Move && !m_MineMove && !Unresolved()
&& (!m_Mounted || !m_Mounted.Busy()) && TruckValid(true);
}
bool DepotAvailable() { return DepotCurrent() && m_Stock.Count() < 96 && m_Spent.Count() < 96; }
bool DepotNeeded()
{
if (!DepotAvailable()) return false;
int committed;
foreach (IEntity item : m_Spent)
if (item && m_Stock.Contains(item)) committed++;
return committed > m_DepotImports;
}
ERR_ConvoyVehicle DepotTruck()
{
if (!DepotCurrent()) return null;
return m_Truck;
}
bool AdmitDepot(ERR_GroundDepotLoading job, IEntity item)
{
if (!job || !DepotAvailable() || !item || m_Stock.Contains(item) || m_Spent.Contains(item)) return false;
if (!job.DeliveredTo(this, item) || !Contains(m_Truck.Entity, item)) return false;
m_Stock.Insert(item); m_DepotImports++; Audit("one exact finite camp magazine admitted after proved physical courier delivery");
return true;
}
protected void Audit(string reason)
{
if (m_MineTrip) reason = "mine top-up: " + reason;
if (m_LauncherTrip) reason = "depleted launcher magazine: " + reason;
m_Reason = reason;
m_HQ.Journal().Write("INFANTRY SUPPLY", string.Format("convoy-slot=%1 trip=%2 state=%3 moved=%4 spent=%5/%6; %7", m_ConvoySlot, m_Trip, m_State, m_Delivered, m_Spent.Count(), m_Stock.Count(), reason), m_ReportId);
}
protected bool Current()
{
return !m_Stopped && Replication.IsServer() && m_Mode && m_Mode == ERR_GameMode.Get() && m_Mode.Active();
}
protected bool Contains(IEntity owner, IEntity item)
{
if (!owner || !item) return false;
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(owner.FindComponent(InventoryStorageManagerComponent));
if (!inventory) return false;
array<IEntity> items = {}; ERR_Inventory.Items(inventory, items);
return items.Count() <= 128 && items.Contains(item);
}
protected bool ConvoyCurrent()
{
if (!m_HQ || !m_Convoy) return false;
if (m_ConvoySlot < 0) return m_HQ.ConvoyTask() == m_Convoy;
return m_HQ.Convoys() && m_ConvoySlot < m_HQ.Convoys().Count() && m_HQ.Convoys().At(m_ConvoySlot) == m_Convoy;
}
protected bool TruckValid(bool allowArrived = false)
{
if (!m_Convoy || m_Convoy.Stopped || !m_Convoy.Ready || !m_Convoy.Receipt || !m_Convoy.Receipt.Consumed
|| !ConvoyCurrent() || m_Convoy.Vehicles.Count() != 6 || m_Convoy.Vehicles[1] != m_Truck) return false;
if (!m_Truck || m_Truck.Index != 1 || !m_Truck.Registered || m_Truck.Released || (m_Truck.Arrived && !allowArrived)
|| !m_Truck.CrewValid(m_Mode) || ERR_ConvoyRules.Captured(m_Truck.Entity, m_Truck.Group)) return false;
ERR_ForceRecord record = m_Mode.Forces().FindGroup(m_Truck.Group);
return record && record.Physical && !record.Eliminated && record.Kind == ERR_ForceKind.CONVOY
&& m_Convoy.Receipt.Records.Count() >= 6 && record.Id == m_Convoy.Receipt.Records[1];
}
protected bool Quiet(ERR_HQUnit unit)
{
return unit && unit.Group && unit.Utility && !unit.Group.IsSlave() && unit.Group.GetPlayerCount(true) == 0
&& unit.Group.GetFaction() == m_Mode.EnemyFaction()
&& (!unit.Utility.m_Perception || unit.Utility.m_Perception.m_aTargets.IsEmpty());
}
protected bool Roster()
{
if (!m_Recipient || !Quiet(m_Recipient) || !m_Recipient.StatusSampler.OwnsRoster(m_Recipient.Group)) return false;
int strength = m_Recipient.StatusSampler.InitialStrength();
if (strength < 1 || strength > 32) return false;
array<AIAgent> agents = {}; m_Recipient.Group.GetAgents(agents);
if (agents.IsEmpty() || agents.Count() > 32) return false;
foreach (AIAgent agent : agents)
if (!agent || !m_Members.Contains(agent.GetControlledEntity())) return false;
bool living;
foreach (IEntity actor : m_Members)
{
if (!actor || !ERR_Actors.Alive(actor)) continue;
// Returning to the group cannot undo a sampler's permanent release of an
// original slot after transfer or player control during this trip.
bool attributed = false;
for (int slot = 0; slot < strength; slot++)
{
if (m_Recipient.StatusSampler.InitialMember(slot) != actor) continue;
attributed = m_Recipient.StatusSampler.CanAttribute(slot, actor);
break;
}
if (!attributed) return false;
if (ERR_Actors.Player(actor) || ERR_Actors.Group(actor) != m_Recipient.Group
|| SCR_FactionManager.SGetFaction(actor) != m_Mode.EnemyFaction() || CompartmentAccessComponent.GetVehicleIn(actor)) return false;
if (ERR_Actors.CanFight(actor)) living = true;
}
return living;
}
protected void Stock(float now)
{
if (!m_Convoy) m_Convoy = m_HQ.ConvoyTask();
if (m_Convoy && !ConvoyCurrent()) { Stop(); return; }
if (!m_Convoy || !m_Convoy.Ready || m_Convoy.Vehicles.Count() != 6) return;
m_Truck = m_Convoy.Vehicles[1];
if (!TruckValid()) { Stop(); return; }
// Cargo has a later, separately bounded lifecycle than native crew admission.
// Supply convoys first request faction weapons, then their matching magazines.
if (m_StockDeadline < 0) m_StockDeadline = now + 90000;
if (now >= m_StockDeadline) { Stop(); return; }
if (!m_Convoy.CargoIssued) return;
ERR_SupplyConvoyTask supply = ERR_SupplyConvoyTask.Cast(m_Convoy);
if (supply && !supply.MagazinesIssued) return;
if (supply) m_MineBatch = supply.MineStock;
foreach (ERR_ConvoyCargoCallback operation : m_Convoy.Cargo)
if (!operation.Finished) return;
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(m_Truck.Entity.FindComponent(InventoryStorageManagerComponent));
if (!inventory) { Stop(); return; }
array<IEntity> items = {}; ERR_Inventory.Items(inventory, items);
if (items.Count() > 128) { Stop(); return; }
foreach (IEntity item : items)
{
if (!item || !item.FindComponent(BaseMagazineComponent)) continue;
if (m_Stock.Count() >= 96) { Stop(); return; }
m_Stock.Insert(item);
}
m_State = 1; Audit("truck magazine identities captured once after startup; absent/looted stock is never replaced");
}
bool TryPlan(ERR_HQUnit recipient, ERR_HQUnit donor, ERR_HQWatchEntry demand, ERR_HQWatchEntry donorStatus, float now, bool mineTrip = false, bool launcherTrip = false)
{
if (!Current() || m_State != 1 || m_Move || m_MineMove || (m_Mounted && m_Mounted.Busy()) || m_Trip >= 128 || !TruckValid()) return false;
if ((mineTrip && launcherTrip) || m_Truck.Combat.Held || m_Truck.SupplyToken > 0 || !recipient || !donor) return false;
if (donor.Group != m_Truck.Group || recipient.HomeGuard || !Quiet(recipient) || !Quiet(donor)) return false;
if (donor.Downlink || donor.PendingOrder || donor.PendingConvoyOrder) return false;
if (mineTrip)
{
if (!ERR_InfantryResupplyRules.MinePair(demand, donorStatus, now)) return false;
// Empty reports authorize only an original startup-designated engineer.
// This reads retained entitlement, never live positions/inventory for planning.
if (demand.AmmoState.ReadyMines == 0)
{
bool designated;
for (int member = 0; member < recipient.StatusSampler.InitialStrength() && member < 32; member++)
{
IEntity original = recipient.StatusSampler.InitialMember(member);
if (original && recipient.StatusSampler.CanAttribute(member, original)
&& m_HQ.MineSupplyReady(recipient.Group, original, true)) { designated = true; break; }
}
if (!designated) return false;
}
}
else if (launcherTrip)
{
if (!ERR_InfantryResupplyRules.LauncherPair(demand, donorStatus, now)) return false;
}
else if (!ERR_InfantryResupplyRules.Pair(demand, donorStatus, now)) return false;
if (demand.SourceId != recipient.ForceSourceId || donorStatus.SourceId != donor.ForceSourceId) return false;
if (demand.MessageId <= m_Consumed.Get(demand.SourceId) || now < m_NextAttempt.Get(demand.SourceId)) return false;
bool available;
if (mineTrip)
{
if (!CaptureMines()) return false;
foreach (IEntity mine : m_MineStock)
if (ERR_InfantryMineTransfer.ReadyMine(mine) && !m_Spent.Contains(mine) && Contains(m_Truck.Entity, mine)) { available = true; break; }
}
else if (launcherTrip) available = LauncherStock(recipient);
else foreach (IEntity magazine : m_Stock) if (magazine && !m_Spent.Contains(magazine) && Contains(m_Truck.Entity, magazine)) { available = true; break; }
if (m_Spent.Count() >= 96) return false;
if (!available) return false;
m_Members.Clear();
for (int i = 0; i < recipient.StatusSampler.InitialStrength(); i++)
{
IEntity actor = recipient.StatusSampler.InitialMember(i);
if (actor && recipient.StatusSampler.CanAttribute(i, actor)) m_Members.Insert(actor);
}
m_Recipient = recipient; m_Donor = donor;
if (!Roster()) { m_Recipient = null; m_Donor = null; return false; }
// Disjoint bounded token range from the existing mortar courier's trip IDs.
int slot = m_ConvoySlot;
if (slot < 0) slot = 0; // Isolated legacy fixture uses the first convoy's range.
int token = 100000 + slot * 128 + m_Trip + 1;
if (!m_HQ.ReserveLogistics(recipient.Group, token)) { m_Recipient = null; m_Donor = null; return false; }
m_MineTrip = mineTrip; m_LauncherTrip = launcherTrip; m_MineRecipientSlot = -1; m_Receiving = null; m_EmptyMineReplacement = false;
m_Trip++; m_Token = token; m_Truck.SupplyToken = token; m_ReportId = demand.MessageId; m_TripDelivered = 0; m_Served.Clear();
m_Consumed.Set(demand.SourceId, demand.MessageId); m_NextAttempt.Set(demand.SourceId, now + 180000);
m_Deadline = Math.Min(now + 120000, demand.ObservedAt + 180000); m_State = 2;
string purpose = "stop existing ammo truck and report position";
if (mineTrip) purpose += "; one finite PMN4 top-up for an existing carrier";
if (launcherTrip) purpose += "; finite compatible magazine for a reported depleted launcher";
m_Order = m_HQ.Downlink(donor.Group, "INFANTRY SUPPLY HOLD", purpose, m_ReportId);
if (!m_Order) Finish("truck radio downlink unavailable");
else Audit("existing patrol reserved; truck hold awaits radio delivery");
return true;
}
// Optional mine startup must never delay normal magazine readiness. Pin the
// original batch during Stock(), then copy its closed exact identities once.
protected bool CaptureMines()
{
ERR_SupplyConvoyTask supply = ERR_SupplyConvoyTask.Cast(m_Convoy);
if (!supply || !m_MineBatch || supply.MineStock != m_MineBatch || !m_MineBatch.Complete()) return false;
if (m_MineStockCaptured) return !m_MineStock.IsEmpty();
m_MineStockCaptured = true;
int count = m_MineBatch.Count();
if (count < 0 || count > 2) return false;
for (int index = 0; index < count; index++)
{
IEntity item = m_MineBatch.At(index);
if (item && !m_MineStock.Contains(item)) m_MineStock.Insert(item);
}
return true;
}
// Compatibility-only preflight of original own carriers; HQ still uses the
// delivered report for demand and positions. No item is reserved or moved here.
protected bool LauncherStock(ERR_HQUnit recipient)
{
if (!recipient || !recipient.StatusSampler || m_Stock.Count() > 96) return false;
int strength = recipient.StatusSampler.InitialStrength();
if (strength < 1 || strength > 32) return false;
int checks;
foreach (IEntity magazine : m_Stock)
{
if (!magazine || m_Spent.Contains(magazine) || !Contains(m_Truck.Entity, magazine)) continue;
for (int slot = 0; slot < strength; slot++)
{
if (++checks > 128) return false;
IEntity actor = recipient.StatusSampler.InitialMember(slot);
if (!actor || !recipient.StatusSampler.CanAttribute(slot, actor) || !ERR_Actors.CanFight(actor)) continue;
if (ERR_InfantryAmmoTransfer.Compatible(actor, magazine, true)) return true;
}
}
return false;
}
bool TryPlanLauncher(ERR_HQUnit recipient, ERR_HQUnit donor, ERR_HQWatchEntry demand, ERR_HQWatchEntry donorStatus, float now)
{
return TryPlan(recipient, donor, demand, donorStatus, now, false, true);
}
bool TryPlanMines(ERR_HQUnit recipient, ERR_HQUnit donor, ERR_HQWatchEntry demand, ERR_HQWatchEntry donorStatus, float now)
{
return TryPlan(recipient, donor, demand, donorStatus, now, true);
}
// Share exact frozen inventory identities and global radio replay state.
bool TryPlanMounted(ERR_HQUnit recipient, ERR_HQUnit donor, ERR_HQWatchEntry demand, ERR_HQWatchEntry donorStatus, float now)
{
if (!Current() || m_State != 1 || m_Move || m_MineMove || m_ConvoySlot < 0 || !TruckValid(true)) return false;
if (m_Mounted && m_Mounted.Busy()) return false;
if (!m_Mounted) m_Mounted = new ERR_MountedResupply(m_HQ, m_Mode, m_Convoy, m_ConvoySlot, m_Stock, m_Spent, m_Consumed, m_NextAttempt);
return m_Mounted.Begin(recipient, donor, demand, donorStatus, now);
}
protected bool Waypoints()
{
m_MoveWaypoint = AIWaypoint.Cast(ERR_Actors.Spawn(ERR_Resources.MoveWaypoint(), ERR_Actors.Ground(m_Rendezvous)));
m_HoldWaypoint = AIWaypoint.Cast(ERR_Actors.Spawn(ERR_Resources.DefendWaypoint(), ERR_Actors.Ground(m_Rendezvous)));
if (!m_MoveWaypoint || !m_HoldWaypoint) return false;
// The reported rendezvous is 3 m from the donor report. A 2 m move
// radius allowed arrival 5 m away, beyond the transfer's physical reach.
m_MoveWaypoint.SetCompletionRadius(0.5); m_HoldWaypoint.SetCompletionRadius(0.5);
m_Recipient.Group.AddWaypointAt(m_HoldWaypoint, 0); m_Recipient.Group.AddWaypointAt(m_MoveWaypoint, 0);
m_Recipient.Group.ActivateAI(); return true;
}
protected void ClearWaypoints()
{
if (m_MoveWaypoint) { if (m_Recipient && m_Recipient.Group) m_Recipient.Group.RemoveWaypoint(m_MoveWaypoint); ERR_Actors.DeleteSpawned(m_MoveWaypoint); }
if (m_HoldWaypoint) { if (m_Recipient && m_Recipient.Group) m_Recipient.Group.RemoveWaypoint(m_HoldWaypoint); ERR_Actors.DeleteSpawned(m_HoldWaypoint); }
m_MoveWaypoint = null; m_HoldWaypoint = null;
}
protected void Finish(string reason)
{
Audit(reason);
if (m_Move) m_Move.Stop();
if (m_MineMove) m_MineMove.Stop();
ClearWaypoints();
m_Order = null;
// A late native move must not race another truck's newer report. Retain both
// exact claims until this one submitted identity proves custody or deletion.
if ((m_MineMove && m_MineMove.Unresolved()) || (m_LauncherTrip && m_Move && m_Move.Unresolved())) { m_State = 7; return; }
if (m_Recipient) m_HQ.ReleaseLogistics(m_Recipient.Group, m_Token);
if (m_Truck && m_Truck.SupplyToken == m_Token) { m_Truck.SupplyToken = 0; m_Truck.SupplyHeld = false; }
m_Recipient = null; m_Donor = null; m_Order = null; m_Receiving = null; m_State = 1; m_MineTrip = false; m_LauncherTrip = false; m_MineRecipientSlot = -1;
}
protected void TransferMine(float now)
{
if (m_MineMove)
{
int result = m_MineMove.Tick(now);
if (result == 0) return;
if (result < 0) { Finish("native mine transfer failed or expired; exact submitted identity stays spent"); return; }
m_MineMove = null; m_MinesDelivered++;
Finish("one exact finite PMN4 confirmed in the original carrier inventory"); return;
}
if (!m_Receiving)
{
int strength = m_Recipient.StatusSampler.InitialStrength();
for (int slot = 0; slot < strength && slot < 32; slot++)
{
IEntity actor = m_Recipient.StatusSampler.InitialMember(slot);
if (!actor || !m_Members.Contains(actor) || !m_Recipient.StatusSampler.CanAttribute(slot, actor, false)) continue;
if (!ERR_Actors.CanFight(actor) || vector.DistanceXZ(actor.GetOrigin(), m_Truck.Entity.GetOrigin()) > 4) continue;
if (!m_HQ.MineSupplyReady(m_Recipient.Group, actor)) continue;
IEntity carried;
int count = ERR_InfantryMineTransfer.CarriedCount(actor, m_Recipient.Group, carried);
if (count != 1 && (count != 0 || !m_HQ.MineSupplyReady(m_Recipient.Group, actor, true))) continue;
m_EmptyMineReplacement = count == 0;
m_Receiving = actor; m_MineRecipientSlot = slot; break;
}
if (!m_Receiving) return;
}
if (!m_HQ.MineSupplyReady(m_Recipient.Group, m_Receiving, m_EmptyMineReplacement)) return;
IEntity carried;
int count = ERR_InfantryMineTransfer.CarriedCount(m_Receiving, m_Recipient.Group, carried);
if (!m_Recipient.StatusSampler.CanAttribute(m_MineRecipientSlot, m_Receiving, false)
|| (m_EmptyMineReplacement && count != 0) || (!m_EmptyMineReplacement && count != 1))
{ Finish("original mine recipient lost or its pinned zero/one stock changed"); return; }
foreach (IEntity mine : m_MineStock)
{
if (!ERR_InfantryMineTransfer.ReadyMine(mine) || m_Spent.Contains(mine) || !Contains(m_Truck.Entity, mine)) continue;
ERR_FiniteMagazineReservation reservation = new ERR_FiniteMagazineReservation(m_MineStock, m_Spent, mine);
if (!reservation.Reserve()) continue;
m_MineMove = new ERR_InfantryMineTransfer(m_Mode, m_Truck.Entity, m_Truck.Group, m_Receiving, m_Recipient.Group,
mine, reservation, m_Recipient.StatusSampler, m_MineRecipientSlot, m_EmptyMineReplacement);
m_MineMove.Tick(now);
// The leaf commits before the one native move. Only an unsubmitted refusal
// may release its tentative claim while the same carrier approaches/retries.
if (m_MineMove.Submitted()) return;
reservation.ReleaseUnsubmitted(); m_MineMove = null;
break;
}
}
protected void Transfer(float now)
{
if (m_Move)
{
int result = m_Move.Tick(now);
if (result == 0) return;
if (result < 0) { Finish("native transfer rejected, expired or lost authority; no item retry"); return; }
m_Move = null; m_Delivered++; m_TripDelivered++; m_Served.Insert(m_Receiving); m_Receiving = null;
Audit("exact finite magazine confirmed in original soldier inventory");
if (m_TripDelivered >= 6) { Finish("six-magazine trip allowance reached"); return; }
}
foreach (IEntity actor : m_Members)
{
if (!ERR_Actors.CanFight(actor) || m_Served.Contains(actor) || vector.DistanceXZ(actor.GetOrigin(), m_Truck.Entity.GetOrigin()) > 4) continue;
ERR_HQAmmoSnapshot local = new ERR_HQAmmoSnapshot(); local.Complete = true; local.LauncherComplete = true;
ERR_HQAmmoSampler.Inspect(actor, m_Recipient.Group, local);
if (m_LauncherTrip)
{
if (!local.Valid(1) || !local.LauncherComplete || local.Members != 1 || local.LauncherMuzzles <= 0
|| local.LauncherRounds != 0 || local.LauncherChambers != 0) continue;
}
else if (!local.Complete || local.SmallArmsMuzzles <= 0 || local.SmallArmsRounds + local.SmallArmsChambers > 30) continue;
foreach (IEntity magazine : m_Stock)
{
if (!magazine || m_Spent.Contains(magazine) || !Contains(m_Truck.Entity, magazine) || !ERR_InfantryAmmoTransfer.Compatible(actor, magazine, m_LauncherTrip)) continue;
ERR_FiniteMagazineReservation reservation = new ERR_FiniteMagazineReservation(m_Stock, m_Spent, magazine);
if (!reservation.Reserve()) continue;
m_Receiving = actor;
m_Move = new ERR_InfantryAmmoTransfer(m_Mode, m_Truck.Entity, m_Truck.Group, actor, m_Recipient.Group, magazine, !m_LauncherTrip, m_LauncherTrip);
m_Move.Tick(now);
if (m_Move.Submitted()) { reservation.Commit(); return; }
// No native operation exists yet: approach geometry, stopped state or
// capacity may improve while the same bounded rendezvous remains valid.
// Keep every submitted identity spent, including a synchronous rejection.
reservation.ReleaseUnsubmitted(); m_Move = null; m_Receiving = null;
break; // At most one unsubmitted preflight per member on this slow tick.
}
}
}
void Tick(float now)
{
if (!ERR_ForceRules.Finite(now) || now < 0) return;
// Stopped services retain uncertain leaf refs until exact late proof settles.
if (m_Stopped) { PollTransfers(now); return; }
if (!Current() || now < m_NextTick) return;
m_NextTick = now + 1000;
PollTransfers(now);
if (m_Mounted && m_Mounted.Busy()) return;
if (m_State == 7) return;
if (m_State == 0) { Stock(now); return; }
if (m_State == 1)
{
if (m_Move && !m_Move.Unresolved()) m_Move = null;
return;
}
if (now >= m_Deadline || !TruckValid() || !Quiet(m_Donor) || !Roster() || m_Truck.Combat.Held
|| m_HQ.LogisticsToken(m_Recipient.Group) != m_Token)
{ Finish("rendezvous expired, contact appeared or original ownership changed"); return; }
if (m_Truck.SupplyToken != m_Token) { Finish("truck hold authority lost"); return; }
if (m_State == 2)
{
int delivered = m_Order.Advance(now, m_HQ.Journal());
if (delivered == 0) return;
if (delivered < 0 || m_Truck.SupplyToken != m_Token) { Finish("truck hold downlink interrupted or reservation lost"); return; }
m_Truck.SupplyHeld = true; m_Truck.ClearOrder(); m_HeldAt = -1;
m_Order = null; m_State = 3; Audit("truck received stop; waiting for delivered stopped-position report"); return;
}
if (m_State == 3)
{
// The delivered hold asks the original crew to report only after physically stopping.
if (m_HeldAt < 0)
{
Physics physics = m_Truck.Entity.GetPhysics();
if (!physics) return;
float speed = physics.GetVelocity().Length();
float angular = physics.GetAngularVelocity().Length();
if (!(speed >= 0 && speed <= 0.25 && angular >= 0 && angular <= 0.1)) return;
m_HeldAt = now; m_Donor.NextStatus = now; return;
}
ERR_HQWatchEntry stopped = m_HQ.Watch().Find(m_Donor.ForceSourceId);
if (!ERR_InfantryResupplyRules.Fresh(stopped, now) || stopped.ObservedAt < m_HeldAt) return;
m_Rendezvous = stopped.Position + "3 0 0";
m_Order = m_HQ.Downlink(m_Recipient.Group, "INFANTRY SUPPLY RENDEZVOUS", string.Format("walk to reported stopped ammo truck at %1", m_Rendezvous), m_ReportId);
if (!m_Order) { Finish("infantry radio downlink unavailable"); return; }
m_State = 4; return;
}
if (m_State == 4)
{
int delivered = m_Order.Advance(now, m_HQ.Journal());
if (delivered == 0) return;
if (delivered < 0 || !Waypoints()) { Finish("infantry order interrupted or native waypoint creation failed"); return; }
m_Order = null; m_State = 5; Audit("infantry received reported rendezvous; native movement and physical transfer required");
}
if (m_State == 5)
{
if (m_MineTrip) TransferMine(now);
else Transfer(now);
}
}
string Status()
{
string detail = string.Format("Ground supply: state=%1 trips=%2 infantry moved=%3 stock=%4 spent=%5", State(), m_Trip, m_Delivered, m_Stock.Count(), m_Spent.Count());
return detail + string.Format("; mines moved=%1 initial=%2; %3", m_MinesDelivered, m_MineStock.Count(), m_Reason);
}
void Stop()
{
if (m_Stopped) return;
if (m_Mounted) m_Mounted.Stop();
Finish("service stopped; original forces and all remaining cargo retained"); m_Stopped = true;
}
}
@@ -0,0 +1,97 @@
{
"decision": "FINAL APPROVAL: no remaining concrete source blocker after LauncherMuzzles, full bounded stock scan and settled terminal-refusal corrections.",
"producer": "/tmp/erri-launcher-resupply-1140",
"patch_sha256": "31d28c0f618a72bdb5230612bff9f823e2ad981961ba0c57dfbfd4bb03078252",
"source_sha256": {
"addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c": "52e601a0571b4942c9d4d0ce820727df8498febd6726efbd7da0c5eff7041b58",
"addons/core/Scripts/Game/ERR/10_AI/ERR_HQAmmoSampler.c": "8bb0ec1422887feaa340307d3c60bae50fc82d424fce6390876e580ff8b75e8b",
"addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c": "b01fa948f204d49acbfbdfc5fa641b1d914e0eb91081e92a93816ae52ae88b1e",
"addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryAmmoTransfer.c": "463f8a355aeac40e5ee80981b09e8561fae1b44e179facf911a0fecbfc7722fe"
},
"independent_tests": {
"test_hq_ammo_sampler.py": {
"exit_code": 0,
"test_sha256": "e9c2d147eb1547be471df7459f1be815cdc9ef0a64c9a4ae4ef58936424dbc55",
"log": "/tmp/erri-launcher-review-1130/final-test_hq_ammo_sampler.py.log"
},
"test_infantry_ammo_transfer.py": {
"exit_code": 0,
"test_sha256": "7929033fe661de87f9ca72bc33d64fe996ac8b8e3630a17b4210a6f498b2930f",
"log": "/tmp/erri-launcher-review-1130/final-test_infantry_ammo_transfer.py.log"
},
"test_infantry_resupply_planning.py": {
"exit_code": 0,
"test_sha256": "9929ee0f5a3272c348ee329355b6ba67fdcb745fc9ce1f377f6c989eae91bf9f",
"log": "/tmp/erri-launcher-review-1130/final-test_infantry_resupply_planning.py.log"
},
"test_infantry_resupply_coordinator.py": {
"exit_code": 0,
"test_sha256": "f9cf7d583a25a12cec9f5f80539b2590449560331f4ef34e33ea2db283c5d54a",
"log": "/tmp/erri-launcher-review-1130/final-test_infantry_resupply_coordinator.py.log"
},
"test_mounted_supply_integration.py": {
"exit_code": 0,
"test_sha256": "f52acf7a16c9d5273894eb5a4afe7657a7c97de2ed72ba06671d25e163eea5d7",
"log": "/tmp/erri-launcher-review-1130/final-test_mounted_supply_integration.py.log"
},
"test_hq_multiconvoy_supply.py": {
"exit_code": 0,
"test_sha256": "e58d1d6eba48b2c5c6073d54c76302b2a7250ad22176fa94c386e1bc2ce65588",
"log": "/tmp/erri-launcher-review-1130/final-test_hq_multiconvoy_supply.py.log"
},
"test_hq_ammo_reporting.py": {
"exit_code": 0,
"test_sha256": "bdd4a30440501a2861aca80c951098c48cf999023eaaabd9a2eef82174f8173d",
"log": "/tmp/erri-launcher-review-1130/final-test_hq_ammo_reporting.py.log"
},
"test_hq_watch.py": {
"exit_code": 0,
"test_sha256": "51d5a728b0be83ff957c0df61289d14f90aba109b49365e457c9b200d5ede36a",
"log": "/tmp/erri-launcher-review-1130/final-test_hq_watch.py.log"
},
"test_infantry_mine_coordinator.py": {
"exit_code": 0,
"test_sha256": "2fdb2138685a329cef11557973450bd5189d3d7d657dac5cc51b3df2530fab6b",
"log": "/tmp/erri-launcher-review-1130/final-test_infantry_mine_coordinator.py.log"
},
"test_infantry_supply_pool.py": {
"exit_code": 0,
"test_sha256": "6fc0053337bbb889021c6eba608796ef946ff4d30a1b01cf678927293218d9d2",
"log": "/tmp/erri-launcher-review-1130/final-test_infantry_supply_pool.py.log"
},
"test_hq_status_sampler.py": {
"exit_code": 0,
"test_sha256": "b8582fb71b7ac77a23158ccd6230e8124ef0432831204e4b8992dee3a9c10062",
"log": "/tmp/erri-launcher-review-1130/final-test_hq_status_sampler.py.log"
}
},
"reviewed_final_changes": [
"LauncherMuzzles is detached in Snapshot and validated from0 through32 per sampled member. Inspect counts only non-disposable well-bearing supported launcher muzzles; SCR_MuzzleInMagComponent.CanBeReloaded refusal makes launcher state incomplete. Native APIs verified in installed generated BaseMuzzleComponent and SCR_MuzzleInMagComponent.",
"Delivered demand requires fresh complete original living roster counts and at least one supported launcher muzzle with zero rounds and chambers. Empty GetMagazine is allowed, matching consumed one-round projectile-magazines; no absent-weapon inference.",
"Optional existing loaded magazine retains exact current muzzle-container/attached-entity/well proof. Incoming positive unused finite compatible magazine and original carried weapon custody still required.",
"LauncherStock scans the full bounded96x32 original stock/roster pair space, preventing permanent prefix starvation. No claims/replay/cooldown consumed for rifle-only or incompatible stock.",
"Rifle/mounted requests keep priority; launcher precedes mines. Same spent ledger, original finite inventory, report replay and recipient cooldown remain.",
"Mode reaches physical per-actor sampler, Compatible, transfer constructor and Context. Launcher empty-return is disabled; no speculative recycling of fired projectile identities.",
"Native uncertainty retains exact donor/recipient claims, no retry/refund after submission; stopped polling continues only proof/cleanup.",
"Terminal negative callback settles only with pinned original reciprocal slot/storage, unchanged original managers, exclusive donor inventory, no recipient copy and managed original donor storage. Unknown callback/native false, detached/foreign/replaced custody remain quarantined; spent identity is never refunded or retried."
],
"limitations": [
"No native compile/run by reviewer. Actual RPG/VOG inventory transfer, reload, animations and multiplayer remain unqualified; tests use modeled native boundaries.",
"Does not manufacture ammunition or guarantee automatic reload. Disposable/unsupported no-well configurations remain refused."
],
"source_changes_by_reviewer": "None; only isolated review artifacts/logs/mutation under /tmp.",
"mutations": [
{
"log": "/tmp/erri-launcher-review-1130/prefix-starvation-mutation-red.log",
"result": "128-pair cap restoration compiled then failed actual late-stock admission assertion."
},
{
"log": "/tmp/erri-launcher-review-1130/snapshot-mutation-red.log",
"result": "Removing LauncherMuzzles Snapshot copy compiled then failed detached-count assertion."
},
{
"log": "/tmp/erri-launcher-review-1130/terminal-refusal-mutation-red.log",
"result": "Removing only RefusedAtSource settlement compiled then failed terminal-negative-source-custody assertion; spent remained1."
}
]
}
@@ -0,0 +1,19 @@
check: /tmp/erri-launcher-review-1130/scratch/err-policy-smw7jj3i/check.cpp:278: int main(): Assertion `launcherWire->LauncherMuzzles==1' failed.
Traceback (most recent call last):
File "/tmp/erri-launcher-resupply-1140/tests/test_hq_ammo_sampler.py", line 24, in <module>
run(r'''
~~~^^^^^
#include <cassert>
^^^^^^^^^^^^^^^^^^
...<142 lines>...
}
^
''')
^^^^
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/erri-launcher-review-1130/scratch/err-policy-smw7jj3i/check']' died with <Signals.SIGABRT: 6>.
@@ -0,0 +1,209 @@
// Detached counts of compatible small-arms magazines/chambers and separately
// supported launcher magazines. Unknown inventories are never reported empty.
class ERR_HQAmmoSnapshot
{
bool Complete;
bool LauncherComplete;
bool MineComplete;
int MineMembers;
int ReadyMines;
int Members;
int SmallArmsMuzzles;
int SmallArmsRounds;
int SmallArmsMagazines;
int SmallArmsChambers;
int LauncherMuzzles;
int LauncherRounds;
int LauncherMagazines;
int LauncherChambers;
bool KnownSmallArmsEmpty()
{
return Complete && Members > 0 && SmallArmsMuzzles > 0 && SmallArmsRounds == 0 && SmallArmsChambers == 0;
}
ERR_HQAmmoSnapshot Snapshot()
{
ERR_HQAmmoSnapshot copy = new ERR_HQAmmoSnapshot();
copy.Complete = Complete; copy.LauncherComplete = LauncherComplete; copy.Members = Members;
copy.SmallArmsMuzzles = SmallArmsMuzzles; copy.SmallArmsRounds = SmallArmsRounds;
copy.SmallArmsMagazines = SmallArmsMagazines; copy.SmallArmsChambers = SmallArmsChambers;
copy.LauncherRounds = LauncherRounds; copy.LauncherMagazines = LauncherMagazines;
copy.LauncherChambers = LauncherChambers;
copy.MineComplete = MineComplete; copy.MineMembers = MineMembers; copy.ReadyMines = ReadyMines;
return copy;
}
bool Valid(int living)
{
if (MineMembers < 0 || MineMembers > living || living > 32 || (MineComplete && MineMembers != living)) return false;
if (ReadyMines < 0 || ReadyMines > MineMembers * 128) return false;
if (Members < 0 || Members > living || living > 32 || (Complete && Members != living)) return false;
if (SmallArmsMuzzles < 0 || SmallArmsMuzzles > 1024) return false;
if (SmallArmsRounds < 0 || SmallArmsRounds > 4096000) return false;
if (SmallArmsMagazines < 0 || SmallArmsMagazines > 4096) return false;
if (SmallArmsChambers < 0 || SmallArmsChambers > 4096) return false;
if (LauncherMuzzles < 0 || LauncherMuzzles > Members * 32) return false;
if (LauncherRounds < 0 || LauncherRounds > 4096000) return false;
if (LauncherMagazines < 0 || LauncherMagazines > 4096) return false;
return LauncherChambers >= 0 && LauncherChambers <= 4096;
}
}
class ERR_HQAmmoSampler
{
protected static int Category(BaseWeaponComponent weapon, BaseMuzzleComponent muzzle)
{
if (!weapon || !muzzle) return 0;
EWeaponType kind = weapon.GetWeaponType();
EMuzzleType muzzleKind = muzzle.GetMuzzleType();
if (muzzleKind == EMuzzleType.MT_RPGMuzzle || muzzleKind == EMuzzleType.MT_UGLMuzzle
|| kind == EWeaponType.WT_ROCKETLAUNCHER || kind == EWeaponType.WT_GRENADELAUNCHER) return 2;
if (kind == EWeaponType.WT_RIFLE || kind == EWeaponType.WT_SNIPERRIFLE
|| kind == EWeaponType.WT_MACHINEGUN || kind == EWeaponType.WT_HANDGUN) return 1;
return 0;
}
protected static bool Compatible(BaseMagazineComponent magazine, array<BaseMuzzleComponent> muzzles)
{
if (!magazine || !magazine.GetMagazineWell()) return false;
foreach (BaseMuzzleComponent muzzle : muzzles)
if (muzzle.GetMagazineWell() && muzzle.GetMagazineWell().Type() == magazine.GetMagazineWell().Type()) return true;
return false;
}
// Native magazine search permits derived wells, while the reload UI uses exact
// types. Keep exact counts, but never infer empty from uncertain derived stock.
protected static bool InheritedCandidate(BaseMagazineComponent magazine, array<BaseMuzzleComponent> muzzles)
{
if (!magazine || !magazine.GetMagazineWell()) return false;
foreach (BaseMuzzleComponent muzzle : muzzles)
{
if (!muzzle.GetMagazineWell()) continue;
if (magazine.GetMagazineWell().Type() == muzzle.GetMagazineWell().Type()) continue;
if (magazine.GetMagazineWell().IsInherited(muzzle.GetMagazineWell().Type())) return true;
}
return false;
}
// Caller must enumerate the immutable own roster. No group discovery, position,
// spawning, ammunition mutation, vehicle weapons or virtual inventory work.
static void Inspect(IEntity actor, SCR_AIGroup group, ERR_HQAmmoSnapshot result)
{
if (!result) return;
if (!Replication.IsServer() || !group || group.IsSlave() || group.GetPlayerCount(true) > 0
|| !ERR_Actors.Alive(actor) || ERR_Actors.Player(actor) || ERR_Actors.Group(actor) != group)
{ result.Complete = false; result.LauncherComplete = false; return; }
if (result.Members >= 32) { result.Complete = false; result.LauncherComplete = false; return; }
result.Members++;
InventoryStorageManagerComponent inventory;
inventory = InventoryStorageManagerComponent.Cast(actor.FindComponent(InventoryStorageManagerComponent));
BaseWeaponManagerComponent manager;
manager = BaseWeaponManagerComponent.Cast(actor.FindComponent(BaseWeaponManagerComponent));
if (!inventory || !manager) { result.Complete = false; result.LauncherComplete = false; return; }
array<IEntity> raw = {}; inventory.GetItems(raw);
array<IEntity> weapons = {}; manager.GetWeaponsList(weapons);
if (raw.Count() > 128 || weapons.Count() > 8)
{ result.Complete = false; result.LauncherComplete = false; return; }
array<IEntity> items = {};
foreach (IEntity item : raw)
{
if (!item) { result.Complete = false; result.LauncherComplete = false; continue; }
if (!items.Contains(item)) items.Insert(item);
}
array<BaseMuzzleComponent> small = {};
array<BaseMuzzleComponent> launchers = {};
array<IEntity> seenWeapons = {};
foreach (IEntity weaponEntity : weapons)
{
if (!weaponEntity) { result.Complete = false; result.LauncherComplete = false; continue; }
if (seenWeapons.Contains(weaponEntity)) continue;
seenWeapons.Insert(weaponEntity);
BaseWeaponComponent weapon = BaseWeaponComponent.Cast(weaponEntity.FindComponent(BaseWeaponComponent));
if (!weapon) { result.Complete = false; result.LauncherComplete = false; continue; }
// Throwables have no relevant magazine well; smoke/grenades do not pretend to be rifle ammunition.
if (weapon.GetWeaponType() == EWeaponType.WT_FRAGGRENADE
|| weapon.GetWeaponType() == EWeaponType.WT_SMOKEGRENADE) continue;
bool launcherWeapon = weapon.GetWeaponType() == EWeaponType.WT_ROCKETLAUNCHER
|| weapon.GetWeaponType() == EWeaponType.WT_GRENADELAUNCHER;
array<BaseMuzzleComponent> muzzles = {}; weapon.GetMuzzlesList(muzzles);
if (muzzles.IsEmpty() || muzzles.Count() > 4)
{
if (!launcherWeapon) result.Complete = false;
result.LauncherComplete = false;
continue;
}
foreach (BaseMuzzleComponent muzzle : muzzles)
{
if (!muzzle)
{
if (!launcherWeapon) result.Complete = false;
result.LauncherComplete = false;
continue;
}
if (small.Contains(muzzle) || launchers.Contains(muzzle)) continue;
int category = Category(weapon, muzzle);
if (category == 0) { result.Complete = false; result.LauncherComplete = false; continue; }
if (category == 1) { small.Insert(muzzle); result.SmallArmsMuzzles++; }
else
{
launchers.Insert(muzzle);
SCR_MuzzleInMagComponent reloadable = SCR_MuzzleInMagComponent.Cast(muzzle);
bool supported = muzzle.GetMagazineWell() && !muzzle.IsDisposable();
if (reloadable && !reloadable.CanBeReloaded()) supported = false;
if (supported) result.LauncherMuzzles++;
else result.LauncherComplete = false;
}
if (!muzzle.GetMagazineWell())
{
if (category == 1) result.Complete = false;
else result.LauncherComplete = false; // loose launcher projectiles are not compatible-magazine counts
}
int barrels = muzzle.GetBarrelsCount();
if (barrels < 1 || barrels > 4)
{
if (category == 1) result.Complete = false;
else result.LauncherComplete = false;
continue;
}
for (int barrel = 0; barrel < barrels; barrel++)
{
if (!muzzle.IsBarrelChambered(barrel)) continue;
if (category == 1) result.SmallArmsChambers++;
else result.LauncherChambers++;
}
BaseMagazineComponent loaded = muzzle.GetMagazine();
if (loaded && (!loaded.GetOwner() || !loaded.GetMagazineWell()))
{
if (category == 1) result.Complete = false;
else result.LauncherComplete = false;
}
if (loaded && loaded.GetOwner() && !items.Contains(loaded.GetOwner())) items.Insert(loaded.GetOwner());
}
}
// Physical identity deduplicates loaded/carried enumeration and shared compatible weapon wells.
if (items.Count() > 128) { result.Complete = false; result.LauncherComplete = false; return; }
foreach (IEntity item : items)
{
BaseMagazineComponent magazine = BaseMagazineComponent.Cast(item.FindComponent(BaseMagazineComponent));
if (!magazine) continue;
if (!magazine.GetMagazineWell())
{
// Stock reload and magazine-search predicates reject a candidate with no well.
// It cannot replenish our fully inspected well-bearing small arms. A loaded
// no-well small-arms magazine was already rejected in its muzzle above.
result.LauncherComplete = false;
continue;
}
if (InheritedCandidate(magazine, small)) result.Complete = false;
if (InheritedCandidate(magazine, launchers)) result.LauncherComplete = false;
bool rifle = Compatible(magazine, small);
bool launcher = Compatible(magazine, launchers);
if (!rifle && !launcher) continue;
int rounds = magazine.GetAmmoCount();
int capacity = magazine.GetMaxAmmoCount();
if ((rifle && launcher) || rounds < 0 || capacity < 1 || capacity > 1000 || rounds > capacity)
{
if (rifle) result.Complete = false;
if (launcher) result.LauncherComplete = false;
continue;
}
if (rifle) { result.SmallArmsRounds += rounds; result.SmallArmsMagazines++; }
else { result.LauncherRounds += rounds; result.LauncherMagazines++; }
}
}
}
@@ -0,0 +1,12 @@
check: /tmp/erri-launcher-review-1130/scratch/err-policy-dq0m6hc6/check.cpp:495: int main(): Assertion `f.c.m_Move->Submitted()&&!f.c.m_Move->Unresolved()&&f.c.m_Spent.Count()==1' failed.
Traceback (most recent call last):
File "/tmp/erri-launcher-resupply-1140/tests/test_infantry_ammo_transfer.py", line 338, 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/erri-launcher-review-1130/scratch/err-policy-dq0m6hc6/check']' died with <Signals.SIGABRT: 6>.
@@ -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();
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,37 @@
{
"test_infantry_ammo_transfer.py": {
"exit_code": 0,
"test_sha256": "382b7a567d4a99fa6521160aac5d7eb332eeae20e14bf64eb603d59fdd0bb1a8",
"log": "/tmp/erri-launcher-review-1130/test_infantry_ammo_transfer.py.log"
},
"test_infantry_resupply_planning.py": {
"exit_code": 0,
"test_sha256": "7bcb3461cc16b04a0247e928c1c831a3fb0944f48edcb9125cf7ef463a07168a",
"log": "/tmp/erri-launcher-review-1130/test_infantry_resupply_planning.py.log"
},
"test_infantry_resupply_coordinator.py": {
"exit_code": 0,
"test_sha256": "f9cf7d583a25a12cec9f5f80539b2590449560331f4ef34e33ea2db283c5d54a",
"log": "/tmp/erri-launcher-review-1130/test_infantry_resupply_coordinator.py.log"
},
"test_mounted_supply_integration.py": {
"exit_code": 0,
"test_sha256": "f52acf7a16c9d5273894eb5a4afe7657a7c97de2ed72ba06671d25e163eea5d7",
"log": "/tmp/erri-launcher-review-1130/test_mounted_supply_integration.py.log"
},
"test_hq_multiconvoy_supply.py": {
"exit_code": 0,
"test_sha256": "e58d1d6eba48b2c5c6073d54c76302b2a7250ad22176fa94c386e1bc2ce65588",
"log": "/tmp/erri-launcher-review-1130/test_hq_multiconvoy_supply.py.log"
},
"test_infantry_mine_coordinator.py": {
"exit_code": 0,
"test_sha256": "2fdb2138685a329cef11557973450bd5189d3d7d657dac5cc51b3df2530fab6b",
"log": "/tmp/erri-launcher-review-1130/test_infantry_mine_coordinator.py.log"
},
"test_infantry_supply_pool.py": {
"exit_code": 0,
"test_sha256": "6fc0053337bbb889021c6eba608796ef946ff4d30a1b01cf678927293218d9d2",
"log": "/tmp/erri-launcher-review-1130/test_infantry_supply_pool.py.log"
}
}
@@ -0,0 +1 @@
PASS: actual HQ rotates finite donors, skips unavailable/contacted trucks, uses delivered reports and dispatches at most one trip per planning cadence
@@ -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,2 @@
PASS: actual designated zero/one original carrier selection, settled startup, shared finite claims, no refund after submission
PASS: actual HQ startup grant readiness forwarding is authoritative and fail-closed
@@ -0,0 +1,2 @@
infantry resupply actual rules, radio transitions, owned cleanup and unresolved-operation tests passed
PASS: actual resupply Roster preserves permanent release, original slot mapping and bounded ownership
@@ -0,0 +1 @@
PASS: actual convoy binding, finite identities, disjoint tokens, shared report replay/cooldown, original recipients and HQ cleanup
@@ -0,0 +1 @@
PASS: actual finite convoy supply pool; immutable slots, shared demand state, sparse/capped discovery and permanent shutdown
@@ -0,0 +1 @@
PASS: actual shared mounted stock/history ownership, atomic exact HQ pair claim, and bounded stopped-owner proof polling