Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions scripts/magic/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,7 @@ import { runScript, type ScriptContext } from '../../sdk/runner';
import { generateSave, TestPresets } from '../../sdk/test/utils/save-generator';
import { launchBotWithSDK } from '../../sdk/test/utils/browser';
import type { NearbyNpc } from '../../sdk/types';

// Spell component IDs
const Spells = {
WIND_STRIKE: 1152,
WATER_STRIKE: 1154,
EARTH_STRIKE: 1156,
};
import { Spells } from '../../sdk/spells';

// Locations
const CHICKEN_COOP = { x: 3235, z: 3295 }; // Near Lumbridge
Expand Down
59 changes: 56 additions & 3 deletions sdk/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ These methods wait for the **effect to complete**, not just server acknowledgmen
| `findEquippedItem(pattern)` | Find an equipped item by name pattern. |
| `eatFood(target)` | Eat food to restore hitpoints. |
| `attackNpc(target, timeout)` | Attack an NPC, walking to it if needed. |
| `castSpellOnNpc(target, spellComponent, timeout)` | Cast a combat spell on an NPC. |
| `castSpellOnNpc(target, spell, timeout)` | Cast a combat spell on an NPC. `spell` accepts a `SpellName` (e.g. `'WIND_STRIKE'`) or numeric component ID. |
| `craftLeather(product?)` | Craft leather into armour using needle and thread. |

