--- a/addons/core/Scripts/Game/ERR/00_Core/ERR_SiteService.c +++ b/addons/core/Scripts/Game/ERR/00_Core/ERR_SiteService.c @@ -13,6 +13,7 @@ ERR_LocationComponent Location; IEntity Container; ref array DepotMagazines = {}; // Exact authored startup identities; never refreshed from live inventory. + ref array DepotMines = {}; // At most two authored inert PMN4 per camp, never replenished. SCR_AIGroup Group; ref ERR_SiteRuntime Responders; string ExpectedGuardFaction; @@ -58,7 +59,7 @@ if (index < 0 || index >= m_Sites.Count()) return null; return m_Sites[index]; } - // Optional ground loading consumes only retained original camp magazines. + // Optional ground loading consumes only retained original camp items. int DepotMagazineCount(ERR_SiteRuntime site) { if (!Replication.IsServer() || m_Stopped || m_State != ERR_PrepareResult.READY || !site || !m_Sites.Contains(site)) return 0; @@ -67,11 +68,33 @@ if (site.DepotMagazines.Count() > 96) return 0; return site.DepotMagazines.Count(); } + int DepotMineCount(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.DepotMines.Count() > 2) return 0; + return site.DepotMines.Count(); + } + int DepotItemCount(ERR_SiteRuntime site) + { + if (!site || site.DepotMagazines.Count() > 96 || site.DepotMines.Count() > 2) return 0; + int count = DepotMagazineCount(site) + DepotMineCount(site); + if (count > 96) return 0; + return count; + } + IEntity DepotItem(ERR_SiteRuntime site, int index) + { + if (index < 0 || index >= DepotItemCount(site)) return null; + int magazines = DepotMagazineCount(site); + if (index < magazines) return site.DepotMagazines[index]; + return site.DepotMines[index - magazines]; + } 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]); + int count = DepotItemCount(site); + for (int i = 0; i < count; i++) items.Insert(DepotItem(site, i)); } IEntity DepotMagazine(ERR_SiteRuntime site, int index) { @@ -80,7 +103,7 @@ } bool DepotOriginal(ERR_SiteRuntime site, IEntity item) { - return item && DepotMagazineCount(site) > 0 && site.DepotMagazines.Contains(item); + return item && DepotItemCount(site) > 0 && (site.DepotMagazines.Contains(item) || site.DepotMines.Contains(item)); } bool Owns(IEntity entity) { return entity && m_Entities.Contains(entity); } // Snapshot only nearby mission-owned objects. Distant sites must not block @@ -416,7 +439,12 @@ } 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); + && site.DepotMagazines.Count() + site.DepotMines.Count() < 96) + { + if (item.FindComponent(BaseMagazineComponent)) site.DepotMagazines.Insert(item); + else if (stock.Prefab == "{B05A816C0BF50802}Prefabs/Weapons/Explosives/Mine_PMN4/Mine_PMN4.et" + && site.DepotMines.Count() < 2) site.DepotMines.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 @@ -123,6 +123,7 @@ // 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; + protected int m_DepotMineImports; bool DepotCurrent() { return Current() && m_State == 1 && !m_Move && !m_MineMove && !Unresolved() @@ -136,6 +137,32 @@ foreach (IEntity item : m_Spent) if (item && m_Stock.Contains(item)) committed++; return committed > m_DepotImports; + } + // Mine imports replace only proved outgoing original mine commitments. + // Keep the startup batch binding and both lifetime identity/spent ceilings. + bool DepotMineAvailable() + { + return DepotCurrent() && CaptureMines() && m_MineStock.Count() < 96 && m_Spent.Count() < 96; + } + bool DepotMineNeeded() + { + if (!DepotMineAvailable()) return false; + int committed; + foreach (IEntity item : m_Spent) + if (item && m_MineStock.Contains(item)) committed++; + return committed > m_DepotMineImports; + } + bool AdmitDepotMine(ERR_GroundDepotLoading job, IEntity item) + { + if (!job || !DepotMineNeeded() || !ERR_InfantryMineTransfer.ReadyMine(item) + || m_MineStock.Contains(item) || m_Stock.Contains(item) || m_Spent.Contains(item)) return false; + if (!job.DeliveredTo(this, item, true) || !Contains(m_Truck.Entity, item) + || !ERR_InfantryAmmoTransfer.StoredUnder(item, m_Truck.Entity)) return false; + InventoryItemComponent carried = InventoryItemComponent.Cast(item.FindComponent(InventoryItemComponent)); + if (!carried || !carried.GetParentSlot() || carried.GetParentSlot().GetAttachedEntity() != item) return false; + m_MineStock.Insert(item); m_DepotMineImports++; + Audit("one exact finite camp PMN4 admitted after proved physical courier delivery"); + return true; } ERR_ConvoyVehicle DepotTruck() { @@ -554,7 +581,7 @@ 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); + return detail + string.Format("; mines moved=%1 allowance=%2; %3", m_MinesDelivered, m_MineStock.Count(), m_Reason); } void Stop() { --- a/addons/core/Configs/ERR/EveronSites.conf +++ b/addons/core/Configs/ERR/EveronSites.conf @@ -131,6 +131,7 @@ ERR_SiteProp { Prefab "{CFFEB23FBEA00322}Prefabs/Props/Military/Sandbags/Sandbag_01_short_burlap.et" Offset 5 0 -7 } } Stock { + ERR_SiteStock { Prefab "{B05A816C0BF50802}Prefabs/Weapons/Explosives/Mine_PMN4/Mine_PMN4.et" Count 2 } ERR_SiteStock { Prefab "{FA5C25BF66A53DCF}Prefabs/Weapons/Rifles/AK74/Rifle_AK74.et" Count 2 } ERR_SiteStock { Prefab "{BBB50A815A2F916B}Prefabs/Weapons/Magazines/Magazine_545x39_AK_30rnd_Ball.et" Count 10 } ERR_SiteStock { Prefab "{243948B23D90BECB}Prefabs/Items/Equipment/Binoculars/Binoculars_B8/Binoculars_B8.et" Count 2 } @@ -254,6 +255,7 @@ ERR_SiteProp { Prefab "{CFFEB23FBEA00322}Prefabs/Props/Military/Sandbags/Sandbag_01_short_burlap.et" Offset 5 0 -7 } } Stock { + ERR_SiteStock { Prefab "{B05A816C0BF50802}Prefabs/Weapons/Explosives/Mine_PMN4/Mine_PMN4.et" Count 2 } ERR_SiteStock { Prefab "{FA5C25BF66A53DCF}Prefabs/Weapons/Rifles/AK74/Rifle_AK74.et" Count 2 } ERR_SiteStock { Prefab "{BBB50A815A2F916B}Prefabs/Weapons/Magazines/Magazine_545x39_AK_30rnd_Ball.et" Count 10 } ERR_SiteStock { Prefab "{243948B23D90BECB}Prefabs/Items/Equipment/Binoculars/Binoculars_B8/Binoculars_B8.et" Count 2 } @@ -283,6 +285,7 @@ ERR_SiteProp { Prefab "{CFFEB23FBEA00322}Prefabs/Props/Military/Sandbags/Sandbag_01_short_burlap.et" Offset 5 0 -7 } } Stock { + ERR_SiteStock { Prefab "{B05A816C0BF50802}Prefabs/Weapons/Explosives/Mine_PMN4/Mine_PMN4.et" Count 2 } ERR_SiteStock { Prefab "{FA5C25BF66A53DCF}Prefabs/Weapons/Rifles/AK74/Rifle_AK74.et" Count 2 } ERR_SiteStock { Prefab "{BBB50A815A2F916B}Prefabs/Weapons/Magazines/Magazine_545x39_AK_30rnd_Ball.et" Count 10 } ERR_SiteStock { Prefab "{243948B23D90BECB}Prefabs/Items/Equipment/Binoculars/Binoculars_B8/Binoculars_B8.et" Count 2 } @@ -312,6 +315,7 @@ ERR_SiteProp { Prefab "{CFFEB23FBEA00322}Prefabs/Props/Military/Sandbags/Sandbag_01_short_burlap.et" Offset 5 0 -7 } } Stock { + ERR_SiteStock { Prefab "{B05A816C0BF50802}Prefabs/Weapons/Explosives/Mine_PMN4/Mine_PMN4.et" Count 2 } ERR_SiteStock { Prefab "{FA5C25BF66A53DCF}Prefabs/Weapons/Rifles/AK74/Rifle_AK74.et" Count 2 } ERR_SiteStock { Prefab "{BBB50A815A2F916B}Prefabs/Weapons/Magazines/Magazine_545x39_AK_30rnd_Ball.et" Count 10 } ERR_SiteStock { Prefab "{243948B23D90BECB}Prefabs/Items/Equipment/Binoculars/Binoculars_B8/Binoculars_B8.et" Count 2 } @@ -341,6 +345,7 @@ ERR_SiteProp { Prefab "{CFFEB23FBEA00322}Prefabs/Props/Military/Sandbags/Sandbag_01_short_burlap.et" Offset 5 0 -7 } } Stock { + ERR_SiteStock { Prefab "{B05A816C0BF50802}Prefabs/Weapons/Explosives/Mine_PMN4/Mine_PMN4.et" Count 2 } ERR_SiteStock { Prefab "{FA5C25BF66A53DCF}Prefabs/Weapons/Rifles/AK74/Rifle_AK74.et" Count 2 } ERR_SiteStock { Prefab "{BBB50A815A2F916B}Prefabs/Weapons/Magazines/Magazine_545x39_AK_30rnd_Ball.et" Count 10 } ERR_SiteStock { Prefab "{243948B23D90BECB}Prefabs/Items/Equipment/Binoculars/Binoculars_B8/Binoculars_B8.et" Count 2 } --- a/tests/test_ground_depot_provenance.py +++ b/tests/test_ground_depot_provenance.py @@ -5,14 +5,41 @@ from test_finite_magazine_reservation import translated_reservation B=Path(os.environ.get('ERR_GROUND_DEPOT_ROOT',ROOT)) S=Path(os.environ.get('ERR_SITE_SERVICE_SOURCE',B/'addons/core/Scripts/Game/ERR/00_Core/ERR_SiteService.c')) -I=B/'addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c' -L=B/'addons/core/Scripts/Game/ERR/10_AI/ERR_GroundDepotLoading.c' +I=Path(os.environ.get('ERR_INFANTRY_RESUPPLY_SOURCE',B/'addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c')) +L=Path(os.environ.get('ERR_GROUND_DEPOT_LOADING_SOURCE',B/'addons/core/Scripts/Game/ERR/10_AI/ERR_GroundDepotLoading.c')) +assert 'int DepotItemCount(' in S.read_text() and 'bool AdmitDepotMine(' in I.read_text(), 'finite authored camp mine provenance/import API is absent' +# Read the actual authored configuration, not a duplicated stock fixture. +conf=(B/'addons/core/Configs/ERR/EveronSites.conf').read_text() +pmn='{B05A816C0BF50802}Prefabs/Weapons/Explosives/Mine_PMN4/Mine_PMN4.et' +camps=[] +for match in re.finditer(r' ERR_SiteDefinition \{',conf): + end=match.end();depth=1 + while depth: + if conf[end]=='{':depth+=1 + elif conf[end]=='}':depth-=1 + end+=1 + block=conf[match.start():end] + eligible=bool(re.search(r'^ Kind 3$',block,re.M)) and not re.search(r'^ RevealOnEscape 1$',block,re.M) + stocks=re.findall(r'ERR_SiteStock \{ Prefab "([^"\n]+)" Count (\d+)([^}]*?) \}',block) + mines=[row for row in stocks if row[0]==pmn] + if eligible: + assert len(mines)==1 and mines[0][1]=='2' and 'TargetId' not in mines[0][2] + camps.append(re.search(r'Id "([^"]+)"',block).group(1)) + else:assert not mines +assert len(camps)==5 +assert 'ERR_InfantryMineTransfer' not in S.read_text(), 'Core must not depend on the AI mine transfer layer' source=S.read_text();start=source.index(' if (spec.Kind == ERR_LocationKind.OUTPOST');capture=source[start:source.index(' placement.Item = item;',start)] -access='\n'.join(body(S,k) for k in ('int DepotMagazineCount(', 'void DepotStock(', 'IEntity DepotMagazine(', 'bool DepotOriginal(')) -admit='\n'.join(body(I,k) for k in ('bool DepotCurrent(', 'bool DepotAvailable(', 'bool DepotNeeded(', 'bool AdmitDepot(')) +access='\n'.join(body(S,k) for k in ('int DepotMagazineCount(', 'int DepotMineCount(', 'int DepotItemCount(', 'IEntity DepotItem(', 'void DepotStock(', 'IEntity DepotMagazine(', 'bool DepotOriginal(')) +admit='\n'.join(body(I,k) for k in ('bool DepotCurrent(', 'bool DepotAvailable(', 'bool DepotNeeded(', 'bool AdmitDepot(', 'bool DepotMineAvailable(', 'bool DepotMineNeeded(', 'bool AdmitDepotMine(', 'protected bool CaptureMines(')) delivered=body(L,'bool DeliveredTo(') def adapt(s): s=s.replace('array','Array').replace('out Array items','Array& items').replace('ERR_SiteRuntime site','Site* site').replace('ERR_GroundDepotLoading job','Loading* job').replace('ERR_InfantryResupply service','Service* service') + s=s.replace('protected ', '').replace('ERR_SupplyConvoyTask supply = ERR_SupplyConvoyTask.Cast(m_Convoy);','Supply* supply = m_Convoy;') + s=s.replace('InventoryItemComponent carried = InventoryItemComponent.Cast(item.FindComponent(InventoryItemComponent));','InventoryItem* carried = item->inventoryItem;') + s=s.replace('foreach (IEntity item : m_MineStock)', 'for (IEntity item : m_MineStock)') + s=s.replace('ERR_InfantryMineTransfer.', 'ERR_InfantryMineTransfer::').replace('ERR_InfantryAmmoTransfer.', 'ERR_InfantryAmmoTransfer::') + for name in ('supply','m_MineBatch','carried'):s=s.replace(name+'.',name+'->') + s=s.replace('GetParentSlot().','GetParentSlot()->') for name in ('site','spec','stock','item','m_Stock','m_Spent','m_Mounted','m_Truck','job','m_Move','m_Reservation'): if name not in ('m_Stock','m_Spent'): s=s.replace(name+'.',name+'->') s=s.replace('site->Definition.', 'site->Definition->').replace('site->Responders.', 'site->Responders->') @@ -26,26 +53,47 @@ #include #include using string=std::string;bool server=true; -templatestruct Array:std::vector{using std::vector::vector;int Count(){return this->size();}bool Contains(T x){return std::find(this->begin(),this->end(),x)!=this->end();}void Insert(T x){this->push_back(x);}void RemoveItem(T x){this->erase(std::remove(this->begin(),this->end(),x),this->end());}}; -struct Entity{bool magazine=true;};using IEntity=Entity*; -struct Def{int Kind=3;bool RevealOnEscape=false;};struct Stock{string TargetId;}; -struct Responders{bool Group=true;};struct Site{Def* Definition=nullptr;IEntity Container=nullptr;struct Responders* Responders=nullptr;ArrayDepotMagazines;}; +templatestruct Array:std::vector{using std::vector::vector;int Count(){return this->size();}bool IsEmpty(){return this->empty();}bool Contains(T x){return std::find(this->begin(),this->end(),x)!=this->end();}void Insert(T x){this->push_back(x);}void RemoveItem(T x){this->erase(std::remove(this->begin(),this->end(),x),this->end());}}; +struct Entity;using IEntity=Entity*; +struct Slot{IEntity attached=nullptr;IEntity GetAttachedEntity(){return attached;}}; +struct InventoryItem{Slot* slot=nullptr;Slot* GetParentSlot(){return slot;}}; +struct Entity{bool magazine=true,ready=false,stored=true;Slot slot;InventoryItem inventory;InventoryItem*inventoryItem=&inventory;Entity(){slot.attached=this;inventory.slot=&slot;}}; +// Native ready predicate and storage graph are doubled; actual readiness is separately tested by mine-transfer suites. +struct ERR_InfantryMineTransfer{static bool ReadyMine(IEntity item){return item&&item->ready;}}; +struct ERR_InfantryAmmoTransfer{static bool StoredUnder(IEntity item,IEntity){return item&&item->stored;}}; +struct Def{int Kind=3;bool RevealOnEscape=false;};struct Stock{string TargetId,Prefab;}; +struct Responders{bool Group=true;};struct Site{Def* Definition=nullptr;IEntity Container=nullptr;struct Responders* Responders=nullptr;ArrayDepotMagazines,DepotMines;}; struct Sites{bool m_Stopped=false;int m_State=0;Arraym_Sites;Arraym_Entities;bool Owns(IEntity x){return m_Entities.Contains(x);} ''' + adapt(access)+ '\nvoid Capture(Def* spec,Stock* stock,Site* site,IEntity item){'+adapt(capture)+r'''} }; ''' + translated_reservation()+r''' struct Service; -struct Proof{bool done=true,delivery=true;bool Confirmed(){return done;}bool Delivery(){return delivery;}}; -struct Loading{bool m_Imported=false,valid=true;int m_State=5;Service* m_Service=nullptr;IEntity m_Item=nullptr;Proof* m_Move=nullptr;ERR_FiniteMagazineReservation* m_Reservation=nullptr;bool Valid(){return valid;}bool DeliveredTo(Service*,IEntity);}; +struct Proof{bool done=true,delivery=true,mine=false;bool MineCargo(){return mine;}bool Confirmed(){return done;}bool Delivery(){return delivery;}}; +struct Loading{bool m_Imported=false,valid=true,m_MineCargo=false;int m_State=5;Service* m_Service=nullptr;IEntity m_Item=nullptr;Proof* m_Move=nullptr;ERR_FiniteMagazineReservation* m_Reservation=nullptr;bool Valid(){return valid;}bool DeliveredTo(Service*,IEntity,bool=false);}; struct Mounted{bool busy=false;bool Busy(){return busy;}}; struct Truck{IEntity Entity=nullptr;}; -struct Service{bool current=true,truckValid=true,unresolved=false,m_Move=false,m_MineMove=false;int m_State=1,audits=0,m_DepotImports=0;Arraym_Stock,m_Spent,cargo;Mounted* m_Mounted=nullptr;Truck* m_Truck=nullptr;bool Current(){return current;}bool Unresolved(){return unresolved;}bool TruckValid(bool endpoint){assert(endpoint);return truckValid;}bool Contains(IEntity,IEntity item){return cargo.Contains(item);}void Audit(string){audits++;} +struct Batch{bool complete=true;Arrayitems;bool Complete(){return complete;}int Count(){return items.Count();}IEntity At(int index){return items[index];}}; +struct Supply{Batch*MineStock=nullptr;}; +struct Service{Supply*m_Convoy=nullptr;Batch*m_MineBatch=nullptr;bool m_MineStockCaptured=false;Arraym_MineStock;int m_DepotMineImports=0;bool current=true,truckValid=true,unresolved=false,m_Move=false,m_MineMove=false;int m_State=1,audits=0,m_DepotImports=0;Arraym_Stock,m_Spent,cargo;Mounted* m_Mounted=nullptr;Truck* m_Truck=nullptr;bool Current(){return current;}bool Unresolved(){return unresolved;}bool TruckValid(bool endpoint){assert(endpoint);return truckValid;}bool Contains(IEntity,IEntity item){return cargo.Contains(item);}void Audit(string){audits++;} ''' + adapt(admit)+r'''}; -''' + adapt(delivered).replace('bool DeliveredTo(', 'bool Loading::DeliveredTo(')+r''' +''' + adapt(delivered).replace('bool DeliveredTo(', 'bool Loading::DeliveredTo(').replace('bool mineCargo = false','bool mineCargo')+r''' struct F{Entity cache,item,foreign,truck;Def def;Stock stock;Responders responders;Site site;Sites sites;Arrayoriginal,spent;ERR_FiniteMagazineReservation reservation;Service service;Loading job;Proof proof;Truck vehicle;Mounted mounted; F():reservation(&original,&spent,&item){site.Definition=&def;site.Container=&cache;site.Responders=&responders;sites.m_Sites={&site};sites.m_Entities={&cache};sites.Capture(&def,&stock,&site,&item);original={&item};assert(reservation.Reserve()&&reservation.Commit());job.m_Reservation=&reservation;job.m_Move=&proof;job.m_Item=&item;job.m_Service=&service;vehicle.Entity=&truck;service.m_Truck=&vehicle;service.m_Mounted=&mounted;service.cargo={&item};} }; +struct M:F{Entity initial;Batch batch;Supply supply; + M(){item.magazine=false;item.ready=true;initial.magazine=false;initial.ready=true;batch.items={&initial};supply.MineStock=&batch;service.m_Convoy=&supply;service.m_MineBatch=&batch;service.m_Spent={&initial};job.m_MineCargo=true;proof.mine=true;} +}; int main(){ + // Authored inert mines join detached startup provenance, without replacing magazine APIs. + {F f;f.sites.m_State=1;f.stock.Prefab="{B05A816C0BF50802}Prefabs/Weapons/Explosives/Mine_PMN4/Mine_PMN4.et";Entity mine;mine.magazine=false;mine.ready=true;f.sites.Capture(&f.def,&f.stock,&f.site,&mine);assert(f.sites.DepotMagazineCount(&f.site)==1&&f.sites.DepotMineCount(&f.site)==1&&f.sites.DepotItemCount(&f.site)==2);assert(f.sites.DepotItem(&f.site,1)==&mine&&f.sites.DepotOriginal(&f.site,&mine));Arrayout;f.sites.DepotStock(&f.site,out);out.clear();assert(f.sites.DepotItemCount(&f.site)==2);Entity other;other.magazine=false;other.ready=true;assert(!f.sites.DepotOriginal(&f.site,&other));f.sites.Capture(&f.def,&f.stock,&f.site,&other);Entity third;third.magazine=false;third.ready=true;f.sites.Capture(&f.def,&f.stock,&f.site,&third);assert(f.sites.DepotMineCount(&f.site)==2&&!f.sites.DepotOriginal(&f.site,&third));} + for(int bad=0;bad<4;bad++){F f;f.item.magazine=false;f.item.ready=true;f.stock.Prefab="{B05A816C0BF50802}Prefabs/Weapons/Explosives/Mine_PMN4/Mine_PMN4.et";f.site.DepotMagazines.clear();if(bad==0)f.def.RevealOnEscape=true;if(bad==1)f.def.Kind=4;if(bad==2)f.stock.TargetId="radio";if(bad==3)f.stock.Prefab="foreign";f.sites.Capture(&f.def,&f.stock,&f.site,&f.item);assert(f.site.DepotMines.empty());} + {M f;assert(f.service.DepotMineNeeded()&&!f.service.DepotNeeded());assert(f.service.AdmitDepotMine(&f.job,&f.item));assert(f.service.m_MineStock.Count()==2&&f.service.m_DepotMineImports==1&&f.service.m_Stock.empty());assert(!f.service.DepotMineNeeded()&&!f.service.AdmitDepotMine(&f.job,&f.item));assert(f.spent.Count()==1&&f.service.m_Spent.Count()==1);ERR_FiniteMagazineReservation next(&f.service.m_MineStock,&f.service.m_Spent,&f.item);assert(next.Reserve()&&next.Commit());assert(f.service.DepotMineNeeded());} + // Known mine expenditure only; missing/deleted/magazine identities never become mine demand. + {M f;f.service.m_Spent.clear();assert(!f.service.DepotMineNeeded());f.service.m_Spent={nullptr,&f.foreign};f.service.m_Stock={&f.foreign};assert(f.service.DepotNeeded()&&!f.service.DepotMineNeeded());f.service.m_MineStock={nullptr};assert(!f.service.DepotMineNeeded());} + for(int bad=0;bad<21;bad++){M f;assert(f.service.DepotMineNeeded());if(bad==0)f.service.current=false;if(bad==1)f.service.m_State=7;if(bad==2)f.batch.complete=false;if(bad==3)f.supply.MineStock=nullptr;if(bad==4)f.service.m_MineBatch=nullptr;if(bad==5)f.service.m_MineStock.resize(96,&f.initial);if(bad==6)f.service.m_Spent.resize(96,&f.initial);if(bad==7)f.item.ready=false;if(bad==8)f.service.m_Stock.Insert(&f.item);if(bad==9)f.service.m_Spent.Insert(&f.item);if(bad==10)f.service.cargo.clear();if(bad==11)f.item.stored=false;if(bad==12)f.item.inventoryItem=nullptr;if(bad==13)f.item.inventory.slot=nullptr;if(bad==14)f.item.slot.attached=&f.foreign;if(bad==15)f.job.m_MineCargo=false;if(bad==16)f.proof.mine=false;if(bad==17)f.job.m_State=7;if(bad==18)f.job.valid=false;if(bad==19)f.proof.done=false;if(bad==20)f.service.m_Spent.clear();int before=f.service.m_MineStock.Count();assert(!f.service.AdmitDepotMine(&f.job,&f.item));assert(f.service.m_MineStock.Count()==before&&f.service.m_DepotMineImports==0);} + {M f;f.batch.items.clear();f.service.m_Spent.clear();assert(!f.service.DepotMineNeeded()&&!f.service.AdmitDepotMine(&f.job,&f.item));} + {M f;assert(f.service.DepotMineNeeded());f.service.m_MineStock.resize(95,&f.initial);assert(f.service.AdmitDepotMine(&f.job,&f.item));assert(f.service.m_MineStock.Count()==96&&f.service.DepotCurrent()&&!f.service.DepotMineAvailable());} + {F f;Arrayout;f.sites.DepotStock(&f.site,out);assert(out.empty());f.sites.m_State=1;f.sites.DepotStock(&f.site,out);assert(out.Count()==1&&out[0]==&f.item);out[0]=&f.foreign;out.clear();assert(f.sites.DepotOriginal(&f.site,&f.item)&&!f.sites.DepotOriginal(&f.site,&f.foreign));assert(f.sites.DepotMagazine(&f.site,-1)==nullptr&&f.sites.DepotMagazine(&f.site,1)==nullptr);f.sites.m_Stopped=true;assert(!f.sites.DepotOriginal(&f.site,&f.item));} for(int kind=0;kind<6;kind++){F f;f.site.DepotMagazines.clear();if(kind==0)f.def.RevealOnEscape=true;if(kind==1)f.def.Kind=4;if(kind==2)f.stock.TargetId="radio";if(kind==3)f.item.magazine=false;if(kind==4)f.site.DepotMagazines.resize(96,&f.foreign);f.sites.Capture(&f.def,&f.stock,&f.site,&f.item);assert(f.site.DepotMagazines.Contains(&f.item)==(kind==5));} for(int bad=0;bad<7;bad++){F f;f.sites.m_State=1;if(bad==0)server=false;if(bad==1)f.sites.m_Sites.clear();if(bad==2)f.site.Container=nullptr;if(bad==3)f.sites.m_Entities.clear();if(bad==4)f.site.Responders=nullptr;if(bad==5)f.responders.Group=false;if(bad==6)f.site.DepotMagazines.resize(97,&f.item);assert(f.sites.DepotMagazineCount(&f.site)==0);server=true;} @@ -80,4 +128,4 @@ {F f;f.job.m_State=7;f.proof.done=true;assert(!f.service.AdmitDepot(&f.job,&f.item));f.job.m_State=5;f.job.valid=false;assert(!f.service.AdmitDepot(&f.job,&f.item));} } ''') -print('PASS: actual authored identity capture, READY-only detached provenance, same shared stock admission, completed exact two-leg proof, lifetime caps and Stop no late adoption') +print('PASS: actual mine/magazine provenance and separated finite mine imports; actual authored identity capture, READY-only detached provenance, same shared stock admission, completed exact two-leg proof, lifetime caps and Stop no late adoption')