Skip to content

broker0/js_stealth

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

js-stealth

JavaScript client for Stealth with async/await API. Minimal implementation matching py_stealth functionality.

Installation

# No dependencies - pure Bun.js

Connect to remote stealth server

import './js_stealth';

config.HOST = '192.168.88.13';

const selfId = await Self();
const [x, y, z] = await parallel([
  [GetX, selfId],
  [GetY, selfId],
  [GetZ, selfId],
]);

console.log(`Position: ${x}, ${y}, ${z}`);

Connection

connect(host, port)

Connect to Stealth client. Port is auto-discovered if not provided.

await connect('192.168.88.13', 50026);
// or
config.HOST = '192.168.88.13';
// await connect(); 

disconnect()

Close connection to

await disconnect();

on(event, callback)

Subscribe to Stealth events.

on('evspeech', (data) => {
  console.log('Speech:', data);
});

Available Events

All event names are available as constants. Common events:

Item Events:

  • 'eviteminfo' - Item information received
  • 'evitemdeleted' - Item deleted
  • 'evadditemtocontainer' - Item added to container
  • 'evaddmultipleitemsincont' - Multiple items added
  • 'evrejectmoveitem' - Item move rejected

Speech/Communication:

  • 'evspeech' - Speech event (most common)
  • 'evclilocspeech' - Cliloc speech
  • 'evclilocspeechaffix' - Cliloc speech with affix
  • 'evunicodespeech' - Unicode speech
  • 'evglobalchat' - Global chat message

Character/Movement:

  • 'evdrawgameplayer' - Player drawn
  • 'evmoverejection' - Movement rejected
  • 'evupdatechar' - Character updated
  • 'evcharanimation' - Character animation

UI/Interaction:

  • 'evmenu' - Menu event
  • 'evmapmessage' - Map message
  • 'evincominggump' - Gump received
  • 'evgumptextentry' - Gump text entry
  • 'evcontextmenu' - Context menu

Combat:

  • 'evallowrefuseattack' - Attack allowed/refused
  • 'evwardamage' - War damage
  • 'evdeath' - Death event

System:

  • 'evtimer1', 'evtimer2' - Timer events
  • 'evbuffdebuffsystem' - Buff/debuff system
  • 'evupdateobjstats' - Object stats updated
  • 'evsetglobalvar' - Global variable set
  • 'evgraphicaleffect' - Graphical effect
  • 'evsound' - Sound event

Complete list: See EVENTS constant - all event names are exported and available globally after import.

Parallel Operations

parallel(commands)

Execute multiple commands in parallel for maximum performance.

const [x, y, z, type, name] = await parallel([
  [GetX, selfId],
  [GetY, selfId],
  [GetZ, selfId],
  [GetType, selfId],
  [GetName, selfId],
]);

parallel_items(items, operations, numConnections?)

Execute operations on multiple items in parallel.

const items = await FindType(0xFFFF, Ground());
const results = await parallel_items(items, [
  GetX,
  GetY,
  GetZ,
  GetType,
  GetName,
]);

// Returns array of {id, data: [x, y, z, type, name]}
// Note: Use Find() or FindProps() for a more convenient API with automatic key mapping

Player Information

Self & Identity

  • Self() - Get player's object ID
  • CharName() - Get character name
  • ProfileName() - Get profile name
  • ShardName() - Get shard name
  • WorldNum() - Get current world number
const selfId = await Self();
const charName = await CharName();
const profileName = await ProfileName();
const shardName = await ShardName();
const worldNum = await WorldNum();

console.log(`${charName} on ${shardName} (profile: ${profileName}, world: ${worldNum})`);

Position

  • GetX(objId) - Get X coordinate
  • GetY(objId) - Get Y coordinate
  • GetZ(objId) - Get Z coordinate
  • PredictedX() - Get predicted X (for movement)
  • PredictedY() - Get predicted Y (for movement)
  • PredictedZ() - Get predicted Z (for movement)
  • PredictedDirection() - Get predicted facing direction
const selfId = await Self();
const x = await GetX(selfId);
const y = await GetY(selfId);
const z = await GetZ(selfId);
console.log(`Position: [${x}, ${y}, ${z}]`);

// Predicted position (for movement calculations)
const predX = await PredictedX();
const predY = await PredictedY();
const predZ = await PredictedZ();
const predDir = await PredictedDirection();

Attributes (Self)

  • Str() - Get strength
  • Int() - Get intelligence
  • Dex() - Get dexterity
  • HP() - Get current hit points
  • Mana() - Get current mana
  • Stam() - Get current stamina
  • MaxHP() - Get maximum hit points
  • MaxMana() - Get maximum mana
  • MaxStam() - Get maximum stamina
  • Weight() - Get current weight
  • MaxWeight() - Get maximum weight
  • Gold() - Get gold amount
  • Armor() - Get armor rating
  • Luck() - Get luck value
  • Race() - Get race
  • Sex() - Get gender
  • Backpack() - Get backpack ID
// Get all stats in parallel
const [str, int, dex, hp, maxhp, mana, maxmana, stam, maxstam] = await parallel([
  [Str],
  [Int],
  [Dex],
  [HP],
  [MaxHP],
  [Mana],
  [MaxMana],
  [Stam],
  [MaxStam],
]);

console.log(`Stats: STR ${str}, INT ${int}, DEX ${dex}`);
console.log(`HP: ${hp}/${maxhp}, Mana: ${mana}/${maxmana}, Stam: ${stam}/${maxstam}`);

