This commit is contained in:
max bittker committed 2026-03-06 19:17:46 -05:00
1 parent 98b3b16d58
commit 41414d8b20
2144 files changed
+53035 -66

No files matched your search

+160 -58
View File
@@ -2,72 +2,96 @@
You're here to play the mmo game through the progressive development of botting scripts, starting small then adapting to your desires and ideas.
## CRITICAL FIRST STEP: Start Services
## First Time Setup
Before doing anything else, run this command to start the game services:
**Create a new bot using the setup script:**
Ask the user for a bot name (max 12 chars, alphanumeric). If they skip, use the command without a username to auto-generate a random 9-character name.
```bash
/ensure-services.sh
# With custom username
bun bots/create-bot.ts {username}
# Auto-generate random username
bun bots/create-bot.ts
# Use local server (sets SERVER=localhost in bot.env)
bun bots/create-bot.ts {username} --local
# Use a custom server
bun bots/create-bot.ts {username} --server=myserver.example.com
```
This starts the game engine, gateway, and bot client. Wait for it to complete before running any scripts.
After this, the environment will be:
- Game engine: running on localhost:8888
- Gateway: running on localhost:7780
- Bot client: connected and in-game
This automatically creates:
- `bots/{username}/bot.env` - Credentials with auto-generated password
- `bots/{username}/lab_log.md` - Session notes template
- `bots/{username}/script.ts` - Ready-to-run starter script
Do NOT try to start these services manually or write your own startup scripts.
## CLI Daemon (Interactive Mode)
The bot account "agent" already exists with credentials in `bots/agent/bot.env`.
The CLI supports a daemon mode for persistent bot connections and inline code execution.
## How to Control the Bot
Write TypeScript scripts and run them with `bun`. Use the `runScript` helper for automatic connection management.
### Check Bot State First
### Quick Start
```bash
bun sdk/cli.ts agent
# Execute code on a bot (auto-starts daemon if needed)
bun sdk/cli.ts mybot exec "return sdk.getState()?.player"
# Execute via heredoc (avoids shell quoting)
bun sdk/cli.ts mybot exec <<'EOF'
await bot.chopTree()
return sdk.getInventory()
EOF
# Check state (uses daemon if running, otherwise one-shot)
bun sdk/cli.ts mybot state
bun sdk/cli.ts mybot state --json
# Stop the daemon
bun sdk/cli.ts mybot stop
```
The daemon holds the bot connection alive between commands. It auto-starts on first `exec` and shuts down after 5 minutes of idle time.
Code runs in an async context with `bot` (BotActions) and `sdk` (BotSDK) available as globals.
## Session Workflow
This is a **persistent character** - you don't restart fresh each time. The workflow is:
### 1. Check World State First
Before writing any script, check where the bot is and what it has:
```bash
bun sdk/cli.ts {username}
```
This shows: position, inventory, skills, nearby NPCs/objects, and more.
### Script Template
**Exception**: Skip this if you just created the character and know it's at spawn.
Create scripts in `bots/agent/` and run them:
**Tutorial Check**: If the character is in the tutorial area, call `await bot.skipTutorial()` before running any other scripts. The tutorial blocks normal gameplay.
```typescript
// bots/agent/woodcutter.ts
import { runScript } from '../../sdk/runner';
### 2. Write Your Script
const result = await runScript(async (ctx) => {
const { bot, sdk, log } = ctx;
Edit `bots/{username}/script_name.ts` with your goal. Keep scripts focused on one task. you may write multiple scripts for different tasks and switch between them.
let treesChopped = 0;
### 3. Run the Script
while (treesChopped < 50) {
await bot.dismissBlockingUI();
const tree = sdk.findNearbyLoc(/^tree$/i);
if (tree) {
const r = await bot.chopTree(tree);
if (r.success) count++;
}
}
log(`Chopped ${count} trees`);
return { count };
}, { timeout: 90_000 });
console.log(result);
```
Run with:
```bash
bun bots/agent/woodcutter.ts
bun bots/{username}/script_name.ts
```
The runner automatically finds `bot.env` in the same directory as the script.
### 4. Observe and Iterate
Watch the output. After the script finishes (or fails), check state again:
```bash
bun sdk/cli.ts {username}
```
Record observations in `lab_log.md`, then improve the script.
## Script Duration Guidelines
@@ -77,39 +101,57 @@ The runner automatically finds `bot.env` in the same directory as the script.
|----------|----------|
| **10-30s** | New script, single actions, untested logic, debugging |
| **2-5 min** | Validated approach, building confidence |
| **10+ min** | Proven strategy, grinding runs, AVOID UNLESS CONFIDENT |
| **10+ min** | Proven strategy, grinding runs. USE SPARINGLY |
A failed 5-minute run wastes more time than five 30 second diagnostic runs. **Fail fast and start simple.**
Be extremely cognizant of pathing issues. It's very common to have issues because of closed doors and gates.
Look out for "I can't reach" messages - the solution is often to open closed gates.
Read and grep in the learnings folder for tips, and the wiki folder for skill guides, item and npc locations, and shop information.
## SDK API Reference
See **sdk/API.md** for the complete method reference.
For the complete method reference, see **[sdk/API.md](sdk/API.md)** (auto-generated from source).
### bot.* (High-level actions that wait for completion)
**Quick overview:**
- `bot.*` - High-level actions that wait for effects to complete (chopTree, walkTo, attackNpc, etc.)
- `sdk.*` - Low-level methods that resolve on server acknowledgment (sendWalk, getState, findNearbyNpc, etc.)
### bot.* Quick Reference
| Method | What it does |
|--------|-------------|
| `walkTo(x, z, tolerance?)` | Pathfind to coords, opens doors along the way |
| `talkTo(target)` | Walk to NPC, start dialog |
| `interactNpc(target, option?)` | Walk to NPC, interact (e.g. `'trade'`, `'fish'`) |
| `interactLoc(target, option?)` | Walk to loc, interact (e.g. `'mine'`, `'smelt'`) |
| `interactNpc(target, option?)` | Walk to NPC, interact with any option (e.g. `'trade'`, `'fish'`) |
| `interactLoc(target, option?)` | Walk to loc, interact with any option (e.g. `'mine'`, `'smelt'`) |
| `attackNpc(target)` | Walk to NPC, start combat |
| `pickpocketNpc(target)` | Pickpocket NPC, detects XP gain vs stun |
| `castSpellOnNpc(target, spell)` | Cast combat spell on NPC |
| `chopTree(target?)` | Chop tree, wait for logs |
| `pickupItem(target)` | Pick up ground item |
| `openDoor(target?)` | Open a door or gate |
| `openBank()` | Open nearest bank |
| `depositItem(target, amount?)` | Deposit item to bank |
| `withdrawItem(slot, amount?)` | Withdraw item from bank |
| `openShop(target?)` | Open shop via shopkeeper NPC |
| `buyFromShop(target, amount?)` | Buy item from open shop |
| `sellToShop(target, amount?)` | Sell item to open shop |
| `equipItem(target)` | Equip from inventory |
| `unequipItem(target)` | Unequip to inventory |
| `eatFood(target)` | Eat food, returns HP gained |
| `useItemOnLoc(item, loc)` | Use inventory item on loc (e.g. fish on range) |
| `useItemOnNpc(item, npc)` | Use inventory item on NPC |
| `burnLogs(target?)` | Light logs with tinderbox |
| `fletchLogs(product?)` | Fletch logs with knife |
| `craftLeather(product?)` | Craft leather with needle |
| `smithAtAnvil(product)` | Smith bars at anvil |
| `dismissBlockingUI()` | Dismiss level-up dialogs (**call in every loop**) |
| `dismissBlockingUI()` | Dismiss level-up dialogs (called automatically by all actions) |
| `navigateDialog(choices)` | Auto-click through dialog options |
| `skipTutorial()` | Skip the tutorial island |
### sdk.* (Queries and low-level commands)
### sdk.* Commonly Used Directly
| Method | What it does |
|--------|-------------|
@@ -118,17 +160,77 @@ See **sdk/API.md** for the complete method reference.
| `getInventory()` / `findInventoryItem(pattern)` | Inventory queries |
| `findNearbyNpc(pattern)` / `findNearbyLoc(pattern)` | Find nearby entities |
| `findGroundItem(pattern)` | Find ground items |
| `getDialog()` | Current dialog state |
| `sendClickDialog(option)` | Click dialog option |
| `sendClickComponent(id)` | Click interface button |
| `sendDropItem(slot)` | Drop inventory item |
| `sendUseItem(slot)` | Use inventory item (bury, etc.) |
| `sendUseItemOnItem(src, dst)` | Combine two items |
| `sendSay(message)` | Send chat message |
| `waitForCondition(pred)` | Wait for state predicate |
| `waitForTicks(n)` | Wait n game ticks |
| `scanNearbyLocs(radius?)` | Extended-range loc scan |
## Key Tips
---
- **ALWAYS** call `bot.dismissBlockingUI()` in every loop iteration — level-ups block everything
- Use specific regex patterns: `/^tree$/i` not `/tree/i` (which matches "tree stump")
- Look out for "I can't reach" messages — the solution is often to open closed gates with `bot.openDoor()`
- Read files in `learnings/` for game-specific tips on banking, combat, shops, etc.
- Run scripts in the **foreground** so you can see their output
- The game runs at 8x speed — actions complete much faster than normal
### Dismiss Level-Up Dialogs
All BotActions methods automatically dismiss blocking UI (level-up dialogs, etc.) before executing. You don't need to call this manually in loops.
```typescript
// Only needed if using low-level sdk methods directly:
await bot.dismissBlockingUI();
// Or manually check
if (sdk.getState()?.dialog.isOpen) {
await sdk.sendClickDialog(0);
}
```
### Error Handling
```typescript
const result = await bot.chopTree();
if (!result.success) {
console.log(`Failed: ${result.message}`);
// Handle failure - maybe walk somewhere else
}
```
## Project Structure
```
bots/
└── {username}/
├── bot.env # Credentials (BOT_USERNAME, PASSWORD, SERVER)
├── lab_log.md # Session notes and observations
└── script.ts # Current script
sdk/
├── index.ts # BotSDK (low-level)
├── actions.ts # BotActions (high-level)
├── cli.ts # CLI for checking state
└── types.ts # Type definitions
learnings/
├── banking.md
└── ...etc
wiki/
├── npcs/
├── items/
├── skills/
└── shops/
```
## Troubleshooting
**"No state received"** - Bot isn't connected to game. Open browser first or use `autoLaunchBrowser: true`.
**Script stalls** - Check for open dialogs (`state.dialog.isOpen`). Level-ups block everything.
**"Can't reach"** - Path is blocked. Try walking closer first, or find a different target.
**Wrong target** - Use more specific regex patterns: `/^tree$/i` not `/tree/i` (which matches "tree stump").
+7 -4
View File
@@ -108,7 +108,7 @@ A failed 5-minute run wastes more time than five 30 second diagnostic runs. **Fa
Be extremely cognizant of pathing issues. It's very common to have issues because of closed doors and gates.
Look out for "I can't reach" messages - the solution is often to open closed gates.
Read and grep in the learnings folder for tips.
Read and grep in the learnings folder for tips, and the wiki folder for skill guides, item and npc locations, and shop information.
## SDK API Reference
@@ -215,11 +215,14 @@ sdk/
learnings/
├── banking.md
├── combat.md
├── shops.md
├── fletching.md
└── ...etc
wiki/
├── npcs/
├── items/
├── skills/
└── shops/
```
## Troubleshooting
+7 -4
View File
@@ -108,7 +108,7 @@ A failed 5-minute run wastes more time than five 30 second diagnostic runs. **Fa
Be extremely cognizant of pathing issues. It's very common to have issues because of closed doors and gates.
Look out for "I can't reach" messages - the solution is often to open closed gates.
Read and grep in the learnings folder for tips.
Read and grep in the learnings folder for tips, and the wiki folder for skill guides, item and npc locations, and shop information.
## SDK API Reference
@@ -215,11 +215,14 @@ sdk/
learnings/
├── banking.md
├── combat.md
├── shops.md
├── fletching.md
└── ...etc
wiki/
├── npcs/
├── items/
├── skills/
└── shops/
```
## Troubleshooting
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+522
View File
@@ -0,0 +1,522 @@
#!/usr/bin/env bun
/**
* Shop Wiki Generator
* Parses server content files and generates markdown wiki pages for all shops.
* Run: bun wiki/generate-shops.ts
*/
import { readFileSync, readdirSync, writeFileSync, mkdirSync, existsSync } from 'fs';
import { join, relative } from 'path';
const CONTENT_DIR = join(import.meta.dir, '..', 'server', 'content');
const WIKI_DIR = join(import.meta.dir);
const SHOP_DIR = join(WIKI_DIR, 'shops');
// ─── Types ───────────────────────────────────────────────────────────────────
interface StockItem {
configName: string;
quantity: number;
price: number;
}
interface ShopInventory {
name: string; // section name e.g. "axeshop"
scope: string;
restock: boolean;
allstock: boolean;
size: number;
stock: StockItem[];
sourcePath: string;
}
interface ShopOwner {
npcConfigName: string;
npcDisplayName: string;
shopName: string; // matches ShopInventory.name
shopTitle: string;
sellMultiplier: number;
buyMultiplier: number;
delta: number;
sourcePath: string;
}
interface ShopPage {
shopName: string;
shopTitle: string;
inventory: ShopInventory;
owners: ShopOwner[];
location: string;
isGeneralStore: boolean;
}
// ─── Area Name Mappings ──────────────────────────────────────────────────────
const SOURCE_PATH_AREAS: Record<string, string> = {
'area_lumbridge': 'Lumbridge',
'area_alkharid': 'Al Kharid',
'area_ardougne_east': 'East Ardougne',
'area_ardougne_west': 'West Ardougne',
'area_barbarian_village': 'Barbarian Village',
'area_barbarian_outpost': 'Barbarian Outpost',
'area_brimhaven': 'Brimhaven',
'area_camelot': 'Camelot',
'area_canifis': 'Canifis',
'area_catherby': 'Catherby',
'area_combat_training': 'Combat Training Camp',
'area_desert': 'Desert',
'area_draynor': 'Draynor Village',
'area_edgeville': 'Edgeville',
'area_entrana': 'Entrana',
'area_falador': 'Falador',
'area_fishing_platform': 'Fishing Platform',
'area_gnome': 'Tree Gnome Stronghold',
'area_karamja': 'Karamja',
'area_lostcity': 'Zanaris',
'area_mage_arena': 'Mage Arena',
'area_port_sarim': 'Port Sarim',
'area_rimmington': 'Rimmington',
'area_seers': "Seers' Village",
'area_shilo': 'Shilo Village',
'area_taverly': 'Taverley',
'area_varrock': 'Varrock',
'area_white_wolf_mountain': 'White Wolf Mountain',
'area_wilderness': 'Wilderness',
'area_wizard_tower': 'Wizard Tower',
'area_yanille': 'Yanille',
'areas_heroes_guild': "Heroes' Guild",
'monastery': 'Monastery',
};
// ─── Parsers ─────────────────────────────────────────────────────────────────
function parseInvConfigs(content: string, sourcePath: string): ShopInventory[] {
const shops: ShopInventory[] = [];
let current: ShopInventory | null = null;
for (const rawLine of content.split('\n')) {
const line = rawLine.trim();
if (line.startsWith('//') || line.startsWith('/ ')) continue;
const sectionMatch = line.match(/^\[([^\]]+)\]$/);
if (sectionMatch) {
if (current && current.stock.length > 0) shops.push(current);
current = {
name: sectionMatch[1],
scope: '',
restock: false,
allstock: false,
size: 40,
stock: [],
sourcePath,
};
continue;
}
if (!current || !line) continue;
const eqIdx = line.indexOf('=');
if (eqIdx === -1) continue;
const key = line.slice(0, eqIdx).trim();
const value = line.slice(eqIdx + 1).trim();
if (key === 'scope') current.scope = value;
else if (key === 'restock') current.restock = value === 'yes';
else if (key === 'allstock') current.allstock = value === 'yes';
else if (key === 'size') current.size = parseInt(value) || 40;
else if (key.startsWith('stock')) {
const parts = value.split(',');
if (parts.length >= 3) {
current.stock.push({
configName: parts[0].trim(),
quantity: parseInt(parts[1]) || 0,
price: parseInt(parts[2]) || 0,
});
}
}
}
if (current && current.stock.length > 0) shops.push(current);
return shops;
}
interface NpcShopInfo {
configName: string;
displayName: string;
params: Map<string, string>;
sourcePath: string;
}
function parseNpcConfigs(content: string, sourcePath: string): NpcShopInfo[] {
const results: NpcShopInfo[] = [];
let currentName = '';
let currentDisplayName = '';
let currentParams = new Map<string, string>();
function flush() {
if (currentName && currentParams.has('owned_shop')) {
results.push({
configName: currentName,
displayName: currentDisplayName,
params: currentParams,
sourcePath,
});
}
}
for (const rawLine of content.split('\n')) {
const line = rawLine.trim();
if (line.startsWith('//') || line.startsWith('/ ')) continue;
const sectionMatch = line.match(/^\[([^\]]+)\]$/);
if (sectionMatch) {
flush();
currentName = sectionMatch[1];
currentDisplayName = '';
currentParams = new Map();
continue;
}
if (!currentName || !line) continue;
const eqIdx = line.indexOf('=');
if (eqIdx === -1) continue;
const key = line.slice(0, eqIdx).trim();
const value = line.slice(eqIdx + 1).trim();
if (key === 'name') currentDisplayName = value;
else if (key === 'param') {
const commaIdx = value.indexOf(',');
if (commaIdx !== -1) {
currentParams.set(value.slice(0, commaIdx), value.slice(commaIdx + 1));
}
}
}
flush();
return results;
}
interface ObjInfo {
displayName: string;
cost: number;
}
function parseObjConfigsFull(content: string): Map<string, ObjInfo> {
const items = new Map<string, ObjInfo>();
let currentName = '';
let currentDisplayName = '';
let currentCost = 0;
for (const rawLine of content.split('\n')) {
const line = rawLine.trim();
if (line.startsWith('//') || line.startsWith('/ ')) continue;
const sectionMatch = line.match(/^\[([^\]]+)\]$/);
if (sectionMatch) {
if (currentName && currentDisplayName) {
items.set(currentName, { displayName: currentDisplayName, cost: currentCost });
}
currentName = sectionMatch[1];
currentDisplayName = '';
currentCost = 0;
continue;
}
if (line.startsWith('name=')) {
currentDisplayName = line.slice(5);
} else if (line.startsWith('cost=')) {
currentCost = parseInt(line.slice(5)) || 0;
}
}
if (currentName && currentDisplayName) {
items.set(currentName, { displayName: currentDisplayName, cost: currentCost });
}
return items;
}
function findFilesRecursive(dir: string, ext: string): string[] {
const results: string[] = [];
function walk(d: string) {
const entries = readdirSync(d, { withFileTypes: true });
for (const entry of entries) {
const fullPath = join(d, entry.name);
if (entry.isDirectory()) walk(fullPath);
else if (entry.name.endsWith(ext)) results.push(fullPath);
}
}
walk(dir);
return results;
}
function prettifyConfigName(name: string): string {
return name
.replace(/_/g, ' ')
.replace(/\b\w/g, c => c.toUpperCase())
.trim();
}
function getAreaFromSourcePath(sourcePath: string): string {
const match = sourcePath.match(/scripts\/(?:areas\/)?(\w+)\//);
if (!match) return 'Unknown';
return SOURCE_PATH_AREAS[match[1]] || prettifyConfigName(match[1]);
}
function slugify(name: string): string {
return name.toLowerCase()
.replace(/['']/g, '')
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '');
}
// ─── Main Pipeline ───────────────────────────────────────────────────────────
function main() {
console.log('Shop Wiki Generator');
console.log('===================\n');
// ─── Step 1: Parse shop inventories ─────────────────────────────────
console.log('Step 1: Parsing shop inventories...');
const invFiles = findFilesRecursive(join(CONTENT_DIR, 'scripts'), '.inv');
const allShops = new Map<string, ShopInventory>();
for (const file of invFiles) {
const content = readFileSync(file, 'utf-8');
const relPath = relative(CONTENT_DIR, file);
const shops = parseInvConfigs(content, relPath);
for (const shop of shops) {
// Filter non-shop inventories: must have restock=yes and not be temp scope
if (shop.restock && shop.scope !== 'temp') {
allShops.set(shop.name, shop);
}
}
}
console.log(` Found ${allShops.size} shop inventories from ${invFiles.length} .inv files`);
// ─── Step 2: Parse shop owner NPCs ──────────────────────────────────
console.log('\nStep 2: Parsing shop owner NPCs...');
const npcFiles = findFilesRecursive(join(CONTENT_DIR, 'scripts'), '.npc');
const shopOwners = new Map<string, ShopOwner[]>(); // shopName -> owners
for (const file of npcFiles) {
const content = readFileSync(file, 'utf-8');
const relPath = relative(CONTENT_DIR, file);
const npcs = parseNpcConfigs(content, relPath);
for (const npc of npcs) {
const shopName = npc.params.get('owned_shop')!;
const owner: ShopOwner = {
npcConfigName: npc.configName,
npcDisplayName: npc.displayName,
shopName,
shopTitle: npc.params.get('shop_title') || prettifyConfigName(shopName),
sellMultiplier: parseInt(npc.params.get('shop_sell_multiplier') || '1000'),
buyMultiplier: parseInt(npc.params.get('shop_buy_multiplier') || '600'),
delta: parseInt(npc.params.get('shop_delta') || '10'),
sourcePath: relPath,
};
if (!shopOwners.has(shopName)) shopOwners.set(shopName, []);
shopOwners.get(shopName)!.push(owner);
}
}
let totalOwners = 0;
for (const owners of shopOwners.values()) totalOwners += owners.length;
console.log(` Found ${totalOwners} shop owners for ${shopOwners.size} shops`);
// ─── Step 3: Load item data (names + costs) ────────────────────────
console.log('\nStep 3: Loading item data...');
const objData = new Map<string, ObjInfo>();
const allObjContent = readFileSync(join(CONTENT_DIR, 'scripts', '_unpack', '225', 'all.obj'), 'utf-8');
const allObjData = parseObjConfigsFull(allObjContent);
for (const [k, v] of allObjData) objData.set(k, v);
const objConfigFiles = findFilesRecursive(join(CONTENT_DIR, 'scripts'), '.obj');
for (const file of objConfigFiles) {
if (file.includes('_unpack')) continue;
const content = readFileSync(file, 'utf-8');
const parsed = parseObjConfigsFull(content);
for (const [k, v] of parsed) {
const existing = objData.get(k);
if (existing) {
// Merge: override name/cost if set
if (v.displayName) existing.displayName = v.displayName;
if (v.cost) existing.cost = v.cost;
} else {
objData.set(k, v);
}
}
}
console.log(` Loaded ${objData.size} items`);
// ─── Step 4: Build shop pages ───────────────────────────────────────
console.log('\nStep 4: Building shop pages...');
const shopPages: ShopPage[] = [];
for (const [shopName, inventory] of allShops) {
const owners = shopOwners.get(shopName) || [];
const primaryOwner = owners[0];
const shopTitle = (primaryOwner?.shopTitle || prettifyConfigName(shopName)).replace(/\.$/, '');
const location = primaryOwner
? getAreaFromSourcePath(primaryOwner.sourcePath)
: getAreaFromSourcePath(inventory.sourcePath);
shopPages.push({
shopName,
shopTitle,
inventory,
owners,
location,
isGeneralStore: inventory.allstock,
});
}
// Sort by shop title
shopPages.sort((a, b) => a.shopTitle.localeCompare(b.shopTitle));
console.log(` Built ${shopPages.length} shop pages`);
// ─── Step 5: Generate markdown ──────────────────────────────────────
console.log('\nStep 5: Generating markdown...');
mkdirSync(SHOP_DIR, { recursive: true });
const pageIndex: { name: string; file: string; location: string }[] = [];
for (const page of shopPages) {
const slug = slugify(page.shopTitle);
const fileName = `${slug}.md`;
const filePath = join(SHOP_DIR, fileName);
const md = generateShopMarkdown(page, objData);
writeFileSync(filePath, md);
pageIndex.push({ name: page.shopTitle, file: fileName, location: page.location });
}
console.log(` Generated ${pageIndex.length} shop pages`);
// ─── Step 6: Update README ──────────────────────────────────────────
console.log('\nStep 6: Updating README...');
updateReadme(pageIndex);
console.log('\nDone!');
}
// ─── Generate Markdown ───────────────────────────────────────────────────────
function generateShopMarkdown(page: ShopPage, objData: Map<string, ObjInfo>): string {
const lines: string[] = [];
const primaryOwner = page.owners[0];
// Price multipliers (confusing naming in source: shop_sell_multiplier = price when shop sells TO player)
const sellToPlayerMul = primaryOwner?.sellMultiplier || 1000;
const buyFromPlayerMul = primaryOwner?.buyMultiplier || 600;
lines.push(`# ${page.shopTitle}`);
lines.push('');
// Info table
lines.push('| Detail | |');
lines.push('|---|---|');
// Owner(s)
if (page.owners.length > 0) {
const ownerLinks = page.owners.map(o => {
const npcSlug = slugify(o.npcDisplayName);
const npcPagePath = `../npcs/${npcSlug}.md`;
return `[${o.npcDisplayName}](${npcPagePath})`;
});
lines.push(`| **Owner** | ${ownerLinks.join(', ')} |`);
} else {
lines.push('| **Owner** | Unknown |');
}
lines.push(`| **Location** | ${page.location} |`);
lines.push(`| **Type** | ${page.isGeneralStore ? 'General Store' : 'Specialty Shop'} |`);
lines.push('');
// Stock table with computed prices
lines.push('## Stock');
lines.push('');
lines.push('| Item | Stock | Buy Price | Sell Price |');
lines.push('|------|-------|-----------|------------|');
for (const item of page.inventory.stock) {
const info = objData.get(item.configName);
const displayName = info?.displayName || prettifyConfigName(item.configName);
const baseCost = info?.cost || 0;
// Buy price = what player pays to buy from shop (at normal stock)
const buyPrice = Math.max(1, Math.floor(baseCost * sellToPlayerMul / 1000));
// Sell price = what player gets selling to shop (at normal stock)
const sellPrice = Math.floor(baseCost * buyFromPlayerMul / 1000);
const itemSlug = slugify(displayName);
const itemLink = `[${displayName}](../items/${itemSlug}.md)`;
lines.push(`| ${itemLink} | ${item.quantity} | ${buyPrice} gp | ${sellPrice} gp |`);
}
lines.push('');
return lines.join('\n');
}
// ─── Update README ───────────────────────────────────────────────────────────
function updateReadme(pages: { name: string; file: string; location: string }[]) {
const readmePath = join(WIKI_DIR, 'README.md');
let content = readFileSync(readmePath, 'utf-8');
// Remove existing Shops section if present
const shopsStart = content.indexOf('## Shops');
if (shopsStart !== -1) {
const afterShops = content.slice(shopsStart + 8);
const nextSection = afterShops.search(/\n## /);
if (nextSection !== -1) {
content = content.slice(0, shopsStart) + content.slice(shopsStart + 8 + nextSection + 1);
} else {
content = content.slice(0, shopsStart).trimEnd() + '\n';
}
}
// Sort alphabetically
pages.sort((a, b) => a.name.localeCompare(b.name));
// Generate Shops section
const section = [
'## Shops',
'',
'| Shop | Location |',
'|------|----------|',
];
for (const page of pages) {
section.push(`| [${page.name}](shops/${page.file}) | ${page.location} |`);
}
section.push('');
// Insert before NPCs section if it exists, otherwise append
const npcsStart = content.indexOf('## NPCs');
if (npcsStart !== -1) {
content = content.slice(0, npcsStart) + section.join('\n') + '\n' + content.slice(npcsStart);
} else {
content = content.trimEnd() + '\n\n' + section.join('\n');
}
writeFileSync(readmePath, content);
console.log(` Updated README.md with ${pages.length} shop entries`);
}
// ─── Run ─────────────────────────────────────────────────────────────────────
main();
+10
View File
@@ -0,0 +1,10 @@
# 1/2 anchovy pizza
*Half of this pizza has been eaten.*
| Detail | |
|---|---|
| **Type** | Food |
| **Members** | No |
| **Weight** | 450g |
| **Value** | 30 gp |
+10
View File
@@ -0,0 +1,10 @@
# 1/2 meat pizza
*Half of this pizza has been eaten.*
| Detail | |
|---|---|
| **Type** | Food |
| **Members** | No |
| **Weight** | 450g |
| **Value** | 25 gp |
+10
View File
@@ -0,0 +1,10 @@
# 1/2 plain pizza
*Half of this pizza has been eaten.*
| Detail | |
|---|---|
| **Type** | Food |
| **Members** | No |
| **Weight** | 400g |
| **Value** | 20 gp |
+10
View File
@@ -0,0 +1,10 @@
# 1/2pineapple pizza
*Half of this pizza has been eaten.*
| Detail | |
|---|---|
| **Type** | Food |
| **Members** | Yes |
| **Weight** | 450g |
| **Value** | 50 gp |
+10
View File
@@ -0,0 +1,10 @@
# 2/3 cake
*Someone has eaten a big chunk of this cake.*
| Detail | |
|---|---|
| **Type** | Food |
| **Members** | No |
| **Weight** | 200g |
| **Value** | 30 gp |
+10
View File
@@ -0,0 +1,10 @@
# 2/3 chocolate cake
*Someone has eaten a big chunk of this cake.*
| Detail | |
|---|---|
| **Type** | Food |
| **Members** | No |
| **Weight** | 200g |
| **Value** | 50 gp |
+17
View File
@@ -0,0 +1,17 @@
# A key
*This will unlock something*
| Detail | |
|---|---|
| **Type** | Quest Item |
| **Members** | Yes |
| **Weight** | 10g |
| **Value** | 0 gp |
| **Tradeable** | No |
## Spawn Locations
| Area | Floor | Quantity |
|------|-------|----------|
| m40_154 | Ground | 1 |
+11
View File
@@ -0,0 +1,11 @@
# A magic scroll
*Maybe I should read it...*
| Detail | |
|---|---|
| **Type** | Quest Item |
| **Members** | Yes |
| **Weight** | 20g |
| **Value** | 1 gp |
| **Tradeable** | No |
+11
View File
@@ -0,0 +1,11 @@
# A scribbled note
*A scrawled note with spidery writing on it.*
| Detail | |
|---|---|
| **Type** | Quest Item |
| **Members** | Yes |
| **Weight** | 20g |
| **Value** | 0 gp |
| **Tradeable** | No |
+11
View File
@@ -0,0 +1,11 @@
# A scruffy note
*It seems to say "hongorer lure"...*
| Detail | |
|---|---|
| **Type** | Quest Item |
| **Members** | Yes |
| **Weight** | 10g |
| **Value** | 2 gp |
| **Tradeable** | No |
+11
View File
@@ -0,0 +1,11 @@
# A small key
*Quite a small key.*
| Detail | |
|---|---|
| **Type** | Quest Item |
| **Members** | Yes |
| **Weight** | 10g |
| **Value** | 1 gp |
| **Tradeable** | No |
+11
View File
@@ -0,0 +1,11 @@
# A stone bowl
*This is an empty stone bowl.*
| Detail | |
|---|---|
| **Type** | Quest Item |
| **Members** | No |
| **Weight** | 5oz |
| **Value** | 1 gp |
| **Tradeable** | No |
+11
View File
@@ -0,0 +1,11 @@
# A used spell
*A used druidic spell given to you freely by the spirit of Filliman Tarlock.*
| Detail | |
|---|---|
| **Type** | Quest Item |
| **Members** | Yes |
| **Weight** | 20g |
| **Value** | 0 gp |
| **Tradeable** | No |
+10
View File
@@ -0,0 +1,10 @@
# Achey tree logs
*These logs are longer than normal.*
| Detail | |
|---|---|
| **Type** | Other |
| **Members** | Yes |
| **Weight** | 3lb |
| **Value** | 4 gp |
+23
View File
@@ -0,0 +1,23 @@
# Adam full helm (g)
*Adamant full helm with gold trim.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 7lb |
| **Value** | 3520 gp |
| **Equipable** | Yes — Head |
## Combat Stats
| Stat | Value |
|------|-------|
| Magic Attack | -6 |
| Range Attack | -2 |
| Stab Defence | +19 |
| Slash Defence | +21 |
| Crush Defence | +16 |
| Magic Defence | -1 |
| Range Defence | +19 |
+23
View File
@@ -0,0 +1,23 @@
# Adam full helm (t)
*Adamant full helm with trim.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 7lb |
| **Value** | 3520 gp |
| **Equipable** | Yes — Head |
## Combat Stats
| Stat | Value |
|------|-------|
| Magic Attack | -6 |
| Range Attack | -2 |
| Stab Defence | +19 |
| Slash Defence | +21 |
| Crush Defence | +16 |
| Magic Defence | -1 |
| Range Defence | +19 |
+23
View File
@@ -0,0 +1,23 @@
# Adam kiteshield (g)
*Adamant kiteshield with gold trim.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 13lb |
| **Value** | 5440 gp |
| **Equipable** | Yes — Shield |
## Combat Stats
| Stat | Value |
|------|-------|
| Magic Attack | -8 |
| Range Attack | -2 |
| Stab Defence | +27 |
| Slash Defence | +31 |
| Crush Defence | +29 |
| Magic Defence | -1 |
| Range Defence | +29 |
+23
View File
@@ -0,0 +1,23 @@
# Adam kiteshield (t)
*Adamant kiteshield with trim.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 13lb |
| **Value** | 5440 gp |
| **Equipable** | Yes — Shield |
## Combat Stats
| Stat | Value |
|------|-------|
| Magic Attack | -8 |
| Range Attack | -2 |
| Stab Defence | +27 |
| Slash Defence | +31 |
| Crush Defence | +29 |
| Magic Defence | -1 |
| Range Defence | +29 |
+23
View File
@@ -0,0 +1,23 @@
# Adam platebody (g)
*Adamant platebody with gold trim.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 25lb |
| **Value** | 12800 gp |
| **Equipable** | Yes — Body |
## Combat Stats
| Stat | Value |
|------|-------|
| Magic Attack | -30 |
| Range Attack | -10 |
| Stab Defence | +65 |
| Slash Defence | +63 |
| Crush Defence | +55 |
| Magic Defence | -6 |
| Range Defence | +63 |
+23
View File
@@ -0,0 +1,23 @@
# Adam platebody (t)
*Adamant platebody with trim.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 25lb |
| **Value** | 12800 gp |
| **Equipable** | Yes — Body |
## Combat Stats
| Stat | Value |
|------|-------|
| Magic Attack | -30 |
| Range Attack | -10 |
| Stab Defence | +65 |
| Slash Defence | +63 |
| Crush Defence | +55 |
| Magic Defence | -6 |
| Range Defence | +63 |
+23
View File
@@ -0,0 +1,23 @@
# Adam platelegs (g)
*Adamant platelegs with gold trim.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 23lb |
| **Value** | 6400 gp |
| **Equipable** | Yes — Legs |
## Combat Stats
| Stat | Value |
|------|-------|
| Magic Attack | -21 |
| Range Attack | -7 |
| Stab Defence | +33 |
| Slash Defence | +31 |
| Crush Defence | +29 |
| Magic Defence | -4 |
| Range Defence | +31 |
+23
View File
@@ -0,0 +1,23 @@
# Adam platelegs (t)
*Adamant platelegs with trim.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 23lb |
| **Value** | 6400 gp |
| **Equipable** | Yes — Legs |
## Combat Stats
| Stat | Value |
|------|-------|
| Magic Attack | -21 |
| Range Attack | -7 |
| Stab Defence | +33 |
| Slash Defence | +31 |
| Crush Defence | +29 |
| Magic Defence | -4 |
| Range Defence | +31 |
+29
View File
@@ -0,0 +1,29 @@
# Adamant 2h sword
*A two handed sword.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | No |
| **Weight** | 9lb |
| **Value** | 6400 gp |
| **Equipable** | Yes — Weapon |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | -4 |
| Slash Attack | +43 |
| Crush Attack | +30 |
| Magic Attack | -4 |
| Range Defence | -1 |
| Strength Bonus | +44 |
| Attack Speed | 7 |
## Sources
- Sold by: [Armoury](../shops/armoury.md) for 9600 gp
- Sold by: [Gaius' Two Handed Shop](../shops/gaius-two-handed-shop.md) for 6400 gp
- Sold by: [Gulluck and Sons](../shops/gulluck-and-sons.md) for 6400 gp
+19
View File
@@ -0,0 +1,19 @@
# Adamant arrow(p)
*Venomous looking arrows.*
| Detail | |
|---|---|
| **Type** | Ammunition |
| **Members** | Yes |
| **Weight** | 18g |
| **Value** | 80 gp |
| **Equipable** | Yes — Ammo |
| **Stackable** | Yes |
## Combat Stats
| Stat | Value |
|------|-------|
| Range Bonus | +31 |
| Level Requirement | 30 |
+26
View File
@@ -0,0 +1,26 @@
# Adamant arrow
*Arrows with Adamantite heads.*
| Detail | |
|---|---|
| **Type** | Ammunition |
| **Members** | Yes |
| **Weight** | 18g |
| **Value** | 80 gp |
| **Equipable** | Yes — Ammo |
| **Stackable** | Yes |
## Combat Stats
| Stat | Value |
|------|-------|
| Range Bonus | +31 |
| Level Requirement | 30 |
## Sources
- Sold by: [Dargaud's Bow and Arrows](../shops/dargauds-bow-and-arrows.md) for 80 gp
- Sold by: [Hicktons Archery Emporium](../shops/hicktons-archery-emporium.md) for 80 gp
- Dropped by: [Ice warrior](../npcs/ice-warrior.md) (2/128 (members))
- Dropped by: [White Knight](../npcs/white-knight.md) (1/128)
+16
View File
@@ -0,0 +1,16 @@
# Adamant arrowtips
*I can make an arrow with these.*
| Detail | |
|---|---|
| **Type** | Other |
| **Members** | Yes |
| **Weight** | 5g |
| **Value** | 40 gp |
| **Stackable** | Yes |
## Sources
- Sold by: [Dargaud's Bow and Arrows](../shops/dargauds-bow-and-arrows.md) for 40 gp
- Sold by: [Hicktons Archery Emporium](../shops/hicktons-archery-emporium.md) for 40 gp
+23
View File
@@ -0,0 +1,23 @@
# Adamant axe
*A powerful axe.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | No |
| **Weight** | 56oz |
| **Value** | 1280 gp |
| **Equipable** | Yes — Weapon |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | -2 |
| Slash Attack | +17 |
| Crush Attack | +15 |
| Slash Defence | +1 |
| Strength Bonus | +19 |
| Level Requirement | 31 |
| Attack Speed | 5 |
+26
View File
@@ -0,0 +1,26 @@
# Adamant battleaxe
*A vicious looking axe.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | No |
| **Weight** | 7lb |
| **Value** | 4160 gp |
| **Equipable** | Yes — Weapon |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | -2 |
| Slash Attack | +31 |
| Crush Attack | +26 |
| Range Defence | -1 |
| Strength Bonus | +41 |
| Attack Speed | 6 |
## Sources
- Sold by: [Brian's Battleaxe Bazaar](../shops/brians-battleaxe-bazaar.md) for 4160 gp
+26
View File
@@ -0,0 +1,26 @@
# Adamant chainbody
*A series of connected metal rings.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 17lb |
| **Value** | 4800 gp |
| **Equipable** | Yes — Body |
## Combat Stats
| Stat | Value |
|------|-------|
| Magic Attack | -15 |
| Stab Defence | +36 |
| Slash Defence | +50 |
| Crush Defence | +61 |
| Magic Defence | -3 |
| Range Defence | +38 |
## Sources
- Sold by: [Wayne's Chains! - Chainmail specialist](../shops/waynes-chains-chainmail-specialist.md) for 4800 gp
+22
View File
@@ -0,0 +1,22 @@
# Adamant dagger(p)
*A very dangerous poisoned dagger.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | Yes |
| **Weight** | 18oz |
| **Value** | 800 gp |
| **Equipable** | Yes — Weapon |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | +15 |
| Slash Attack | +8 |
| Crush Attack | -4 |
| Magic Attack | +1 |
| Magic Defence | +1 |
| Strength Bonus | +14 |
+26
View File
@@ -0,0 +1,26 @@
# Adamant dagger
*Short and deadly.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | No |
| **Weight** | 18oz |
| **Value** | 800 gp |
| **Equipable** | Yes — Weapon |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | +15 |
| Slash Attack | +8 |
| Crush Attack | -4 |
| Magic Attack | +1 |
| Magic Defence | +1 |
| Strength Bonus | +14 |
## Sources
- Sold by: [Varrock Swordshop](../shops/varrock-swordshop.md) for 800 gp
+25
View File
@@ -0,0 +1,25 @@
# Adamant dart(p)
*A deadly poisoned dart with an adamantite tip.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | Yes |
| **Weight** | 12g |
| **Value** | 65 gp |
| **Equipable** | Yes — Weapon |
| **Stackable** | Yes |
## Combat Stats
| Stat | Value |
|------|-------|
| Range Attack | +11 |
| Range Bonus | +10 |
| Level Requirement | 30 |
| Attack Speed | 3 |
## Sources
- Dropped by: [Black dragon](../npcs/black-dragon.md) (7/128)
+11
View File
@@ -0,0 +1,11 @@
# Adamant dart tip
*A deadly looking dart tip made of adamantite - needs feathers for flight.*
| Detail | |
|---|---|
| **Type** | Other |
| **Members** | Yes |
| **Weight** | 10g |
| **Value** | 36 gp |
| **Stackable** | Yes |
+21
View File
@@ -0,0 +1,21 @@
# Adamant dart
*A deadly throwing dart with an adamantite tip.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | Yes |
| **Weight** | 12g |
| **Value** | 65 gp |
| **Equipable** | Yes — Weapon |
| **Stackable** | Yes |
## Combat Stats
| Stat | Value |
|------|-------|
| Range Attack | +11 |
| Range Bonus | +10 |
| Level Requirement | 30 |
| Attack Speed | 3 |
+29
View File
@@ -0,0 +1,29 @@
# Adamant full helm
*A full face helmet.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 7lb |
| **Value** | 3520 gp |
| **Equipable** | Yes — Head |
## Combat Stats
| Stat | Value |
|------|-------|
| Magic Attack | -6 |
| Range Attack | -2 |
| Stab Defence | +19 |
| Slash Defence | +21 |
| Crush Defence | +16 |
| Magic Defence | -1 |
| Range Defence | +19 |
## Sources
- Sold by: [Helmet Shop](../shops/helmet-shop.md) for 3520 gp
- Dropped by: [Green dragon](../npcs/green-dragon.md) (1/128)
- Dropped by: [Blue dragon](../npcs/blue-dragon.md) (1/128)
+20
View File
@@ -0,0 +1,20 @@
# Adamant javelin(p)
*An adamantite tipped javelin.*
| Detail | |
|---|---|
| **Type** | Ammunition |
| **Members** | Yes |
| **Value** | 160 gp |
| **Equipable** | Yes — Ammo |
| **Stackable** | Yes |
## Combat Stats
| Stat | Value |
|------|-------|
| Range Attack | +24 |
| Range Bonus | +28 |
| Level Requirement | 30 |
| Attack Speed | 6 |
+25
View File
@@ -0,0 +1,25 @@
# Adamant javelin
*An adamantite tipped javelin.*
| Detail | |
|---|---|
| **Type** | Ammunition |
| **Members** | Yes |
| **Value** | 160 gp |
| **Equipable** | Yes — Ammo |
| **Stackable** | Yes |
## Combat Stats
| Stat | Value |
|------|-------|
| Range Attack | +24 |
| Range Bonus | +28 |
| Level Requirement | 30 |
| Attack Speed | 6 |
## Sources
- Sold by: [Authentic Throwing Weapons](../shops/authentic-throwing-weapons.md) for 208 gp
- Dropped by: [Black dragon](../npcs/black-dragon.md) (20/128)
+23
View File
@@ -0,0 +1,23 @@
# Adamant kiteshield
*A large metal shield.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 13lb |
| **Value** | 5440 gp |
| **Equipable** | Yes — Shield |
## Combat Stats
| Stat | Value |
|------|-------|
| Magic Attack | -8 |
| Range Attack | -2 |
| Stab Defence | +27 |
| Slash Defence | +31 |
| Crush Defence | +29 |
| Magic Defence | -1 |
| Range Defence | +29 |
+21
View File
@@ -0,0 +1,21 @@
# Adamant knife(p)
*A finely balanced throwing knife.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | Yes |
| **Weight** | 14oz |
| **Value** | 66 gp |
| **Equipable** | Yes — Weapon |
| **Stackable** | Yes |
## Combat Stats
| Stat | Value |
|------|-------|
| Range Attack | +15 |
| Range Bonus | +14 |
| Level Requirement | 30 |
| Attack Speed | 3 |
+21
View File
@@ -0,0 +1,21 @@
# Adamant knife
*A finely balanced throwing knife.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | Yes |
| **Weight** | 14oz |
| **Value** | 66 gp |
| **Equipable** | Yes — Weapon |
| **Stackable** | Yes |
## Combat Stats
| Stat | Value |
|------|-------|
| Range Attack | +15 |
| Range Bonus | +14 |
| Level Requirement | 30 |
| Attack Speed | 3 |
+27
View File
@@ -0,0 +1,27 @@
# Adamant longsword
*A razor sharp longsword.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | No |
| **Weight** | 72oz |
| **Value** | 3200 gp |
| **Equipable** | Yes — Weapon |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | +20 |
| Slash Attack | +29 |
| Crush Attack | -2 |
| Slash Defence | +3 |
| Crush Defence | +2 |
| Strength Bonus | +31 |
| Attack Speed | 5 |
## Sources
- Sold by: [Varrock Swordshop](../shops/varrock-swordshop.md) for 3200 gp
+26
View File
@@ -0,0 +1,26 @@
# Adamant mace
*A spiky mace.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | No |
| **Weight** | 72oz |
| **Value** | 1440 gp |
| **Equipable** | Yes — Weapon |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | +13 |
| Slash Attack | -2 |
| Crush Attack | +25 |
| Strength Bonus | +23 |
| Prayer Bonus | +3 |
| Attack Speed | 5 |
## Sources
- Sold by: [Flynn's Mace Market](../shops/flynns-mace-market.md) for 1440 gp
+27
View File
@@ -0,0 +1,27 @@
# Adamant med helm
*A medium sized helmet.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 5lb |
| **Value** | 1920 gp |
| **Equipable** | Yes — Head |
## Combat Stats
| Stat | Value |
|------|-------|
| Magic Attack | -3 |
| Range Attack | -1 |
| Stab Defence | +14 |
| Slash Defence | +15 |
| Crush Defence | +13 |
| Magic Defence | -1 |
| Range Defence | +14 |
## Sources
- Sold by: [Helmet Shop](../shops/helmet-shop.md) for 1920 gp
+27
View File
@@ -0,0 +1,27 @@
# Adamant pickaxe
*Used for mining.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | No |
| **Weight** | 6lb |
| **Value** | 3200 gp |
| **Equipable** | Yes — Weapon |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | +17 |
| Slash Attack | -2 |
| Crush Attack | +15 |
| Slash Defence | +1 |
| Strength Bonus | +19 |
| Level Requirement | 31 |
| Attack Speed | 5 |
## Sources
- Sold by: [Nurmof's Pickaxe Shop](../shops/nurmofs-pickaxe-shop.md) for 3200 gp
+29
View File
@@ -0,0 +1,29 @@
# Adamant platebody
*Provides excellent protection.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 25lb |
| **Value** | 12800 gp |
| **Equipable** | Yes — Body |
## Combat Stats
| Stat | Value |
|------|-------|
| Magic Attack | -30 |
| Range Attack | -10 |
| Stab Defence | +65 |
| Slash Defence | +63 |
| Crush Defence | +55 |
| Magic Defence | -6 |
| Range Defence | +63 |
## Sources
- Sold by: [Valaine's Shop of Champions](../shops/valaines-shop-of-champions.md) for 16640 gp
- Dropped by: [Black dragon](../npcs/black-dragon.md) (1/128)
- Dropped by: [Red dragon](../npcs/red-dragon.md) (1/128)
+28
View File
@@ -0,0 +1,28 @@
# Adamant platelegs
*These look pretty heavy.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 23lb |
| **Value** | 6400 gp |
| **Equipable** | Yes — Legs |
## Combat Stats
| Stat | Value |
|------|-------|
| Magic Attack | -21 |
| Range Attack | -7 |
| Stab Defence | +33 |
| Slash Defence | +31 |
| Crush Defence | +29 |
| Magic Defence | -4 |
| Range Defence | +31 |
## Sources
- Sold by: [Louie's Armoured Legs Bazaar](../shops/louies-armoured-legs-bazaar.md) for 6400 gp
- Dropped by: [Greater demon](../npcs/greater-demon.md) (1/128)
+27
View File
@@ -0,0 +1,27 @@
# Adamant plateskirt
*Designer leg protection.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 20lb |
| **Value** | 6400 gp |
| **Equipable** | Yes — Legs |
## Combat Stats
| Stat | Value |
|------|-------|
| Magic Attack | -21 |
| Range Attack | -7 |
| Stab Defence | +33 |
| Slash Defence | +31 |
| Crush Defence | +29 |
| Magic Defence | -4 |
| Range Defence | +31 |
## Sources
- Sold by: [Ranael's Super Skirt Store](../shops/ranaels-super-skirt-store.md) for 6400 gp
+21
View File
@@ -0,0 +1,21 @@
# Adamant scimitar
*A vicious curved sword.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | No |
| **Weight** | 72oz |
| **Value** | 2560 gp |
| **Equipable** | Yes — Weapon |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | +6 |
| Slash Attack | +29 |
| Crush Attack | -2 |
| Slash Defence | +1 |
| Strength Bonus | +28 |
+23
View File
@@ -0,0 +1,23 @@
# Adamant spear(p)
*An adamantite tipped spear.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | Yes |
| **Weight** | 6lb |
| **Value** | 2080 gp |
| **Equipable** | Yes — Weapon |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | +24 |
| Slash Attack | +24 |
| Crush Attack | +24 |
| Stab Defence | +1 |
| Slash Defence | +1 |
| Strength Bonus | +28 |
| Attack Speed | 6 |
+27
View File
@@ -0,0 +1,27 @@
# Adamant spear
*An adamantite tipped spear.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | Yes |
| **Weight** | 6lb |
| **Value** | 2080 gp |
| **Equipable** | Yes — Weapon |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | +24 |
| Slash Attack | +24 |
| Crush Attack | +24 |
| Stab Defence | +1 |
| Slash Defence | +1 |
| Strength Bonus | +28 |
| Attack Speed | 6 |
## Sources
- Dropped by: [Shadow warrior](../npcs/shadow-warrior.md) (1/128)
+22
View File
@@ -0,0 +1,22 @@
# Adamant sq shield
*A medium square shield.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 9lb |
| **Value** | 3840 gp |
| **Equipable** | Yes — Shield |
## Combat Stats
| Stat | Value |
|------|-------|
| Magic Attack | -6 |
| Range Attack | -2 |
| Stab Defence | +24 |
| Slash Defence | +26 |
| Crush Defence | +22 |
| Range Defence | +24 |
+26
View File
@@ -0,0 +1,26 @@
# Adamant sword
*A razor sharp sword.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | No |
| **Weight** | 72oz |
| **Value** | 2080 gp |
| **Equipable** | Yes — Weapon |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | +23 |
| Slash Attack | +18 |
| Crush Attack | -2 |
| Slash Defence | +2 |
| Crush Defence | +1 |
| Strength Bonus | +24 |
## Sources
- Sold by: [Varrock Swordshop](../shops/varrock-swordshop.md) for 2080 gp
+16
View File
@@ -0,0 +1,16 @@
# Adamantite bar
*It's a bar of adamantite.*
| Detail | |
|---|---|
| **Type** | Other |
| **Members** | No |
| **Weight** | 72oz |
| **Value** | 640 gp |
## Sources
- Dropped by: [Black dragon](../npcs/black-dragon.md) (3/128)
- Dropped by: [Black Demon](../npcs/black-demon.md) (2/128)
- Dropped by: [Red dragon](../npcs/red-dragon.md) (1/128)
+15
View File
@@ -0,0 +1,15 @@
# Adamantite ore
*This needs refining.*
| Detail | |
|---|---|
| **Type** | Other |
| **Members** | No |
| **Weight** | 6lb |
| **Value** | 400 gp |
## Sources
- Dropped by: [Green dragon](../npcs/green-dragon.md) (3/128)
- Dropped by: [Blue dragon](../npcs/blue-dragon.md) (3/128)
+19
View File
@@ -0,0 +1,19 @@
# Adamnt fire arrows
*These adamant headed arrows are ablaze with fire.*
| Detail | |
|---|---|
| **Type** | Ammunition |
| **Members** | Yes |
| **Weight** | 18g |
| **Value** | 10 gp |
| **Equipable** | Yes — Ammo |
| **Stackable** | Yes |
## Combat Stats
| Stat | Value |
|------|-------|
| Range Bonus | +31 |
| Level Requirement | 30 |
+25
View File
@@ -0,0 +1,25 @@
# Adamnt thrownaxe
*A finely balanced throwing axe.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | Yes |
| **Weight** | 46oz |
| **Value** | 176 gp |
| **Equipable** | Yes — Weapon |
| **Stackable** | Yes |
## Combat Stats
| Stat | Value |
|------|-------|
| Range Attack | +17 |
| Range Bonus | +23 |
| Level Requirement | 30 |
| Attack Speed | 5 |
## Sources
- Sold by: [Authentic Throwing Weapons](../shops/authentic-throwing-weapons.md) for 228 gp
+22
View File
@@ -0,0 +1,22 @@
# Adamnt warhammer
*I don't think it's intended for joinery.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | No |
| **Weight** | 72oz |
| **Value** | 4120 gp |
| **Equipable** | Yes — Weapon |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | -4 |
| Slash Attack | -4 |
| Crush Attack | +35 |
| Magic Attack | -4 |
| Strength Bonus | +31 |
| Attack Speed | 6 |
+11
View File
@@ -0,0 +1,11 @@
# Address label
*It says 'To Lord Handelmort, Handelmort Mansion'.*
| Detail | |
|---|---|
| **Type** | Quest Item |
| **Members** | Yes |
| **Weight** | 10g |
| **Value** | 10 gp |
| **Tradeable** | No |
+26
View File
@@ -0,0 +1,26 @@
# Air battlestaff
*It's a slightly magical stick.*
| Detail | |
|---|---|
| **Type** | Weapon |
| **Members** | Yes |
| **Weight** | 5lb |
| **Value** | 15500 gp |
| **Equipable** | Yes — Weapon |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | +7 |
| Slash Attack | -1 |
| Crush Attack | +28 |
| Magic Attack | +10 |
| Stab Defence | +2 |
| Slash Defence | +3 |
| Crush Defence | +1 |
| Magic Defence | +10 |
| Strength Bonus | +35 |
| Attack Speed | 5 |
+10
View File
@@ -0,0 +1,10 @@
# Air orb
*A magic glowing orb.*
| Detail | |
|---|---|
| **Type** | Other |
| **Members** | Yes |
| **Weight** | 1lb |
| **Value** | 300 gp |
+44
View File
@@ -0,0 +1,44 @@
# Air rune
*One of the 4 basic elemental Runes.*
| Detail | |
|---|---|
| **Type** | Rune |
| **Members** | No |
| **Weight** | 2g |
| **Value** | 4 gp |
| **Stackable** | Yes |
## Spawn Locations
| Area | Floor | Quantity |
|------|-------|----------|
| Karamja | Ground | 1 |
| Lumbridge (west) | Ground | 1 |
| m47_56 | Ground | 5 |
## Sources
- Sold by: [Magic Guild Store](../shops/magic-guild-store.md) for 4 gp
- Sold by: [Aubury's Rune Shop](../shops/auburys-rune-shop.md) for 4 gp
- Sold by: [Lundail's Arena-side Rune Shop](../shops/lundails-arena-side-rune-shop.md) for 4 gp
- Sold by: [Betty's Magic Emporium](../shops/bettys-magic-emporium.md) for 4 gp
- Dropped by: [Tower guard](../npcs/tower-guard.md) (2/128)
- Dropped by: [Black dragon](../npcs/black-dragon.md) (1/128)
- Dropped by: [Guard](../npcs/guard.md) (2/128)
- Dropped by: [Chaos druid warrior](../npcs/chaos-druid-warrior.md) (1/128)
- Dropped by: [Wizard](../npcs/wizard.md) (3/128)
- Dropped by: [Black Demon](../npcs/black-demon.md) (1/128)
- Dropped by: [Colonel Radick](../npcs/colonel-radick.md) (2/128)
- Dropped by: [Chaos druid](../npcs/chaos-druid.md) (3/128)
- Dropped by: [Bandit](../npcs/bandit.md) (2/128)
- Dropped by: [Chaos dwarf](../npcs/chaos-dwarf.md) (3/128)
- Dropped by: [Dark wizard](../npcs/dark-wizard.md) (3/128)
- Dropped by: [Pirate](../npcs/pirate.md) (2/128)
- Dropped by: [Zombie](../npcs/zombie.md) (4/128)
- Dropped by: [Soldier](../npcs/soldier.md) (2/128)
- Dropped by: [Moss giant](../npcs/moss-giant.md) (3/128)
- Dropped by: [Necromancer](../npcs/necromancer.md) (3/128)
- Dropped by: [Shadow warrior](../npcs/shadow-warrior.md) (4/128)
- Dropped by: [Skeleton](../npcs/skeleton.md) (2/128)
+15
View File
@@ -0,0 +1,15 @@
# Air talisman
*A mysterious power emanates from the talisman...*
| Detail | |
|---|---|
| **Type** | Other |
| **Members** | No |
| **Weight** | 15g |
| **Value** | 4 gp |
## Sources
- Dropped by: [Goblin guard](../npcs/goblin-guard.md) (1/128)
- Dropped by: [Goblin](../npcs/goblin.md) (1/128)
+15
View File
@@ -0,0 +1,15 @@
# Ammo mould
*Used to make cannon ammunition.*
| Detail | |
|---|---|
| **Type** | Quest Item |
| **Members** | Yes |
| **Weight** | 10lb |
| **Value** | 5 gp |
| **Tradeable** | No |
## Sources
- Sold by: [Multi cannon parts for sale:](../shops/multi-cannon-parts-for-sale.md) for 5 gp
+11
View File
@@ -0,0 +1,11 @@
# Ammonium nitrate
*An acrid chemical.*
| Detail | |
|---|---|
| **Type** | Quest Item |
| **Members** | Yes |
| **Weight** | 15g |
| **Value** | 0 gp |
| **Tradeable** | No |
+24
View File
@@ -0,0 +1,24 @@
# Amulet mould
*Used to make gold amulets*
| Detail | |
|---|---|
| **Type** | Other |
| **Members** | No |
| **Weight** | 1lb |
| **Value** | 5 gp |
## Spawn Locations
| Area | Floor | Quantity |
|------|-------|----------|
| Falador (west) | Ground | 1 |
## Sources
- Sold by: [Rommiks Crafty Supplies](../shops/rommiks-crafty-supplies.md) for 5 gp
- Sold by: [Craftingshop Free](../shops/craftingshop-free.md) for 5 gp
- Sold by: [Dommiks Crafting Store](../shops/dommiks-crafting-store.md) for 5 gp
- Sold by: [Craftingshop2 Free](../shops/craftingshop2-free.md) for 5 gp
- Dropped by: [Gunthor the brave](../npcs/gunthor-the-brave.md) (1/128)
+21
View File
@@ -0,0 +1,21 @@
# Amulet of accuracy
*It increases my aim.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 10g |
| **Value** | 100 gp |
| **Equipable** | Yes — Front |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | +4 |
| Slash Attack | +4 |
| Crush Attack | +4 |
| Magic Attack | +4 |
| Range Attack | +4 |
+25
View File
@@ -0,0 +1,25 @@
# Amulet of defence
*An enchanted emerald amulet of protection.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 10g |
| **Value** | 1275 gp |
| **Equipable** | Yes — Front |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Defence | +7 |
| Slash Defence | +7 |
| Crush Defence | +7 |
| Magic Defence | +7 |
| Range Defence | +7 |
## Sources
- Sold by: [Davon's Amulet Store](../shops/davons-amulet-store.md) for 1530 gp
+11
View File
@@ -0,0 +1,11 @@
# Amulet of doomion
*A mystical demonic amulet.*
| Detail | |
|---|---|
| **Type** | Quest Item |
| **Members** | Yes |
| **Weight** | 10g |
| **Value** | 0 gp |
| **Tradeable** | No |
+28
View File
@@ -0,0 +1,28 @@
# Amulet of glory(1)
*A dragonstone amulet with 1 magic charge.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | Yes |
| **Weight** | 10g |
| **Value** | 17625 gp |
| **Equipable** | Yes — Front |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | +10 |
| Slash Attack | +10 |
| Crush Attack | +10 |
| Magic Attack | +10 |
| Range Attack | +10 |
| Stab Defence | +3 |
| Slash Defence | +3 |
| Crush Defence | +3 |
| Magic Defence | +3 |
| Range Defence | +3 |
| Strength Bonus | +6 |
| Prayer Bonus | +3 |
+28
View File
@@ -0,0 +1,28 @@
# Amulet of glory(2)
*A dragonstone amulet with 2 magic charges.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | Yes |
| **Weight** | 10g |
| **Value** | 17625 gp |
| **Equipable** | Yes — Front |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | +10 |
| Slash Attack | +10 |
| Crush Attack | +10 |
| Magic Attack | +10 |
| Range Attack | +10 |
| Stab Defence | +3 |
| Slash Defence | +3 |
| Crush Defence | +3 |
| Magic Defence | +3 |
| Range Defence | +3 |
| Strength Bonus | +6 |
| Prayer Bonus | +3 |
+28
View File
@@ -0,0 +1,28 @@
# Amulet of glory(3)
*A dragonstone amulet with 3 magic charges.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | Yes |
| **Weight** | 10g |
| **Value** | 17625 gp |
| **Equipable** | Yes — Front |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | +10 |
| Slash Attack | +10 |
| Crush Attack | +10 |
| Magic Attack | +10 |
| Range Attack | +10 |
| Stab Defence | +3 |
| Slash Defence | +3 |
| Crush Defence | +3 |
| Magic Defence | +3 |
| Range Defence | +3 |
| Strength Bonus | +6 |
| Prayer Bonus | +3 |
+28
View File
@@ -0,0 +1,28 @@
# Amulet of glory(4)
*A dragonstone amulet with 4 magic charges.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | Yes |
| **Weight** | 10g |
| **Value** | 17625 gp |
| **Equipable** | Yes — Front |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | +10 |
| Slash Attack | +10 |
| Crush Attack | +10 |
| Magic Attack | +10 |
| Range Attack | +10 |
| Stab Defence | +3 |
| Slash Defence | +3 |
| Crush Defence | +3 |
| Magic Defence | +3 |
| Range Defence | +3 |
| Strength Bonus | +6 |
| Prayer Bonus | +3 |
+28
View File
@@ -0,0 +1,28 @@
# Amulet of glory
*A very powerful dragonstone amulet.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | Yes |
| **Weight** | 10g |
| **Value** | 17625 gp |
| **Equipable** | Yes — Front |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | +10 |
| Slash Attack | +10 |
| Crush Attack | +10 |
| Magic Attack | +10 |
| Range Attack | +10 |
| Stab Defence | +3 |
| Slash Defence | +3 |
| Crush Defence | +3 |
| Magic Defence | +3 |
| Range Defence | +3 |
| Strength Bonus | +6 |
| Prayer Bonus | +3 |
+11
View File
@@ -0,0 +1,11 @@
# Amulet of holthion
*A mystical demonic amulet.*
| Detail | |
|---|---|
| **Type** | Quest Item |
| **Members** | Yes |
| **Weight** | 10g |
| **Value** | 0 gp |
| **Tradeable** | No |
+21
View File
@@ -0,0 +1,21 @@
# Amulet of magic
*An enchanted sapphire amulet of magic.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 10g |
| **Value** | 900 gp |
| **Equipable** | Yes — Front |
## Combat Stats
| Stat | Value |
|------|-------|
| Magic Attack | +10 |
## Sources
- Sold by: [Davon's Amulet Store](../shops/davons-amulet-store.md) for 1080 gp
+11
View File
@@ -0,0 +1,11 @@
# Amulet of othanian
*A mystical demonic amulet.*
| Detail | |
|---|---|
| **Type** | Quest Item |
| **Members** | Yes |
| **Weight** | 10g |
| **Value** | 0 gp |
| **Tradeable** | No |
+32
View File
@@ -0,0 +1,32 @@
# Amulet of power
*An enchanted diamond amulet of power.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 10g |
| **Value** | 3525 gp |
| **Equipable** | Yes — Front |
## Combat Stats
| Stat | Value |
|------|-------|
| Stab Attack | +6 |
| Slash Attack | +6 |
| Crush Attack | +6 |
| Magic Attack | +6 |
| Range Attack | +6 |
| Stab Defence | +6 |
| Slash Defence | +6 |
| Crush Defence | +6 |
| Magic Defence | +6 |
| Range Defence | +6 |
| Strength Bonus | +6 |
| Prayer Bonus | +1 |
## Sources
- Sold by: [Davon's Amulet Store](../shops/davons-amulet-store.md) for 4230 gp
+21
View File
@@ -0,0 +1,21 @@
# Amulet of strength
*An enchanted ruby amulet.*
| Detail | |
|---|---|
| **Type** | Armour |
| **Members** | No |
| **Weight** | 10g |
| **Value** | 2025 gp |
| **Equipable** | Yes — Front |
## Combat Stats
| Stat | Value |
|------|-------|
| Strength Bonus | +10 |
## Sources
- Sold by: [Davon's Amulet Store](../shops/davons-amulet-store.md) for 2430 gp
+11
View File
@@ -0,0 +1,11 @@
# Ana in a barrel
*A mining barrel with Ana in it.*
| Detail | |
|---|---|
| **Type** | Quest Item |
| **Members** | Yes |
| **Weight** | 32kg |
| **Value** | 1 gp |
| **Tradeable** | No |
+10
View File
@@ -0,0 +1,10 @@
# Anchovies
*Some nicely cooked fish.*
| Detail | |
|---|---|
| **Type** | Food |
| **Members** | No |
| **Weight** | 100g |
| **Value** | 15 gp |
+10
View File
@@ -0,0 +1,10 @@
# Anchovy pizza
*A pizza with anchovies.*
| Detail | |
|---|---|
| **Type** | Food |
| **Members** | No |
| **Weight** | 900g |
| **Value** | 60 gp |
+11
View File
@@ -0,0 +1,11 @@
# Annas' print
*An imprint of Annas' fingerprint.*
| Detail | |
|---|---|
| **Type** | Quest Item |
| **Members** | Yes |
| **Weight** | 10g |
| **Value** | 0 gp |
| **Tradeable** | No |
+10
View File
@@ -0,0 +1,10 @@
# Antifire potion(1)
*1 dose of anti-firebreath potion.*
| Detail | |
|---|---|
| **Type** | Potion |
| **Members** | Yes |
| **Weight** | 20g |
| **Value** | 132 gp |
+10
View File
@@ -0,0 +1,10 @@
# Antifire potion(2)
*2 doses of anti-firebreath potion.*
| Detail | |
|---|---|
| **Type** | Potion |
| **Members** | Yes |
| **Weight** | 25g |
| **Value** | 198 gp |
+10
View File
@@ -0,0 +1,10 @@
# Antifire potion(3)
*3 doses of anti-firebreath potion.*
| Detail | |
|---|---|
| **Type** | Potion |
| **Members** | Yes |
| **Weight** | 30g |
| **Value** | 264 gp |
+10
View File
@@ -0,0 +1,10 @@
# Antifire potion(4)
*4 doses of anti-firebreath potion.*
| Detail | |
|---|---|
| **Type** | Potion |
| **Members** | Yes |
| **Weight** | 35g |
| **Value** | 330 gp |
+16
View File
@@ -0,0 +1,16 @@
# Antipoison(1)
*1 dose of antipoison potion.*
| Detail | |
|---|---|
| **Type** | Potion |
| **Members** | Yes |
| **Weight** | 20g |
| **Value** | 144 gp |
## Spawn Locations
| Area | Floor | Quantity |
|------|-------|----------|
| m38_49 | Ground | 1 |
+10
View File
@@ -0,0 +1,10 @@
# Antipoison(2)
*2 doses of antipoison potion.*
| Detail | |
|---|---|
| **Type** | Potion |
| **Members** | Yes |
| **Weight** | 25g |
| **Value** | 216 gp |
+15
View File
@@ -0,0 +1,15 @@
# Antipoison(3)
*3 doses of antipoison potion.*
| Detail | |
|---|---|
| **Type** | Potion |
| **Members** | Yes |
| **Weight** | 30g |
| **Value** | 288 gp |
## Sources
- Sold by: [Jiminua's Jungle Store](../shops/jiminuas-jungle-store.md) for 432 gp
- Dropped by: [Druid](../npcs/druid.md) (1/128)
+10
View File
@@ -0,0 +1,10 @@
# Antipoison(4)
*4 doses of antipoison potion.*
| Detail | |
|---|---|
| **Type** | Potion |
| **Members** | Yes |
| **Weight** | 35g |
| **Value** | 360 gp |
+14
View File
@@ -0,0 +1,14 @@
# Apple pie
*Mmm Apple pie.*
| Detail | |
|---|---|
| **Type** | Food |
| **Members** | No |
| **Weight** | 450g |
| **Value** | 30 gp |
## Sources
- Sold by: [Legends Guild General Store](../shops/legends-guild-general-store.md) for 46 gp
+18
View File
@@ -0,0 +1,18 @@
# Arcenia root
*The root of an Arcenia plant.*
| Detail | |
|---|---|
| **Type** | Quest Item |
| **Members** | Yes |
| **Weight** | 10g |
| **Value** | 0 gp |
| **Tradeable** | No |
## Spawn Locations
| Area | Floor | Quantity |
|------|-------|----------|
| m52_152 | Ground | 1 |
| m52_153 | Ground | 1 |
+11
View File
@@ -0,0 +1,11 @@
# Archery ticket
*I can exchange this for equipment.*
| Detail | |
|---|---|
| **Type** | Other |
| **Members** | Yes |
| **Weight** | 5g |
| **Value** | 25 gp |
| **Stackable** | Yes |
+11
View File
@@ -0,0 +1,11 @@
# Ardrigal mixture
*It's a mixture of Ardrigal and water, I need another ingredient to finish this potion.*
| Detail | |
|---|---|
| **Type** | Quest Item |
| **Members** | Yes |
| **Weight** | 25g |
| **Value** | 0 gp |
| **Tradeable** | No |
Loaded 100 of 2144 files, more files were not shown because too many files have changed in this diff. Show more