If you want to make a mount capable of flight in UOX3, follow the steps below to add the required scripts:
Place the following two scripts in your custom JS folder:
flyinggump.js
flyingmount.js
Open your file_association.scp file and add the following entries to register the scripts:
(Add these lines after the existing entry for 15007=server/house/houseAddonUse.js)
16000=custom/flyinggump.js
16001=custom/flyingmount.js
16001=custom/flyingmount.js
In your mounts.dfn, add the following entries to any mount you want to make flyable:
custominttag=isFlyingMount 1
SCRIPT=16001
SCRIPT=16001
const Directions = {
NONE: 0,
UP: 1,
NORTH: 2,
RIGHT: 3,
EAST: 4,
DOWN: 5,
SOUTH: 6,
LEFT: 7,
WEST: 8
};
function showFlyingGump(pUser, activeDir)
{
var gump = new Gump;
gump.AddPage(0);
gump.AddBackground(30, 30, 200, 200, 5054);
var directions = [
{ dir: Directions.UP, x: 85, y: 40, btn: 0x1194 },
{ dir: Directions.NORTH, x: 115, y: 50, btn: 0x1195 },
{ dir: Directions.RIGHT, x: 130, y: 80, btn: 0x1196 },
{ dir: Directions.EAST, x: 115, y: 115, btn: 0x1197 },
{ dir: Directions.DOWN, x: 85, y: 130, btn: 0x1198 },
{ dir: Directions.SOUTH, x: 50, y: 115, btn: 0x1199 },
{ dir: Directions.LEFT, x: 40, y: 80, btn: 0x119A },
{ dir: Directions.WEST, x: 50, y: 50, btn: 0x119B },
{ dir: Directions.NONE, x: 85, y: 85, btn: 0x1193 } // STOP button in center
];
for (var i = 0; i < directions.length; i++)
{
var entry = directions[i];
gump.AddButton(entry.x, entry.y, entry.btn, entry.btn, 1, 0, entry.dir);
if (entry.dir == activeDir)
{
gump.AddGump(entry.x, entry.y, entry.btn, 69);
}
}
gump.Send(pUser.socket);
gump.Free();
}
function GetFacingDirectionFromButton(buttonID)
{
switch(buttonID)
{
case 2: return 0; // NORTH
case 3: return 1; // RIGHT (Northeast)
case 4: return 2; // EAST
case 6: return 4; // SOUTH
case 7: return 5; // LEFT (Southwest)
case 8: return 6; // WEST
default: return null; // STOP, UP, DOWN have no facing
}
}
function onGumpPress(pSock, buttonID)
{
var pChar = pSock.currentChar;
if(!ValidateObject(pChar))
return;
if(!pChar.GetTag("FlyingMounted"))
return;
if(buttonID == 0 && !pChar.GetTag("FlyingMounted")) // Stop button
return;
pChar.SetTag("flyingDirection", buttonID);
showFlyingGump(pChar, buttonID);
var facing = GetFacingDirectionFromButton(buttonID);
if (facing != null)
{
pChar.direction = facing;
//pChar.SysMessage("DEBUG: ButtonID=" + buttonID + " | Facing=" + facing);
}
if (!pChar.GetTag("isFlyingTimerRunning"))
{
pChar.SetTag("isFlyingTimerRunning", true);
pChar.StartTimer(500, 1, 16000);
}
}
function onTimer(pChar, timerID)
{
if (timerID != 1)
return;
var dir = parseInt(pChar.GetTag("flyingDirection"), 10);
if (dir == null || dir == 0)
{
pChar.KillTimers(1);
pChar.SetTag("isFlyingTimerRunning", null);
return;
}
if (pChar.dead)
{
pChar.KillTimers(1);
pChar.SetTag("isFlyingTimerRunning", null);
pChar.SetTag("flyingDirection", null);
return;
}
if (!pChar.isOnline)
{
pChar.KillTimers(1);
pChar.SetTag("isFlyingTimerRunning", null);
pChar.SetTag("flyingDirection", null);
return;
}
var newX = pChar.x;
var newY = pChar.y;
var newZ = pChar.z;
switch(dir)
{
case Directions.UP: newZ++; break;
case Directions.DOWN: newZ--; break;
case Directions.NORTH: newY--; break;
case Directions.SOUTH: newY++; break;
case Directions.EAST: newX++; break;
case Directions.WEST: newX--; break;
case Directions.RIGHT: newX++; newY--; break;
case Directions.LEFT: newX--; newY++; break;
}
pChar.DoAction(24);
pChar.SoundEffect(0x2D0, true);
// Optional: Add trail effect
// pChar.StaticEffect(0x377A, 0, 0);
if (IsSafeFlying(pChar, newX, newY, newZ))
{
pChar.Teleport(newX, newY, newZ);
pChar.StartTimer(300, 1, 16000); // continue flight if successful
}
else
{
// Flight blocked — stop flying
pChar.KillTimers(1);
pChar.SetTag("isFlyingTimerRunning", null);
pChar.SetTag("flyingDirection", null);
pChar.SysMessage("Flight path blocked.");
}
}
function IsSafeFlying(pChar, newX, newY, newZ)
{
var world = pChar.worldnumber;
var instance = pChar.instanceID;
const MAX_FLIGHT_HEIGHT = 140;
//pChar.SysMessage("Testing flying to: X=" + newX + " Y=" + newY + " Z=" + newZ);
var mapBlock = DoesMapBlock(newX, newY, newZ, world, false, false, false, false);
var staticFlag = CheckStaticFlag(newX, newY, newZ, world, 6);
var dynamicBlock = DoesDynamicBlock(newX, newY, newZ, world, instance, false, false, false, false);
//if (mapBlock)
// pChar.SysMessage("Map Blocked");
//if (staticFlag)
// pChar.SysMessage("Static Blocked");
//if (dynamicBlock)
// pChar.SysMessage("Dynamic Blocked");
// Block flying into dungeon zone
if (newX >= 5117)
return false;
// Block Lost Lands no-fly zone
if (newX >= 5123 && newX <= 6140 && newY >= 2305 && newY <= 4093)
return false;
var bounds = GetMaxBounds(world);
// Prevent out-of-map flying
if (newX < 0 || newY < 0 || newX >= bounds.maxX || newY >= bounds.maxY)
return false;
if (newZ >= MAX_FLIGHT_HEIGHT)
return false;
if( newZ <= GetMapElevation( newX, newY, world ))
return false;
if (mapBlock || staticFlag || dynamicBlock)
return false;
return true;
}
function GetMaxBounds(world)
{
switch(world)
{
case 0: return { maxX: 6144, maxY: 4096 }; // Felucca
case 1: return { maxX: 6144, maxY: 4096 }; // Trammel
case 2: return { maxX: 2304, maxY: 1600 }; // Ilshenar
case 3: return { maxX: 2560, maxY: 2048 }; // Malas
case 4: return { maxX: 1448, maxY: 1448 }; // Tokuno
case 5: return { maxX: 1280, maxY: 4096 }; // TerMur
default: return { maxX: 6144, maxY: 4096 }; // default fallback
}
}
NONE: 0,
UP: 1,
NORTH: 2,
RIGHT: 3,
EAST: 4,
DOWN: 5,
SOUTH: 6,
LEFT: 7,
WEST: 8
};
function showFlyingGump(pUser, activeDir)
{
var gump = new Gump;
gump.AddPage(0);
gump.AddBackground(30, 30, 200, 200, 5054);
var directions = [
{ dir: Directions.UP, x: 85, y: 40, btn: 0x1194 },
{ dir: Directions.NORTH, x: 115, y: 50, btn: 0x1195 },
{ dir: Directions.RIGHT, x: 130, y: 80, btn: 0x1196 },
{ dir: Directions.EAST, x: 115, y: 115, btn: 0x1197 },
{ dir: Directions.DOWN, x: 85, y: 130, btn: 0x1198 },
{ dir: Directions.SOUTH, x: 50, y: 115, btn: 0x1199 },
{ dir: Directions.LEFT, x: 40, y: 80, btn: 0x119A },
{ dir: Directions.WEST, x: 50, y: 50, btn: 0x119B },
{ dir: Directions.NONE, x: 85, y: 85, btn: 0x1193 } // STOP button in center
];
for (var i = 0; i < directions.length; i++)
{
var entry = directions[i];
gump.AddButton(entry.x, entry.y, entry.btn, entry.btn, 1, 0, entry.dir);
if (entry.dir == activeDir)
{
gump.AddGump(entry.x, entry.y, entry.btn, 69);
}
}
gump.Send(pUser.socket);
gump.Free();
}
function GetFacingDirectionFromButton(buttonID)
{
switch(buttonID)
{
case 2: return 0; // NORTH
case 3: return 1; // RIGHT (Northeast)
case 4: return 2; // EAST
case 6: return 4; // SOUTH
case 7: return 5; // LEFT (Southwest)
case 8: return 6; // WEST
default: return null; // STOP, UP, DOWN have no facing
}
}
function onGumpPress(pSock, buttonID)
{
var pChar = pSock.currentChar;
if(!ValidateObject(pChar))
return;
if(!pChar.GetTag("FlyingMounted"))
return;
if(buttonID == 0 && !pChar.GetTag("FlyingMounted")) // Stop button
return;
pChar.SetTag("flyingDirection", buttonID);
showFlyingGump(pChar, buttonID);
var facing = GetFacingDirectionFromButton(buttonID);
if (facing != null)
{
pChar.direction = facing;
//pChar.SysMessage("DEBUG: ButtonID=" + buttonID + " | Facing=" + facing);
}
if (!pChar.GetTag("isFlyingTimerRunning"))
{
pChar.SetTag("isFlyingTimerRunning", true);
pChar.StartTimer(500, 1, 16000);
}
}
function onTimer(pChar, timerID)
{
if (timerID != 1)
return;
var dir = parseInt(pChar.GetTag("flyingDirection"), 10);
if (dir == null || dir == 0)
{
pChar.KillTimers(1);
pChar.SetTag("isFlyingTimerRunning", null);
return;
}
if (pChar.dead)
{
pChar.KillTimers(1);
pChar.SetTag("isFlyingTimerRunning", null);
pChar.SetTag("flyingDirection", null);
return;
}
if (!pChar.isOnline)
{
pChar.KillTimers(1);
pChar.SetTag("isFlyingTimerRunning", null);
pChar.SetTag("flyingDirection", null);
return;
}
var newX = pChar.x;
var newY = pChar.y;
var newZ = pChar.z;
switch(dir)
{
case Directions.UP: newZ++; break;
case Directions.DOWN: newZ--; break;
case Directions.NORTH: newY--; break;
case Directions.SOUTH: newY++; break;
case Directions.EAST: newX++; break;
case Directions.WEST: newX--; break;
case Directions.RIGHT: newX++; newY--; break;
case Directions.LEFT: newX--; newY++; break;
}
pChar.DoAction(24);
pChar.SoundEffect(0x2D0, true);
// Optional: Add trail effect
// pChar.StaticEffect(0x377A, 0, 0);
if (IsSafeFlying(pChar, newX, newY, newZ))
{
pChar.Teleport(newX, newY, newZ);
pChar.StartTimer(300, 1, 16000); // continue flight if successful
}
else
{
// Flight blocked — stop flying
pChar.KillTimers(1);
pChar.SetTag("isFlyingTimerRunning", null);
pChar.SetTag("flyingDirection", null);
pChar.SysMessage("Flight path blocked.");
}
}
function IsSafeFlying(pChar, newX, newY, newZ)
{
var world = pChar.worldnumber;
var instance = pChar.instanceID;
const MAX_FLIGHT_HEIGHT = 140;
//pChar.SysMessage("Testing flying to: X=" + newX + " Y=" + newY + " Z=" + newZ);
var mapBlock = DoesMapBlock(newX, newY, newZ, world, false, false, false, false);
var staticFlag = CheckStaticFlag(newX, newY, newZ, world, 6);
var dynamicBlock = DoesDynamicBlock(newX, newY, newZ, world, instance, false, false, false, false);
//if (mapBlock)
// pChar.SysMessage("Map Blocked");
//if (staticFlag)
// pChar.SysMessage("Static Blocked");
//if (dynamicBlock)
// pChar.SysMessage("Dynamic Blocked");
// Block flying into dungeon zone
if (newX >= 5117)
return false;
// Block Lost Lands no-fly zone
if (newX >= 5123 && newX <= 6140 && newY >= 2305 && newY <= 4093)
return false;
var bounds = GetMaxBounds(world);
// Prevent out-of-map flying
if (newX < 0 || newY < 0 || newX >= bounds.maxX || newY >= bounds.maxY)
return false;
if (newZ >= MAX_FLIGHT_HEIGHT)
return false;
if( newZ <= GetMapElevation( newX, newY, world ))
return false;
if (mapBlock || staticFlag || dynamicBlock)
return false;
return true;
}
function GetMaxBounds(world)
{
switch(world)
{
case 0: return { maxX: 6144, maxY: 4096 }; // Felucca
case 1: return { maxX: 6144, maxY: 4096 }; // Trammel
case 2: return { maxX: 2304, maxY: 1600 }; // Ilshenar
case 3: return { maxX: 2560, maxY: 2048 }; // Malas
case 4: return { maxX: 1448, maxY: 1448 }; // Tokuno
case 5: return { maxX: 1280, maxY: 4096 }; // TerMur
default: return { maxX: 6144, maxY: 4096 }; // default fallback
}
}
function onCharDoubleClick(pUser, targChar)
{
// Only handle this for flying mounts
if (!targChar.GetTag("isFlyingMount"))
return false;
if (!pUser.InRange(targChar, 1))
{
pUser.SysMessage("You are too far away.");
return false;
}
TriggerEvent(16000, "showFlyingGump", pUser, 0);
pUser.SetTag("FlyingMounted", true);
return true;
}
function onDismount( pUser, npcMount )
{
// Dismounting, so let's stop the disco effect
pUser.KillJSTimer( 1, 16000 );
pUser.SetTag("FlyingMounted", null);
}
{
// Only handle this for flying mounts
if (!targChar.GetTag("isFlyingMount"))
return false;
if (!pUser.InRange(targChar, 1))
{
pUser.SysMessage("You are too far away.");
return false;
}
TriggerEvent(16000, "showFlyingGump", pUser, 0);
pUser.SetTag("FlyingMounted", true);
return true;
}
function onDismount( pUser, npcMount )
{
// Dismounting, so let's stop the disco effect
pUser.KillJSTimer( 1, 16000 );
pUser.SetTag("FlyingMounted", null);
}