const weight = await Weight();
const maxWeight = await MaxWeight();
const gold = await Gold();
const armor = await Armor();
const luck = await Luck();
const race = await Race();
const sex = await Sex();
const backpackId = await Backpack();

console.log(`Weight: ${weight}/${maxWeight}, Gold: ${gold}, Armor: ${armor}, Luck: ${luck}`);
console.log(`Race: ${race}, Gender: ${sex}, Backpack ID: ${backpackId}`);

Attributes (Objects)

  • GetStr(objId) - Get strength
  • GetInt(objId) - Get intelligence
  • GetDex(objId) - Get dexterity
  • GetHP(objId) - Get hit points
  • GetMana(objId) - Get mana
  • GetStam(objId) - Get stamina
  • GetMaxHP(objId) - Get max hit points
  • GetMaxMana(objId) - Get max mana
  • GetMaxStam(objId) - Get max stamina
const targetId = await WarTargetID();
if (targetId) {
  const [hp, maxhp, str, int, dex] = await parallel([
    [GetHP, targetId],
    [GetMaxHP, targetId],
    [GetStr, targetId],
    [GetInt, targetId],
    [GetDex, targetId],
  ]);
  
  console.log(`Target HP: ${hp}/${maxhp}, Stats: ${str}/${int}/${dex}`);
}

Status

  • Connected() - Check if connected to server
  • Dead() - Check if dead
  • Hidden() - Check if hidden
  • Poisoned() - Check if poisoned
  • Paralyzed() - Check if paralyzed
  • WarMode() - Check if in war mode
  • IsDead(objId) - Check if object is dead
  • IsRunning(objId) - Check if object is running
  • IsNPC(objId) - Check if object is NPC
  • IsContainer(objId) - Check if object is container
  • IsMovable(objId) - Check if object is movable
// Check connection and self status
if (!(await Connected())) {
  console.log('Not connected!');
}

const isDead = await Dead();
const isHidden = await Hidden();
const isPoisoned = await Poisoned();
const isParalyzed = await Paralyzed();
const inWarMode = await WarMode();

// Check object status
const targetId = await WarTargetID();
if (targetId) {
  const isTargetDead = await IsDead(targetId);
  const isTargetRunning = await IsRunning(targetId);
  const isTargetNPC = await IsNPC(targetId);
  const isTargetContainer = await IsContainer(targetId);
  const isTargetMovable = await IsMovable(targetId);
  
  console.log(`Target: Dead=${isTargetDead}, Running=${isTargetRunning}, NPC=${isTargetNPC}`);
}

Object Information

Basic Properties

  • GetType(objId) - Get object type (graphic ID)
  • GetName(objId) - Get object name
  • GetAltName(objId) - Get alternate name
  • GetTitle(objId) - Get title
  • GetColor(objId) - Get color
  • GetDirection(objId) - Get facing direction
  • GetLayer(objId) - Get equipped layer
  • GetDistance(objId) - Get distance to object
  • GetQuantity(objId) - Get quantity
  • GetPrice(objId) - Get price
  • GetTooltip(objId) - Get tooltip text
  • GetParent(objId) - Get parent container ID
  • GetNotoriety(objId) - Get notoriety
  • IsObjectExists(objId) - Check if object exists
const itemId = 12345;

// Get basic properties in parallel
const [type, name, color, quantity, distance, parent] = await parallel([
  [GetType, itemId],
  [GetName, itemId],
  [GetColor, itemId],
  [GetQuantity, itemId],
  [GetDistance, itemId],
  [GetParent, itemId],
]);

console.log(`${name} (type: 0x${type.toString(16)}, color: 0x${color.toString(16)})`);
console.log(`Quantity: ${quantity}, Distance: ${distance}, Parent: ${parent}`);

// Check if object exists before using
if (await IsObjectExists(itemId)) {
  const tooltip = await GetTooltip(itemId);
  const notoriety = await GetNotoriety(itemId);
  console.log(`Tooltip: ${tooltip}, Notoriety: ${notoriety}`);
}

Object Checks

  • IsYellowHits(objId) - Check if yellow hits
  • IsFemale(objId) - Check if female
  • IsHouse(objId) - Check if house
const targetId = await WarTargetID();
if (targetId) {
  const hasYellowHits = await IsYellowHits(targetId);
  const isFemale = await IsFemale(targetId);
  const isHouse = await IsHouse(targetId);
  
  console.log(`Yellow hits: ${hasYellowHits}, Female: ${isFemale}, House: ${isHouse}`);
}

Finding Objects

Search Configuration

  • SetFindDistance(value) - Set find distance
  • GetFindDistance() - Get find distance
  • SetFindVertical(value) - Set vertical find range
  • GetFindVertical() - Get vertical find range
  • Ground() - Ground container constant (0)
// Set find distance to 35 tiles
await SetFindDistance(35);
const currentDistance = await GetFindDistance();
console.log(`Find distance: ${currentDistance}`);

// Set vertical range to 100
await SetFindVertical(100);
const currentVertical = await GetFindVertical();
console.log(`Find vertical: ${currentVertical}`);

// Use Ground() constant (equals 0)
const groundItems = await FindType(0xFFFF, Ground());

Finding

Find()

Finds items and optionally gets their properties in one call. Supports filtering and getting additional properties after filtering.

Basic usage:

// Find items with basic properties
const creatures = await Find({
  objTypes: [0xFFFF],              // Find items of ANY of these types
  colors: [0xFFFF],                // With ANY of these colors
  containers: [Ground()],          // In ANY of these containers
  operations: [GetHP, GetDistance] // Get properties: hp, distance
});
// Returns: [{ id: 123, hp: 100, distance: 50 }, { id: 456, hp: 75, distance: 120 }, ...]