### Woodcutting & Firemaking
Expand Down Expand Up @@ -158,8 +158,8 @@ These methods resolve when server **acknowledges** them (not when effects comple
| `sendShopSell(slot, amount)` | Sell to shop by slot and amount. |
| `sendSetCombatStyle(style)` | Set combat style (0-3). |
| `sendTogglePrayer(prayer)` | Toggle a prayer on or off by name or index (0-14). |
| `sendSpellOnNpc(npcIndex, spellComponent)` | Cast spell on NPC using spell component ID. |
| `sendSpellOnItem(slot, spellComponent)` | Cast spell on inventory item. |
| `sendSpellOnNpc(npcIndex, spell)` | Cast spell on NPC. Accepts `SpellName` or numeric component ID. |
| `sendSpellOnItem(slot, spell)` | Cast spell on inventory item. Accepts `SpellName` or numeric component ID. |
| `sendSetTab(tabIndex)` | Switch to a UI tab by index. |
| `sendSay(message)` | Send a chat message. |
| `sendWait(ticks)` | Wait for specified number of game ticks. |
Expand Down Expand Up @@ -192,6 +192,59 @@ These methods resolve when server **acknowledges** them (not when effects comple

---

## Constants

### Spells

Spell constants for use with `castSpellOnNpc`, `sendSpellOnNpc`, and `sendSpellOnItem`.

```typescript
import { Spells } from './actions';

// Use by name
await bot.castSpellOnNpc(target, 'WIND_STRIKE');

// Or use the Spells constant
await bot.castSpellOnNpc(target, Spells.WIND_STRIKE);
```

| Name | ID | Notes |
|------|----|-------|
| `WIND_STRIKE` | 1152 | Level 1 combat spell |
| `CONFUSE` | 1153 | |
| `WATER_STRIKE` | 1154 | Level 5 combat spell |
| `ENCHANT_LVL1` | 1155 | Sapphire |
| `EARTH_STRIKE` | 1156 | Level 9 combat spell |
| `WEAKEN` | 1157 | |
| `FIRE_STRIKE` | 1158 | Level 13 combat spell |
| `WIND_BOLT` | 1160 | |
| `CURSE` | 1161 | |
| `LOW_ALCHEMY` | 1162 | |
| `WATER_BOLT` | 1163 | |
| `VARROCK_TELEPORT` | 1164 | |
| `ENCHANT_LVL2` | 1165 | Emerald |
| `EARTH_BOLT` | 1166 | |
| `LUMBRIDGE_TELEPORT` | 1167 | |
| `FIRE_BOLT` | 1169 | |
| `FALADOR_TELEPORT` | 1170 | |
| `WIND_BLAST` | 1172 | |
| `SUPERHEAT` | 1173 | |
| `CAMELOT_TELEPORT` | 1174 | |
| `WATER_BLAST` | 1175 | |
| `ENCHANT_LVL3` | 1176 | Ruby |
| `EARTH_BLAST` | 1177 | |
| `HIGH_ALCHEMY` | 1178 | |
| `ENCHANT_LVL4` | 1180 | Diamond |
| `FIRE_BLAST` | 1181 | |
| `WIND_WAVE` | 1183 | |
| `WATER_WAVE` | 1185 | |
| `ENCHANT_LVL5` | 1187 | Dragonstone |
| `EARTH_WAVE` | 1188 | |
| `FIRE_WAVE` | 1189 | |
| `BIND` | 1572 | |

---

## Result Types

### PlayerCombatState
Expand Down
11 changes: 9 additions & 2 deletions sdk/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import type {
PrayerName
} from './types';
import { PRAYER_INDICES, PRAYER_NAMES, PRAYER_LEVELS } from './types';
import { resolveSpell, type SpellName } from './spells';

export class BotActions {
private helpers: ActionHelpers;
Expand Down Expand Up @@ -1477,8 +1478,13 @@ export class BotActions {
}
}

/** Cast a combat spell on an NPC. */
async castSpellOnNpc(target: NearbyNpc | string | RegExp, spellComponent: number, timeout: number = 3000): Promise<CastSpellResult> {
/** Cast a combat spell on an NPC. Accepts a spell name (e.g. 'WIND_STRIKE') or numeric component ID. */
async castSpellOnNpc(target: NearbyNpc | string | RegExp, spell: SpellName | number, timeout: number = 3000): Promise<CastSpellResult> {
const spellComponent = resolveSpell(spell);
if (spellComponent === undefined) {
return { success: false, message: `Unknown spell: ${spell}` };
}

const npc = this.helpers.resolveNpc(target);
if (!npc) {
return { success: false, message: `NPC not found: ${target}`, reason: 'npc_not_found' };
Expand Down Expand Up @@ -2600,3 +2606,4 @@ export class BotActions {
// Re-export for convenience
export { BotSDK } from './index';
export * from './types';
export { Spells, type SpellName } from './spells';
17 changes: 13 additions & 4 deletions sdk/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type {
PrayerName
} from './types';
import { PRAYER_INDICES, PRAYER_NAMES } from './types';
import { resolveSpell, type SpellName } from './spells';
import * as pathfinding from './pathfinding';

interface SyncToSDKMessage {
Expand Down Expand Up @@ -913,13 +914,21 @@ export class BotSDK {
.filter((name): name is PrayerName => name !== null);
}

/** Cast spell on NPC using spell component ID. */
async sendSpellOnNpc(npcIndex: number, spellComponent: number): Promise<ActionResult> {
/** Cast spell on NPC. Accepts a spell name (e.g. 'WIND_STRIKE') or numeric component ID. */
async sendSpellOnNpc(npcIndex: number, spell: SpellName | number): Promise<ActionResult> {
const spellComponent = resolveSpell(spell);
if (spellComponent === undefined) {
return { success: false, message: `Unknown spell: ${spell}` };
}
return this.sendAction({ type: 'spellOnNpc', npcIndex, spellComponent, reason: 'SDK' });
}

/** Cast spell on inventory item. */
async sendSpellOnItem(slot: number, spellComponent: number): Promise<ActionResult> {
/** Cast spell on inventory item. Accepts a spell name (e.g. 'LOW_ALCHEMY') or numeric component ID. */
async sendSpellOnItem(slot: number, spell: SpellName | number): Promise<ActionResult> {
const spellComponent = resolveSpell(spell);
if (spellComponent === undefined) {
return { success: false, message: `Unknown spell: ${spell}` };
}
return this.sendAction({ type: 'spellOnItem', slot, spellComponent, reason: 'SDK' });
}

Expand Down
71 changes: 71 additions & 0 deletions sdk/spells.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Spell component IDs (from content/pack/interface.pack)
// These map to the interface component ID used by the game protocol.

export const Spells = {
// Strike spells (lowest tier combat)
WIND_STRIKE: 1152,
CONFUSE: 1153,
WATER_STRIKE: 1154,
ENCHANT_LVL1: 1155, // Sapphire
EARTH_STRIKE: 1156,
WEAKEN: 1157,
FIRE_STRIKE: 1158,

// Bolt spells
WIND_BOLT: 1160,
CURSE: 1161,
LOW_ALCHEMY: 1162,
WATER_BOLT: 1163,
VARROCK_TELEPORT: 1164,
ENCHANT_LVL2: 1165, // Emerald
EARTH_BOLT: 1166,
LUMBRIDGE_TELEPORT: 1167,
FIRE_BOLT: 1169,
FALADOR_TELEPORT: 1170,

// Blast spells
WIND_BLAST: 1172,
SUPERHEAT: 1173,
CAMELOT_TELEPORT: 1174,
WATER_BLAST: 1175,
ENCHANT_LVL3: 1176, // Ruby
EARTH_BLAST: 1177,
HIGH_ALCHEMY: 1178,
ENCHANT_LVL4: 1180, // Diamond
FIRE_BLAST: 1181,

// Wave spells (highest tier)
WIND_WAVE: 1183,
WATER_WAVE: 1185,
ENCHANT_LVL5: 1187, // Dragonstone
EARTH_WAVE: 1188,
FIRE_WAVE: 1189,

// Other
BIND: 1572,
} as const;

/** The name of a spell (e.g. 'WIND_STRIKE', 'FIRE_BOLT'). */
export type SpellName = keyof typeof Spells;

// Reverse lookup: component ID → spell name (for validation/error messages)
const spellById = new Map<number, SpellName>(
(Object.entries(Spells) as [SpellName, number][]).map(([name, id]) => [id, name])
);

/**
* Resolve a spell name or component ID to a numeric component ID.
* Returns undefined if the spell name is not recognized.
*/
export function resolveSpell(spell: SpellName | number): number | undefined {
if (typeof spell === 'number') {
return spell;
}
const id = Spells[spell];
return id;
}

/** Get the spell name for a component ID, if known. */
export function getSpellName(componentId: number): SpellName | undefined {
return spellById.get(componentId);
}
38 changes: 2 additions & 36 deletions sdk/test/utils/save-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,42 +108,8 @@ export const Items = {
BIG_BONES: 532,
};

// Spell component IDs (from content/pack/interface.pack)
export const Spells = {
// Combat spells
WIND_STRIKE: 1152,
CONFUSE: 1153,
WATER_STRIKE: 1154,
ENCHANT_LVL1: 1155, // Sapphire
EARTH_STRIKE: 1156,
WEAKEN: 1157,
FIRE_STRIKE: 1158,
WIND_BOLT: 1160,
CURSE: 1161,
LOW_ALCHEMY: 1162,
WATER_BOLT: 1163,
VARROCK_TELEPORT: 1164,
ENCHANT_LVL2: 1165, // Emerald
EARTH_BOLT: 1166,
LUMBRIDGE_TELEPORT: 1167,
FIRE_BOLT: 1169,
FALADOR_TELEPORT: 1170,
WIND_BLAST: 1172,
SUPERHEAT: 1173,
CAMELOT_TELEPORT: 1174,
WATER_BLAST: 1175,
ENCHANT_LVL3: 1176, // Ruby
EARTH_BLAST: 1177,
HIGH_ALCHEMY: 1178,
ENCHANT_LVL4: 1180, // Diamond
FIRE_BLAST: 1181,
WIND_WAVE: 1183,
WATER_WAVE: 1185,
ENCHANT_LVL5: 1187, // Dragonstone
EARTH_WAVE: 1188,
FIRE_WAVE: 1189,
BIND: 1572,
};
// Re-export spell constants from the canonical source
export { Spells } from '../../spells';

// Skill indices
export const Skills = {
Expand Down