--- a/addons/core/Scripts/Game/ERR/00_Core/ERR_SiteService.c +++ b/addons/core/Scripts/Game/ERR/00_Core/ERR_SiteService.c @@ -12,6 +12,7 @@ ref ERR_SiteDefinition Definition; ERR_LocationComponent Location; IEntity Container; + ref array DepotMagazines = {}; // Exact authored startup identities; never refreshed from live inventory. SCR_AIGroup Group; ref ERR_SiteRuntime Responders; string ExpectedGuardFaction; @@ -56,6 +57,30 @@ { if (index < 0 || index >= m_Sites.Count()) return null; return m_Sites[index]; + } + // Optional ground loading consumes only retained original camp magazines. + int DepotMagazineCount(ERR_SiteRuntime site) + { + if (!Replication.IsServer() || m_Stopped || m_State != ERR_PrepareResult.READY || !site || !m_Sites.Contains(site)) return 0; + if (!site.Definition || site.Definition.Kind != ERR_LocationKind.OUTPOST || site.Definition.RevealOnEscape) return 0; + if (!site.Container || !Owns(site.Container) || !site.Responders || !site.Responders.Group) return 0; + if (site.DepotMagazines.Count() > 96) return 0; + return site.DepotMagazines.Count(); + } + void DepotStock(ERR_SiteRuntime site, out array items) + { + items = {}; + int count = DepotMagazineCount(site); + for (int i = 0; i < count; i++) items.Insert(site.DepotMagazines[i]); + } + IEntity DepotMagazine(ERR_SiteRuntime site, int index) + { + if (index < 0 || index >= DepotMagazineCount(site)) return null; + return site.DepotMagazines[index]; + } + bool DepotOriginal(ERR_SiteRuntime site, IEntity item) + { + return item && DepotMagazineCount(site) > 0 && site.DepotMagazines.Contains(item); } bool Owns(IEntity entity) { return entity && m_Entities.Contains(entity); } // Snapshot only nearby mission-owned objects. Distant sites must not block @@ -389,6 +414,8 @@ document.SetTarget(stock.TargetId); } ERR_StockPlacement placement = new ERR_StockPlacement(); + if (spec.Kind == ERR_LocationKind.OUTPOST && !spec.RevealOnEscape && stock.TargetId.IsEmpty() + && item.FindComponent(BaseMagazineComponent) && site.DepotMagazines.Count() < 96) site.DepotMagazines.Insert(item); placement.Item = item; placement.Inventory = inventory; m_Placements.Insert(placement); --- a/addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c +++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c @@ -102,6 +102,36 @@ 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; --- a/tools/check.py +++ b/tools/check.py @@ -15,6 +15,10 @@ subprocess.run([sys.executable,str(ROOT/'tests/test_prison_ai.py')],check=True) subprocess.run([sys.executable,str(ROOT/'tests/test_native_player_death_fixture.py')],check=True) subprocess.run([sys.executable,str(ROOT/'tests/test_hq_planner_budget.py')],check=True) + subprocess.run([sys.executable,str(ROOT/'tests/test_ground_depot_loading.py')],check=True) + subprocess.run([sys.executable,str(ROOT/'tests/test_ground_depot_lifecycle.py')],check=True) + subprocess.run([sys.executable,str(ROOT/'tests/test_ground_depot_transfer.py')],check=True) + subprocess.run([sys.executable,str(ROOT/'tests/test_ground_depot_provenance.py')],check=True) ids={};graph={};resources={};paths={};owners={} for p in sorted((ROOT/'addons').glob('*/addon.gproj')): text=p.read_text();g=re.search(r'GUID "([A-F0-9]{16})"',text)[1] --- a/docs/current-status.md +++ b/docs/current-status.md @@ -1,4 +1,8 @@ # Current development status + +**2 October 2026 — finite camp-to-truck ammunition loading:** HQ can assign one available original camp responder to carry one exact original cache magazine to a nearby original ammunition truck after delivered radio orders. Recorded finite magazine expenditure gates admission; separate pickup/delivery proof, unchanged rounds, original identities and bounded reservations prevent refill or replay. Existing tactical/logistics work retains priority. [Behavior and limits](ground-depot-loading.md). + +The full **277-program regression suite passed** before native compilation. The first native run exposed getter formatting and a helper-name conflict; its [failed evidence](evidence/native-2026-10-02-ground-depot-compile/report.json) is retained. After those behavior-preserving corrections, all four relevant programs passed again. The [corrected hidden/muted normal route](evidence/native-2026-10-02-ground-depot-integration/report.json) completes **63 assertions and one round**, with zero script/runtime failures and thirteen retained installed-resource errors. Cleanup and unchanged source/external inputs are confirmed. This qualifies compilation and normal-loop integration only. [Independent review, mutation evidence and preservation](evidence/ground-depot-loading-2026-10-02/review.json). The existing playtest package predates this feature. Original missions, inherited changes and that package remain preserved. Physical loading, human play, real multiplayer, comparative FPS and clean release remain unfinished. **2 October 2026 — HQ planning removes repeated force counts:** the final pursuit selection pass now counts global commitments once after cleanup/retasking/investigation and increments only for a newly queued order. Separate hunting/global limits, orders and reservations are preserved. A controlled 64-belief/128-unit no-dispatch regression reduces the repeated count scans from over 10,000 unit visits to 128; this measures that subpass only, **not FPS**. --- a/docs/infantry-resupply.md +++ b/docs/infantry-resupply.md @@ -78,3 +78,7 @@ Each trip still transfers at most one exact identity from the original finite convoy mine snapshot through the shared spent ledger. Existing report freshness, priority, cooldown, callback proof and uncertain-operation quarantine are unchanged. This replenishes carried stock only: it does not reset `EngineerMineAttempted`, authorize repeated mine laying, spawn replacement stock or refund submitted items. Six focused actual-method programs passed independently: startup designation and settings changes, original equipment membership, known-zero versus unknown inventory, pinned modes through retries, delivered-report admission, and existing callback/quarantine behavior. The old planning method compiled and failed the new designated-zero admission assertion. All **272 registered regression programs pass across a resumed run**: the initial `native_package` stop exhausted the per-user `/tmp` quota; verified relocation of completed task snapshots recovered space, and that program plus the remaining suite passed with the existing canonical `/tmp` guards. The infrastructure failure is preserved. The [hidden/muted frozen-source normal route](evidence/native-2026-10-02-zero-mine-integration/report.json) compiles this increment and passes **63 assertions and one round**, with zero script/runtime failures and thirteen retained installed-resource errors. Cleanup and unchanged inputs are confirmed. This proves compilation/base-loop integration, not actual zero-stock transfer. [Review and preservation](evidence/zero-mine-resupply-2026-10-02/review.json). Actual walking mine delivery, repeated placement and multiplayer remain unqualified; earlier native evidence retains its original source scope. + +### Authored camp imports (2 October 2026) + +The historical one-time truck snapshot above now has one narrow extension: [proved finite camp deliveries](ground-depot-loading.md) can append an original startup camp magazine after two separately acknowledged native moves. Existing stock/spent entries and the lifetime 96-identity limit remain intact; arbitrary later deposits and returned empty magazines remain excluded. Admission requires conservative known finite-magazine commitments beyond prior imports, not merely unused truck capacity. Autonomous physical loading remains unqualified. --- a/docs/ai-feature-migration.json +++ b/docs/ai-feature-migration.json @@ -2562,6 +2562,15 @@ "evidence": "Six focused actual-method programs independently pass, with compiled old-planning assertion failure. All272 registered programs pass across a resumed canonical-/tmp run after verified quota recovery; initial infrastructure failure preserved. Current frozen-source normal route63 assertions/one round/zero script failures;13 installed-resource errors retained, cleanup confirmed. Compilation/base-loop integration only; physical zero-stock transfer remains unqualified.", "remaining": "Physical walking mine transfer for this increment, per-actor full need manifests, repeated placement and true multiplayer qualification.", "detail": "docs/infantry-resupply.md#designated-engineer-zero-stock-replacement-2-october-2026" + }, + { + "id": "resupply", + "updated_on": "2026-10-02", + "status": "partial", + "implementation": "Bounded opportunistic camp-to-original-truck loading through one existing mobile responder, delivered radio hold/new stopped report/porter order, two isolated native callback+custody proofs, retained exact original identities and finite shared allowance. Admission requires prior committed magazine expenditure beyond imports. No generated stock, actors or native retries.", + "evidence": "277 programs passed initial integration, four relevant programs rerun after behavior-preserving native compile fixes. Failed compile retained; corrected frozen normal route63 assertions/one round/zero script failures/13 retained resource errors. Cleanup and unchanged inputs confirmed.", + "remaining": "Native autonomous depot walking/access/two-leg delivery, multiplayer, full logistics parity and release qualification.", + "detail": "docs/ground-depot-loading.md" } ] }