With filters:

const healthyCreatures = await Find({
  objTypes: [0xFFFF],
  operations: [GetHP],
  filters: [(item) => item.hp > 0 && item.hp < 100] // Only creatures with HP between 0 and 100
});

With properties (runs FindProps after filters):

// First get HP, filter by HP > 0, then get additional properties on filtered results
const creaturesDetails = await Find({
  objTypes: [0xFFFF],
  operations: [GetHP],                              // Get HP first
  filters: [(item) => item.hp > 0],                // Filter by HP > 0
  properties: [GetName, GetNotoriety, GetX, GetY]  // Then get these properties on filtered results
});
// Returns: [{ id: 123, hp: 100, name: 'a drake', notoriety: 4, x: 100, y: 200 }, ...]

Parameters:

  • objTypes or objType: Array or single number/hex (e.g., [0x190, 0x191] or 0x190)
  • colors or color: Array or single number/hex (default: [0xFFFF] - any color)
  • containers or container: Array or single container (numbers, hex, or Ground(), Backpack() - promises auto-awaited)
  • operations: Optional array of functions to execute on found items (e.g., [GetX, GetY, GetHP])
    • If omitted or empty, returns objects with just { id } properties
    • Keys are auto-derived from function names (e.g., GetX → x, GetName → name)
  • keys: Optional custom keys array (auto-derived if omitted)
  • filters: Optional function or array of functions to filter results (applied after operations)
    • All filters must return true for an item to be included (AND logic)
  • properties: Optional array of functions to get additional properties after filtering (runs FindProps on filtered results)
    • Executed after filters, so you can filter first, then get expensive properties on fewer items
  • numConnections: Number of parallel connections (default: 16)

Examples:

// Find all items (just IDs)
const allItems = await Find({ objTypes: [0xFFFF], colors: [0xFFFF] });
// Returns: [{ id: 123 }, { id: 456 }, ...]

// Find with properties
const items = await Find({
  objTypes: [0x0191, 0x0190],
  operations: [GetName, GetQuantity],
  filters: [(item) => item.quantity > 2]
});
// Returns: [{ id: 123, name: 'Apple', quantity: 5 }, ...]

// Find, filter, then get additional properties
const nearbyCreatures = await Find({
  objTypes: [0xFFFF],
  operations: [GetDistance, GetHP],
  filters: [
    (item) => item.distance < 10,  // Filter by distance first
    (item) => item.hp > 0           // Then by HP
  ],
  properties: [GetName, GetNotoriety, GetType] // Get these only on filtered results
});
  • FindType(objType, container) - Find objects by type
    • Returns array of object IDs (automatically calls GetFindedList)
    • container defaults to backpack if null, use Ground() for ground
    • Automatically awaits promises (e.g., Backpack()) - no await needed
const items = await FindType(0x0EED, Ground());
const backpackItems = await FindType(0x0EED); // null = backpack
const backpackItems2 = await FindType(0x0EED, Backpack()); // âś… No await needed - works directly!
  • FindTypeEx(objType, color, container, inSub) - Find objects by type and color
    • Returns array of object IDs (automatically calls GetFindedList)
    • Automatically awaits promises (e.g., Backpack()) - no await needed
const redApples = await FindTypeEx(0x09D0, 0x0021, Ground(), false);
const backpackApples = await FindTypeEx(0x09D0, 0x0021, Backpack(), false); // âś… No await needed!
  • GetFindedList() - Get list of found objects (usually auto-called)
  • FindCount() - Get count of found objects
  • FindItem() - Get first found item
  • FindAtCoord(x, y) - Find items at coordinates
  • FindNotoriety(objType, notoriety) - Find by notoriety
// FindCount and FindItem (manual usage - usually auto-called)
const count = await FindCount();
const firstItem = await FindItem();
console.log(`Found ${count} items, first: ${firstItem}`);

// Find items at specific coordinates
const selfId = await Self();
const x = await GetX(selfId);
const y = await GetY(selfId);
const itemsAtCoord = await FindAtCoord(x, y);
console.log(`Items at [${x}, ${y}]:`, itemsAtCoord);

// Find by notoriety
const innocents = await FindNotoriety(0x0190, NOTORIETY.Innocent);
const enemies = await FindNotoriety(0x0191, NOTORIETY.Enemy);
console.log(`Innocents: ${innocents.length}, Enemies: ${enemies.length}`);

Notoriety Constants

Available notoriety values for FindNotoriety:

NOTORIETY.Innocent     // 1 - Blue
NOTORIETY.Ally         // 2 - Green
NOTORIETY.Attackable   // 3 - Grey
NOTORIETY.Criminal     // 4 - Grey
NOTORIETY.Enemy        // 5 - Orange/Red
NOTORIETY.Murderer     // 6 - Red
NOTORIETY.Invulnerable // 7 - Yellow

// Usage
const innocents = await FindNotoriety(0x0190, NOTORIETY.Innocent);
const enemies = await FindNotoriety(0x0191, NOTORIETY.Enemy);
  • FindFullQuantity(objId) - Get full quantity of found item
  • FindTypesArrayEx(objTypes, colors, containers, inSub) - Find multiple types with multiple colors/containers
    • Automatically awaits promises in containers array (e.g., Backpack()) - no await needed
// Find full quantity of an item
const itemId = 12345;
const fullQuantity = await FindFullQuantity(itemId);
console.log(`Full quantity: ${fullQuantity}`);

