Finish airborne extraction after 30 seconds and hold rescue aircraft; record Workshop 0.0.134
continuous-integration/drone/push Build is passing

This commit is contained in:
2026-10-31 15:01:51 +01:00
parent d21adb90d5
commit 2c2158702d
14 changed files with 981 additions and 22 deletions
+13
View File
@@ -23,3 +23,16 @@ int main() {
}
""")
print("PASS: all survivors required, continuous eight-second boarding hold, reset on exit and clock rollback")
flight_rules=body(source,'static float AirborneSince(')+body(source,'static bool FlightComplete(')
run("#include <cassert>\n"+flight_rules+"""
int main(){
assert(AirborneSince(false,-1,1000)==-1);
float since=AirborneSince(true,-1,1000);
assert(!FlightComplete(since,30999));assert(FlightComplete(since,31000));
assert(AirborneSince(false,since,2000)==-1);
assert(AirborneSince(true,since,0)==0);
assert(!FlightComplete(-1,100000));assert(!FlightComplete(5000,4999));
}
""")
print('PASS: thirty continuous airborne seconds, grounded reset and clock rollback')
+26 -14
View File
@@ -1,4 +1,4 @@
"""Execute production fleet ownership, recovery and escape-boundary gates."""
"""Execute production fleet ownership, recovery and airborne completion gates."""
from policy_harness import ROOT, body, run
source = ROOT / 'addons/core/Scripts/Game/ERR/00_Core/ERR_ExtractionService.c'
@@ -12,10 +12,11 @@ s = s.replace('foreach (ERR_RescueFlightComponent ', 'for (Flight* ').replace('f
for name in ('transport', 'departing', 'life', 'm_Roster'):
s = s.replace(name + '.', name + '->')
s = s.replace('departing->Aircraft().GetOrigin()', 'departing->Aircraft()->GetOrigin()')
s = s.replace('GetGame().GetWorld().GetWorldTime()', '0')
for name in ('ERR_Actors', 'CompartmentAccessComponent', 'Vehicle', 'Replication', 'ERR_MissionRules', 'ERR_ExtractionState', 'ERR_Phase'):
s = s.replace('GetGame().GetWorld().GetWorldTime()', 'this->now')
for name in ('ERR_Actors', 'CompartmentAccessComponent', 'Vehicle', 'Replication', 'ERR_MissionRules', 'ERR_RescueRules', 'ERR_ExtractionState', 'ERR_Phase'):
s = s.replace(name + '.', name + '::')
s = s.replace('vector.DistanceXZ', 'Position::DistanceXZ').replace('int aboard;', 'int aboard=0;')
rules = body(source, 'static float AirborneSince(') + body(source, 'static bool FlightComplete(')
run(r'''
#include <algorithm>
#include <cassert>
@@ -32,14 +33,18 @@ struct Vehicle {bool alive=true;Position position;Position GetOrigin(){return po
struct Actor {bool alive=true;Vehicle* vehicle=nullptr;};
struct ERR_PlayerLife {::Actor* Actor;int AccountId;};
struct Roster {Array<ERR_PlayerLife*> lives;void Participants(Array<ERR_PlayerLife*>& out){out=lives;}};
struct Flight {Vehicle* aircraft;bool lost=false,landed=true;bool Lost(){return lost;}bool Landed(){return landed;}Vehicle* Aircraft(){return aircraft;}};
struct Flight {Vehicle* aircraft;bool lost=false,landed=true,airborne=true;bool AirborneAfterPickup(){return airborne;}bool Lost(){return lost;}bool Landed(){return landed;}Vehicle* Aircraft(){return aircraft;}};
struct ERR_Actors {template<class T>static bool Alive(T* a){return a&&a->alive;}};
struct CompartmentAccessComponent {static Vehicle* GetVehicleIn(Actor* a){return a->vehicle;}};
struct Replication {static inline bool server=true;static bool IsServer(){return server;}};
namespace ERR_Phase {enum {PREPARING,PRISON,ESCAPING,EXTRACTION,VICTORY,DEFEAT};}
namespace ERR_ExtractionState {enum {WAITING,ESCAPED,LOST};}
struct ERR_MissionRules {static bool Terminal(int p){return p>=ERR_Phase::VICTORY;}};
struct ERR_RescueRules {
''' + rules + r'''
};
struct Service {
float now=0,m_AirborneSince=-1;
bool m_Stopped=false,m_Assigned=true,m_Begun=true,m_Mode=false;int m_Stage=5,m_State=ERR_ExtractionState::WAITING;
Flight* m_Transport=nullptr;Array<Flight*> m_Transports;Vehicle* m_Vehicle=nullptr;Roster* m_Roster=nullptr;
Position m_Pickup{500,0},m_Center;float m_Radius=1000;Array<int> m_Escapees;int m_ParticipantCount=0;
@@ -65,19 +70,26 @@ int main(){
service.m_Transports.Insert(nullptr);assert(!service.FleetAlive());service.m_Transports.pop_back();
service.m_Stopped=true;assert(!service.CarryingSurvivorsToSafety());service.m_Stopped=false;
service.m_Assigned=false;assert(!service.CarryingSurvivorsToSafety());service.m_Assigned=true;
// The first transport crossing the boundary cannot declare victory while the second is still inside.
second.position={900,0};service.Tick(ERR_Phase::EXTRACTION);assert(service.m_State==ERR_ExtractionState::WAITING&&service.m_Escapees.IsEmpty());
second.position={1400,0};service.Tick(ERR_Phase::EXTRACTION);assert(service.m_State==ERR_ExtractionState::WAITING); // Outside mission radius but under 1km from pickup.
second.position={1800,0};service.Tick(ERR_Phase::ESCAPING);assert(service.m_State==ERR_ExtractionState::WAITING);
service.m_Stage=4;service.Tick(ERR_Phase::EXTRACTION);assert(service.m_State==ERR_ExtractionState::WAITING);service.m_Stage=5;
service.Tick(ERR_Phase::EXTRACTION);assert(service.m_State==ERR_ExtractionState::ESCAPED);
service.Tick(ERR_Phase::ESCAPING);assert(service.m_AirborneSince==-1);
service.m_Stage=4;service.Tick(ERR_Phase::EXTRACTION);assert(service.m_AirborneSince==-1);service.m_Stage=5;
// One grounded transport prevents the timer, regardless of distance.
f2.airborne=false;service.Tick(ERR_Phase::EXTRACTION);assert(service.m_AirborneSince==-1);
f2.airborne=true;service.now=1000;service.Tick(ERR_Phase::EXTRACTION);assert(service.m_State==ERR_ExtractionState::WAITING);
service.now=30999;service.Tick(ERR_Phase::EXTRACTION);assert(service.m_State==ERR_ExtractionState::WAITING);
// Distance is irrelevant, even right above the pickup.
first.position={500,0};second.position={500,0};
service.now=31000;service.Tick(ERR_Phase::EXTRACTION);assert(service.m_State==ERR_ExtractionState::ESCAPED);
assert(service.m_Escapees.Count()==2&&service.m_Escapees.Contains(101)&&service.m_Escapees.Contains(202)&&!service.m_Escapees.Contains(303));
assert(service.m_ParticipantCount==3);
// An empty second transport still needs to clear the boundary safely.
one.vehicle=&first;two.vehicle=&first;service.m_State=ERR_ExtractionState::WAITING;service.m_Escapees.Clear();second.position={900,0};
service.Tick(ERR_Phase::EXTRACTION);assert(service.m_State==ERR_ExtractionState::WAITING);
service.m_State=ERR_ExtractionState::WAITING;service.m_Escapees.Clear();service.m_AirborneSince=-1;
service.now=40000;service.Tick(ERR_Phase::EXTRACTION);
two.vehicle=&first; // An empty second transport must also lift off safely.
f2.airborne=false;service.now=50000;service.Tick(ERR_Phase::EXTRACTION);assert(service.m_AirborneSince==-1);
f2.airborne=true;service.now=60000;service.Tick(ERR_Phase::EXTRACTION);assert(service.m_State==ERR_ExtractionState::WAITING);
service.now=90000;f2.lost=true;service.Tick(ERR_Phase::EXTRACTION);
assert(service.m_State==ERR_ExtractionState::LOST && service.m_Escapees.IsEmpty());
one.alive=false;two.alive=false;assert(!service.CarryingSurvivorsToSafety());roster.lives.clear();assert(!service.CarryingSurvivorsToSafety());
service.m_Transports.Clear();assert(!service.FleetAlive()&&!service.FleetLanded());
}
''')
print('PASS: split-fleet boarding, escort rejection, lost transport failure and both transport escape boundaries')
print('PASS: split-fleet boarding, escort rejection, lost transport failure and both transports airborne for thirty seconds')