Prepare independent successor playtest with spawn recovery and casualty feedback
This commit is contained in:
@@ -7,7 +7,7 @@ def cpp(x):
|
||||
x=x.replace('ERR_AircraftFlightState.','State::').replace('ERR_NativeAircraftFieldRules.','Rules::').replace('ERR_AircraftFieldRules.','FieldRules::').replace('vector.','vector::').replace('ERR_AircraftFieldFlight flight','Flight* flight').replace('flight.','flight->')
|
||||
return re.sub(r'(?<![\w.])(\d+\.\d+)(?![\w.])',r'\1f',x)
|
||||
rules=s[s.index('class ERR_NativeAircraftFieldRules'):s.index('class ERR_NativeAircraftFieldProbe')].replace('class ERR_NativeAircraftFieldRules','struct Rules').rstrip()+';'
|
||||
observed='\n'.join(cpp(body(P,k)) for k in ('void BrakeBegan(', 'void BrakeStopped('))
|
||||
observed='\n'.join(cpp(body(P,k)) for k in ('void ObserveBrakingContact(', 'void BrakeBegan(', 'void BrakeStopped('))
|
||||
run(r'''#include <cassert>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
@@ -19,7 +19,7 @@ struct FieldRules {static float HorizontalSpeed(vector v){return std::hypot(v.x,
|
||||
struct Flight {float ERR_NativeFieldStageDeadline(){return 70000;}vector LandingPoint(){return {100,0,0};}};
|
||||
template<typename...T>void PrintFormat(T...args){}
|
||||
struct Probe {
|
||||
bool m_Stopped=false,m_BrakeObserved=false,m_BrakeStopped=false,m_BrakePassed=false;
|
||||
bool m_Stopped=false,m_BrakeObserved=false,m_BrakeStopped=false,m_BrakePassed=false,m_BrakeContactObserved=false;
|
||||
string m_ObservationFailure;
|
||||
Flight* m_Flight=nullptr;float m_BrakeAt=0,m_BrakeReserve=0,m_BrakeStageDeadline=0,m_ControlDeadline=240000,m_BrakeSampledTravel=0,m_BrakeLowSince=-1;
|
||||
vector m_BrakeOrigin,m_BrakePrevious,m_BrakeLandingPoint;
|
||||
@@ -46,31 +46,45 @@ int main(){
|
||||
assert(!Rules::PhysicalFinish(true,true,true,1,2,1,1,101,101));
|
||||
for(int i=0;i<5;i++)assert(!Rules::CanDispose(i!=0,i!=1,i!=2,i!=3,i!=4));
|
||||
Flight flight,foreign;Probe p;p.m_Flight=&flight;
|
||||
p.BrakeBegan(&foreign,1000,{0,0,0},{2,0,0});assert(!p.m_BrakeObserved);
|
||||
p.BrakeBegan(&flight,1000,{0,0,0},{2,0,0});assert(p.m_BrakeObserved&&p.m_BrakeReserve>18&&p.m_BrakeReserve<19);
|
||||
p.BrakeBegan(&foreign,1000,{0,0,0},{2,0,0},false);assert(!p.m_BrakeObserved);
|
||||
p.BrakeBegan(&flight,1000,{0,0,0},{2,0,0},false);assert(p.m_BrakeObserved&&p.m_BrakeReserve>18&&p.m_BrakeReserve<19);
|
||||
p.m_BrakeSampledTravel=15;p.m_BrakePrevious={15,0,0};p.m_BrakeLowSince=2000;
|
||||
p.BrakeStopped(&flight,4000,{16,0,0},{.1f,0,0});assert(p.m_BrakePassed&&p.m_BrakeStopped&&p.m_ObservationFailure.empty());
|
||||
p.BrakeBegan(&flight,5000,{50,0,0},{2,0,0});assert(!p.m_ObservationFailure.empty()&&p.m_BrakeAt==1000); // no renewal
|
||||
p.BrakeStopped(&flight,4000,{16,0,0},{.1f,0,0},false);assert(p.m_BrakePassed&&p.m_BrakeStopped&&p.m_ObservationFailure.empty());
|
||||
p.BrakeBegan(&flight,5000,{50,0,0},{2,0,0},false);assert(!p.m_ObservationFailure.empty()&&p.m_BrakeAt==1000); // no renewal
|
||||
for(int fault=0;fault<4;fault++){
|
||||
Probe bad;bad.m_Flight=&flight;bad.BrakeBegan(&flight,1000,{0,0,0},{2,0,0});bad.m_BrakeLowSince=2000;
|
||||
Probe bad;bad.m_Flight=&flight;bad.BrakeBegan(&flight,1000,{0,0,0},{2,0,0},false);bad.m_BrakeLowSince=2000;
|
||||
float stopped=4000;vector at={16,0,0},vel={.1f,0,0};
|
||||
if(fault==0){at={19,0,0};} if(fault==1){stopped=3999;} if(fault==2){vel={.25f,0,0};} if(fault==3){bad.m_BrakeSampledTravel=20;}
|
||||
bad.BrakeStopped(&flight,stopped,at,vel);assert(!bad.m_BrakePassed&&!bad.m_ObservationFailure.empty()&&!bad.m_Stopped); // failure deferred outside callback
|
||||
bad.BrakeStopped(&flight,stopped,at,vel,false);assert(!bad.m_BrakePassed&&!bad.m_ObservationFailure.empty()&&!bad.m_Stopped); // failure deferred outside callback
|
||||
}
|
||||
assert(!Rules::Braking(NAN,1,1,0,3000,0)&&!Rules::Braking(10,NAN,1,0,3000,0));
|
||||
// Ground friction at entry, an intermediate callback, or the stop sample
|
||||
// permanently invalidates an otherwise plausible measured reserve/low-speed pass.
|
||||
for(int contactAt=0;contactAt<3;contactAt++){
|
||||
Probe grounded;grounded.m_Flight=&flight;
|
||||
grounded.BrakeBegan(&flight,1000,{0,0,0},{2,0,0},contactAt==0);
|
||||
grounded.m_BrakeLowSince=2000;
|
||||
grounded.ObserveBrakingContact(contactAt==1);
|
||||
grounded.ObserveBrakingContact(false); // later loss of contact cannot clear evidence
|
||||
grounded.BrakeStopped(&flight,4000,{16,0,0},{.1f,0,0},contactAt==2);
|
||||
assert(grounded.m_BrakeContactObserved&&!grounded.m_BrakePassed&&!grounded.m_ObservationFailure.empty()&&!grounded.m_Stopped);
|
||||
}
|
||||
Probe before;before.ObserveBrakingContact(true);assert(!before.m_BrakeContactObserved);
|
||||
Probe after;after.m_BrakeObserved=true;after.m_BrakeStopped=true;after.ObserveBrakingContact(true);assert(!after.m_BrakeContactObserved); // actual landing is separate
|
||||
assert(!Rules::Braking(NAN,1,1,0,3000,0,false)&&!Rules::Braking(10,NAN,1,0,3000,0,false));
|
||||
}
|
||||
''')
|
||||
assert s.startswith('#ifdef WORKBENCH') and s.rstrip().endswith('#endif')
|
||||
assert 'outcome == "aircraft-field"' in body(P,'static bool Enabled(')
|
||||
find=body(P,'protected bool FindPad(')
|
||||
assert '"4850.953 0 11787.412"' in find and '"100 0 0"' in find
|
||||
assert '"4890 0 11730"' in find and '"0 0 100"' in find
|
||||
assert find.count('.Survey(')==1 and 'for (' not in find and 'AirportOffset' not in s
|
||||
assert s.count('ERR_Actors.Spawn(AIRCRAFT,')==1 and s.count('SpawnCharacterInCompartment(')==1
|
||||
assert s.count('physics.SetActive(ActiveState.ACTIVE)')==1
|
||||
for prohibited in ('SetWorldTransform(', 'SetOrigin(', 'SetVelocity(', '.StartEngine(', '.StopEngine(', 'm_Mode.Own(', 'm_Flight.Tick(', 'm_Flight.Cancel(', 'm_Flight.StartField(', 'FieldDemand('):assert prohibited not in s
|
||||
for sig in ('void PrepareActions(', 'void PrepareHelicopter('):
|
||||
assert '++' in body(P,sig) and '.Prepare' not in body(P,sig)
|
||||
for sig in ('void BrakeBegan(', 'void BrakeStopped('):assert 'Check(' not in body(P,sig) and 'Stop(' not in body(P,sig)
|
||||
for sig in ('void ObserveBrakingContact(', 'void BrakeBegan(', 'void BrakeStopped('):assert 'Check(' not in body(P,sig) and 'Stop(' not in body(P,sig)
|
||||
assert 'ObserveBrakingContact' in body(P,'void PrepareHelicopter(') and 'ObserveBrakingContact(contact)' in body(P,'int Tick(')
|
||||
assert 'super.BeginFieldBrake(now)' in body(P,'protected override bool BeginFieldBrake(')
|
||||
assert 'super.CheckTranslation(now, position, height, speed, upright)' in body(P,'protected override void CheckTranslation(')
|
||||
start=body(P,'protected bool StartRetainedField(');assert '.StartField(m_Aircraft, m_Envelope, ORDER_TOKEN, m_ControlDeadline)' in start
|
||||
@@ -101,3 +115,78 @@ assert 'm_ERRTestAircraftControl || m_ERRTestAircraftRoute || m_ERRTestAircraftF
|
||||
assert 'm_ERRAircraftField.Stop()' in body(chat,'protected void ERR_StopHQPatrol(')
|
||||
assert 'errSmokeFieldServiceCancel' in cs and 'current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 241' in cs
|
||||
print('PASS: actual field result/braking observer and Chat dispatch rules; one finite fixed candidate, deferred callback failure and guarded cleanup; no native measurement')
|
||||
|
||||
# Execute the real optional grid/FindPad flow, faking only CLI/world/terrain/logging.
|
||||
def audit_cpp(text):
|
||||
text=text.replace('protected ','').replace('out vector result','vector& result')
|
||||
for before,after in (('System.','System::'),('Replication.','Replication::'),('SCR_TerrainHelper.','Terrain::'),('ERR_AircraftFieldEnvelope.','Envelope::'),('ERR_Actors.','Actors::'),('Math.','Math::'),('GetGame().GetWorld()','GetGame()->GetWorld()')):text=text.replace(before,after)
|
||||
for before,after in (('"0 0 0"','vector{}'),('"0 0 100"','vector{0,0,100}'),('"4890 0 11730"','vector{4890,0,11730}')):text=text.replace(before,after)
|
||||
return text
|
||||
run(r'''#include <cassert>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
using string=std::string;
|
||||
struct vector {float v[3]={0,0,0};vector()=default;vector(float x,float y,float z):v{x,y,z}{}float& operator[](int i){return v[i];}float operator[](int i)const{return v[i];}vector operator+(vector b)const{return {v[0]+b[0],v[1]+b[1],v[2]+b[2]};}};
|
||||
std::ostream& operator<<(std::ostream& out,vector v){return out<<v[0]<<','<<v[1]<<','<<v[2];}
|
||||
std::vector<string> logs;
|
||||
void Print(const char* value){logs.push_back(value);}
|
||||
template<class...A>void PrintFormat(const char* format,A...args){std::ostringstream out;((out<<args<<' '),...);logs.push_back(string(format)+"|"+out.str());}
|
||||
struct System {static std::map<string,string> args;static bool GetCLIParam(string name,string& value){auto i=args.find(name);if(i==args.end())return false;value=i->second;return true;}};std::map<string,string> System::args;
|
||||
struct Replication {static bool server;static bool IsServer(){return server;}};bool Replication::server=true;
|
||||
struct Game {bool world=true;bool GetWorld(){return world;}} game;bool gamePresent=true;Game* GetGame(){return gamePresent?&game:nullptr;}
|
||||
struct Math {static float AbsFloat(float value){return std::abs(value);}};
|
||||
struct Terrain {static std::vector<vector> points;static int invalidAt;static float invalidValue;static float GetTerrainY(vector point){points.push_back(point);if(int(points.size())==invalidAt)return invalidValue;return point[0]*.001f+point[2]*.002f;}};std::vector<vector> Terrain::points;int Terrain::invalidAt=0;float Terrain::invalidValue=NAN;
|
||||
struct Actors {static int groundCalls;static vector Ground(vector p){groundCalls++;p[1]=28.5f;return p;}};int Actors::groundCalls=0;
|
||||
struct Envelope {static bool permitted;static int calls;static bool Survey(vector from,vector to){calls++;assert(std::abs(from[0]-4890)<.01f&&std::abs(from[2]-11730)<.01f&&to[2]-from[2]==100);return permitted;}};bool Envelope::permitted=false;int Envelope::calls=0;
|
||||
struct Probe {bool m_TerrainAudited=false,m_Stopped=false,m_AllocationRequested=false,m_Aircraft=false,m_Group=false,m_Pilot=false;vector m_Destination;
|
||||
'''+ '\n'.join(audit_cpp(body(P,key)) for key in ('static bool Enabled(', 'static bool TerrainAuditEnabled(', 'protected void AuditTerrain(', 'protected bool FindPad('))+r'''
|
||||
};
|
||||
void reset(){System::args={{"errSmoke","1"},{"errSmokeOutcome","aircraft-field"},{"errSmokeFieldTerrainAudit","1"}};logs.clear();Terrain::points.clear();Terrain::invalidAt=0;Terrain::invalidValue=NAN;Actors::groundCalls=0;Envelope::calls=0;Envelope::permitted=false;Replication::server=true;gamePresent=true;game.world=true;}
|
||||
int main(){
|
||||
reset();Probe p;vector unchanged={99,88,77};assert(!p.FindPad(unchanged));assert(unchanged[0]==99&&unchanged[1]==88&&unchanged[2]==77&&p.m_Destination[0]==0);
|
||||
assert(Envelope::calls==1&&Actors::groundCalls==2&&Terrain::points.size()==1681&&logs.size()==1683&&p.m_TerrainAudited&&!p.m_AllocationRequested&&!p.m_Aircraft&&!p.m_Group&&!p.m_Pilot);
|
||||
assert(logs.front().find("minX=4750 maxX=5150 minZ=11600 maxZ=12000 spacing=10 columns=41 rows=41 nodes=1681")!=string::npos);
|
||||
assert(logs.back().find("original survey refusal unchanged; no candidate qualified")!=string::npos);
|
||||
for(int i=0;i<1681;i++){
|
||||
auto q=Terrain::points[i];assert(q[0]==4750+(i/41)*10&&q[1]==0&&q[2]==11600+(i%41)*10);
|
||||
std::ostringstream expected;expected<<'|'<<i<<' '<<q[0]<<' '<<q[2]<<' '<<(q[0]*.001f+q[2]*.002f)<<' ';
|
||||
assert(logs[i+1].find("ERR AIRCRAFT FIELD TERRAIN AUDIT POINT:")==0&&logs[i+1].find(expected.str())!=string::npos);
|
||||
}
|
||||
p.AuditTerrain();assert(Terrain::points.size()==1681&&logs.size()==1683); // no retry, even if directly reentered
|
||||
// Neither CLI omission nor malformed/conflicting scope permits collection.
|
||||
for(int fault=0;fault<13;fault++){
|
||||
reset();Probe refused;
|
||||
if(fault==0)System::args.erase("errSmokeFieldTerrainAudit");
|
||||
if(fault==1)System::args["errSmokeFieldTerrainAudit"]="0";
|
||||
if(fault==2)System::args["errSmoke"]="0";
|
||||
if(fault==3)System::args["errSmokeOutcome"]="aircraft-route";
|
||||
if(fault==4)Replication::server=false;
|
||||
if(fault==5)System::args["errSmokeFieldServiceCancel"]="1";
|
||||
if(fault==6)refused.m_Stopped=true;
|
||||
if(fault==7)refused.m_AllocationRequested=true;
|
||||
if(fault==8)refused.m_Aircraft=true;
|
||||
if(fault==9)refused.m_Group=true;
|
||||
if(fault==10)refused.m_Pilot=true;
|
||||
if(fault==11)gamePresent=false;
|
||||
if(fault==12)game.world=false;
|
||||
assert(!refused.FindPad(unchanged));assert(Terrain::points.empty()&&logs.empty()&&!refused.m_TerrainAudited&&Envelope::calls==1);
|
||||
}
|
||||
// Successful original survey never runs the optional audit or changes its result.
|
||||
reset();Envelope::permitted=true;Probe success;assert(success.FindPad(unchanged));assert(Terrain::points.empty()&&!success.m_TerrainAudited&&Envelope::calls==1&&success.m_Destination[2]-unchanged[2]==100);
|
||||
// Malformed heights stop without retry, completion, candidate selection or allocation.
|
||||
for(float invalid:{float(NAN),float(INFINITY),float(-INFINITY),1000001.0f}){
|
||||
reset();Terrain::invalidAt=17;Terrain::invalidValue=invalid;Probe malformed;assert(!malformed.FindPad(unchanged));
|
||||
assert(Terrain::points.size()==17&&logs.size()==18&&logs.back().find("AUDIT ABORT:")!=string::npos&&malformed.m_TerrainAudited&&!malformed.m_AllocationRequested);
|
||||
malformed.AuditTerrain();assert(Terrain::points.size()==17);
|
||||
}
|
||||
}
|
||||
''')
|
||||
audit=body(P,'protected void AuditTerrain(')
|
||||
for forbidden in ('Spawn(', 'SetOrigin(', 'SetActive(', 'TraceMove(', '.Survey(', '.Qualify', 'new ', 'GetEntities', 'm_Destination ='):
|
||||
assert forbidden not in audit
|
||||
assert audit.count('GetTerrainY(')==1
|
||||
assert find.index('.Survey(')<find.index('AuditTerrain();')<find.index('return false;')<find.index('result = candidate')
|
||||
print('PASS: actual opt-in terrain audit records one fixed1681-point grid, preserves original survey refusal/success and exact no-allocation scope; malformed heights stop without retry; no native data acquired')
|
||||
|
||||
Reference in New Issue
Block a user