// Find multiple types with multiple colors/containers
const items = await FindTypesArrayEx(
  [0x0191, 0x0190], // Human and creature types
  [0xFFFF, 0x0000], // Any color or black
  [Backpack(), Ground()], // âś… No await needed - promises auto-awaited!
  false // Don't search in subcontainers
);
console.log(`Found ${items.length} items matching criteria`);

Advanced Finding

  • Find(options) - Find items and get their properties in one call (custom convenience method)
    • Finds items matching criteria and automatically fetches properties
    • Returns objects with properties as keys (auto-derived from function names)
    • Automatically awaits promises in containers - no await needed for Backpack() etc.
// Basic usage - single type, auto-derived keys
const creatures = await Find({
  objTypes: [0xFFFF],
  containers: [Ground()],
  operations: [GetHP, GetX, GetY, GetName, GetDistance]
});
// Returns: [{ id: 123, hp: 25, x: 100, y: 200, name: 'a drake', distance: 10 }, ...]

// Multiple types, colors, containers - promises auto-awaited!
const items = await Find({
  objTypes: [0x0191, 0x0190],
  colors: [0xFFFF, 0x0000],
  containers: [Backpack(), Ground()], // âś… No await needed - works directly!
  operations: [GetName, GetQuantity]
});

// With filters
const alive = await Find({
  objTypes: [0xFFFF],
  operations: [GetHP, GetDistance],
  filters: [(item) => item.hp > 0 && item.distance < 100]
});

// Custom keys
const data = await Find({
  objTypes: [0x0EED],
  operations: [GetX, GetY, GetName],
  keys: ['posX', 'posY', 'itemName']
});
  • FindProps(items, operations, keys?) - Get properties for existing objects (custom convenience method)
    • Adds properties to existing objects or creates new ones from IDs
    • Automatically preserves existing properties when objects are passed
// Get properties for IDs
const objects = await FindProps([obj1, obj2], [GetX, GetY, GetName]);
// Returns: [{ id: obj1, x: 100, y: 200, name: 'item' }, ...]

// Get properties for existing objects (preserves existing properties)
const creatures = await Find({ objTypes: [0xFFFF], operations: [GetHP] });
const withNames = await FindProps(creatures, [GetName, GetDistance]);
// Returns: [{ id: 123, hp: 25, name: 'a drake', distance: 10 }, ...]
// Note: hp property is preserved from the original objects!

Ignore List

  • Ignore(objId) - Add object to ignore list
  • IgnoreOff(objId) - Remove object from ignore list
  • IgnoreReset() - Clear ignore list
  • GetIgnoreList() - Get ignore list
// Ignore self so we don't find ourselves
const selfId = await Self();
await Ignore(selfId);

// Ignore specific items/creatures
const itemId = 12345;
await Ignore(itemId);

// Check ignore list
const ignoreList = await GetIgnoreList();
console.log(`Ignoring ${ignoreList.length} objects`);

// Remove from ignore list
await IgnoreOff(itemId);

// Clear all ignores
await IgnoreReset();

Actions

Interaction

  • ClickOnObject(objId) - Click object
  • UseObject(objId) - Use object
  • UseType(objType, color) - Use object by type/color
  • UseFromGround(objType, color) - Use object from ground
  • Attack(objId) - Attack object
  • DragItem(objId, count) - Drag item
  • DropItem(objId, x, y, z) - Drop item at coordinates
  • OpenDoor(objId) - Open door
  • Bow() - Bow gesture
  • Salute() - Salute gesture
// Click on an object
const itemId = 12345;
await ClickOnObject(itemId);

// Use an object
await UseObject(itemId);

// Use object by type (tinker tools)
await UseType(0x1EB8);

// Use from ground
await UseFromGround(0x0EED, 0xFFFF); // Use gold from ground

// Attack target
const targetId = await WarTargetID();
if (targetId) {
  await Attack(targetId);
}

// Drag and drop item
const itemToMove = 12345;
const success = await DragItem(itemToMove, 10); // Drag 10 items
if (success) {
  const selfId = await Self();
  const x = await GetX(selfId);
  const y = await GetY(selfId);
  const z = await GetZ(selfId);
  await DropItem(itemToMove, x + 1, y + 1, z); // Drop 1 tile away
}

// Open a door
const doorId = 67890;
await OpenDoor(doorId);

// Gestures
await Bow();
await Salute();

Equipment

  • WearItem(layer, objId) - Equip item to layer
  • ObjAtLayerEx(layer, objId) - Get object at layer
// Equip a weapon
const weaponId = 12345;
await WearItem(LAYERS.Rhand, weaponId);

// Check what's equipped
const equippedWeapon = await ObjAtLayerEx(LAYERS.Rhand);
const equippedShield = await ObjAtLayerEx(LAYERS.Lhand);
const equippedArmor = await ObjAtLayerEx(LAYERS.Torso);

console.log(`Weapon: ${equippedWeapon}, Shield: ${equippedShield}, Armor: ${equippedArmor}`);

// Equip full set
const [weapon, shield, armor, legs] = await parallel([
  [FindType, 0x0F47, Backpack()], // Sword
  [FindType, 0x1BC4, Backpack()], // Shield
  [FindType, 0x144E, Backpack()], // Armor
  [FindType, 0x144E, Backpack()], // Legs
]);

if (weapon.length > 0) await WearItem(LAYERS.Rhand, weapon[0]);
if (shield.length > 0) await WearItem(LAYERS.Lhand, shield[0]);
if (armor.length > 0) await WearItem(LAYERS.Torso, armor[0]);
if (legs.length > 0) await WearItem(LAYERS.Legs, legs[0]);

