PopRiotTarget.Routes.cs
using Sandbox;
using Sandbox.Navigation;
using System;
namespace PaintballBaddies;
public sealed partial class PopRiotTarget
{
// Reuse the map's native navigation for room/stair connectivity, then lift
// the route into collision-checked air lanes sized for the whole airframe.
private bool TryArenaCourse()
{
var start=Scene.NavMesh.GetClosestPoint(WorldPosition-Vector3.Up*110,240);
if(!start.HasValue)return false;
var actors=Scene.GetAllComponents<PaintballCombatant>().Where(a=>a.AcceptHits).Select(a=>a.WorldPosition);
var goals=ArenaMap.PatrolPoints(Scene).Concat(actors).Where(p=>(p-WorldPosition).WithZ(0).Length>300)
.OrderBy(p=>MathF.Abs(MathF.Sin(p.x*.0031f+p.y*.0053f+Seed+routeNumber))).Take(7).ToArray();
routeNumber++;
foreach(var goal in goals)
{
var end=Scene.NavMesh.GetClosestPoint(goal,120);if(!end.HasValue)continue;
var path=Scene.NavMesh.CalculatePath(new CalculatePathRequest{Start=start.Value,Target=end.Value});
if(path.Status!=NavMeshPathStatus.Complete)continue;
var samples=new List<Vector3>{start.Value};var prev=start.Value;
foreach(var node in path.Points)
{
var delta=node.Position-prev;int count=Math.Max(1,(int)MathF.Ceiling(delta.Length/64));
for(int i=1;i<=count;i++)samples.Add(Vector3.Lerp(prev,node.Position,(float)i/count));
prev=node.Position;
}
if(samples.Count>90)continue;
var layers=new List<(Vector3 point,int parent)[]>();
layers.Add(new[]{(WorldPosition,-1)});
bool failed=false;
for(int i=0;i<samples.Count;i++)
{
var direction=(samples[Math.Min(i+1,samples.Count-1)]-samples[Math.Max(0,i-1)]).WithZ(0).Normal;
var side=Vector3.Cross(Vector3.Up,direction);var layer=new List<(Vector3,int)>();
foreach(float height in new[]{112f,156,76})
foreach(float offset in new[]{0f,44,-44})
{
var candidate=samples[i]+Vector3.Up*height+side*offset;
var previous=layers.Last();
for(int j=0;j<previous.Length;j++)
if(FlightLaneClear(previous[j].point,candidate)){layer.Add((candidate,j));break;}
}
if(layer.Count==0){failed=true;break;}layers.Add(layer.ToArray());
}
if(failed)continue;
var route=new List<Vector3>();int index=0;
for(int i=layers.Count-1;i>0;i--){route.Add(layers[i][index].point);index=layers[i][index].parent;}
route.Reverse();flightPath.Clear();flightPath.Add(WorldPosition);flightPath.AddRange(route);
nextWaypoint=1;touring=true;Anchor=flightPath.Last();return true;
}
return false;
}
}