Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
88 lines
7.8 KiB
Diff
88 lines
7.8 KiB
Diff
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c
|
|
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_InfantryResupply.c
|
|
@@ -106,10 +106,10 @@
|
|
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)))
|
|
+ if (m_Move && !m_Move.Unresolved() && (m_State <= 1 || m_State == 7))
|
|
{
|
|
m_Move = null;
|
|
- if (m_State == 7) Finish("launcher custody settled; original claims released without retry or refund");
|
|
+ if (m_State == 7) Finish("magazine custody settled; original claims released without retry or refund");
|
|
}
|
|
if (m_MineMove && !m_MineMove.Unresolved() && (m_State <= 1 || m_State == 7))
|
|
{
|
|
@@ -425,8 +425,8 @@
|
|
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; }
|
|
+ // exact claims until the existing transfer leaf reports that its operation has settled.
|
|
+ if ((m_MineMove && m_MineMove.Unresolved()) || (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;
|
|
--- a/tests/test_infantry_resupply_planning.py
|
|
+++ b/tests/test_infantry_resupply_planning.py
|
|
@@ -251,6 +251,20 @@
|
|
f.ammo.ReadyMines=1;f.ammo.MineComplete=false;assert(!f.s.TryPlanMines(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0));f.ammo.MineComplete=true;
|
|
batch.complete=false;assert(!f.s.TryPlanMines(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0));batch.complete=true;
|
|
assert(f.s.TryPlanMines(&f.recipient,&f.donor,&f.demand,&f.donorStatus,0));assert(f.s.m_MineTrip&&f.s.m_MineStock.Count()==1);}
|
|
+ // An accepted unresolved rifle move keeps both original claims across a new
|
|
+ // report after the shared180s cooldown. Distinct convoy slots own distinct tokens.
|
|
+ {Fixture first,second;Leaf leaf;leaf.unresolved=true;Map<int> consumed;Map<float> cooldown;
|
|
+ first.hq.convoys.tasks={&first.convoy,&second.convoy};first.hq.m_Units={&first.recipient,&first.donor,&second.donor};second.s.m_HQ=&first.hq;
|
|
+ assert(first.s.Bind(&first.convoy,0,&consumed,&cooldown));assert(second.s.Bind(&second.convoy,1,&consumed,&cooldown));
|
|
+ first.s.Stock(0);second.s.Stock(0);assert(first.Plan());first.s.m_Move=&leaf;first.s.Finish("accepted native rifle move remains unresolved");
|
|
+ assert(first.s.m_Move==&leaf&&leaf.Unresolved());
|
|
+ first.Refresh(180001,2);second.donorStatus.ObservedAt=180001;
|
|
+ assert(!second.s.TryPlan(&first.recipient,&second.donor,&first.demand,&second.donorStatus,180001));
|
|
+ assert(first.s.m_State==7&&first.recipient.LogisticsReservation==100001&&first.truck.SupplyToken==100001);
|
|
+ assert(consumed.Get("recipient")==1&&cooldown.Get("recipient")==180000&&second.s.m_Trip==0);
|
|
+ leaf.unresolved=false;first.s.Finish("settled");assert(first.recipient.LogisticsReservation==0);
|
|
+ assert(second.s.TryPlan(&first.recipient,&second.donor,&first.demand,&second.donorStatus,180001));
|
|
+ assert(first.recipient.LogisticsReservation==100129&&second.truck.SupplyToken==100129&&first.truck.SupplyToken==0);}
|
|
// A submitted unknown mine retains the real HQ claim across a newer delivered
|
|
// report and expired shared cooldown; a second truck cannot race the same actor.
|
|
{Fixture first,second;Leaf leaf;leaf.unresolved=true;first.s.Stock(0);assert(first.Plan());first.s.m_MineMove=&leaf;first.s.m_MineTrip=true;first.s.Finish("unknown native move");
|
|
--- a/tests/test_infantry_resupply_coordinator.py
|
|
+++ b/tests/test_infantry_resupply_coordinator.py
|
|
@@ -81,7 +81,7 @@
|
|
{Fixture f;f.order.result=-1;f.s.Tick(0);assert(f.s.m_State==1&&f.hq.token==0&&f.truck.SupplyToken==0&&!f.truck.SupplyHeld&&f.truck.clears==0);}
|
|
{Fixture f;f.s.m_State=4;f.order.result=-1;f.s.Tick(0);assert(f.s.waypointCalls==0&&f.s.transfers==0&&f.hq.token==0);}
|
|
{Fixture f;f.hq.token=99;f.truck.SupplyToken=99;f.truck.SupplyHeld=true;f.s.Tick(0);assert(f.s.m_State==1&&f.hq.token==99&&f.truck.SupplyToken==99&&f.truck.SupplyHeld);}
|
|
- {Fixture f;Waypoint own1{1},own2{2},other{3};f.group.points={&own1,&other,&own2};f.s.m_MoveWaypoint=&own1;f.s.m_HoldWaypoint=&own2;Leaf leaf;f.s.m_Move=&leaf;f.truck.SupplyHeld=true;f.s.Stop();assert(f.group.points.size()==1&&f.group.points[0]==&other&&leaf.stops==1&&f.s.m_Move==&leaf&&f.hq.token==0&&!f.truck.SupplyHeld&&f.s.m_Stopped);int audits=f.s.audits;f.s.Stop();f.s.Tick(5000);assert(f.s.audits==audits&&leaf.stops==1);}
|
|
+ {Fixture f;Waypoint own1{1},own2{2},other{3};f.group.points={&own1,&other,&own2};f.s.m_MoveWaypoint=&own1;f.s.m_HoldWaypoint=&own2;Leaf leaf;f.s.m_Move=&leaf;f.truck.SupplyHeld=true;f.s.Stop();assert(f.group.points.size()==1&&f.group.points[0]==&other&&leaf.stops==1&&f.s.m_Move==&leaf&&f.hq.token==7&&f.truck.SupplyHeld&&f.s.m_State==7&&f.s.m_Stopped);int audits=f.s.audits;f.s.Stop();f.s.Tick(5000);assert(f.s.audits==audits&&leaf.stops==1);leaf.unresolved=false;f.s.Tick(6000);assert(f.hq.token==0&&!f.truck.SupplyHeld&&f.s.m_State==1&&!f.s.m_Move);}
|
|
{Fixture f;Leaf leaf;f.s.m_Move=&leaf;f.s.Finish("timeout");f.s.Tick(0);assert(f.s.m_Move==&leaf);leaf.unresolved=false;f.s.Tick(1000);assert(f.s.m_Move==nullptr);}
|
|
{Fixture f;f.s.m_State=3;f.physics.linear.x=std::numeric_limits<float>::quiet_NaN();f.s.Tick(0);assert(f.s.m_HeldAt==-1&&f.donor.NextStatus==-1);}
|
|
{Fixture f;f.s.m_State=3;f.physics.angular.x=std::numeric_limits<float>::quiet_NaN();f.s.Tick(0);assert(f.s.m_HeldAt==-1&&f.donor.NextStatus==-1);}
|
|
--- a/tests/test_infantry_ammo_transfer.py
|
|
+++ b/tests/test_infantry_ammo_transfer.py
|
|
@@ -150,6 +150,24 @@
|
|
rifle.parent=&recipient;loaded.parent=&rifle;loaded.magazine=∅empty.owner=&loaded;empty.well=&well;empty.rounds=0;muzzle.loaded=∅loaded.item=&loadedItem;loadedItem.slot=&loadedSlot;loadedSlot.container=&muzzle;loadedSlot.attached=&loaded;}
|
|
};
|
|
int main(){
|
|
+
|
|
+ // The shared force claim is necessary: independent rifle leaves can otherwise
|
|
+ // submit the same still-carried empty to two different trucks. Native acceptance
|
|
+ // is delayed here; this proves submission interference, not an engine outcome.
|
|
+ {EmptyFixture first;Fixture second;ERR_GameMode::current=&first.mode;
|
|
+ first.ledger.records.Insert(&second.donorRecord);second.source.selected=&second.sourceStorage;
|
|
+ auto old=first.Job();assert(old.Tick(0)==PENDING&&old.Unresolved());
|
|
+ assert(old.m_ReturnMagazine==&first.empty&&old.m_ReturnStorage==&first.sourceStorage);
|
|
+ old.Stop();assert(old.Unresolved()&&old.Tick(180001)==FAILED);
|
|
+ ERR_InfantryAmmoTransfer next(&first.mode,&second.truck,&second.donorGroup,&first.recipient,&first.recipientGroup,&second.magEntity,true);
|
|
+ assert(next.Tick(180001)==PENDING&&next.Unresolved());
|
|
+ assert(first.destination.calls==2&&next.m_ReturnMagazine==old.m_ReturnMagazine);
|
|
+ assert(next.m_ReturnStorage==&second.sourceStorage&&next.m_ReturnStorage!=old.m_ReturnStorage);
|
|
+ assert(first.destination.items.Contains(&first.empty)&&first.source.calls==0&&second.source.calls==0);
|
|
+ // No duplicate object is invented: one physical completion can settle only its
|
|
+ // original donor proof; the competing outstanding submission remains unknown.
|
|
+ first.Return();old.m_ReturnCallback->OnComplete();assert(!old.Unresolved()&&next.Unresolved());
|
|
+ assert(old.Tick(180002)==FAILED&&first.source.calls==0&&second.source.calls==0);}
|
|
// Rifle moves must obey the same irreversible cancellation boundary as launchers.
|
|
// Native inventory processing may synchronously invoke mission/ownership events.
|
|
for(int loss=0;loss<4;loss++){Fixture f;auto t=f.Job();
|