Equipment Layers

Available layer constants (use with WearItem and ObjAtLayerEx):

// Core equipment
LAYERS.Rhand    // 0x01 - Right hand
LAYERS.Lhand    // 0x02 - Left hand
LAYERS.Shoes    // 0x03
LAYERS.Pants    // 0x04
LAYERS.Shirt    // 0x05
LAYERS.Hat      // 0x06
LAYERS.Gloves   // 0x07
LAYERS.Ring     // 0x08
LAYERS.Neck     // 0x0A
LAYERS.Hair     // 0x0B
LAYERS.Waist    // 0x0C
LAYERS.Torso    // 0x0D
LAYERS.Arms     // 0x13
LAYERS.Cloak    // 0x14
LAYERS.Bpack    // 0x15 - Backpack
LAYERS.Robe     // 0x16
LAYERS.Legs     // 0x18

// Special
LAYERS.Talisman // 0x09
LAYERS.Brace    // 0x0E
LAYERS.Beard    // 0x10
LAYERS.TorsoH   // 0x11
LAYERS.Ear      // 0x12
LAYERS.Eggs     // 0x17
LAYERS.Horse    // 0x19

// Usage
await WearItem(LAYERS.Rhand, weaponId);
const equippedWeapon = await ObjAtLayerEx(LAYERS.Rhand);

War Mode

  • SetWarMode(value) - Set war mode on/off
  • WarTargetID() - Get war target ID
  • Attack(objId) - Attack target
// Check war mode status
const inWarMode = await WarMode();

// Toggle war mode
if (!inWarMode) {
  await SetWarMode(true);
  console.log('War mode enabled');
}

// Get current war target
const targetId = await WarTargetID();
if (targetId) {
  console.log(`War target: ${targetId}`);
  await Attack(targetId);
} else {
  console.log('No war target');
}

// Disable war mode
await SetWarMode(false);

Targeting

Target Selection

  • TargetID() - Get current target ID
  • LastTarget() - Get last target ID
  • CancelTarget() - Cancel current target
  • TargetToObject(objId) - Target object
  • TargetToXYZ(x, y, z) - Target coordinates
  • TargetToTile(tileType, x, y) - Target tile
// Get current target
const currentTarget = await TargetID();
console.log(`Current target: ${currentTarget}`);

// Get last target
const lastTarget = await LastTarget();
console.log(`Last target: ${lastTarget}`);

// Target an object
const enemyId = 12345;
await TargetToObject(enemyId);

// Target coordinates
const selfId = await Self();
const x = await GetX(selfId);
const y = await GetY(selfId);
const z = await GetZ(selfId);
await TargetToXYZ(x + 5, y + 5, z);

// Target a tile (floor tile type)
await TargetToTile(0x400, x + 5, y + 5);

// Cancel target
await CancelTarget();

Wait Target

  • WaitTargetObject(objId) - Wait for target on object
  • WaitTargetSelf() - Wait for target on self
  • WaitTargetLast() - Wait for target on last target
  • WaitTargetType(objType, color) - Wait for target on type
  • WaitTargetGround() - Wait for target on ground
  • CancelWaitTarget() - Cancel wait target
// Cast heal and wait for target on self
await Cast('heal');
await WaitTargetSelf();
await Wait(500);

// Cast heal and wait for target on specific object
const friendId = 12345;
await Cast('greater heal');
await WaitTargetObject(friendId);
await Wait(500);

// Cast teleport and wait for target on last target
await Cast('teleport');
await WaitTargetLast();
await Wait(500);

// Cast dispel and wait for target on specific type
await Cast('dispel');
await WaitTargetType(0x0190, 0xFFFF); // Target humans
await Wait(500);

// Cast wall of stone and wait for target on ground
await Cast('wall of stone');
await WaitTargetGround();
await Wait(500);

// Cancel wait target
await CancelWaitTarget();

Movement

Basic Movement

  • Step(direction, run) - Step in direction (0-7, 0=North, 2=East, 4=South, 6=West)
  • StepQ(direction, run) - Quick step with queue
// Step north (not running)
await Step(DIRECTIONS.North, false);

// Step east (running)
await Step(DIRECTIONS.East, true);

// Step in all directions
await Step(DIRECTIONS.North, false);
await Wait(500);
await Step(DIRECTIONS.East, false);
await Wait(500);
await Step(DIRECTIONS.South, false);
await Wait(500);
await Step(DIRECTIONS.West, false);

// Quick step (queued)
await StepQ(DIRECTIONS.Northeast, true);

Direction Constants

Available direction constants:

DIRECTIONS.North      // 0
DIRECTIONS.Northeast  // 1
DIRECTIONS.East       // 2
DIRECTIONS.Southeast  // 3
DIRECTIONS.South      // 4
DIRECTIONS.Southwest  // 5
DIRECTIONS.West       // 6
DIRECTIONS.Northwest  // 7

// Usage
await Step(DIRECTIONS.North, false);
await Step(DIRECTIONS.East, true); // run

Advanced Movement

  • MoveXY(x, y, accuracyXY, running, exact) - Move to X, Y coordinates
  • MoveXYZ(x, y, z, accuracyXY, accuracyZ, running) - Move to X, Y, Z coordinates
  • newMoveXY(x, y, optimized, accuracy, running) - Wrapper for MoveXYZ with Z=0
  • newMoveXYZ(x, y, z, accuracyXY, accuracyZ, running, callback) - Pathfinding movement
