Implement dynamic clientless free-flight travel

Integrate transient flight alternatives with graph routing, native flying mounts, phased terrain and collision checks, bounded avoidance and confirmed landing. Add safe-return recovery, node and location integration, profile settings, diagnostic commands, regression tests and flight documentation.
This commit is contained in:
Nico committed 2026-10-26 17:35:58 +01:00
1 parent 4474714c00
commit 8df9591c99
21 files changed
+1536 -16

No files matched your search

+67
View File
@@ -0,0 +1,67 @@
"""Run the production 3D flight geometry against deterministic terrain/collisions."""
from pathlib import Path
import subprocess,tempfile
root=Path(__file__).resolve().parents[2]
harness=r'''
#include "ClientlessFlightPath.h"
#include <cassert>
#include <iostream>
using namespace ClientlessFlightPath;
struct Run {Point End;float Peak=0,Side=0;unsigned Steps=0,Avoidances=0;bool Arrived=false;};
Run simulate(Point start,Point target,Sample sample,Ray ray,int limit=500){
Settings cfg;Memory mem;mem.Departed=start.Z-sample(start.X,start.Y).Ground>cfg.Safety;Run r;Point p=start;
for(int i=0;i<limit;++i){
auto next=Next(p,target,cfg,mem,sample,ray);
if(!next.Valid)break;
assert(Corridor(p,next.Next,target,cfg,sample,ray,p.Z-sample(p.X,p.Y).Ground<cfg.Safety));
assert(next.Next.Z+5<=sample(next.Next.X,next.Next.Y).Ceiling);
p=next.Next;++r.Steps;r.Peak=std::max(r.Peak,p.Z);r.Side=std::max(r.Side,std::fabs(p.Y));r.Avoidances+=next.Avoiding;
if(Distance(p,target)<0.8f){r.Arrived=true;break;}
}
r.End=p;return r;
}
int main(){
auto flat=[](float,float){return Surface{0,300,true};};
auto clear=[](Point const&,Point const&){return true;};
auto normal=simulate({0,0,0},{800,0,0},flat,clear);
assert(normal.Arrived&&normal.Peak>30&&normal.Peak<=60.1f);
auto low=[](float,float){return Surface{0,42,true};};
auto ceiling=simulate({0,0,0},{800,0,0},low,clear);
assert(ceiling.Arrived&&ceiling.Peak<=36.1f);
auto peak=[](float x,float){return Surface{x>390&&x<400?40.0f:0.0f,300,true};};
auto smooth=simulate({0,0,0},{800,0,0},peak,clear);
assert(smooth.Arrived&&smooth.Peak<75); // no nominal clearance chase over short peak
auto wall=[](Point const&a,Point const&b){
for(int i=0;i<=80;++i){auto p=Mix(a,b,i/80.0f);if(p.X>=240&&p.X<=260&&std::fabs(p.Y)<16&&p.Z<110)return false;}return true;
};
auto side=simulate({0,0,0},{800,0,0},flat,wall);
assert(side.Arrived&&side.Avoidances>0&&side.Side>10);
auto wide=[](Point const&a,Point const&b){
for(int i=0;i<=80;++i){auto p=Mix(a,b,i/80.0f);if(p.X>=240&&p.X<=260&&std::fabs(p.Y)<500&&p.Z<95)return false;}return true;
};
auto climb=simulate({0,0,0},{800,0,0},flat,wide);
assert(climb.Arrived&&climb.Peak>95&&climb.Avoidances>0);
auto unknown=[](float x,float){return Surface{0,300,x<120};};
assert(!simulate({0,0,0},{800,0,0},unknown,clear).Arrived);
auto blocked=[](Point const&,Point const&){return false;};
assert(!simulate({0,0,0},{800,0,0},flat,blocked).Arrived);
// Missing/covered/wrong-floor landing is not an arrival.
auto invalidEnd=[](float x,float){return Surface{x>790?20.0f:0.0f,300,true};};
assert(!simulate({0,0,0},{800,0,0},invalidEnd,clear).Arrived);
auto shortFlight=simulate({0,0,0},{90,0,0},flat,clear);
assert(shortFlight.Arrived&&shortFlight.Peak>5);
// An avoidance may leave the bot high immediately above the landing site.
// Open a descending approach instead of oscillating over the target.
Memory landingMemory;landingMemory.Departed=true;
Point landingStart{660,0,60};
auto firstDescent=Next(landingStart,{800,0,0},Settings{},landingMemory,flat,clear);
assert(firstDescent.Valid&&firstDescent.Next.X>660&&firstDescent.Next.Z<60);
auto highApproach=simulate({0,0,70},{1,0,0},flat,clear);
assert(highApproach.Arrived&&highApproach.Steps<20&&highApproach.Peak<=70);
std::cout<<"Flight geometry passed: takeoff/landing, smoothed peak, ceiling, lateral/climbing avoidance, missing terrain and blocked corridors.\n";
}
'''
with tempfile.TemporaryDirectory(prefix='vcore-free-flight-') as folder:
cpp=Path(folder)/'test.cpp';binary=Path(folder)/'test';cpp.write_text(harness)
subprocess.run(['c++','-std=c++17','-I',str(root/'src/server/game/World'),str(cpp),'-o',str(binary)],check=True)
subprocess.run([str(binary)],check=True)