Examples and best practices
Tips and best practices
Separation management
javascript
// Check both lateral AND vertical separation
const isSeparated = (a, b) => {
const lateral = distance(a.pos, b.pos);
const vertical = Math.abs(a.altFt - b.altFt);
return lateral >= 5 || vertical >= 1000;
};Efficient querying
javascript
// Cache frequently used values
onTick(({ traffic }) => {
const flights = traffic.all(); // Call once, reuse
flights.forEach(ac => {
// Process aircraft
});
});Method chaining
javascript
// Issue multiple clearances together
onSpawn((ac) => {
ac
.climb(fl(ac.plan.cruiseFL))
.speed(400)
.direct(ac.plan.exitPoint);
});Error handling
javascript
onConflict((pair) => {
const acA = traffic.byCallsign(pair.a);
const acB = traffic.byCallsign(pair.b);
if (!acA || !acB) {
log(`Missing aircraft in conflict: ${pair.a}, ${pair.b}`);
return;
}
// Resolve conflict...
});Using flight levels
javascript
// Use FL properties directly
log(`Aircraft at FL${ac.altFL}`);
log(`Target: FL${ac.targetFL}`);
log(`Cruise: FL${ac.plan.cruiseFL}`);
// Use fl() helper for commands
ac.climb(fl(350)); // Much clearer than 35000Complete example script
javascript
// Basic traffic management
onTick(({ traffic, time }) => {
const flights = traffic.all();
// Maintain spacing with speed control
for (let i = 0; i < flights.length - 1; i++) {
const lead = flights[i];
const trail = flights[i + 1];
const dist = distance(trail.pos, lead.pos);
if (dist < 8) {
// Too close - slow down trailing aircraft
trail.speed(Math.max(lead.targetIASKts - 30, 320));
} else if (dist > 12) {
// Too far - speed up trailing aircraft
trail.speed(Math.min(trail.targetIASKts + 20, 450));
}
}
// Ensure vertical separation if lateral is tight
for (let i = 0; i < flights.length - 1; i++) {
const a = flights[i];
const b = flights[i + 1];
const lateral = distance(a.pos, b.pos);
const vertical = Math.abs(a.altFt - b.altFt);
if (lateral < 5 && vertical < 1000) {
a.climb(a.altFt + 2000);
}
}
});
onConflict((pair) => {
log(`Conflict: ${pair.a} and ${pair.b}`);
const acA = traffic.byCallsign(pair.a);
const acB = traffic.byCallsign(pair.b);
if (!acA || !acB) return;
// Resolve with vertical separation
const verticalSep = Math.abs(acA.altFt - acB.altFt);
if (verticalSep < 1000) {
if (acA.altFt > acB.altFt) {
acA.climb(acA.altFt + 2000);
} else {
acB.climb(acB.altFt + 2000);
}
}
});
onSpawn((aircraft) => {
log(`${aircraft.cs} (${aircraft.type}) at FL${aircraft.altFL}`);
log(`Route: ${aircraft.plan.route.join('-')}`);
log(`Exit: ${aircraft.plan.exitPoint} at FL${aircraft.plan.exitFL}`);
// Set up handover at exit fix
aircraft.over(aircraft.plan.exitPoint, (ac) => {
ac.handover();
});
// Ensure aircraft reaches exit altitude
if (aircraft.plan.exitAltitude) {
aircraft.reach(aircraft.plan.exitPoint).altitude(aircraft.plan.exitAltitude);
}
// Initial clearances
aircraft
.climb(fl(aircraft.plan.cruiseFL))
.speed(420);
});Named parameter examples
Many aircraft methods accept both positional arguments and a single object with named parameters. The named style is often clearer, especially when multiple options are involved.
hold with named params
javascript
// Hold at a fix with default options
aircraft.hold({ fix: 'MERIT' });
// Hold with custom leg length and left turns
aircraft.hold({ fix: 'GREKI', legNm: 8, turn: 'L' });
// Hold with expect further clearance time
aircraft.hold({ fix: 'JUDDS', efc: 3600 });
// Combine hold with other clearances
aircraft
.descend(fl(240))
.hold({ fix: 'MERIT', legNm: 6, turn: 'R' });sta with named params
javascript
// Assign a scheduled time of arrival
aircraft.sta({ fix: 'MERGE', time: 1800 });
// Space arrivals 2 minutes apart
onSpawn((aircraft) => {
const slot = nextAvailableSlot; // your sequencing logic
aircraft.sta({ fix: 'MERGE', time: slot });
});crossFix with named params
javascript
// Override a STAR constraint
aircraft.crossFix({ fix: 'GREKI', altFt: 20000 });
// Use flight level shorthand (values < 2000 are treated as FL)
aircraft.crossFix({ fix: 'GREKI', altFt: 200 }); // FL200
// Override multiple constraints
aircraft
.crossFix({ fix: 'GREKI', altFt: 20000 })
.crossFix({ fix: 'ROBER', altFt: 12000 });over with named params
javascript
// Trigger handover at exit fix
aircraft.over({
waypoint: aircraft.plan.exitPoint,
callback: (ac) => ac.handover()
});
// Step-climb at a waypoint
aircraft.over({
waypoint: 'KMART',
callback: (ac) => ac.climb(fl(380))
});
// Chain multiple waypoint callbacks
aircraft
.over({
waypoint: 'GREKI',
callback: (ac) => ac.descend(fl(240))
})
.over({
waypoint: aircraft.plan.exitPoint,
callback: (ac) => ac.handover()
});before with named params
javascript
// Begin descent 40nm before arrival fix
aircraft.before({
waypoint: 'KMART',
distanceNm: 40,
callback: (ac) => ac.descend(fl(240))
});
// Reduce speed before a merge point
aircraft.before({
waypoint: 'MERGE',
distanceNm: 20,
callback: (ac) => ac.speed(280)
});
// Set up a full arrival sequence on spawn
onSpawn((aircraft) => {
aircraft
.climb(fl(aircraft.plan.cruiseFL))
.speed(420)
.before({
waypoint: aircraft.plan.exitPoint,
distanceNm: 50,
callback: (ac) => ac.descend(fl(ac.plan.exitFL))
})
.over({
waypoint: aircraft.plan.exitPoint,
callback: (ac) => ac.handover()
});
});direct with array
javascript
// Reroute through specific waypoints
aircraft.direct(['KMART', 'ROBUC', 'HIBER']);
// Shortcut direct to exit
aircraft.direct(aircraft.plan.exitPoint);