// Simple movement
await MoveXY(2800, 480, 0, false, false);

// Pathfinding movement (may take longer)
await newMoveXYZ(2800, 480, 15, 0, 0, false);

Bad Locations

  • SetBadLocation(x, y) - Mark location as bad (avoid)
  • SetGoodLocation(x, y) - Mark location as good
  • ClearBadLocationList() - Clear bad location list
  • SetBadObject(objType, color, radius) - Mark object type as bad
  • ClearBadObjectList() - Clear bad object list
// Mark a location as bad (to avoid it)
await SetBadLocation(2800, 480);
await SetBadLocation(2801, 481);

// Mark a location as good
await SetGoodLocation(2900, 500);

// Clear all bad locations
await ClearBadLocationList();

// Mark object type as bad (avoid creatures)
await SetBadObject(0x0190, 0xFFFF, 5); // Avoid humans within 5 tiles

// Clear bad object list
await ClearBadObjectList();

Pathfinding

  • GetPathArray(x, y, running, accuracyXY) - Get 2D path array
  • GetPathArray3D(x1, y1, z1, x2, y2, z2, worldNum, accuracyXY, accuracyZ, running) - Get 3D path array
  • GetNextStepZ(x1, y1, z1, x2, y2, worldNum, stepZ) - Get next step Z coordinate
  • CheckLOS(x1, y1, z1, x2, y2, z2, worldNum, flags, objId) - Check line of sight
const selfId = await Self();
const x1 = await GetX(selfId);
const y1 = await GetY(selfId);
const z1 = await GetZ(selfId);
const worldNum = await WorldNum();

// Get 2D path to destination
const path = await GetPathArray(3000, 500, false, 0);
console.log(`Path has ${path.length} steps`);

// Get 3D path
const path3D = await GetPathArray3D(x1, y1, z1, 3000, 500, 0, worldNum, 0, 0, false);
console.log(`3D path:`, path3D);

// Get next step Z coordinate
const nextZ = await GetNextStepZ(x1, y1, z1, 3000, 500, worldNum, 1);
console.log(`Next step Z: ${nextZ}`);

// Check line of sight
const targetId = await WarTargetID();
if (targetId) {
  const [x2, y2, z2] = await parallel([
    [GetX, targetId],
    [GetY, targetId],
    [GetZ, targetId],
  ]);
  
  const hasLOS = await CheckLOS(x1, y1, z1, x2, y2, z2, worldNum, 0, 0);
  console.log(`Line of sight: ${hasLOS}`);
}

Skills

Skill Usage

  • UseSkill(skillNameOrId) - Use skill by name (recommended) or ID
  • UseSkillID(skillId) - Use skill by ID
  • GetSkillValue(skillNameOrId) - Get skill value by name or ID
  • GetSkillCap(skillNameOrId) - Get skill cap by name or ID
  • GetSkillID(skillName) - Get skill ID from name
  • GetSkillCurrentValue(skillName) - Get skill value by name
// Use skill by name (recommended)
await UseSkill('hiding');
await UseSkill('stealth');
await UseSkill('detect hidden');

// Get skill values by name
const hidingValue = await GetSkillValue('hiding');
const hidingCap = await GetSkillCap('hiding');

// Or use by ID
await UseSkillID(14); // hiding
await UseSkill(14); // also works with ID
const value = await GetSkillValue(14); // also works with ID

Available Skill Names

Combat: 'alchemy', 'anatomy', 'archery', 'arms lore', 'fencing', 'healing', 'mace fighting', 'parrying', 'swordsmanship', 'tactics', 'wrestling'

Magic: 'magery', 'necromancy', 'chivalry', 'bushido', 'ninjitsu', 'spellweaving', 'mysticism', 'evaluating intelligence', 'meditation', 'resisting spells', 'spirit speak'

Bard: 'discordance', 'musicianship', 'peacemaking', 'provocation'

Crafting: 'alchemy', 'blacksmithy', 'bowcraft', 'fletching', 'carpentry', 'cooking', 'inscription', 'tailoring', 'tinkering', 'imbuing', 'glassblowing', 'masonry'

Wilderness: 'animal lore', 'animal taming', 'camping', 'fishing', 'herding', 'lumberjacking', 'mining', 'tracking', 'veterinary'

Thief: 'detect hidden', 'hiding', 'lockpicking', 'poisoning', 'remove trap', 'snooping', 'stealing', 'stealth'

Miscellaneous: 'begging', 'cartography', 'focus', 'forensic evaluation', 'item identification', 'taste identification', 'throwing'

Note: Skill names are case-insensitive and should match UO skill names exactly. Use GetSkillID('skill name') to verify a skill name is valid.

Spells

Casting

  • Cast(spellName) - Cast spell by name
  • CastToObj(spellName, objId) - Cast spell and target object
  • CastToObject(spellName, objId) - Alias for CastToObj
  • CastToSelf(spellName) - Cast spell and target self
  • CastSelf(spellName) - Alias for CastToSelf
  • CastSpell(spellId) - Cast spell by ID
  • IsActiveSpellAbility(spellNameOrId) - Check if spell ability is active
// Cast without target (waits for manual targeting)
await Cast('heal');
await WaitForTarget(3000);
if (await TargetPresent()) {
  await TargetToSelf();
}

// Cast with target object (waits for target cursor, then casts)
const targetId = await WarTargetID();
if (targetId) {
  await CastToObj('heal', targetId);
  await CastToObject('greater heal', targetId); // Alias
}

