X Wing Flying Race (WTVC)
This is a flying race I created for WTVC called X Wing, built using the W-ing plane. The race is being removed, so I'm releasing it for others to remake and use. Simply change the positions to fit your map. Make sure to remove any Discord-related code or set it up for your own environment. Enjoy and have fun building on it.
// X Wing Race System
// Extracted from main.nut
xwing <- {
startPos = Vector(-1559.859, 648.125, 14.403),
startAngle = -1.55,
checkpoints = [
Vector(-1559.859, 648.125, 14.403),
Vector(-1517.019, 653.148, 24.320),
Vector(-1430.378, 636.621, 80.225),
Vector(-1360.666, 650.439, 79.066),
Vector(-1196.620, 623.834, 87.602),
Vector(-1079.288, 687.980, 62.136),
Vector(-746.709, 635.587, 108.336),
Vector(-524.010, 629.381, 84.195),
Vector(116.522, 600.012, 115.677),
Vector(248.778, 593.669, 130.582),
Vector(446.930, 581.749, 110.502),
Vector(711.708, 440.553, 93.366),
Vector(706.734, 115.256, 100.559),
Vector(501.730, -161.831, 85.142),
Vector(431.403, -209.527, 87.621),
Vector(201.000, -427.811, 67.505),
Vector(-37.105, -657.533, 70.343),
Vector(-184.701, -761.630, 36.300),
Vector(-336.744, -847.958, 23.296),
Vector(-540.863, -1029.752, 27.241),
Vector(-812.201, -1265.412, 110.468),
Vector(-961.785, -1242.002, 94.126),
Vector(-1236.962, -1060.772, 74.228),
Vector(-1308.234, -801.706, 122.914),
Vector(-1355.306, -665.239, 82.976),
Vector(-1402.260, -553.120, 45.981),
Vector(-1424.713, -499.361, 19.229)
],
markers = [],
permanentMarkers = [],
leaderboard = [],
inProgress = false,
data = {},
countdown = 0
};
xwingExpanded <- false;
function ExpandXWingCheckpoints()
{
if(xwingExpanded) return;
xwingExpanded = true;
local orig = xwing.checkpoints;
if(!orig || orig.len() < 2) return;
local expanded = [];
local maxSegDist = 150.0;
for(local i = 0; i < orig.len() - 1; i++)
{
local a = orig;
local b = orig[i + 1];
expanded.append(a);
local dx = b.x - a.x;
local dy = b.y - a.y;
local dz = b.z - a.z;
local dist = sqrt((dx * dx) + (dy * dy) + (dz * dz));
local segments = 1;
if(dist > maxSegDist) {
segments = ceil(dist / maxSegDist).tointeger();
if(segments < 1) segments = 1;
}
for(local s = 1; s < segments; s++)
{
local t = s.tofloat() / segments.tofloat();
expanded.append(Vector(a.x + (dx * t), a.y + (dy * t), a.z + (dz * t)));
}
}
expanded.append(orig[orig.len() - 1]);
xwing.checkpoints = expanded;
local maxFound = 0.0;
for(local j = 0; j < expanded.len() - 1; j++)
{
local p = expanded[j];
local q = expanded[j + 1];
local dx2 = q.x - p.x;
local dy2 = q.y - p.y;
local dz2 = q.z - p.z;
local d2 = sqrt((dx2 * dx2) + (dy2 * dy2) + (dz2 * dz2));
if(d2 > maxFound) maxFound = d2;
if(d2 > maxSegDist) {
print("[XWing] Gap still large between expanded " + j + "->" + (j+1) + ": dist=" + d2);
}
}
print("[XWing] Checkpoints expanded: " + orig.len() + " -> " + expanded.len() + " (maxSeg=" + maxFound + ")");
}
ExpandXWingCheckpoints();
XWING_TIME_LIMIT_SECONDS <- 180;
XWING_REWARD_XP <- 2000;
xwingTimeoutTimer <- null;
xwingRunId <- 0;
function XWingTimeoutTick(runId)
{
if(runId != xwingRunId) return;
if(!xwingTimeoutTimer) return;
try { KillTimer(xwingTimeoutTimer); } catch(e) {}
xwingTimeoutTimer = null;
if(xwing.data.len() == 0) return;
foreach(pid, data in xwing.data)
{
local pl = FindPlayer(pid);
if(data.marker && typeof data.marker == "instance") { try { data.marker.Delete(); } catch(e) {} }
if(data.checkpoint && typeof data.checkpoint == "instance") { try { data.checkpoint.Remove(); } catch(e) {} }
if(pl) {
try { RemoveRaceUniform(pl); } catch(e) {}
MessagePlayer("[#FF0000]X Wing failed - time ran out!", pl);
}
}
xwing.data.clear();
raceWinner = null;
Message("[#FF0000]X Wing ended - time ran out.");
}
function CreatePermanentXWingMarkers()
{
// Airport radar pickup object (637) at start
CreatePickup(637, xwing.startPos);
local startMarker = CreateMarker(1, xwing.startPos, 3, RGB(0, 255, 0), 21);
xwing.permanentMarkers.append(startMarker);
for(local i = 0; i < xwing.checkpoints.len(); i++){
local checkpoint = xwing.checkpoints;
local color = (i == xwing.checkpoints.len() - 1) ? RGB(255, 0, 0) : RGB(0, 255, 255);
local markerIcon = (i == xwing.checkpoints.len() - 1) ? 15 : 21;
local mk = CreateMarker(1, checkpoint, 3, color, markerIcon);
if(mk) xwing.permanentMarkers.append(mk);
// Place airport radar pickup on top of the marker
CreatePickup(637, checkpoint);
}
}
function JoinXWing(player)
{
if(!player) return 1;
if(!stats[player.ID].Logged){ MessagePlayer("You must be logged in to use this command.", player); return 1; }
if(IsAnyRaceActive()){
MessagePlayer("[#FF0000]A race is already running. Please wait for it to finish.", player);
return 1;
}
if(xwing.data.rawin(player.ID)){
MessagePlayer("[#FF0000]You are already in the X Wing race.", player);
return 1;
}
ApplyRaceUniform(player);
try { if(player.Vehicle) player.Vehicle = null; } catch(e) {}
try { player.World = 1; } catch(e) {}
player.Pos = xwing.startPos;
player.Angle = xwing.startAngle;
player.Frozen = true;
MessagePlayer("[#00FF00]X Wing starting in 10 seconds!", player);
MessagePlayer("[#FFFF00]Get in a vehicle and follow the AIRPORT RADAR checkpoints.", player);
MessagePlayer("[#FF69B4]Finish within 3 minutes to win +" + XWING_REWARD_XP + " XP.", player);
xwing.data[player.ID] <- {
startTime = 0,
checkpointIndex = 0,
marker = null,
checkpoint = null
};
NewTimer("StartXWingRace", 10000, 1, player.ID);
return 1;
}
function LeaveXWing(player, quiet = false)
{
if(!player) return false;
if(!xwing.data.rawin(player.ID)) return false;
local data = xwing.data[player.ID];
if(data && data.rawin("marker") && typeof data.marker == "instance") { try { data.marker.Delete(); } catch(e) {} }
if(data && data.rawin("checkpoint") && typeof data.checkpoint == "instance") { try { data.checkpoint.Remove(); } catch(e) {} }
xwing.data.rawdelete(player.ID);
try { RemoveRaceUniform(player); } catch(e) {}
if(!quiet) MessagePlayer("[#FF0000]You have left X Wing!", player);
if(xwing.data.len() == 0 && xwingTimeoutTimer) { try { KillTimer(xwingTimeoutTimer); } catch(e) {} xwingTimeoutTimer = null; }
return true;
}
function StartXWingRace(playerID)
{
local player = FindPlayer(playerID);
if(!player || !xwing.data.rawin(playerID)) return;
local data = xwing.data[playerID];
player.Frozen = false;
data.startTime = time();
// Start timeout timer
if(xwingTimeoutTimer) { try { KillTimer(xwingTimeoutTimer); } catch(e) {} }
xwingRunId++;
xwingTimeoutTimer = NewTimer("XWingTimeoutTick", XWING_TIME_LIMIT_SECONDS * 1000, 1, xwingRunId);
// Place first marker and checkpoint
data.marker = CreateMarker(0, xwing.checkpoints[0], 3, RGB(0, 255, 255), 32);
data.checkpoint = CreateCheckpoint(player, player.World, false, Vector(xwing.checkpoints[0].x, xwing.checkpoints[0].y, xwing.checkpoints[0].z - 0.9), ARGB(255, 0, 255, 255), 65);
MessagePlayer("[#00FF00]GO! Follow the AIRPORT RADAR checkpoints!", player);
}
// Checkpoint logic in onCheckpointEntered
// This needs to be integrated into the main onCheckpointEntered function
function HandleXWingCheckpoint(player)
{
if(xwing.data.rawin(player.ID)) {
local data = xwing.data[player.ID];
local idx = data.checkpointIndex;
if(idx >= xwing.checkpoints.len()) return;
if(data.marker && typeof data.marker == "instance") data.marker.Delete();
if(data.checkpoint && typeof data.checkpoint == "instance") {
try { data.checkpoint.Remove(); } catch(e) { try { data.checkpoint.Delete(); } catch(e2) {} }
}
data.checkpointIndex++;
if(data.checkpointIndex < xwing.checkpoints.len()) {
local markerIcon = (data.checkpointIndex == xwing.checkpoints.len() - 1) ? 15 : 32;
data.marker = CreateMarker(0, xwing.checkpoints[data.checkpointIndex], 3, RGB(0, 255, 255), markerIcon);
local checkpointPos = Vector(xwing.checkpoints[data.checkpointIndex].x, xwing.checkpoints[data.checkpointIndex].y, xwing.checkpoints[data.checkpointIndex].z - 0.9);
data.checkpoint = CreateCheckpoint(player, player.World, false, checkpointPos, ARGB(255, 0, 255, 255), 65);
MessagePlayer("[#00FF00]X Wing checkpoint " + (data.checkpointIndex+1) + "/" + xwing.checkpoints.len() + " - follow the AIRPORT RADAR!", player);
} else {
local elapsed = 999999;
try { elapsed = time() - data.startTime; } catch(e) { elapsed = 999999; }
if(elapsed <= XWING_TIME_LIMIT_SECONDS) {
AddXPToPlayer(player.Name, XWING_REWARD_XP);
MessagePlayer("[#00FF00]X Wing complete in " + elapsed + "s! Reward: +" + XWING_REWARD_XP + " XP", player);
Message("[#FFD700]WINNER! " + player.Name + " completed X Wing in " + elapsed + " seconds!");
try { SendMessage(format("✈️🏁 **X WING** — %s completed X Wing in %d seconds (+%d XP)!", player.Name, elapsed, XWING_REWARD_XP)); } catch(e) {}
try { Announce("~g~YOU WON!~w~", player, 5); } catch(e) {}
try { player.PlaySound(50007); } catch(e) {}
} else {
MessagePlayer("[#FF0000]Too late! You missed the 3 minute limit. No reward.", player);
}
try { RemoveRaceUniform(player); } catch(e) {}
xwing.data.rawdelete(player.ID);
if(xwing.data.len() == 0 && xwingTimeoutTimer) { try { KillTimer(xwingTimeoutTimer); } catch(e) {} xwingTimeoutTimer = null; }
}
}
}
// Auto event case for xwing
function StartAutoXWing(players)
{
raceWinner = null;
RecordEventStart("XWing");
foreach(pl in players){
if(pl.ID >= stats.len()) continue;
local s = stats[pl.ID];
if(!s || !("Logged" in s) || !s.Logged) continue;
if(xwing.data.rawin(pl.ID)) continue;
ApplyRaceUniform(pl);
try { if(pl.Vehicle) pl.Vehicle = null; } catch(e) {}
try { pl.World = 1; } catch(e) {}
pl.Pos = xwing.startPos;
pl.Angle = xwing.startAngle;
pl.Frozen = true;
MessagePlayer("[#00FF00][EVENT] Auto X Wing starting in 10 seconds!", pl);
MessagePlayer("[#FFFF00]Get in a vehicle and follow the AIRPORT RADAR checkpoints.", pl);
MessagePlayer("[#FF69B4]Finish within 3 minutes to win +" + XWING_REWARD_XP + " XP.", pl);
xwing.data[pl.ID] <- {
startTime = 0,
checkpointIndex = 0,
marker = null,
checkpoint = null
};
NewTimer("StartXWingRace", 10000, 1, pl.ID);
}
}