Allow off-airport extraction across terrains; record Workshop 0.0.124
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -8,7 +8,7 @@ s=s.replace('System.GetTickCount','Clock::Now').replace('Replication.IsServer','
|
||||
s=s.replace('float floor;','float floor=0;').replace('protected ', '').replace('ref ERR_SiteDefinition retained', 'Site* retained').replace('retained.', 'retained->').replace('m_Definition.', 'm_Definition->').replace('ERR_LocationKind.', 'Kind::')
|
||||
s=s.replace('foreach (Site* site : m_Definition->Sites)', 'for (Site* site : m_Definition->Sites)')
|
||||
s=s.replace('bool fits;', 'bool fits=false;')
|
||||
s=s.replace('ERR_Actors.', 'ERR_Actors::').replace('ERR_CampTerrain.', 'ERR_CampTerrain::').replace('ERR_RescueLanding.', 'ERR_RescueLanding::')
|
||||
s=s.replace('ERR_Actors.', 'ERR_Actors::').replace('ERR_CampTerrain.', 'ERR_CampTerrain::').replace('ERR_RescueLanding.', 'ERR_RescueLanding::').replace('ERR_AirfieldPlacement.', 'ERR_AirfieldPlacement::')
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
@@ -19,12 +19,13 @@ struct vector{float a[3];float& operator[](int i){return a[i];}};vector Vector(f
|
||||
vector operator+(vector a,vector b){return Vector(a[0]+b[0],a[1]+b[1],a[2]+b[2]);}
|
||||
namespace ERR_Actors{vector Ground(vector p){return p;}}
|
||||
namespace ERR_CampTerrain{vector Rotate(vector p,float){return p;}}
|
||||
namespace ERR_AirfieldPlacement{bool clear=true;int groundChecks=0,airportChecks=0;bool Clear(vector){airportChecks++;return clear;}bool ClearGround(vector){groundChecks++;return clear;}}
|
||||
namespace ERR_RescueLanding{bool fits=true;bool Fits(vector){return fits;}}
|
||||
namespace Result{enum Value{FAILED=-1,PENDING,READY};}using ERR_PrepareResult=Result::Value;
|
||||
namespace Clock {int now=0,step=1;int Now(){int t=now;now+=step;return t;}}
|
||||
bool Server(){return true;}float RandomFloat(float a,float b){return (a+b)/2;}template<class...T>void PrintFormat(T...){}
|
||||
namespace Kind{enum{PRISON,RADIO,EXTRACTION,OUTPOST,DEPOT,MORTAR,HOSPITAL};}
|
||||
struct Site{int Kind=Kind::PRISON;bool required=false;string Label="Test site";vector Position=Vector(10,0,20),AircraftOffset=Vector(12,0,0);float Heading=0;bool PlacementResolved=false,RandomClear=true;string Id="site";};
|
||||
struct Site{int Kind=Kind::PRISON;bool required=false;string Label="Test site";vector Position=Vector(10,0,20),AircraftOffset=Vector(12,0,0);float Heading=0;bool PlacementResolved=false,RandomClear=true,RequireAirfield=false;string Id="site";};
|
||||
template<class T>struct Array:std::vector<T>{using std::vector<T>::operator=;int Count(){return this->size();}int Find(T v){auto i=std::find(this->begin(),this->end(),v);return i==this->end()?-1:i-this->begin();}void Remove(int i){this->erase(this->begin()+i);}void InsertAt(T v,int i){this->insert(this->begin()+i,v);}};
|
||||
struct Definition{bool TerrainGenerated=false;Array<Site*> Sites;Site* required=nullptr;bool Valid(){return !required||Sites.Find(required)>=0;}};
|
||||
struct Terrain{string Rejection(){return "terrain";}bool clear=true;Site* refused=nullptr;int queries=0;bool SiteAt(Site* site,vector,float,float& height){queries++;height=42;return clear&&site!=refused;}};
|
||||
@@ -74,6 +75,12 @@ int main(){
|
||||
{Clock::now=0;Scheduler s;Site before,a,after;a.Kind=Kind::HOSPITAL;
|
||||
s.definition.Sites={&before,&a,&after};s.definition.required=&a;s.m_Pending={&a};s.m_Attempts=12000;s.m_Terrain.clear=false;
|
||||
assert(s.Tick()==Result::FAILED&&s.definition.Sites.size()==3&&s.definition.Sites[1]==&a&&!a.PlacementResolved);}
|
||||
// Primary pad failures retry before commitment; authored fallback cannot bypass them.
|
||||
{Clock::now=0;Scheduler s;Site a;a.Kind=Kind::EXTRACTION;s.m_Pending={&a};ERR_AirfieldPlacement::clear=false;
|
||||
assert(s.Tick()==Result::PENDING&&!a.PlacementResolved);s.m_Attempts=12000;
|
||||
assert(s.Tick()==Result::FAILED&&!a.PlacementResolved);ERR_AirfieldPlacement::clear=true;}
|
||||
{Clock::now=0;Scheduler s;Site a;a.Kind=Kind::EXTRACTION;s.m_Pending={&a};int checks=ERR_AirfieldPlacement::groundChecks;
|
||||
while(s.Tick()==Result::PENDING){}assert(a.PlacementResolved&&ERR_AirfieldPlacement::groundChecks>checks&&ERR_AirfieldPlacement::airportChecks==0);}
|
||||
// Extraction candidates without secondary fleet space remain uncommitted and are retried.
|
||||
{Clock::now=0;Scheduler s;Site a;a.Kind=Kind::EXTRACTION;s.m_Pending={&a};s.definition.Sites={&a};ERR_RescueLanding::fits=false;
|
||||
assert(s.Tick()==Result::PENDING&&!a.PlacementResolved);ERR_RescueLanding::fits=true;
|
||||
@@ -113,3 +120,47 @@ int main(){Scenario s;
|
||||
}
|
||||
''')
|
||||
print('PASS: actual background activation waits an hour empty without consuming placement time; disconnect does not reset active budget')
|
||||
|
||||
# Execute actual spacing policy in both placement orders: randomized extraction
|
||||
# must not be committed outside the prison's escape boundary.
|
||||
spacing=body(p,'protected bool Separated(')
|
||||
for before,after in [('protected ',''),('ERR_SiteDefinition candidate','Site* candidate'),('foreach (ERR_SiteDefinition other :','for (Site* other :'),('candidate.','candidate->'),('other.','other->'),('m_Definition.','m_Definition->'),('ERR_LocationKind.','Kind::'),('vector.DistanceXZ','DistanceXZ'),('ERR_GameMode.Get().EscapeRadius()','escapeRadius')]:
|
||||
spacing=spacing.replace(before,after)
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
struct vector{float x,z;};float DistanceXZ(vector a,vector b){return std::hypot(a.x-b.x,a.z-b.z);}
|
||||
namespace Kind{enum{PRISON,RADIO,EXTRACTION,OUTPOST,DEPOT,MORTAR,HOSPITAL};}
|
||||
struct Site{int Kind=Kind::PRISON;bool RandomClear=true,PlacementResolved=true;vector Position{0,0};};
|
||||
struct Definition{std::vector<Site*> Sites;};
|
||||
struct Placement{Definition definition;Definition* m_Definition=&definition;float escapeRadius=5000;
|
||||
'''+spacing+r'''
|
||||
};
|
||||
int main(){
|
||||
for(bool reverse:{false,true}){
|
||||
Placement s;Site a,b;a.Kind=reverse?Kind::PRISON:Kind::EXTRACTION;b.Kind=reverse?Kind::EXTRACTION:Kind::PRISON;
|
||||
s.definition.Sites={&a,&b};
|
||||
assert(s.Separated(&a,{1000,0},false));
|
||||
assert(!s.Separated(&a,{4900,0},false));
|
||||
assert(!s.Separated(&a,{6000,0},true)); // authored fallback has identical safety
|
||||
assert(!s.Separated(&a,{100,0},false));
|
||||
b.PlacementResolved=false;assert(s.Separated(&a,{6000,0},false)); // unused authored seed
|
||||
}
|
||||
}
|
||||
''')
|
||||
print('PASS: actual separation keeps randomized extraction within the escape boundary in both placement orders, including fallback')
|
||||
|
||||
# Exercise the production randomized-kind rejection expression, including extraction.
|
||||
validation=body(ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_SiteDefinition.c','bool Valid(')
|
||||
random_rule=next(line.strip() for line in validation.splitlines() if 'if (site.RandomClear' in line)
|
||||
rule=random_rule.replace('ERR_LocationKind.','Kind::')
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <initializer_list>
|
||||
namespace Kind{enum{PRISON,RADIO,EXTRACTION,OUTPOST,DEPOT,MORTAR,HOSPITAL};}
|
||||
struct Site{bool RandomClear=true;int Kind=Kind::EXTRACTION;};
|
||||
bool Accept(Site site){''' + rule + r''' return true;}
|
||||
int main(){for(int kind:{Kind::PRISON,Kind::RADIO,Kind::EXTRACTION,Kind::OUTPOST,Kind::DEPOT,Kind::MORTAR,Kind::HOSPITAL}){Site site;site.Kind=kind;assert(Accept(site));}Site invalid;invalid.Kind=99;assert(!Accept(invalid));}
|
||||
''')
|
||||
print('PASS: scenario validation admits randomized extraction definitions and rejects unsupported randomized site kinds')
|
||||
|
||||
+14
-38
@@ -1,7 +1,7 @@
|
||||
"""Run production generic placement scheduling with clock/terrain adapters."""
|
||||
from policy_harness import ROOT, body, run
|
||||
base = ROOT/'addons/core/Scripts/Game/ERR/00_Core'
|
||||
s = '\n'.join(body(base/'ERR_GeneratedSites.c', signature) for signature in ('static bool StartupSite(', 'void StartupOnly(', 'bool StartupReady(', 'protected bool SkipOptional(')) + '\n' + body(base/'ERR_TerrainSites.c', 'override ERR_PrepareResult Tick(') + '\n' + body(base/'ERR_TerrainSites.c', 'protected bool PrepareAirports(') + '\n' + '\n'.join(body(base/'ERR_TerrainSites.c', sig) for sig in ('static int SearchAttempts(', 'static int SearchMilliseconds(', 'protected void ExpandExtractionSearch('))
|
||||
s = '\n'.join(body(base/'ERR_GeneratedSites.c', signature) for signature in ('static bool StartupSite(', 'void StartupOnly(', 'bool StartupReady(', 'protected bool SkipOptional(')) + '\n' + body(base/'ERR_TerrainSites.c', 'override ERR_PrepareResult Tick(') + '\n' + '\n'.join(body(base/'ERR_TerrainSites.c', sig) for sig in ('static int SearchAttempts(', 'static int SearchMilliseconds('))
|
||||
for before, after in [('override ', ''), ('protected ', ''), ('ref ERR_SiteDefinition retained','Site* retained'),
|
||||
('ERR_SiteDefinition site','Site* site'), ('foreach (', 'for ('), ('site.', 'site->'), ('retained.','retained->'),
|
||||
('m_Definition.', 'm_Definition->'), ('m_Airports.', 'm_Airports->'), ('System.GetTickCount','Clock::Now'),
|
||||
@@ -25,10 +25,8 @@ namespace Kind{enum{PRISON,RADIO,EXTRACTION,OUTPOST,DEPOT,MORTAR,HOSPITAL};}
|
||||
struct Site{int Kind=Kind::PRISON;string Label="Test site",Id="test";vector Position=Vector(11,0,22);float Heading=0;bool PlacementResolved=false,RequireAirfield=true,RandomClear=false;};
|
||||
template<class T>struct Array:std::vector<T>{using std::vector<T>::operator=;bool IsEmpty(){return this->empty();}int Count(){return this->size();}int Find(T v){auto i=std::find(this->begin(),this->end(),v);return i==this->end()?-1:i-this->begin();}void Remove(int i){this->erase(this->begin()+i);}void InsertAt(T v,int i){this->insert(this->begin()+i,v);}};
|
||||
struct Definition{bool TerrainGenerated=true;Array<Site*> Sites;Site* required=nullptr;bool valid=true;bool Valid(){return valid&&(!required||Sites.Find(required)>=0);}};
|
||||
struct Airports{Array<vector> m_Positions;Airports(){m_Positions.push_back(Vector(0,0,0));}bool complete=true;int updates=0,cost=0,finishAfter=0;void Update(){updates++;Clock::now+=cost;if(finishAfter&&updates>=finishAfter)complete=true;}bool Complete(){return complete;}};
|
||||
struct Scheduler{
|
||||
void RecordRejection(string){}void ReportSearch(Site*,string){}void ReportProgress(Site*){}void ClearDiagnostics(){}string RejectionSummary(){return "terrain";}
|
||||
Airports airports;Airports* m_Airports=&airports;bool m_AirportsReady=true;
|
||||
Definition definition;Definition* m_Definition=&definition;Array<Site*> m_Pending;
|
||||
ERR_PrepareResult m_Result=Result::PENDING;string m_Error;
|
||||
bool m_StartupOnly=false;
|
||||
@@ -43,13 +41,6 @@ struct Scheduler{
|
||||
'''+s+r'''
|
||||
};
|
||||
int main(){
|
||||
// Cheap airport tiles batch; native-query time and count both bound a slice.
|
||||
{Clock::now=0;Clock::step=0;Scheduler s;s.m_AirportsReady=false;s.airports.complete=false;
|
||||
assert(!s.PrepareAirports()&&s.airports.updates==8);assert(!s.PrepareAirports()&&s.airports.updates==16);}
|
||||
{Clock::now=0;Scheduler s;s.m_AirportsReady=false;s.airports.complete=false;s.airports.cost=7;
|
||||
assert(!s.PrepareAirports()&&s.airports.updates==1);}
|
||||
{Clock::now=0;Scheduler s;s.m_AirportsReady=false;s.airports.complete=false;s.airports.finishAfter=3;
|
||||
assert(s.PrepareAirports()&&s.airports.updates==3);assert(s.PrepareAirports()&&s.airports.updates==3);}
|
||||
Clock::step=1;
|
||||
{Clock::now=0;Scheduler s;Site extraction,prison,radio;extraction.Kind=Kind::EXTRACTION;radio.Kind=Kind::RADIO;
|
||||
s.definition.Sites={&extraction,&prison,&radio};s.m_Pending={&extraction,&prison,&radio};s.StartupOnly(true);
|
||||
@@ -60,11 +51,9 @@ int main(){
|
||||
|
||||
{Scheduler s;s.m_Started=false;assert(s.Tick()==Result::FAILED);}
|
||||
{Scheduler s;assert(s.Tick()==Result::READY);}
|
||||
// Airport discovery yields without spending placement budget, then enables bounded clear-ground extraction.
|
||||
{Clock::now=0;Scheduler s;s.m_AirportsReady=false;s.airports.complete=false;assert(s.Tick()==Result::PENDING&&s.surveys==0);s.airports.complete=true;s.airports.m_Positions.clear();Site extraction;extraction.Kind=Kind::EXTRACTION;s.m_Pending={&extraction};while(s.Tick()==Result::PENDING){}assert(s.m_Result==Result::READY&&!extraction.RequireAirfield&&extraction.PlacementResolved&&extraction.Label=="Extraction landing zone");}
|
||||
{Clock::now=40000;Scheduler s;s.m_AirportsReady=false;Site a;s.m_Pending={&a};while(s.Tick()==Result::PENDING){}assert(s.m_Result==Result::READY&&a.PlacementResolved&&s.airports.updates==1);}
|
||||
// Recognized airports retain their airfield requirement.
|
||||
{Clock::now=0;Scheduler s;s.m_AirportsReady=false;Site extraction;extraction.Kind=Kind::EXTRACTION;s.m_Pending={&extraction};while(s.Tick()==Result::PENDING){}assert(s.m_Result==Result::READY&&extraction.RequireAirfield);}
|
||||
// Extraction starts immediately; there is no airport discovery prerequisite.
|
||||
{Clock::now=0;Scheduler s;Site extraction;extraction.Kind=Kind::EXTRACTION;extraction.RequireAirfield=false;
|
||||
s.m_Pending={&extraction};while(s.Tick()==Result::PENDING){}assert(s.m_Result==Result::READY&&extraction.PlacementResolved&&s.surveys==1);}
|
||||
// Off-origin bounds are sampled; authored Everon positions cannot be retained.
|
||||
{Clock::now=0;Scheduler s;Site a,b;s.m_Pending={&a,&b};s.definition.Sites={&a,&b};while(s.Tick()==Result::PENDING){}assert(s.m_Result==Result::READY&&a.PlacementResolved&&b.PlacementResolved);assert(a.Position[0]==5000&&a.Position[2]==-1000&&a.Position[1]>42);int calls=s.surveys;assert(s.Tick()==Result::READY&&s.surveys==calls);}
|
||||
// Pending navigation keeps the SAME candidate and yields before spinning or resampling.
|
||||
@@ -78,17 +67,6 @@ int main(){
|
||||
// The former ceiling no longer ends a difficult prison search.
|
||||
{Clock::now=30001;Scheduler s;Site a;s.m_Pending={&a};s.definition.Sites={&a};s.m_Attempts=12000;
|
||||
while(s.Tick()==Result::PENDING){}assert(s.m_Result==Result::READY&&a.PlacementResolved);}
|
||||
// A recognized but unusable airport broadens once, preserving the total clock
|
||||
// and attempt counter and discarding any pending airport/navmesh candidate.
|
||||
for(int byTime:{0,1}){
|
||||
Clock::now=byTime?30001:1;Scheduler s;Site a;a.Kind=Kind::EXTRACTION;
|
||||
s.m_Pending={&a};s.definition.Sites={&a};s.m_Attempts=byTime?2:12000;s.m_HaveCandidate=true;
|
||||
int attempts=s.m_Attempts;s.ExpandExtractionSearch(&a);
|
||||
assert(!a.RequireAirfield&&!s.m_HaveCandidate&&s.m_Began==0&&s.m_Attempts==attempts&&a.Label=="Extraction landing zone");
|
||||
s.m_HaveCandidate=true;s.ExpandExtractionSearch(&a);assert(s.m_HaveCandidate); // one transition only
|
||||
while(s.Tick()==Result::PENDING){}assert(s.m_Result==Result::READY&&a.PlacementResolved);}
|
||||
{Clock::now=29990;Scheduler s;Site a;a.Kind=Kind::EXTRACTION;s.m_Attempts=11999;
|
||||
s.ExpandExtractionSearch(&a);assert(a.RequireAirfield);}
|
||||
// An exhausted budget rejects every mandatory kind WITHOUT trying the authored fallback.
|
||||
for(int kind:{Kind::PRISON,Kind::RADIO,Kind::EXTRACTION,Kind::MORTAR}){
|
||||
Clock::now=0;Scheduler s;Site a;a.Kind=kind;s.m_Pending={&a};s.definition.Sites={&a};s.m_Attempts=48000;
|
||||
@@ -129,8 +107,7 @@ int main(){
|
||||
''')
|
||||
print('PASS: actual navigation readiness supports static meshes, yields only for loadable streaming tiles and refuses unreachable positions')
|
||||
|
||||
# Execute sampling and clearance selection: an empty airport list must never be
|
||||
# indexed, and generic helicopter pads retain geometry/nav/physical validation.
|
||||
# Execute world-wide sampling and helicopter-pad geometry/nav/physical validation.
|
||||
placement = body(base/'ERR_TerrainSites.c', 'static bool Bounds(') + '\n' + body(base/'ERR_TerrainSites.c', 'static float PrisonAirportBuffer(') + '\n' + body(base/'ERR_TerrainSites.c', 'protected vector SamplePosition(') + '\n' + body(base/'ERR_TerrainSites.c', 'protected bool Survey(')
|
||||
for before, after in [
|
||||
('protected ', ''), ('ERR_SiteDefinition site', 'Site* site'), ('site.', 'site->'),
|
||||
@@ -165,7 +142,6 @@ namespace ERR_AirfieldPlacement {
|
||||
}
|
||||
namespace ERR_RescueLanding {bool fits=true;int checks=0;bool Fits(vector){checks++;return fits;}}
|
||||
template<class T>struct Array:std::vector<T>{bool IsEmpty(){return this->empty();}int Count(){return this->size();}};
|
||||
struct Airports {Array<vector> m_Positions;};
|
||||
struct Terrain {
|
||||
std::string Rejection(){return "terrain";}
|
||||
bool geometry=true,flat=true;float airportBuffer=0;
|
||||
@@ -174,7 +150,7 @@ struct Terrain {
|
||||
};
|
||||
struct Placement {
|
||||
std::string m_NavigationReason="navigation";void RecordRejection(std::string){}
|
||||
Airports airports;Airports* m_Airports=&airports;Terrain m_Terrain;
|
||||
Terrain m_Terrain;
|
||||
vector m_Low=Vector(1000,0,-5000),m_High=Vector(9000,0,3000);
|
||||
int m_GeometryRejected=0,m_NavigationRejected=0,m_PadRejected=0;
|
||||
bool nav=true;bool Navigable(vector){return nav;}
|
||||
@@ -191,20 +167,18 @@ int main(){
|
||||
assert(s.PrisonAirportBuffer(Vector(0,0,0),Vector(20000,0,20000))==1500);
|
||||
assert(s.PrisonAirportBuffer(Vector(0,0,0),Vector(2000,0,20000))==300);
|
||||
auto p=s.SamplePosition(&site);assert(p[0]==5000&&p[2]==-1000&&Math::indexed==0);
|
||||
s.airports.m_Positions.push_back(Vector(2000,0,3000));site.RequireAirfield=true;
|
||||
p=s.SamplePosition(&site);assert(Math::indexed==1&&std::abs(p[0]-1775)<.01&&std::abs(p[2]-3000)<.01);
|
||||
site.Kind=Kind::PRISON;p=s.SamplePosition(&site);assert(p[0]==5000&&p[2]==-1000&&Math::indexed==1);
|
||||
// Even a stale authored airfield flag cannot bias automatic terrain sampling.
|
||||
site.RequireAirfield=true;p=s.SamplePosition(&site);assert(p[0]==5000&&p[2]==-1000&&Math::indexed==0);
|
||||
site.Kind=Kind::PRISON;p=s.SamplePosition(&site);assert(p[0]==5000&&p[2]==-1000&&Math::indexed==0);
|
||||
site.Kind=Kind::EXTRACTION;site.RequireAirfield=false;
|
||||
p=s.SamplePosition(&site);assert(p[0]==5000&&p[2]==-1000&&Math::indexed==1);
|
||||
assert(s.Survey(&site,p,0,floor)&&floor==42.5);assert(ERR_AirfieldPlacement::groundChecks==1&&ERR_AirfieldPlacement::airportChecks==0&&s.m_Terrain.airportBuffer==1200);
|
||||
ERR_AirfieldPlacement::ground=false;assert(!s.Survey(&site,p,0,floor)&&s.m_PadRejected==1);
|
||||
site.RequireAirfield=true;ERR_AirfieldPlacement::ground=true;
|
||||
assert(!s.Survey(&site,p,0,floor));assert(ERR_AirfieldPlacement::airportChecks==1&&ERR_AirfieldPlacement::groundChecks==2);
|
||||
ERR_AirfieldPlacement::airfield=true;assert(s.Survey(&site,p,0,floor));
|
||||
assert(s.Survey(&site,p,0,floor));assert(ERR_AirfieldPlacement::airportChecks==0&&ERR_AirfieldPlacement::groundChecks==3);
|
||||
site.RequireAirfield=false;s.m_Terrain.flat=false;assert(!s.Survey(&site,p,0,floor));
|
||||
s.m_Terrain.flat=true;s.nav=false;assert(!s.Survey(&site,p,0,floor)&&s.m_NavigationRejected==1);
|
||||
s.nav=true;s.m_Terrain.geometry=false;assert(!s.Survey(&site,p,0,floor)&&s.m_GeometryRejected==1);
|
||||
assert(ERR_AirfieldPlacement::groundChecks==2&&ERR_AirfieldPlacement::airportChecks==2);
|
||||
assert(ERR_AirfieldPlacement::groundChecks==3&&ERR_AirfieldPlacement::airportChecks==0);
|
||||
// A suitable primary pad must not commit an extraction that cannot fit its fleet.
|
||||
s.m_Terrain.geometry=true;ERR_RescueLanding::fits=false;
|
||||
int rejected=s.m_PadRejected;assert(!s.Survey(&site,p,0,floor)&&s.m_PadRejected==rejected+1);
|
||||
@@ -213,4 +187,6 @@ int main(){
|
||||
assert(s.Survey(&site,p,0,floor)&&floor==42&&ERR_RescueLanding::checks==fleetChecks);
|
||||
}
|
||||
''')
|
||||
print('PASS: real sampling handles absent airports and extraction clearance preserves geometry/navigation/flatness while selecting the correct aircraft survey')
|
||||
assert 'site.RequireAirfield = false;' in body(base/'ERR_TerrainSites.c', 'override bool Begin(')
|
||||
assert 'ERR_ReconAirports' not in (base/'ERR_TerrainSites.c').read_text()
|
||||
print('PASS: automatic extraction samples the whole terrain immediately and retains geometry/navigation/flatness and fleet-space checks without airport bias')
|
||||
|
||||
@@ -57,7 +57,8 @@ assert set(transports)=={'everon-supply-1'}
|
||||
assert 'Optional 1' in transports['everon-supply-1']
|
||||
assert '{FA65287C19DE304B}Prefabs/ERR/Vehicles/SupplyUAZ.et' in transports['everon-supply-1']
|
||||
assert 'Offset 0 0 -12' in transports['everon-supply-1'] and 'ClearanceRadius 4' in transports['everon-supply-1']
|
||||
assert 'RequireAirfield 1' in sites['everon-extraction-1']
|
||||
assert 'RequireAirfield 0' in sites['everon-extraction-1']
|
||||
assert 'RandomClear 1' in sites['everon-extraction-1']
|
||||
assert 'Position 4800.953 0 11487.412' in sites['everon-extraction-1']
|
||||
# Own container must be finite and must not turn off native resource eligibility.
|
||||
kit=(R/'addons/core'/kitpath).read_text()
|
||||
|
||||
Reference in New Issue
Block a user