// Cast to self
await CastToSelf('heal');
await CastSelf('greater heal'); // Alias

// Or cast by ID
await CastSpell(4); // heal

// Check if spell ability is active (see list below)
const enemyOfOneActive = await IsActiveSpellAbility('enemy of one');
const confidenceActive = await IsActiveSpellAbility('confidence');

if (!enemyOfOneActive) {
  await Cast('enemy of one');
  await WaitForTarget(3000);
  if (await TargetPresent()) {
    const enemyId = await WarTargetID();
    if (enemyId) {
      await TargetToObject(enemyId);
    }
  }
}

// Also works with spell IDs (using getSpellId helper)
const enemyOfOneId = getSpellId('enemy of one');
const isActive = await IsActiveSpellAbility(enemyOfOneId);

Active Spell Abilities

These are spells/abilities that have an active state and can be checked with IsActiveSpellAbility:

Chivalry (Paladin):

  • 'divine fury' - Increases damage and attack speed
  • 'enemy of one' - Massive damage bonus against one target

Bushido:

  • 'confidence' - Increases damage and reduces damage taken
  • 'counter attack' - Automatic counter-attack
  • 'evasion' - Dodges next attack

Ninjitsu:

  • 'animal form' - Transforms into an animal
  • 'mirror image' - Creates a decoy

Necromancy:

  • 'vampiric embrace' - Vampire form (life leech)
  • 'lich form' - Lich form (mana leech)
  • 'wraith form' - Wraith form (physical damage immunity)

Other:

  • Some transformation spells may also have active states
// Example: Check and activate Chivalry abilities
const divineFuryActive = await IsActiveSpellAbility('divine fury');
const enemyOfOneActive = await IsActiveSpellAbility('enemy of one');

if (!divineFuryActive) {
  await Cast('divine fury');
  await Wait(500);
}

// Example: Check Bushido abilities before combat
const confidenceActive = await IsActiveSpellAbility('confidence');
if (!confidenceActive) {
  await Cast('confidence');
  await Wait(500);
}

// Example: Check Necromancy forms
const vampiricEmbraceActive = await IsActiveSpellAbility('vampiric embrace');
const lichFormActive = await IsActiveSpellAbility('lich form');
const wraithFormActive = await IsActiveSpellAbility('wraith form');

// Only one form can be active at a time
if (!vampiricEmbraceActive && !lichFormActive && !wraithFormActive) {
  await Cast('vampiric embrace');
  await Wait(500);
}

Available Spell Names

1st Circle: 'clumsy', 'create food', 'feeblemind', 'heal', 'magic arrow', 'night sight', 'reactive armor', 'weaken'

2nd Circle: 'agility', 'cunning', 'cure', 'harm', 'magic trap', 'magic untrap', 'protection', 'strength'

3rd Circle: 'bless', 'fireball', 'magic lock', 'poison', 'telekinesis', 'teleport', 'unlock', 'wall of stone'

4th Circle: 'arch cure', 'arch protection', 'curse', 'fire field', 'greater heal', 'lightning', 'mana drain', 'recall'

5th Circle: 'blade spirit', 'dispel field', 'incognito', 'magic reflection', 'spell reflection', 'mind blast', 'paralyze', 'poison field', 'summon creature'

6th Circle: 'dispel', 'energy bolt', 'explosion', 'invisibility', 'mark', 'mass curse', 'paralyze field', 'reveal'

7th Circle: 'chain lightning', 'energy field', 'flame strike', 'gate travel', 'mana vampire', 'mass dispel', 'meteor swarm', 'polymorph'

8th Circle: 'earthquake', 'energy vortex', 'resurrection', 'summon air elemental', 'summon daemon', 'summon earth elemental', 'summon fire elemental', 'summon water elemental'

Necromancy: 'animate dead', 'blood oath', 'corpse skin', 'curse weapon', 'evil omen', 'horrific beast', 'lich form', 'mind rot', 'pain spike', 'poison strike', 'strangle', 'summon familiar', 'vampiric embrace', 'vengeful spirit', 'wither', 'wraith form', 'exorcism'

Paladin: 'cleanse by fire', 'close wounds', 'consecrate weapon', 'dispel evil', 'divine fury', 'enemy of one', 'holy light', 'noble sacrifice', 'remove curse', 'sacred journey'

Bushido: 'confidence', 'counter attack', 'evasion', 'honorable execution', 'lightning strike', 'momentum strike'

Ninjitsu: 'animal form', 'backstab', 'death strike', 'focus attack', 'ki attack', 'mirror image', 'shadow jump', 'surprise attack'

Spellweaving: 'arcane circle', 'arcane empowerment', 'attune weapon', 'dryad allure', 'essence of wind', 'ethereal voyage', 'gift of life', 'gift of renewal', 'immolating weapon', 'mana phantasm', 'nature's fury', 'reaper form', 'rising colossus', 'soul seeker', 'summon fey', 'summon fiend', 'thunderstorm', 'wildfire', 'word of death'

Mysticism: 'animated weapon', 'bombard', 'cleansing winds', 'eagle strike', 'enchant', 'healing stone', 'hail storm', 'mass sleep', 'nether bolt', 'purge magic', 'rising colossus', 'sleep', 'spell plague', 'stone form', 'spell trigger', 'spellweaving'

Journal

Journal Access

  • InJournal(text) - Search for text in journal
  • LastJournalMessage() - Get last journal message
  • Journal(index) - Get journal entry by index
  • LowJournal() - Get lowest journal index
  • HighJournal() - Get highest journal index
  • ClearJournal() - Clear journal
  • AddToSystemJournal(text) - Add message to system journal
