Finish airborne extraction after 30 seconds and hold rescue aircraft; record Workshop 0.0.134
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -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')
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user