// Get journal range
const low = await LowJournal();
const high = await HighJournal();
console.log(`Journal entries: ${low} to ${high}`);

// Get last journal message
const lastMessage = await LastJournalMessage();
console.log(`Last message: ${lastMessage}`);

// Search for text in journal
const hasText = await InJournal('You see');
if (hasText > 0) {
  console.log(`Found 'You see' at index ${hasText}`);
}

// Read all journal entries
for (let i = low; i <= high; i++) {
  const message = await Journal(i);
  if (message) {
    console.log(`[${i}] ${message}`);
  }
}

// Add to system journal
await AddToSystemJournal('Script started');

// Clear journal
await ClearJournal();

Last Actions

  • LastTarget() - Get last target ID
  • LastAttack() - Get last attack ID
  • LastContainer() - Get last container ID
  • LastObject() - Get last object ID
// Get last actions
const lastTarget = await LastTarget();
const lastAttack = await LastAttack();
const lastContainer = await LastContainer();
const lastObject = await LastObject();

console.log(`Last target: ${lastTarget}`);
console.log(`Last attack: ${lastAttack}`);
console.log(`Last container: ${lastContainer}`);
console.log(`Last object: ${lastObject}`);

// Use last target for spell casting
await Cast('heal');
await WaitTargetLast();

Utilities

Timing

  • Wait(ms) - Wait specified milliseconds
await Wait(1000); // Wait 1 second

Event Handling

  • on(event, callback) - Subscribe to events
on('evspeech', (data) => {
  console.log('Speech event:', data);
});

Constants

All constants are exported and available globally after importing js_stealth:

Available Constants

  • EVENTS - Array of all available event names for on() function
  • LAYERS - Equipment layer constants (e.g., LAYERS.Rhand, LAYERS.Cloak)
  • DIRECTIONS - Movement direction constants (e.g., DIRECTIONS.North, DIRECTIONS.East)
  • NOTORIETY - Notoriety constants (e.g., NOTORIETY.Innocent, NOTORIETY.Enemy)
  • SPELLS - Spell name to ID mapping (used internally, but available for reference)
  • SKILL_NAMES - Array of all valid skill names
  • BUFFS - Buff/debuff icon ID mapping (e.g., BUFFS.curse, BUFFS.bless)
  • getBuffName(iconId) - Helper function to get buff name from icon ID (for evbuffdebuffsystem events)
import './js_stealth';

// Use constants directly
await WearItem(LAYERS.Rhand, weaponId);
await Step(DIRECTIONS.North, false);
const innocents = await FindNotoriety(0x0190, NOTORIETY.Innocent);
on("evspeech", (data) => { // 'evspeech'
  console.log('Speech event:', data);
});

// Buff/debuff events - translate icon IDs to names
await on('evbuffdebuffsystem', (characterId, iconId, isAdded) => {
  const buffName = getBuffName(iconId) || `unknown (${iconId})`;
  console.log(`${buffName} ${isAdded ? 'added to' : 'removed from'} character ${characterId}`);
  // Or use BUFFS directly:
  if (iconId === BUFFS.curse) {
    console.log('Curse detected!');
  }
});

Configuration

config.HOST = '192.168.88.13';
config.PORT = 50026; // Optional, auto-discovered if not set

Examples

Find and Use Items

const apples = await FindType(0x09D0, Ground());
for (const appleId of apples) {
  await UseObject(appleId);
  await Wait(1000);
}

Combat Loop

const target = await WarTargetID();
if (target) {
  await Attack(target);
  const hp = await GetHP(target);
  if (hp < 50) {
    await Cast('heal'); // Heal
  }
}

Movement Pattern

const selfId = await Self();
const [x, y] = await parallel([
  [GetX, selfId],
  [GetY, selfId],
]);

// Move to coordinates
await MoveXY(x + 4, y + 4, 0, false, false);

Parallel Data Gathering

// Using Find() - recommended for finding and getting properties in one call
const creatures = await Find({
  objTypes: [0xFFFF],
  containers: [Ground()],
  operations: [GetX, GetY, GetZ, GetType, GetName, GetHP],
  filters: [(item) => item.hp > 0 && item.distance < 100]
});
// Returns: [{ id: 123, x: 100, y: 200, z: 0, type: 60, name: 'a drake', hp: 25 }, ...]

// Using parallel_items (lower-level API)
const items = await FindType(0xFFFF, Ground());
const data = await parallel_items(items, [
  GetX,
  GetY,
  GetZ,
  GetType,
  GetName,
  GetHP,
]);
// Returns: [{id: 123, data: [x, y, z, type, name, hp]}, ...]

// Using FindProps to add properties to existing objects
const filtered = creatures.filter(c => c.hp > 0);
const withDetails = await FindProps(filtered, [GetDistance, GetColor]);
// Returns: [{ id: 123, hp: 25, x: 100, y: 200, ..., distance: 10, color: 0 }, ...]
// Note: All properties from 'filtered' are preserved!

Notes

  • All methods are async and must be awaited
  • Use parallel() for multiple operations to maximize performance
  • FindType and FindTypeEx automatically return the found list
  • Use Find() for finding items and getting their properties in one call (recommended)
  • Use FindProps() to add properties to existing objects or convert IDs to objects
  • parallel_items() is a lower-level API - prefer Find() or FindProps() for convenience
  • Method signatures match py_stealth for easy porting
  • Functions are available globally after import './js_stealth' - no namespace needed

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • TypeScript 100.0%