diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 83e253187..40dd8466d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,42 +1,45 @@ name: Unit Tests on: - push: - branches: - - master - pull_request: + push: + branches: + - master + pull_request: jobs: - test: - name: Tests - runs-on: ubuntu-latest - - steps: - - name: Checkout Project - uses: actions/checkout@v4 - - - run: corepack enable - - run: corepack install - - - name: Install NodeJS - uses: actions/setup-node@v4 - with: - cache: yarn - node-version: 20 - - - name: Restore CI Cache - uses: actions/cache@v4 - with: - path: node_modules - key: NODE-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - - - name: Install Dependencies - run: yarn --immutable - - - name: Fix package.json - run: npx biome check --write package.json - - - name: Test - run: | - yarn test - npm i -g dpdm && dpdm --exit-code circular:1 --progress=false --warning=false --tree=false ./dist/index.js + test: + name: Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout Project + uses: actions/checkout@v4 + + - run: corepack enable + - run: corepack install + + - name: Install NodeJS + uses: actions/setup-node@v4 + with: + cache: yarn + node-version: 20 + + - name: Restore CI Cache + uses: actions/cache@v4 + with: + path: node_modules + key: NODE-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + + - name: Install Dependencies + run: yarn --immutable + + - name: Fix package.json + run: npx biome check --write package.json + + - name: Build + run: tsc -p src + + - name: Test + run: | + yarn test + npm i -g dpdm && dpdm --exit-code circular:1 --progress=false --warning=false --tree=false ./dist/index.js diff --git a/package.json b/package.json index 6a0f0808c..6a1cb6bd7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oldschooljs", - "version": "2.5.14", + "version": "2.6.0", "description": "Allows you to interact with the OSRS Hiscores, Wiki, Items, & more.", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/scripts/enum.ts b/scripts/enum.ts index 85fae1a26..db810028b 100644 --- a/scripts/enum.ts +++ b/scripts/enum.ts @@ -4,6 +4,14 @@ import { Items, Monsters } from "../src"; import { USELESS_ITEMS } from "../src/structures/Items"; import { moidLink } from "./prepareItems"; +export function safeItemName(itemName: string) { + let key = itemName; + key = key.replace("3rd", "third"); + key = key.replace(/[^\w\s]|_/g, ""); + key = key.replace(/\s+/g, "_"); + key = key.toUpperCase(); + return key; +} const exitingKeys = new Set(); const duplicates = new Set(); let str = "export enum EItem {"; @@ -32,11 +40,8 @@ outer: for (const item of Items.values()) { if (USELESS_ITEMS.includes(item.id)) { continue; } - let key = item.wiki_name ?? item.name; - key = key.replace("3rd", "third"); - key = key.replace(/[^\w\s]|_/g, ""); - key = key.replace(/\s+/g, "_"); - key = key.toUpperCase(); + const key = safeItemName(item.wiki_name ?? item.name); + if (exitingKeys.has(key)) { duplicates.add(item.id); continue; diff --git a/src/EItem.ts b/src/EItem.ts index eed71344d..17468a470 100644 --- a/src/EItem.ts +++ b/src/EItem.ts @@ -5767,8 +5767,12 @@ export enum EItem { FLOWER_CROWN_GENDERQUEER = 27151, FLOWER_CROWN_LESBIAN = 27153, FLOWER_CROWN_GAY = 27155, + GUTHIX_CHAPS_LAST_MAN_STANDING = 27180, + ZAMORAK_CHAPS_LAST_MAN_STANDING = 27181, + SARADOMIN_CHAPS_LAST_MAN_STANDING = 27182, LIGHT_BALLISTA_LAST_MAN_STANDING = 27188, VERACS_FLAIL_LAST_MAN_STANDING = 27189, + ANCESTRAL_ROBE_BOTTOM_LAST_MAN_STANDING = 27194, MENAPHITE_REMEDY_4_DOSE = 27202, MENAPHITE_REMEDY_3_DOSE = 27205, MENAPHITE_REMEDY_2_DOSE = 27208, @@ -6330,4 +6334,61 @@ export enum EItem { BLIGHTED_OVERLOAD_1_DOSE = 29640, CHITIN = 29643, GUTHIXIAN_TEMPLE_TELEPORT = 29684, + DNI23_TORSO_LIGHTBUTTONS = 29686, + DNI23_TORSO_DARKBUTTONS_DARK_BUTTONS = 29688, + DNI23_TORSO_DARKBUTTONS_JACKET = 29690, + DNI23_TORSO_SHIRT = 29692, + DNI23_TORSO_STITCHING = 29694, + DNI23_TORSO_TWOTONED = 29696, + DNI23_TORSO_PRINCELY = 29698, + DNI23_TORSO_RIPPEDWESKIT_RIPPED_WESKIT = 29700, + DNI23_TORSO_RIPPEDWESKIT_TORN_WESKIT = 29702, + DNI23_TORSO_CROPTOPS = 29704, + DNI23_TORSO_POLONECK = 29706, + DNI23_TORSO_SIMPLE = 29708, + DNI23_TORSO_FRILLY = 29710, + DNI23_TORSO_CORSETRY = 29712, + DNI23_TORSO_BODICE = 29714, + DNI23_ARMS_THIN = 29716, + DNI23_ARMS_SHOULDERPADS = 29718, + DNI23_ARMS_THICKSTRIPE = 29720, + DNI23_ARMS_LOOSESLEEVES = 29722, + DNI23_ARMS_PRINCELY = 29724, + DNI23_ARMS_TATTYLONG = 29726, + DNI23_ARMS_RIPPED = 29728, + DNI23_ARMS_BARE = 29730, + DNI23_ARMS_FRILLY = 29732, + DNI23_ARMS_TATTYSHORT = 29734, + DNI23_ARMS_BARESHOULDERS = 29736, + DNI23_LEGS_SHORTS = 29738, + DNI23_LEGS_BEACH = 29740, + DNI23_LEGS_PRINCELY = 29742, + DNI23_LEGS_LEGGINGS = 29744, + DNI23_LEGS_SIDESTRIPES = 29746, + DNI23_LEGS_RIPPED = 29748, + DNI23_LEGS_PATCHED = 29750, + DNI23_LEGS_SKIRT = 29752, + DNI23_LEGS_LONGSKIRT = 29754, + DNI23_LEGS_LONGNARROWSKIRT = 29756, + DNI23_LEGS_SHORTSKIRT = 29758, + DNI23_LEGS_LAYERED = 29760, + DNI23_LEGS_SASHDOTS = 29762, + DNI23_LEGS_BIGHEM = 29764, + DNI23_LEGS_SASHTROUSERS = 29766, + DNI23_LEGS_PATTERNED = 29768, + DNI23_LEGS_TORNSKIRT = 29770, + DNI23_LEGS_PATCHEDSKIRT = 29772, + AMYS_SAW_OFFHAND = 29774, + IMCANDO_HAMMER_OFFHAND = 29775, + BRUMA_TORCH_OFFHAND = 29777, + SPIDER_CAVE_TELEPORT = 29782, + ARAXYTE_VENOM_SACK = 29784, + NOXIOUS_HALBERD = 29796, + AMULET_OF_RANCOUR = 29801, + AMULET_OF_RANCOUR_S = 29804, + ARANEA_BOOTS = 29806, + ARAXYTE_SLAYER_HELMET = 29816, + ARAXYTE_SLAYER_HELMET_I_NIGHTMARE_ZONE = 29818, + ARAXYTE_SLAYER_HELMET_I_SOUL_WARS = 29820, + ARAXYTE_SLAYER_HELMET_I_EMIRS_ARENA = 29822, } diff --git a/src/data/items/item_data.json b/src/data/items/item_data.json index 5f05e5f4e..27cb76173 100644 --- a/src/data/items/item_data.json +++ b/src/data/items/item_data.json @@ -75016,7 +75016,7 @@ "lowalch": 0, "highalch": 0, "weight": 0.907, - "buy_limit": 200, + "buy_limit": 100, "release_date": "2005-07-11", "examine": "This sapling is ready to be replanted in a tree patch.", "wiki_name": "Magic sapling", @@ -113992,7 +113992,7 @@ "cost": 1, "lowalch": 0, "highalch": 0, - "buy_limit": 10000, + "buy_limit": 15000, "release_date": "2006-05-31", "examine": "A teleport to Varrock.", "wiki_name": "Varrock teleport (tablet)", @@ -114094,7 +114094,7 @@ "cost": 1, "lowalch": 0, "highalch": 0, - "buy_limit": 10000, + "buy_limit": 15000, "release_date": "2006-05-31", "examine": "A teleport to one's own house.", "wiki_name": "Teleport to house (tablet)", @@ -119784,7 +119784,6 @@ "id": 8648, "name": "Mahogany telescope", "members": true, - "tradeable": true, "noteable": true, "cost": 10, "lowalch": 4, @@ -152573,7 +152572,7 @@ "lowalch": 0, "highalch": 0, "release_date": "2007-05-29", - "examine": "I can use this to claim a reward from the King, if I get the other half..", + "examine": "I can use this to claim a reward from the King, if I get the other half.", "wiki_name": "Half certificate (Right)", "wiki_url": "https://oldschool.runescape.wiki/w/Half_certificate#Right", "price": 0 @@ -210924,7 +210923,7 @@ "lowalch": 460, "highalch": 690, "weight": 0.25, - "buy_limit": 10000, + "buy_limit": 15000, "release_date": "2017-02-02", "examine": "Allows one slayer kill to count as two.", "wiki_name": "Expeditious bracelet", @@ -230055,7 +230054,7 @@ "requirements": null }, "weapon": { - "attack_speed": 5, + "attack_speed": 4, "weapon_type": "unarmed", "stances": [ { @@ -230116,7 +230115,7 @@ "requirements": null }, "weapon": { - "attack_speed": 5, + "attack_speed": 4, "weapon_type": "unarmed", "stances": [ { @@ -230177,7 +230176,7 @@ "requirements": null }, "weapon": { - "attack_speed": 5, + "attack_speed": 4, "weapon_type": "unarmed", "stances": [ { @@ -230290,7 +230289,7 @@ "lowalch": 0, "highalch": 0, "weight": 0.907, - "buy_limit": 200, + "buy_limit": 100, "release_date": "2019-01-10", "examine": "This sapling is ready to be replanted in a Redwood patch.", "wiki_name": "Redwood sapling", @@ -232272,7 +232271,7 @@ "requirements": null }, "weapon": { - "attack_speed": 5, + "attack_speed": 4, "weapon_type": "unarmed", "stances": [ { @@ -252660,7 +252659,7 @@ "cost": 30, "lowalch": 12, "highalch": 18, - "buy_limit": 10000, + "buy_limit": 15000, "release_date": "2020-04-23", "examine": "Enough power for an Entangle, blighted so it can be used only in the Wilderness.", "wiki_name": "Blighted entangle sack", @@ -272963,6 +272962,7 @@ "lowalch": 0, "highalch": 0, "weight": 2.895, + "buy_limit": 8, "release_date": "2023-07-26", "examine": "An ancient mage's robe top.", "wiki_name": "Virtus robe top", @@ -273000,6 +273000,7 @@ "lowalch": 0, "highalch": 0, "weight": 3.79, + "buy_limit": 8, "release_date": "2023-07-26", "examine": "An ancient mage's robe bottom.", "wiki_name": "Virtus robe bottoms", @@ -284454,6 +284455,42 @@ }, "price": 0 }, + "27180": { + "id": 27180, + "name": "Guthix chaps", + "equipable": true, + "cost": 10, + "weight": 5, + "examine": "Guthix blessed dragonhide chaps.", + "wiki_name": "Guthix chaps (Last Man Standing)", + "wiki_url": "https://oldschool.runescape.wiki/w/Guthix_chaps_(Last_Man_Standing)", + "equipment": {}, + "price": 0 + }, + "27181": { + "id": 27181, + "name": "Zamorak chaps", + "equipable": true, + "cost": 10, + "weight": 5, + "examine": "Zamorak blessed dragonhide chaps.", + "wiki_name": "Zamorak chaps (Last Man Standing)", + "wiki_url": "https://oldschool.runescape.wiki/w/Zamorak_chaps_(Last_Man_Standing)", + "equipment": {}, + "price": 0 + }, + "27182": { + "id": 27182, + "name": "Saradomin chaps", + "equipable": true, + "cost": 10, + "weight": 5, + "examine": "Saradomin blessed dragonhide chaps.", + "wiki_name": "Saradomin chaps (Last Man Standing)", + "wiki_url": "https://oldschool.runescape.wiki/w/Saradomin_chaps_(Last_Man_Standing)", + "equipment": {}, + "price": 0 + }, "27187": { "id": 27187, "name": "Bow of faerdhinen", @@ -284655,6 +284692,36 @@ "wiki_url": "https://oldschool.runescape.wiki/w/Verac's_brassard_(Last_Man_Standing)", "price": 0 }, + "27194": { + "id": 27194, + "name": "Ancestral robe bottom", + "equipable": true, + "equipable_by_player": true, + "cost": 55, + "weight": 18.14, + "examine": "The robe bottoms of a powerful sorceress from a bygone era.", + "wiki_name": "Ancestral robe bottom (Last Man Standing)", + "wiki_url": "https://oldschool.runescape.wiki/w/Ancestral_robe_bottom_(Last_Man_Standing)", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 26, + "attack_ranged": -7, + "defence_stab": 27, + "defence_slash": 24, + "defence_crush": 30, + "defence_magic": 20, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 4, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, "27202": { "id": 27202, "name": "Menaphite remedy(4)", @@ -307342,6 +307409,7 @@ "cost": 31, "lowalch": 12, "highalch": 18, + "buy_limit": 11000, "release_date": "2024-03-20", "examine": "Bolts made from the antlers of a sunlight antelope.", "wiki_name": "Sunlight antler bolts", @@ -309648,6 +309716,7 @@ "lowalch": 12000, "highalch": 18000, "weight": 1.814, + "buy_limit": 15, "release_date": "2024-03-20", "examine": "Are these explosive?", "wiki_name": "Sulphur blades", @@ -309829,6 +309898,7 @@ "lowalch": 0, "highalch": 0, "weight": 0.34, + "buy_limit": 13000, "release_date": "2024-03-20", "examine": "I should probably cook this first.", "wiki_name": "Raw pyre fox", @@ -310216,6 +310286,7 @@ "lowalch": 4, "highalch": 6, "weight": 3, + "buy_limit": 18000, "release_date": "2024-03-20", "examine": "I can make something out of this.", "wiki_name": "Moonlight antelope fur", @@ -314537,8 +314608,6 @@ "equipable": true, "equipable_by_player": true, "cost": 80000, - "lowalch": 32000, - "highalch": 48000, "weight": 0.453, "release_date": "2024-07-17", "examine": "A cape from the almighty god Zamorak, imbued with great power.", @@ -314562,7 +314631,9 @@ "slot": "cape", "requirements": null }, - "price": 0 + "price": 0, + "lowalch": 32000, + "highalch": 48000 }, "29615": { "id": 29615, @@ -314571,8 +314642,6 @@ "equipable": true, "equipable_by_player": true, "cost": 80000, - "lowalch": 32000, - "highalch": 48000, "weight": 0.453, "release_date": "2024-07-17", "examine": "A cape from the almighty god Guthix, imbued with great power.", @@ -314596,7 +314665,9 @@ "slot": "cape", "requirements": null }, - "price": 0 + "price": 0, + "lowalch": 32000, + "highalch": 48000 }, "29617": { "id": 29617, @@ -314605,8 +314676,6 @@ "equipable": true, "equipable_by_player": true, "cost": 80000, - "lowalch": 32000, - "highalch": 48000, "weight": 0.453, "release_date": "2024-07-17", "examine": "A cape from the almighty god Saradomin, imbued with great power.", @@ -314630,7 +314699,9 @@ "slot": "cape", "requirements": null }, - "price": 0 + "price": 0, + "lowalch": 32000, + "highalch": 48000 }, "29619": { "id": 29619, @@ -314694,6 +314765,7 @@ "lowalch": 10000, "highalch": 15000, "weight": 0.1, + "buy_limit": 6, "release_date": "2024-07-17", "examine": "Take this to Nigel to give the imbued Saradomin, Guthix or Zamorak Cape a cosmetic theme from Deadman: Armageddon.", "wiki_name": "Armageddon cape fabric", @@ -315136,11 +315208,2029 @@ "cost": 10, "lowalch": 4, "highalch": 6, - "weight": 0.001, "release_date": "2024-07-24", "examine": "Teleports you to the Ancient Guthixian Temple.", "wiki_name": "Guthixian temple teleport", "wiki_url": "https://oldschool.runescape.wiki/w/Guthixian_temple_teleport", "price": 10784 + }, + "29686": { + "id": 29686, + "name": "Dni23 torso lightbuttons", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso lightbuttons", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_lightbuttons", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29688": { + "id": 29688, + "name": "Dni23 torso darkbuttons", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "lowalch": 0, + "highalch": 0, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso darkbuttons (Dark Buttons)", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_darkbuttons#Dark_Buttons", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29690": { + "id": 29690, + "name": "Dni23 torso darkbuttons", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "lowalch": 0, + "highalch": 0, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso darkbuttons (Jacket)", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_darkbuttons#Jacket", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29692": { + "id": 29692, + "name": "Dni23 torso shirt", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso shirt", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_shirt", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29694": { + "id": 29694, + "name": "Dni23 torso stitching", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso stitching", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_stitching", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29696": { + "id": 29696, + "name": "Dni23 torso twotoned", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso twotoned", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_twotoned", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29698": { + "id": 29698, + "name": "Dni23 torso princely", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso princely", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_princely", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29700": { + "id": 29700, + "name": "Dni23 torso rippedweskit", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "lowalch": 0, + "highalch": 0, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso rippedweskit (Ripped Weskit)", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_rippedweskit#Ripped_Weskit", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29702": { + "id": 29702, + "name": "Dni23 torso rippedweskit", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "lowalch": 0, + "highalch": 0, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso rippedweskit (Torn Weskit)", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_rippedweskit#Torn_Weskit", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29704": { + "id": 29704, + "name": "Dni23 torso croptops", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso croptops", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_croptops", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29706": { + "id": 29706, + "name": "Dni23 torso poloneck", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso poloneck", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_poloneck", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29708": { + "id": 29708, + "name": "Dni23 torso simple", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso simple", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_simple", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29710": { + "id": 29710, + "name": "Dni23 torso frilly", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso frilly", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_frilly", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29712": { + "id": 29712, + "name": "Dni23 torso corsetry", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso corsetry", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_corsetry", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29714": { + "id": 29714, + "name": "Dni23 torso bodice", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso bodice", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_bodice", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29716": { + "id": 29716, + "name": "Dni23 arms thin", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms thin", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_thin", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29718": { + "id": 29718, + "name": "Dni23 arms shoulderpads", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms shoulderpads", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_shoulderpads", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29720": { + "id": 29720, + "name": "Dni23 arms thickstripe", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms thickstripe", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_thickstripe", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29722": { + "id": 29722, + "name": "Dni23 arms loosesleeves", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms loosesleeves", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_loosesleeves", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29724": { + "id": 29724, + "name": "Dni23 arms princely", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms princely", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_princely", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29726": { + "id": 29726, + "name": "Dni23 arms tattylong", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms tattylong", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_tattylong", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29728": { + "id": 29728, + "name": "Dni23 arms ripped", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms ripped", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_ripped", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29730": { + "id": 29730, + "name": "Dni23 arms bare", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms bare", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_bare", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29732": { + "id": 29732, + "name": "Dni23 arms frilly", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms frilly", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_frilly", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29734": { + "id": 29734, + "name": "Dni23 arms tattyshort", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms tattyshort", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_tattyshort", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29736": { + "id": 29736, + "name": "Dni23 arms bareshoulders", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms bareshoulders", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_bareshoulders", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29738": { + "id": 29738, + "name": "Dni23 legs shorts", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs shorts", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_shorts", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29740": { + "id": 29740, + "name": "Dni23 legs beach", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs beach", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_beach", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29742": { + "id": 29742, + "name": "Dni23 legs princely", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs princely", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_princely", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29744": { + "id": 29744, + "name": "Dni23 legs leggings", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs leggings", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_leggings", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29746": { + "id": 29746, + "name": "Dni23 legs sidestripes", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs sidestripes", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_sidestripes", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29748": { + "id": 29748, + "name": "Dni23 legs ripped", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs ripped", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_ripped", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29750": { + "id": 29750, + "name": "Dni23 legs patched", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs patched", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_patched", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29752": { + "id": 29752, + "name": "Dni23 legs skirt", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs skirt", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_skirt", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29754": { + "id": 29754, + "name": "Dni23 legs longskirt", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs longskirt", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_longskirt", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29756": { + "id": 29756, + "name": "Dni23 legs longnarrowskirt", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs longnarrowskirt", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_longnarrowskirt", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29758": { + "id": 29758, + "name": "Dni23 legs shortskirt", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs shortskirt", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_shortskirt", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29760": { + "id": 29760, + "name": "Dni23 legs layered", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs layered", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_layered", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29762": { + "id": 29762, + "name": "Dni23 legs sashdots", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs sashdots", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_sashdots", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29764": { + "id": 29764, + "name": "Dni23 legs bighem", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs bighem", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_bighem", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29766": { + "id": 29766, + "name": "Dni23 legs sashtrousers", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs sashtrousers", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_sashtrousers", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29768": { + "id": 29768, + "name": "Dni23 legs patterned", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs patterned", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_patterned", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29770": { + "id": 29770, + "name": "Dni23 legs tornskirt", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs tornskirt", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_tornskirt", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29772": { + "id": 29772, + "name": "Dni23 legs patchedskirt", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs patchedskirt", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_patchedskirt", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29774": { + "id": 29774, + "name": "Amy's saw (off-hand)", + "members": true, + "equipable": true, + "equipable_by_player": true, + "cost": 280, + "weight": 0.8, + "release_date": "2024-08-14", + "examine": "She saw scope for improvement.", + "wiki_name": "Amy's saw (off-hand)", + "wiki_url": "https://oldschool.runescape.wiki/w/Amy's_saw_(off-hand)", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "shield", + "requirements": null + }, + "price": 0 + }, + "29775": { + "id": 29775, + "name": "Imcando hammer (off-hand)", + "members": true, + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "lowalch": 0, + "highalch": 0, + "weight": 2, + "release_date": "2024-08-14", + "examine": "It's good for holding and hitting things!", + "wiki_name": "Imcando hammer (off-hand)", + "wiki_url": "https://oldschool.runescape.wiki/w/Imcando_hammer_(off-hand)", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "shield", + "requirements": null + }, + "price": 0 + }, + "29777": { + "id": 29777, + "name": "Bruma torch (off-hand)", + "members": true, + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "lowalch": 0, + "highalch": 0, + "weight": 0.003, + "release_date": "2024-08-14", + "examine": "A torch made from the wood of the magical Bruma tree.", + "wiki_name": "Bruma torch (off-hand)", + "wiki_url": "https://oldschool.runescape.wiki/w/Bruma_torch_(off-hand)", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "shield", + "requirements": null + }, + "price": 0 + }, + "29781": { + "id": 29781, + "name": "Coagulated venom", + "members": true, + "cost": 10000, + "weight": 0.19, + "release_date": "2024-08-28", + "examine": "Looks nourishing, to the right beast.", + "wiki_name": "Coagulated venom", + "wiki_url": "https://oldschool.runescape.wiki/w/Coagulated_venom", + "price": 0 + }, + "29782": { + "id": 29782, + "name": "Spider cave teleport", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "stackable": true, + "cost": 10, + "lowalch": 4, + "highalch": 6, + "release_date": "2024-08-28", + "examine": "Teleports you to Morytania Spider Cave.", + "wiki_name": "Spider cave teleport", + "wiki_url": "https://oldschool.runescape.wiki/w/Spider_cave_teleport", + "price": 37695 + }, + "29784": { + "id": 29784, + "name": "Araxyte venom sack", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "stackable": true, + "cost": 500, + "lowalch": 200, + "highalch": 300, + "buy_limit": 11000, + "release_date": "2024-08-28", + "examine": "Looks spicy!", + "wiki_name": "Araxyte venom sack", + "wiki_url": "https://oldschool.runescape.wiki/w/Araxyte_venom_sack", + "price": 1987 + }, + "29786": { + "id": 29786, + "name": "Jar of venom", + "members": true, + "cost": 200000, + "lowalch": 80000, + "highalch": 120000, + "weight": 0.04, + "release_date": "2024-08-28", + "examine": "Handy that this comes in an easily transportable jar.", + "wiki_name": "Jar of venom", + "wiki_url": "https://oldschool.runescape.wiki/w/Jar_of_venom", + "price": 0 + }, + "29788": { + "id": 29788, + "name": "Araxyte head", + "members": true, + "cost": 60000, + "lowalch": 24000, + "highalch": 36000, + "weight": 10, + "release_date": "2024-08-28", + "examine": "Do spiders even have heads?", + "wiki_name": "Araxyte head", + "wiki_url": "https://oldschool.runescape.wiki/w/Araxyte_head", + "price": 0 + }, + "29790": { + "id": 29790, + "name": "Noxious point", + "members": true, + "cost": 160000, + "lowalch": 64000, + "highalch": 96000, + "weight": 0.907, + "release_date": "2024-08-28", + "examine": "A powerful piece of venomous weaponry.", + "wiki_name": "Noxious point", + "wiki_url": "https://oldschool.runescape.wiki/w/Noxious_point", + "price": 0 + }, + "29792": { + "id": 29792, + "name": "Noxious blade", + "members": true, + "cost": 160000, + "lowalch": 64000, + "highalch": 96000, + "weight": 0.907, + "release_date": "2024-08-28", + "examine": "A powerful piece of venomous weaponry.", + "wiki_name": "Noxious blade", + "wiki_url": "https://oldschool.runescape.wiki/w/Noxious_blade", + "price": 0 + }, + "29794": { + "id": 29794, + "name": "Noxious pommel", + "members": true, + "cost": 160000, + "lowalch": 64000, + "highalch": 96000, + "weight": 0.907, + "release_date": "2024-08-28", + "examine": "A powerful piece of venomous weaponry.", + "wiki_name": "Noxious pommel", + "wiki_url": "https://oldschool.runescape.wiki/w/Noxious_pommel", + "price": 0 + }, + "29796": { + "id": 29796, + "name": "Noxious halberd", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "noteable": true, + "equipable": true, + "equipable_by_player": true, + "equipable_weapon": true, + "cost": 500000, + "lowalch": 200000, + "highalch": 300000, + "weight": 2.721, + "release_date": "2024-08-28", + "examine": "A venomous halberd created from the parts of an araxyte.", + "wiki_name": "Noxious halberd", + "wiki_url": "https://oldschool.runescape.wiki/w/Noxious_halberd", + "equipment": { + "attack_stab": 80, + "attack_slash": 132, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 142, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "2h", + "requirements": null + }, + "weapon": { + "attack_speed": 5, + "weapon_type": "polearm", + "stances": [ + { + "combat_style": "jab", + "attack_type": "stab", + "attack_style": "controlled", + "experience": "shared", + "boosts": null + }, + { + "combat_style": "swipe", + "attack_type": "slash", + "attack_style": "aggressive", + "experience": "strength", + "boosts": null + }, + { + "combat_style": "fend", + "attack_type": "stab", + "attack_style": "defensive", + "experience": "defence", + "boosts": null + } + ] + }, + "price": 25203118 + }, + "29799": { + "id": 29799, + "name": "Araxyte fang", + "members": true, + "cost": 200000, + "lowalch": 80000, + "highalch": 120000, + "weight": 0.001, + "release_date": "2024-08-28", + "examine": "A still dripping venomous fang from a powerful araxyte.", + "wiki_name": "Araxyte fang", + "wiki_url": "https://oldschool.runescape.wiki/w/Araxyte_fang", + "price": 0 + }, + "29801": { + "id": 29801, + "name": "Amulet of rancour", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "noteable": true, + "equipable": true, + "equipable_by_player": true, + "cost": 400000, + "lowalch": 160000, + "highalch": 240000, + "weight": 0.012, + "release_date": "2024-08-28", + "examine": "A very powerful zenyte amulet, infused with araxyte venom.", + "wiki_name": "Amulet of rancour", + "wiki_url": "https://oldschool.runescape.wiki/w/Amulet_of_rancour", + "equipment": { + "attack_stab": 25, + "attack_slash": 25, + "attack_crush": 25, + "attack_magic": -6, + "attack_ranged": -8, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 12, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 2, + "slot": "neck", + "requirements": null + }, + "price": 88500000 + }, + "29804": { + "id": 29804, + "name": "Amulet of rancour (s)", + "members": true, + "equipable": true, + "equipable_by_player": true, + "cost": 400000, + "lowalch": 160000, + "highalch": 240000, + "weight": 0.011, + "release_date": "2024-08-28", + "examine": "A very powerful zenyte amulet, infused with araxyte venom.", + "wiki_name": "Amulet of rancour (s)", + "wiki_url": "https://oldschool.runescape.wiki/w/Amulet_of_rancour_(s)", + "equipment": { + "attack_stab": 25, + "attack_slash": 25, + "attack_crush": 25, + "attack_magic": -6, + "attack_ranged": -8, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 12, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 2, + "slot": "neck", + "requirements": null + }, + "price": 0 + }, + "29806": { + "id": 29806, + "name": "Aranea boots", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "noteable": true, + "equipable": true, + "equipable_by_player": true, + "cost": 150000, + "lowalch": 60000, + "highalch": 90000, + "weight": 1, + "buy_limit": 8, + "release_date": "2024-08-28", + "examine": "Boots that are surrounded with spider folicles. Great web resistance!", + "wiki_name": "Aranea boots", + "wiki_url": "https://oldschool.runescape.wiki/w/Aranea_boots", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 5, + "attack_ranged": 6, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 4, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 1, + "slot": "feet", + "requirements": null + }, + "price": 15322207 + }, + "29809": { + "id": 29809, + "name": "Venom-riddled note", + "members": true, + "cost": 1, + "lowalch": 0, + "highalch": 0, + "weight": 0.003, + "release_date": "2024-08-28", + "examine": "A tatty note found inside the araxyte spider cave.", + "wiki_name": "Venom-riddled note", + "wiki_url": "https://oldschool.runescape.wiki/w/Venom-riddled_note", + "price": 0 + }, + "29816": { + "id": 29816, + "name": "Araxyte slayer helmet", + "members": true, + "equipable": true, + "equipable_by_player": true, + "cost": 40000, + "lowalch": 16000, + "highalch": 24000, + "weight": 2.267, + "release_date": "2024-08-28", + "examine": "You don't want to wear it inside-out.", + "wiki_name": "Araxyte slayer helmet", + "wiki_url": "https://oldschool.runescape.wiki/w/Araxyte_slayer_helmet", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": -6, + "attack_ranged": -2, + "defence_stab": 30, + "defence_slash": 32, + "defence_crush": 27, + "defence_magic": -1, + "defence_ranged": 30, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "head", + "requirements": null + }, + "price": 0 + }, + "29818": { + "id": 29818, + "name": "Araxyte slayer helmet (i)", + "members": true, + "equipable": true, + "equipable_by_player": true, + "cost": 90000, + "lowalch": 36000, + "highalch": 54000, + "weight": 2.267, + "release_date": "2024-08-28", + "examine": "You really don't want to wear it inside-out.", + "wiki_name": "Araxyte slayer helmet (i) (Nightmare Zone)", + "wiki_url": "https://oldschool.runescape.wiki/w/Araxyte_slayer_helmet_(i)#Nightmare_Zone", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 3, + "attack_ranged": 3, + "defence_stab": 30, + "defence_slash": 32, + "defence_crush": 27, + "defence_magic": 10, + "defence_ranged": 30, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "head", + "requirements": null + }, + "price": 0 + }, + "29820": { + "id": 29820, + "name": "Araxyte slayer helmet (i)", + "members": true, + "equipable": true, + "equipable_by_player": true, + "cost": 90000, + "lowalch": 36000, + "highalch": 54000, + "weight": 2.267, + "release_date": "2024-08-28", + "examine": "You really don't want to wear it inside-out.", + "wiki_name": "Araxyte slayer helmet (i) (Soul Wars)", + "wiki_url": "https://oldschool.runescape.wiki/w/Araxyte_slayer_helmet_(i)#Soul_Wars", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 3, + "attack_ranged": 3, + "defence_stab": 30, + "defence_slash": 32, + "defence_crush": 27, + "defence_magic": 10, + "defence_ranged": 30, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "head", + "requirements": null + }, + "price": 0 + }, + "29822": { + "id": 29822, + "name": "Araxyte slayer helmet (i)", + "members": true, + "equipable": true, + "equipable_by_player": true, + "cost": 90000, + "lowalch": 36000, + "highalch": 54000, + "weight": 2.267, + "release_date": "2024-08-28", + "examine": "You really don't want to wear it inside-out.", + "wiki_name": "Araxyte slayer helmet (i) (Emir's Arena)", + "wiki_url": "https://oldschool.runescape.wiki/w/Araxyte_slayer_helmet_(i)#Emir's_Arena", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 3, + "attack_ranged": 3, + "defence_stab": 30, + "defence_slash": 32, + "defence_crush": 27, + "defence_magic": 10, + "defence_ranged": 30, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "head", + "requirements": null + }, + "price": 0 + }, + "29824": { + "id": 29824, + "name": "Extended anti-venom+(4)", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "noteable": true, + "cost": 444, + "lowalch": 177, + "highalch": 266, + "weight": 0.035, + "release_date": "2024-08-28", + "examine": "4 doses of extended super antivenom potion.", + "wiki_name": "Extended anti-venom+ (4 dose)", + "wiki_url": "https://oldschool.runescape.wiki/w/Extended_anti-venom+#4_dose", + "price": 20360 + }, + "29827": { + "id": 29827, + "name": "Extended anti-venom+(3)", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "noteable": true, + "cost": 333, + "lowalch": 133, + "highalch": 199, + "weight": 0.035, + "release_date": "2024-08-28", + "examine": "3 doses of extended super antivenom potion.", + "wiki_name": "Extended anti-venom+ (3 dose)", + "wiki_url": "https://oldschool.runescape.wiki/w/Extended_anti-venom+#3_dose", + "price": 14678 + }, + "29830": { + "id": 29830, + "name": "Extended anti-venom+(2)", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "noteable": true, + "cost": 222, + "lowalch": 88, + "highalch": 133, + "weight": 0.035, + "release_date": "2024-08-28", + "examine": "2 doses of extended super antivenom potion.", + "wiki_name": "Extended anti-venom+ (2 dose)", + "wiki_url": "https://oldschool.runescape.wiki/w/Extended_anti-venom+#2_dose", + "price": 10282 + }, + "29833": { + "id": 29833, + "name": "Extended anti-venom+(1)", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "noteable": true, + "cost": 111, + "lowalch": 44, + "highalch": 66, + "weight": 0.035, + "release_date": "2024-08-28", + "examine": "1 dose of extended super antivenom potion.", + "wiki_name": "Extended anti-venom+ (1 dose)", + "wiki_url": "https://oldschool.runescape.wiki/w/Extended_anti-venom+#1_dose", + "price": 5838 + }, + "29836": { + "id": 29836, + "name": "Nid", + "members": true, + "cost": 1, + "weight": 0.015, + "release_date": "2024-08-28", + "examine": "Looks like a respectable gentleman.", + "wiki_name": "Nid (Nid)", + "wiki_url": "https://oldschool.runescape.wiki/w/Nid#Nid", + "price": 0 + }, + "29838": { + "id": 29838, + "name": "Rax", + "members": true, + "cost": 1, + "lowalch": 0, + "highalch": 0, + "weight": 0.015, + "release_date": "2024-08-28", + "examine": "Really hope she doesn't crawl into my mouth while I sleep.", + "wiki_name": "Nid (Rax)", + "wiki_url": "https://oldschool.runescape.wiki/w/Nid#Rax", + "price": 0 } } diff --git a/src/data/monsters_data.json b/src/data/monsters_data.json index 8c1f99ab4..a1dc04225 100644 --- a/src/data/monsters_data.json +++ b/src/data/monsters_data.json @@ -372,7 +372,7 @@ "immuneToVenom": false, "attributes": [], "category": ["dogs"], - "examineText": "Looks like it's got Rabies!", + "examineText": "Looks like it's got Rabies!sic", "wikiName": "Wild dog (Normal)", "wikiURL": "https://oldschool.runescape.wiki/w/Wild_dog#Normal", "attackLevel": 53, @@ -3123,7 +3123,7 @@ "members": true, "combatLevel": 333, "hitpoints": 255, - "maxHit": 33, + "maxHit": 31, "attackType": ["stab", "ranged", "magic"], "attackSpeed": 4, "aggressive": true, @@ -3570,7 +3570,7 @@ "poisonous": false, "immuneToPoison": true, "immuneToVenom": true, - "attributes": ["spectral", "undead"], + "attributes": ["shade", "spectral", "undead"], "category": ["shades"], "examineText": "The shadowy remains of a long departed soul.", "wikiName": "Loar Shade (Shade)", @@ -3606,7 +3606,7 @@ "maxHit": 6, "attackType": ["crush"], "attackSpeed": 4, - "aggressive": false, + "aggressive": true, "poisonous": false, "immuneToPoison": true, "immuneToVenom": true, @@ -3686,7 +3686,7 @@ "maxHit": 9, "attackType": ["crush"], "attackSpeed": 4, - "aggressive": false, + "aggressive": true, "poisonous": false, "immuneToPoison": true, "immuneToVenom": true, @@ -10517,7 +10517,7 @@ "isSlayerMonster": true, "slayerLevelRequired": 85, "slayerXP": 35, - "assignableSlayerMasters": ["nieve", "duradel"] + "assignableSlayerMasters": ["vannaka", "chaeldar", "konar", "nieve", "duradel", "krystilia"] }, "7039": { "members": true, diff --git a/src/index.ts b/src/index.ts index 9db85b189..eb1e1bea8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,3 +33,8 @@ export { EItem, EMonster, }; + +export * from "./data/itemConstants"; +export * from "./structures/Items"; +export * from "./meta/types"; +export type { default as Monster } from "./structures/Monster"; diff --git a/src/meta/types.ts b/src/meta/types.ts index e9ace2773..e12f91607 100644 --- a/src/meta/types.ts +++ b/src/meta/types.ts @@ -275,6 +275,9 @@ export interface WikiPage { }[]; } +export interface IntKeyBank { + [key: number]: number; +} export interface ItemBank { [key: string]: number; } @@ -288,32 +291,11 @@ export interface SimpleTableItem { weight: number; } -export interface ReturnedLootItem { - item: number; - quantity: number; -} - export interface BankItem { id: number; qty: number; } -export interface LootTableMoreOptions { - multiply?: boolean; - freeze?: boolean; -} - -export interface LootTableItem { - item: number | LootTable | LootTableItem[]; - weight?: number; - quantity: number | number[]; - options?: LootTableMoreOptions; -} - -export interface OneInItems extends LootTableItem { - chance: number; -} - export type TupleLootItem = [number, number]; export interface MonsterKillOptions { @@ -355,10 +337,6 @@ export interface OpenableOpenOptions { chestSize?: ChestSize; } -export interface LootTableOptions { - limit?: number; -} - export interface ClueOptions { table: LootTable; } diff --git a/src/simulation/clues/Beginner.ts b/src/simulation/clues/Beginner.ts index 9c2b3e593..10fbd9b84 100644 --- a/src/simulation/clues/Beginner.ts +++ b/src/simulation/clues/Beginner.ts @@ -1,5 +1,3 @@ -import { randInt } from "e"; - import Bank from "../../structures/Bank"; import Clue from "../../structures/Clue"; import LootTable from "../../structures/LootTable"; @@ -88,16 +86,14 @@ export const StandardTable = new LootTable() export const BeginnerClueTable = new LootTable().add(StandardTable, 1, 11).add(UniqueTable, 1, 1); -export class BeginnerCasket extends Clue { - public open(quantity = 1): Bank { - const loot = new Bank(); - for (let i = 0; i < quantity; i++) { - const numberOfRolls = randInt(1, 3); +const MainTable = new LootTable().add(BeginnerClueTable, [1, 3]); - for (let i = 0; i < numberOfRolls; i++) { - loot.add(BeginnerClueTable.roll()); - } - } +export class BeginnerCasket extends Clue { + open(quantity: number, targetBank?: undefined): Bank; + open(quantity: number, targetBank: Bank): null; + public open(quantity: number, targetBank?: Bank): Bank | null { + const loot = targetBank ?? new Bank(); + MainTable.roll(quantity, { targetBank: loot }); return loot; } } diff --git a/src/simulation/clues/Easy.ts b/src/simulation/clues/Easy.ts index 0fe3d2270..cb696744f 100644 --- a/src/simulation/clues/Easy.ts +++ b/src/simulation/clues/Easy.ts @@ -1,5 +1,3 @@ -import { randInt, roll } from "e"; - import Bank from "../../structures/Bank"; import Clue from "../../structures/Clue"; import LootTable from "../../structures/LootTable"; @@ -189,20 +187,13 @@ export const EasyStandardTable = new LootTable() export const EasyClueTable = new LootTable().add(EasyStandardTable, 1, 11).add(EasyRareTable, 1, 1); +const MainTable = new LootTable().add(EasyClueTable, [2, 4]).tertiary(50, "Clue scroll (master)"); export class EasyCasket extends Clue { - public open(quantity = 1): Bank { - const loot = new Bank(); - - for (let i = 0; i < quantity; i++) { - const numberOfRolls = randInt(2, 4); - - if (roll(50)) loot.add("Clue scroll (master)"); - - for (let i = 0; i < numberOfRolls; i++) { - loot.add(EasyClueTable.roll()); - } - } - + open(quantity: number, targetBank?: undefined): Bank; + open(quantity: number, targetBank: Bank): null; + public open(quantity: number, targetBank?: Bank): Bank | null { + const loot = targetBank ?? new Bank(); + MainTable.roll(quantity, { targetBank: loot }); return loot; } } diff --git a/src/simulation/clues/Elite.ts b/src/simulation/clues/Elite.ts index 64a5e3cdf..6dba696be 100644 --- a/src/simulation/clues/Elite.ts +++ b/src/simulation/clues/Elite.ts @@ -1,5 +1,3 @@ -import { randInt, roll } from "e"; - import Bank from "../../structures/Bank"; import Clue from "../../structures/Clue"; import LootTable from "../../structures/LootTable"; @@ -152,21 +150,14 @@ export const EliteStandardTable = new LootTable() .add(BlessingTable); export const EliteClueTable = new LootTable().add(EliteStandardTable, 1, 24).add(EliteRareTable, 1, 1); +const MainTable = new LootTable().add(EliteClueTable, [4, 6]).tertiary(5, "Clue scroll (master)"); export class EliteCasket extends Clue { - public open(quantity = 1): Bank { - const loot = new Bank(); - - for (let i = 0; i < quantity; i++) { - const numberOfRolls = randInt(4, 6); - - if (roll(5)) loot.add("Clue scroll (master)"); - - for (let i = 0; i < numberOfRolls; i++) { - loot.add(EliteClueTable.roll()); - } - } - + open(quantity: number, targetBank?: undefined): Bank; + open(quantity: number, targetBank: Bank): null; + public open(quantity: number, targetBank?: Bank): Bank | null { + const loot = targetBank ?? new Bank(); + MainTable.roll(quantity, { targetBank: loot }); return loot; } } diff --git a/src/simulation/clues/Hard.ts b/src/simulation/clues/Hard.ts index be39caf7b..df496ba14 100644 --- a/src/simulation/clues/Hard.ts +++ b/src/simulation/clues/Hard.ts @@ -1,9 +1,7 @@ -import { randInt, roll } from "e"; - import Bank from "../../structures/Bank"; import Clue from "../../structures/Clue"; import LootTable from "../../structures/LootTable"; -import { itemID } from "../../util"; +import { itemID, itemTupleToTable } from "../../util"; import { BlessingTable, FirelighterTable, GildedTable, PrayerPageTable, TeleportScrollTable } from "./General"; export const Hard3rdageTable = new LootTable() @@ -25,11 +23,13 @@ export const HardMegaRareTable = new LootTable() .add("Super energy(4)", 15) .add("Super restore(4)", 15) .add("Antifire potion(4)", 15) - .add([ - ["Super attack(4)", 5], - ["Super strength(4)", 5], - ["Super defence(4)", 5], - ]) + .add( + itemTupleToTable([ + ["Super attack(4)", 5], + ["Super strength(4)", 5], + ["Super defence(4)", 5], + ]), + ) .add(Hard3rdageTable) .add(GildedTable, 1, 5); @@ -206,21 +206,14 @@ export const HardStandardTable = new LootTable() .add(HardBowTable); export const HardClueTable = new LootTable().add(HardStandardTable, 1, 12).add(HardRareTable, 1, 1); +const MainTable = new LootTable().add(HardClueTable, [4, 6]).tertiary(15, "Clue scroll (master)"); export class HardCasket extends Clue { - public open(quantity = 1): Bank { - const loot = new Bank(); - - for (let i = 0; i < quantity; i++) { - const numberOfRolls = randInt(4, 6); - - if (roll(15)) loot.add("Clue scroll (master)"); - - for (let i = 0; i < numberOfRolls; i++) { - loot.add(HardClueTable.roll()); - } - } - + open(quantity: number, targetBank?: undefined): Bank; + open(quantity: number, targetBank: Bank): null; + public open(quantity: number, targetBank?: Bank): Bank | null { + const loot = targetBank ?? new Bank(); + MainTable.roll(quantity, { targetBank: loot }); return loot; } } diff --git a/src/simulation/clues/Master.ts b/src/simulation/clues/Master.ts index d05a39b75..95f016184 100644 --- a/src/simulation/clues/Master.ts +++ b/src/simulation/clues/Master.ts @@ -1,5 +1,3 @@ -import { randInt, roll } from "e"; - import Bank from "../../structures/Bank"; import Clue from "../../structures/Clue"; import LootTable from "../../structures/LootTable"; @@ -161,20 +159,14 @@ export const MasterStandardTable = new LootTable() export const MasterClueTable = new LootTable().add(MasterStandardTable, 1, 22).add(MasterRareTable, 1, 1); -export class MasterCasket extends Clue { - public open(quantity = 1): Bank { - const loot = new Bank(); - - for (let i = 0; i < quantity; i++) { - if (roll(1000)) loot.add("Bloodhound"); - - const numberOfRolls = randInt(5, 7); - - for (let i = 0; i < numberOfRolls; i++) { - loot.add(MasterClueTable.roll()); - } - } +const MainTable = new LootTable().add(MasterClueTable, [5, 7]).tertiary(1000, "Bloodhound"); +export class MasterCasket extends Clue { + open(quantity: number, targetBank?: undefined): Bank; + open(quantity: number, targetBank: Bank): null; + public open(quantity: number, targetBank?: Bank): Bank | null { + const loot = targetBank ?? new Bank(); + MainTable.roll(quantity, { targetBank: loot }); return loot; } } diff --git a/src/simulation/clues/Medium.ts b/src/simulation/clues/Medium.ts index 49999be96..a0050beee 100644 --- a/src/simulation/clues/Medium.ts +++ b/src/simulation/clues/Medium.ts @@ -1,5 +1,3 @@ -import { randInt, roll } from "e"; - import Bank from "../../structures/Bank"; import Clue from "../../structures/Clue"; import LootTable from "../../structures/LootTable"; @@ -173,20 +171,14 @@ export const MediumStandardTable = new LootTable() export const MediumClueTable = new LootTable().add(MediumStandardTable, 1, 10).add(MediumRareTable, 1, 1); -export class MediumCasket extends Clue { - public open(quantity = 1): Bank { - const loot = new Bank(); - - for (let i = 0; i < quantity; i++) { - const numberOfRolls = randInt(3, 5); - - if (roll(30)) loot.add("Clue scroll (master)"); - - for (let i = 0; i < numberOfRolls; i++) { - loot.add(MediumClueTable.roll()); - } - } +const MainTable = new LootTable().add(MediumClueTable, [3, 5]).tertiary(30, "Clue scroll (master)"); +export class MediumCasket extends Clue { + open(quantity: number, targetBank?: undefined): Bank; + open(quantity: number, targetBank: Bank): null; + public open(quantity: number, targetBank?: Bank): Bank | null { + const loot = targetBank ?? new Bank(); + MainTable.roll(quantity, { targetBank: loot }); return loot; } } diff --git a/src/simulation/clues/index.ts b/src/simulation/clues/index.ts index 7bf46c478..e550df200 100644 --- a/src/simulation/clues/index.ts +++ b/src/simulation/clues/index.ts @@ -5,4 +5,10 @@ import Hard from "./Hard"; import Master from "./Master"; import Medium from "./Medium"; +export * from "./Beginner"; +export * from "./Easy"; +export * from "./Elite"; +export * from "./Hard"; +export * from "./Master"; +export * from "./Medium"; export { Beginner, Easy, Elite, Hard, Master, Medium }; diff --git a/src/simulation/misc/Nightmare.ts b/src/simulation/misc/Nightmare.ts index 79c82b76f..fc1c1130d 100644 --- a/src/simulation/misc/Nightmare.ts +++ b/src/simulation/misc/Nightmare.ts @@ -1,11 +1,10 @@ import { calcPercentOfNum, calcWhatPercent, percentChance, randInt, roll } from "e"; -import type { ItemBank, LootBank } from "../../meta/types"; +import type { LootBank } from "../../meta/types"; import Bank from "../../structures/Bank"; import LootTable from "../../structures/LootTable"; import SimpleTable from "../../structures/SimpleTable"; import { resolveNameBank } from "../../util/bank"; -import { convertLootBanksToItemBanks } from "../../util/util"; export interface TeamMember { id: string; @@ -167,9 +166,7 @@ class NightmareClass { return [item, quantity]; } - public kill(options: Readonly): { - [key: string]: ItemBank; - } { + public kill(options: Readonly): LootBank { const mvp = options.team.sort((a, b) => b.damageDone - a.damageDone)[0]; const parsedTeam = options.team.map(teamMember => ({ @@ -229,7 +226,7 @@ class NightmareClass { // Hand out non-uniques for (const teamMember of parsedTeam) { - if (Object.keys(lootResult[teamMember.id].bank).length === 0) { + if (lootResult[teamMember.id].length === 0) { lootResult[teamMember.id].add( ...this.rollNonUniqueLoot(teamMember.scaledPercentDamage, teamMember.mvp, options.isPhosani), ); @@ -244,7 +241,7 @@ class NightmareClass { ); } - return convertLootBanksToItemBanks(lootResult); + return lootResult; } } diff --git a/src/simulation/misc/Tempoross.ts b/src/simulation/misc/Tempoross.ts index 3ab689524..da6185f46 100644 --- a/src/simulation/misc/Tempoross.ts +++ b/src/simulation/misc/Tempoross.ts @@ -163,7 +163,7 @@ export function Tempoross({ for (let index = 0; index < quantity; index++) { const newItem = lootTable.roll(); - if (replaceItems.includes(newItem.items()[0][0].name) && userBank.has(newItem.bank)) { + if (replaceItems.includes(newItem.items()[0][0].name) && userBank.has(newItem)) { loot.add("Soaked page", 25); } else { loot.add(newItem); diff --git a/src/simulation/misc/TheatreOfBlood.ts b/src/simulation/misc/TheatreOfBlood.ts index e104ae33e..d4dbe3a18 100644 --- a/src/simulation/misc/TheatreOfBlood.ts +++ b/src/simulation/misc/TheatreOfBlood.ts @@ -4,7 +4,7 @@ import type { LootBank } from "../../meta/types"; import Bank from "../../structures/Bank"; import LootTable from "../../structures/LootTable"; import SimpleTable from "../../structures/SimpleTable"; -import { JSONClone, convertLootBanksToItemBanks } from "../../util"; +import { JSONClone } from "../../util"; export interface TeamMember { id: string; @@ -135,8 +135,8 @@ export class TheatreOfBloodClass { if (isHardMode) { // Add 15% extra regular loot for hard mode: - for (const [itemID] of Object.entries(loot.bank)) { - loot.bank[Number.parseInt(itemID)] = Math.ceil(loot.bank[Number.parseInt(itemID)] * 1.15); + for (const [item] of loot.items()) { + loot.set(item.id, Math.ceil(loot.amount(item.id) * 1.15)); } // Add HM Tertiary drops: dust / kits loot.add(HardModeExtraTable.roll()); @@ -200,7 +200,7 @@ export class TheatreOfBloodClass { } return { - loot: convertLootBanksToItemBanks(lootResult), + loot: lootResult, percentChanceOfUnique: percentBaseChanceOfUnique, totalDeaths, teamPoints, diff --git a/src/simulation/misc/Zalcano.ts b/src/simulation/misc/Zalcano.ts index 4c53c93b7..daa129bb6 100644 --- a/src/simulation/misc/Zalcano.ts +++ b/src/simulation/misc/Zalcano.ts @@ -1,11 +1,10 @@ import { calcPercentOfNum } from "e"; -import type { ItemBank, LootBank } from "../../meta/types"; +import type { LootBank } from "../../meta/types"; import Bank from "../../structures/Bank"; import LootTable from "../../structures/LootTable"; import SimpleTable from "../../structures/SimpleTable"; import { resolveNameBank } from "../../util/bank"; -import { convertLootBanksToItemBanks } from "../../util/util"; export interface TeamMember { id: string; @@ -83,9 +82,7 @@ class ZalcanoClass { return [item, quantity]; } - public kill({ team }: Readonly): { - [key: string]: ItemBank; - } { + public kill({ team }: Readonly): LootBank { const lootResult: LootBank = {}; for (const teamMember of team) { @@ -101,7 +98,7 @@ class ZalcanoClass { lootResult[teamMember.id] = loot; } - return convertLootBanksToItemBanks(lootResult); + return lootResult; } } diff --git a/src/simulation/monsters/bosses/Bryophyta.ts b/src/simulation/monsters/bosses/Bryophyta.ts index 9f64cbb3b..9e3100641 100644 --- a/src/simulation/monsters/bosses/Bryophyta.ts +++ b/src/simulation/monsters/bosses/Bryophyta.ts @@ -1,5 +1,6 @@ import LootTable from "../../../structures/LootTable"; import SimpleMonster from "../../../structures/SimpleMonster"; +import { itemTupleToTable } from "../../../util"; import HerbDropTable from "../../subtables/HerbDropTable"; import UncommonSeedDropTable from "../../subtables/UncommonSeedDropTable"; @@ -39,10 +40,10 @@ const BryophytaTable = new LootTable() /* Materials */ .add("Runite bar", 2, 6) .add( - [ + itemTupleToTable([ ["Uncut ruby", 5], ["Uncut diamond", 5], - ], + ]), 1, 4, ) diff --git a/src/simulation/monsters/bosses/CommanderZilyana.ts b/src/simulation/monsters/bosses/CommanderZilyana.ts index fce5aa450..d0bb3c010 100644 --- a/src/simulation/monsters/bosses/CommanderZilyana.ts +++ b/src/simulation/monsters/bosses/CommanderZilyana.ts @@ -1,5 +1,6 @@ import LootTable from "../../../structures/LootTable"; import SimpleMonster from "../../../structures/SimpleMonster"; +import { itemTupleToTable } from "../../../util"; import GWRareDropTable, { GWGemTable, ShardTable } from "../../subtables/GWRareDropTable"; const MinionUniqueTable = new LootTable().add("Coins", [1400, 1500], 124).add("Saradomin sword", 1, 3); @@ -45,18 +46,18 @@ const CommanderZilyanaTable = new LootTable() /* Potions */ .add("Prayer potion(4)", 3, 8) .add( - [ + itemTupleToTable([ ["Super defence(3)", 3], ["Magic potion(3)", 3], - ], + ]), 1, 8, ) .add( - [ + itemTupleToTable([ ["Saradomin brew(3)", 3], ["Super restore(4)", 3], - ], + ]), 1, 6, ) diff --git a/src/simulation/monsters/bosses/Kreearra.ts b/src/simulation/monsters/bosses/Kreearra.ts index fc791ca0d..7b01bed86 100644 --- a/src/simulation/monsters/bosses/Kreearra.ts +++ b/src/simulation/monsters/bosses/Kreearra.ts @@ -1,5 +1,6 @@ import LootTable from "../../../structures/LootTable"; import SimpleMonster from "../../../structures/SimpleMonster"; +import { itemTupleToTable } from "../../../util"; import GWRareDropTable, { GWGemTable, ShardTable } from "../../subtables/GWRareDropTable"; const KreearraArmorTable = new LootTable().add("Armadyl helmet").add("Armadyl chestplate").add("Armadyl chainskirt"); @@ -52,10 +53,10 @@ const KreearraTable = new LootTable() /* Other */ .add("Coins", [19_500, 20_000], 40) .add( - [ + itemTupleToTable([ ["Ranging potion(3)", 3], ["Super defence(3)", 3], - ], + ]), 1, 8, ) diff --git a/src/simulation/monsters/bosses/KrilTsutsaroth.ts b/src/simulation/monsters/bosses/KrilTsutsaroth.ts index 939051847..97762ea3a 100644 --- a/src/simulation/monsters/bosses/KrilTsutsaroth.ts +++ b/src/simulation/monsters/bosses/KrilTsutsaroth.ts @@ -1,5 +1,6 @@ import LootTable from "../../../structures/LootTable"; import SimpleMonster from "../../../structures/SimpleMonster"; +import { itemTupleToTable } from "../../../util"; import GWRareDropTable, { GWGemTable, ShardTable } from "../../subtables/GWRareDropTable"; const MinionUniqueTable = new LootTable().add("Coins", [1300, 1400], 124).add("Zamorakian spear", 1, 3); @@ -50,18 +51,18 @@ const KrilTsutsarothTable = new LootTable() /* Potions */ .add( - [ + itemTupleToTable([ ["Super attack(3)", 3], ["Super strength(3)", 3], - ], + ]), 1, 8, ) .add( - [ + itemTupleToTable([ ["Super restore(3)", 3], ["Zamorak brew(3)", 3], - ], + ]), 1, 8, ) diff --git a/src/simulation/monsters/bosses/Obor.ts b/src/simulation/monsters/bosses/Obor.ts index 03cdc2c60..96dc52120 100644 --- a/src/simulation/monsters/bosses/Obor.ts +++ b/src/simulation/monsters/bosses/Obor.ts @@ -1,5 +1,6 @@ import LootTable from "../../../structures/LootTable"; import SimpleMonster from "../../../structures/SimpleMonster"; +import { itemTupleToTable } from "../../../util"; const OborTable = new LootTable({ limit: 118 }) .every("Big Bones") @@ -34,10 +35,10 @@ const OborTable = new LootTable({ limit: 118 }) .add("Limpwurt root", 20, 8) .add("Big bones", 50, 8) .add( - [ + itemTupleToTable([ ["Uncut diamond", 5], ["Uncut ruby", 5], - ], + ]), 1, 5, ); diff --git a/src/simulation/monsters/bosses/slayer/AlchemicalHydra.ts b/src/simulation/monsters/bosses/slayer/AlchemicalHydra.ts index effb548e7..049fefdcf 100644 --- a/src/simulation/monsters/bosses/slayer/AlchemicalHydra.ts +++ b/src/simulation/monsters/bosses/slayer/AlchemicalHydra.ts @@ -1,5 +1,6 @@ import LootTable from "../../../../structures/LootTable"; import SimpleMonster from "../../../../structures/SimpleMonster"; +import { itemTupleToTable } from "../../../../util"; import RareDropTable from "../../../subtables/RareDropTable"; import TreeHerbSeedTable from "../../../subtables/TreeHerbSeedTable"; @@ -22,10 +23,10 @@ const NormalTable = new LootTable() /* Weapons and armour */ .add( - [ + itemTupleToTable([ ["Mystic fire staff", 1], ["Mystic water staff", 1], - ], + ]), 1, 8, ) @@ -36,10 +37,10 @@ const NormalTable = new LootTable() .add("Dragon med helm", 1, 3) .add("Dragon battleaxe", 1, 2) .add( - [ + itemTupleToTable([ ["Mystic robe top (light)", 1], ["Mystic robe bottom (light)", 1], - ], + ]), 1, 1, ) @@ -62,10 +63,10 @@ const NormalTable = new LootTable() .add("Coins", [40_000, 60_000], 10) .add("Shark", [2, 4], 7) .add( - [ + itemTupleToTable([ ["Ranging potion(3)", 1], ["Super restore(3)", 2], - ], + ]), 1, 7, ) diff --git a/src/simulation/monsters/bosses/slayer/GrotesqueGuardians.ts b/src/simulation/monsters/bosses/slayer/GrotesqueGuardians.ts index 006155941..2f254cd6a 100644 --- a/src/simulation/monsters/bosses/slayer/GrotesqueGuardians.ts +++ b/src/simulation/monsters/bosses/slayer/GrotesqueGuardians.ts @@ -1,5 +1,6 @@ import LootTable from "../../../../structures/LootTable"; import SimpleMonster from "../../../../structures/SimpleMonster"; +import { itemTupleToTable } from "../../../../util"; const NormalUniqueTable = new LootTable() /* Unique */ @@ -23,11 +24,11 @@ const NormalUniqueTable = new LootTable() .add("Mushroom potato", [4, 6], 10) .add("Saradomin brew(4)", 2, 8) .add( - [ + itemTupleToTable([ ["Magic potion(2)", 1], ["Ranging potion(2)", 1], ["Super combat potion(2)", 1], - ], + ]), 1, 6, ) diff --git a/src/simulation/monsters/bosses/wildy/ChaosFanatic.ts b/src/simulation/monsters/bosses/wildy/ChaosFanatic.ts index 4c841d3f2..23d5aa371 100644 --- a/src/simulation/monsters/bosses/wildy/ChaosFanatic.ts +++ b/src/simulation/monsters/bosses/wildy/ChaosFanatic.ts @@ -1,5 +1,6 @@ import LootTable from "../../../../structures/LootTable"; import SimpleMonster from "../../../../structures/SimpleMonster"; +import { itemTupleToTable } from "../../../../util"; import RareDropTable, { GemTable } from "../../../subtables/RareDropTable"; const ChaosFanaticUniqueTable = new LootTable().add("Odium shard 1").add("Malediction shard 1"); @@ -15,10 +16,10 @@ const ChaosFanaticTable = new LootTable() .add("Splitbark body", 1, 5) .add("Splitbark legs", 1, 5) .add( - [ + itemTupleToTable([ ["Zamorak monk top", 1], ["Zamorak monk bottom", 1], - ], + ]), 1, 4, ) @@ -43,10 +44,10 @@ const ChaosFanaticTable = new LootTable() .add("Chaos talisman", 1, 6) .add("Wine of zamorak", 10, 6) .add( - [ + itemTupleToTable([ ["Uncut emerald", 6], ["Uncut sapphire", 4], - ], + ]), 1, 5, ) diff --git a/src/simulation/monsters/bosses/wildy/CorporealBeast.ts b/src/simulation/monsters/bosses/wildy/CorporealBeast.ts index 2110f2144..b49fcb961 100644 --- a/src/simulation/monsters/bosses/wildy/CorporealBeast.ts +++ b/src/simulation/monsters/bosses/wildy/CorporealBeast.ts @@ -4,7 +4,7 @@ import { GemTable } from "../../../subtables/RareDropTable"; const SigilTable = new LootTable().add("Spectral sigil", 1, 3).add("Arcane sigil", 1, 3).add("Elysian sigil", 1, 1); -const CorporealBeastTable = new LootTable() +export const CorporealBeastTable = new LootTable() .add("Spirit shield", 1, 8) .add("Holy elixir", 1, 3) diff --git a/src/simulation/monsters/bosses/wildy/CrazyArchaeologist.ts b/src/simulation/monsters/bosses/wildy/CrazyArchaeologist.ts index 061a65aca..c251d0126 100644 --- a/src/simulation/monsters/bosses/wildy/CrazyArchaeologist.ts +++ b/src/simulation/monsters/bosses/wildy/CrazyArchaeologist.ts @@ -1,5 +1,6 @@ import LootTable from "../../../../structures/LootTable"; import SimpleMonster from "../../../../structures/SimpleMonster"; +import { itemTupleToTable } from "../../../../util"; import RareDropTable, { GemTable } from "../../../subtables/RareDropTable"; const CrazyArchaeologistUniqueTable = new LootTable().add("Odium shard 2").add("Malediction shard 2"); @@ -33,10 +34,10 @@ const CrazyArchaeologistTable = new LootTable() .add("White berries", 10, 6) .add("Silver ore", 40, 6) .add( - [ + itemTupleToTable([ ["Uncut emerald", 6], ["Uncut sapphire", 4], - ], + ]), 1, 5, ) diff --git a/src/simulation/monsters/low/a-f/Bloodveld.ts b/src/simulation/monsters/low/a-f/Bloodveld.ts index cb6abcb31..7af62b8e9 100644 --- a/src/simulation/monsters/low/a-f/Bloodveld.ts +++ b/src/simulation/monsters/low/a-f/Bloodveld.ts @@ -1,5 +1,6 @@ import LootTable from "../../../../structures/LootTable"; import SimpleMonster from "../../../../structures/SimpleMonster"; +import { itemTupleToTable } from "../../../../util"; import HerbDropTable from "../../../subtables/HerbDropTable"; import { GemTable } from "../../../subtables/RareDropTable"; @@ -31,18 +32,18 @@ export const BloodveldPreTable = new LootTable() /* Other */ .add( - [ + itemTupleToTable([ ["Big bones", 1], ["Bones", 1], - ], + ]), 1, 7, ) .add( - [ + itemTupleToTable([ ["Big bones", 3], ["Bones", 1], - ], + ]), 1, 3, ) diff --git a/src/simulation/monsters/low/g-m/JungleHorror.ts b/src/simulation/monsters/low/g-m/JungleHorror.ts index 84c7d1514..69e657e8f 100644 --- a/src/simulation/monsters/low/g-m/JungleHorror.ts +++ b/src/simulation/monsters/low/g-m/JungleHorror.ts @@ -1,5 +1,6 @@ import LootTable from "../../../../structures/LootTable"; import SimpleMonster from "../../../../structures/SimpleMonster"; +import { itemTupleToTable } from "../../../../util"; import HerbDropTable from "../../../subtables/HerbDropTable"; import { GemTable } from "../../../subtables/RareDropTable"; import VariableAllotmentSeedTable from "../../../subtables/VariableAllotmentSeedTable"; @@ -32,18 +33,18 @@ const JungleHorrorTable = new LootTable({ limit: 129 }) /* Other */ .add("Pineapple", 1, 8) .add( - [ + itemTupleToTable([ ["Big bones", 1], ["Bones", 1], - ], + ]), 1, 3, ) .add( - [ + itemTupleToTable([ ["Big bones", 3], ["Bones", 1], - ], + ]), 1, 2, ) diff --git a/src/simulation/monsters/low/g-m/MithrilDragon.ts b/src/simulation/monsters/low/g-m/MithrilDragon.ts index fa36c496f..8273db660 100644 --- a/src/simulation/monsters/low/g-m/MithrilDragon.ts +++ b/src/simulation/monsters/low/g-m/MithrilDragon.ts @@ -2,6 +2,34 @@ import LootTable from "../../../../structures/LootTable"; import SimpleMonster from "../../../../structures/SimpleMonster"; import RareDropTable, { GemTable } from "../../../subtables/RareDropTable"; +const ancientPageTable = new LootTable() + .add(11_341, 1, 1) + .add(11_342, 1, 1) + .add(11_343, 1, 1) + .add(11_344, 1, 1) + .add(11_345, 1, 1) + .add(11_346, 1, 1) + .add(11_347, 1, 1) + .add(11_348, 1, 1) + .add(11_349, 1, 1) + .add(11_350, 1, 1) + .add(11_351, 1, 1) + .add(11_352, 1, 1) + .add(11_353, 1, 1) + .add(11_354, 1, 1) + .add(11_355, 1, 1) + .add(11_356, 1, 1) + .add(11_357, 1, 1) + .add(11_358, 1, 1) + .add(11_359, 1, 1) + .add(11_360, 1, 1) + .add(11_361, 1, 1) + .add(11_362, 1, 1) + .add(11_363, 1, 1) + .add(11_364, 1, 1) + .add(11_365, 1, 1) + .add(11_366, 1, 1); + const MithrilDragonTable = new LootTable() .every("Dragon bones") .every("Mithril bar", 3) @@ -32,10 +60,10 @@ const MithrilDragonTable = new LootTable() /* Other */ .add("Coins", 600, 17) - .add("Coins", 876, 7) .add("Dragon javelin heads", 15, 7) .add("Chewed bones", 1, 3) .add("Runite bar", 2, 3) + .add(ancientPageTable, 2, 1) /* RDT */ .add(RareDropTable, 1, 1) diff --git a/src/simulation/monsters/special/Barrows.ts b/src/simulation/monsters/special/Barrows.ts index 1a2ceed38..63e3df3e0 100644 --- a/src/simulation/monsters/special/Barrows.ts +++ b/src/simulation/monsters/special/Barrows.ts @@ -1,6 +1,5 @@ import { roll } from "e"; -import type { MonsterKillOptions } from "../../../meta/types"; import Bank from "../../../structures/Bank"; import LootTable from "../../../structures/LootTable"; import Monster from "../../../structures/Monster"; @@ -54,11 +53,11 @@ const ClueTable = new LootTable().tertiary(34, "Clue scroll (elite)"); const NUMBER_OF_BROTHERS = 6; export class Barrows extends Monster { - public kill(quantity = 1, options?: MonsterKillOptions): Bank { + public kill(quantity = 1): Bank { const loot = new Bank(); for (let i = 0; i < quantity; i++) { - loot.add(ClueTable.roll(1, options?.lootTableOptions)); + ClueTable.roll(1, { targetBank: loot }); // We use a set to track items received, you cannot get // the same item twice per chest. @@ -74,7 +73,7 @@ export class Barrows extends Monster { barrowsItemsThisKill.add(barrowsItem.id); loot.add(barrowsItem.id); } else { - loot.add(OtherTable.roll()); + OtherTable.roll(1, { targetBank: loot }); } } } diff --git a/src/simulation/openables/CrystalChest.ts b/src/simulation/openables/CrystalChest.ts index 155f6b6ba..8877c7dd5 100644 --- a/src/simulation/openables/CrystalChest.ts +++ b/src/simulation/openables/CrystalChest.ts @@ -1,5 +1,6 @@ import LootTable from "../../structures/LootTable"; import SimpleOpenable from "../../structures/SimpleOpenable"; +import { itemTupleToTable } from "../../util"; const runeArmorTable = new LootTable().add("Rune platelegs", 1, 1).add("Rune plateskirt", 1, 1); @@ -11,15 +12,15 @@ const coinsKeyHalfTable = new LootTable() const CrystalChestTable = new LootTable({ limit: 128 }) .every("Uncut dragonstone") .add( - [ + itemTupleToTable([ ["Spinach roll", 1], ["Coins", 2000], - ], + ]), 1, 34, ) .add( - [ + itemTupleToTable([ ["Air rune", 50], ["Water rune", 50], ["Earth rune", 50], @@ -31,15 +32,15 @@ const CrystalChestTable = new LootTable({ limit: 128 }) ["Cosmic rune", 10], ["Nature rune", 10], ["Law rune", 10], - ], + ]), 1, 12, ) .add( - [ + itemTupleToTable([ ["Ruby", 2], ["Diamond", 2], - ], + ]), 1, 12, ) @@ -48,10 +49,10 @@ const CrystalChestTable = new LootTable({ limit: 128 }) .add("Iron ore", 150, 10) .add("Coal", 100, 10) .add( - [ + itemTupleToTable([ ["Raw swordfish", 5], ["Coins", 1000], - ], + ]), 1, 8, ) diff --git a/src/simulation/openables/ElvenCrystalChest.ts b/src/simulation/openables/ElvenCrystalChest.ts index 7b9f2434c..60a25d1d2 100644 --- a/src/simulation/openables/ElvenCrystalChest.ts +++ b/src/simulation/openables/ElvenCrystalChest.ts @@ -1,5 +1,6 @@ import LootTable from "../../structures/LootTable"; import SimpleOpenable from "../../structures/SimpleOpenable"; +import { itemTupleToTable } from "../../util"; /* Dragonstone armour roll */ const DragonStoneArmorTable = new LootTable() @@ -35,89 +36,89 @@ const ElvenCrystalChestTable = new LootTable() .oneIn(500, DragonStoneArmorTable) .add(coinsKeyHalfTable, 1, 64) .add( - [ + itemTupleToTable([ ["Uncut dragonstone", 1], ["Uncut ruby", [10, 13]], ["Uncut diamond", [5, 8]], - ], + ]), 1, 32, ) .add( - [ + itemTupleToTable([ ["Uncut dragonstone", 1], ["Crystal key", 1], - ], + ]), 1, 24, ) .add( - [ + itemTupleToTable([ ["Uncut dragonstone", 1], ["Coins", [30_000, 50_000]], ["Crystal shard", [8, 13]], - ], + ]), 1, 20, ) .add( - [ + itemTupleToTable([ ["Uncut dragonstone", 1], ["Crystal shard", [20, 30]], - ], + ]), 1, 17, ) .add(runeArmorTable, 1, 17) .add( - [ + itemTupleToTable([ ["Uncut dragonstone", 1], ["Cosmic rune", [50, 100]], ["Chaos rune", [50, 100]], ["Nature rune", [50, 100]], ["Law rune", [50, 100]], ["Death rune", [50, 100]], - ], + ]), 1, 17, ) .add( - [ + itemTupleToTable([ ["Uncut dragonstone", 1], ["Yew seed", 1], - ], + ]), 1, 17, ) .add( - [ + itemTupleToTable([ ["Uncut dragonstone", 1], ["Raw shark", [50, 100]], - ], + ]), 1, 17, ) .add( - [ + itemTupleToTable([ ["Uncut dragonstone", 1], ["Gold ore", [350, 500]], - ], + ]), 1, 12, ) .add( - [ + itemTupleToTable([ ["Uncut dragonstone", 1], ["Runite ore", [7, 10]], - ], + ]), 1, 9, ) .add( - [ + itemTupleToTable([ ["Uncut dragonstone", 1], ["Crystal acorn", [1, 2]], - ], + ]), 1, 7, ) diff --git a/src/simulation/openables/GrubbyChest.ts b/src/simulation/openables/GrubbyChest.ts index 4f2330b3f..c198bad37 100644 --- a/src/simulation/openables/GrubbyChest.ts +++ b/src/simulation/openables/GrubbyChest.ts @@ -1,33 +1,34 @@ import LootTable from "../../structures/LootTable"; import SimpleOpenable from "../../structures/SimpleOpenable"; +import { itemTupleToTable } from "../../util"; const FoodTable = new LootTable() .add("Egg potato", 4, 12) .add("Shark", 4, 7) .add( - [ + itemTupleToTable([ ["Saradomin brew(2)", 3], ["Super restore(2)", 1], - ], + ]), 1, 1, ); const PotionTable = new LootTable() .add( - [ + itemTupleToTable([ ["Super attack(2)", 1], ["Super strength(2)", 1], ["Super defence(2)", 1], - ], + ]), 1, 8, ) .add( - [ + itemTupleToTable([ ["Super defence(2)", 1], ["Ranging potion(2)", 1], - ], + ]), 1, 8, ) diff --git a/src/structures/Bank.ts b/src/structures/Bank.ts index e04a53c6e..980b5252b 100644 --- a/src/structures/Bank.ts +++ b/src/structures/Bank.ts @@ -1,55 +1,131 @@ import { randArrItem } from "e"; -import type { BankItem, Item, ItemBank, ReturnedLootItem } from "../meta/types"; -import { fasterResolveBank, resolveNameBank } from "../util/bank"; +import type { BankItem, IntKeyBank, Item, ItemBank } from "../meta/types"; import itemID from "../util/itemID"; +import { toKMB } from "../util/smallUtils"; import Items from "./Items"; const frozenErrorStr = "Tried to mutate a frozen Bank."; +const isValidInteger = (str: string): boolean => /^-?\d+$/.test(str); + +type ItemResolvable = Item | string | number; + +function isValidBankQuantity(qty: number): boolean { + return typeof qty === "number" && qty >= 1 && Number.isInteger(qty); +} + +function sanitizeItemBank(mutSource: ItemBank) { + for (const [key, qty] of Object.entries(mutSource)) { + if (!isValidBankQuantity(qty)) { + delete mutSource[key]; + } + const item = Items.get(Number.parseInt(key)); + if (!item) { + delete mutSource[key]; + } + } +} + export default class Bank { - public bank: ItemBank = {}; + private map: Map; public frozen = false; - constructor(initialBank?: ItemBank | Bank) { - if (initialBank) { - this.bank = JSON.parse( - JSON.stringify(initialBank instanceof Bank ? initialBank.bank : fasterResolveBank(initialBank)), - ); + static withSanitizedValues(source: ItemBank | IntKeyBank): Bank { + const mutSource = { ...source }; + sanitizeItemBank(mutSource); + return new Bank(mutSource); + } + + constructor(initialBank?: IntKeyBank | ItemBank | Bank) { + this.map = this.makeFromInitialBank(initialBank); + } + + public removeInvalidValues(): Bank { + for (const [key, qty] of this.map.entries()) { + if (!isValidBankQuantity(qty) || !Items.has(key)) { + this.map.delete(key); + } } + return this; + } + + private resolveItemID(item: ItemResolvable): number { + if (typeof item === "number") return item; + if (typeof item === "string") return itemID(item); + return item.id; + } + + public clear(item?: Item | string | number): this { + if (this.frozen) throw new Error(frozenErrorStr); + if (item) { + this.set(this.resolveItemID(item), 0); + return this; + } + this.map.clear(); + return this; + } + + private makeFromInitialBank(initialBank?: IntKeyBank | ItemBank | Bank) { + if (!initialBank) return new Map(); + if (initialBank instanceof Bank) { + return new Map(initialBank.map.entries()); + } + const entries = Object.entries(initialBank); + if (entries.length === 0) return new Map(); + if (isValidInteger(entries[0][0])) { + return new Map(entries.map(([k, v]) => [Number(k), v])); + } else { + return new Map(entries.map(([k, v]) => [Items.get(k)!.id, v])); + } + } + + public toJSON(): ItemBank { + return Object.fromEntries(this.map); + } + + public set(item: ItemResolvable, quantity: number): this { + if (this.frozen) throw new Error(frozenErrorStr); + const id = this.resolveItemID(item); + this.map.set(id, quantity); + return this; } public freeze(): this { this.frozen = true; - Object.freeze(this.bank); + Object.freeze(this.map); return this; } - public amount(item: string | number): number { - return this.bank[typeof item === "string" ? itemID(item) : item] ?? 0; + public amount(item: ItemResolvable): number { + const id = this.resolveItemID(item); + return this.map.get(id) ?? 0; } public addItem(item: number, quantity = 1): this { + if (this.frozen) throw new Error(frozenErrorStr); if (quantity < 1) return this; - if (this.bank[item]) this.bank[item] += quantity; - else this.bank[item] = quantity; + const current = this.map.get(item) ?? 0; + this.map.set(item, current + quantity); return this; } public removeItem(item: number | string, quantity = 1): this { - const currentValue = this.bank[item]; + if (this.frozen) throw new Error(frozenErrorStr); + const id = this.resolveItemID(item); + const currentValue = this.map.get(id); - if (typeof currentValue === "undefined") return this; + if (currentValue === undefined) return this; if (currentValue - quantity <= 0) { - delete this.bank[item]; + this.map.delete(id); } else { - this.bank[item] = currentValue - quantity; + this.map.set(id, currentValue - quantity); } return this; } - public add(item: string | number | ReturnedLootItem[] | ItemBank | Bank | Item | undefined, quantity = 1): Bank { + public add(item: string | number | IntKeyBank | Bank | Item | undefined, quantity = 1): Bank { if (this.frozen) throw new Error(frozenErrorStr); // Bank.add(123); @@ -64,15 +140,13 @@ export default class Bank { } if (item instanceof Bank) { - return this.add(item.bank); - } - - if (!item) { + for (const [itemID, qty] of item.map.entries()) { + this.addItem(itemID, qty); + } return this; } - if (Array.isArray(item)) { - for (const _item of item) this.addItem(_item.item, _item.quantity); + if (!item) { return this; } @@ -81,23 +155,21 @@ export default class Bank { return this.addItem(_item.id, quantity); } - const firstKey: string | undefined = Object.keys(item)[0]; - if (firstKey === undefined) { - return this; - } - - if (Number.isNaN(Number(firstKey))) { - this.add(resolveNameBank(item)); - } else { - for (const [itemID, quantity] of Object.entries(item)) { - this.addItem(Number.parseInt(itemID), quantity); + for (const [itemID, qty] of Object.entries(item)) { + let int: number | undefined = Number.parseInt(itemID); + if (Number.isNaN(int)) { + int = Items.get(itemID)?.id; + } + if (!int) { + throw new Error(`${itemID} is not a valid name or id`); } + this.addItem(int, qty); } return this; } - public remove(item: string | number | ReturnedLootItem[] | ItemBank | Bank, quantity = 1): Bank { + public remove(item: string | number | ItemBank | Bank, quantity = 1): Bank { if (this.frozen) throw new Error(frozenErrorStr); // Bank.remove('Twisted bow'); @@ -112,44 +184,28 @@ export default class Bank { } if (item instanceof Bank) { - for (const [key, value] of Object.entries(item.bank)) { - this.removeItem(key, value); - if (this.length === 0) break; + for (const [itemID, qty] of item.map.entries()) { + this.removeItem(itemID, qty); } return this; } - const firstKey = Object.keys(item)[0]; - if (firstKey === undefined) { - return this; - } - - if (Array.isArray(item)) { - for (const _item of item) this.remove(_item.item, _item.quantity); - return this; - } - - if (Number.isNaN(Number(firstKey))) { - this.remove(resolveNameBank(item)); - } else { - return this.remove(new Bank(item)); - } - + this.remove(new Bank(item)); return this; } public random(): BankItem | null { - const entries = Object.entries(this.bank); + const entries = Array.from(this.map.entries()); if (entries.length === 0) return null; const randomEntry = randArrItem(entries); - return { id: Number(randomEntry[0]), qty: randomEntry[1] }; + return { id: randomEntry[0], qty: randomEntry[1] }; } public multiply(multiplier: number, itemsToNotMultiply?: number[]): this { if (this.frozen) throw new Error(frozenErrorStr); - for (const itemID of Object.keys(this.bank).map(Number)) { + for (const [itemID, quantity] of this.map.entries()) { if (itemsToNotMultiply?.includes(itemID)) continue; - this.bank[itemID] *= multiplier; + this.map.set(itemID, quantity * multiplier); } return this; } @@ -176,8 +232,9 @@ export default class Bank { public items(): [Item, number][] { const arr: [Item, number][] = []; - for (const [key, val] of Object.entries(this.bank)) { - arr.push([Items.get(Number.parseInt(key))!, val]); + for (const [key, val] of this.map.entries()) { + if (val < 1) continue; + arr.push([Items.get(key)!, val]); } return arr; } @@ -189,7 +246,7 @@ export default class Bank { } public clone(): Bank { - return new Bank({ ...this.bank }); + return new Bank(this); } public fits(bank: Bank): number { @@ -198,36 +255,29 @@ export default class Bank { return divisions[0] ?? 0; } - public filter(fn: (item: Item, quantity: number) => boolean, mutate = false): Bank { + public filter(fn: (item: Item, quantity: number) => boolean): Bank { const result = new Bank(); for (const item of this.items()) { if (fn(...item)) { result.add(item[0].id, item[1]); } } - if (mutate) { - if (this.frozen) throw new Error(frozenErrorStr); - this.bank = result.bank; - return this; - } return result; } public toString(): string { - const entries = Object.entries(this.bank); - if (entries.length === 0) { + const items = this.items(); + if (items.length === 0) { return "No items"; } - const res = []; - for (const [id, qty] of entries.sort((a, b) => b[1] - a[1])) { - res.push(`${qty.toLocaleString()}x ${Items.get(Number(id))?.name ?? "Unknown item"}`); - } - - return res.join(", "); + return items + .sort((a, b) => a[0].name.localeCompare(b[0].name)) + .map(([item, qty]) => `${qty < 1000 ? `${qty}x` : toKMB(qty)} ${item?.name ?? "Unknown item"}`) + .join(", "); } public get length(): number { - return Object.keys(this.bank).length; + return this.map.size; } public value(): number { @@ -243,10 +293,34 @@ export default class Bank { for (const [item, quantity] of this.items()) { if (otherBank.amount(item.id) !== quantity) return false; } - return JSON.stringify(this.bank) === JSON.stringify(otherBank.bank); + return true; } public difference(otherBank: Bank): Bank { return this.clone().remove(otherBank).add(otherBank.clone().remove(this)); } + + public validate(): string[] { + const errors: string[] = []; + for (const [item, quantity] of this.map.entries()) { + if (typeof quantity !== "number" || quantity < 1 || !Number.isInteger(quantity)) { + errors.push(`Item ${item} has a quantity of ${quantity}`); + } + if (typeof item !== "number" || !item || !Items.get(item)?.id) { + errors.push(`Item ${item} does not exist.`); + } + } + return errors; + } + + public validateOrThrow() { + const errors = this.validate(); + if (errors.length > 0) { + throw new Error(`Bank validation failed: ${errors.join(", ")}`); + } + } + + get itemIDs(): number[] { + return Array.from(this.map.keys()); + } } diff --git a/src/structures/LootTable.ts b/src/structures/LootTable.ts index 876e7e6f6..a17dcd084 100644 --- a/src/structures/LootTable.ts +++ b/src/structures/LootTable.ts @@ -1,10 +1,41 @@ -import { randFloat, randInt, reduceNumByPercent, roll } from "e"; - -import type { LootTableItem, LootTableMoreOptions, LootTableOptions, OneInItems } from "../meta/types"; import itemID from "../util/itemID"; import Bank from "./Bank"; import Items from "./Items"; +export function reduceNumByPercent(value: number, percent: number): number { + if (percent <= 0) return value; + return value - value * (percent / 100); +} +export function randInt(min: number, max: number): number { + return Math.floor(Math.random() * (max - min + 1) + min); +} +export function randFloat(min: number, max: number): number { + return Math.random() * (max - min) + min; +} + +export function roll(upperLimit: number): boolean { + return randInt(1, upperLimit) === 1; +} + +export interface LootTableOptions { + limit?: number; +} + +export interface LootTableMoreOptions { + multiply?: boolean; + freeze?: boolean; +} + +export interface LootTableItem { + item: number | LootTable; + weight?: number; + quantity: number | number[]; + options?: LootTableMoreOptions; +} + +export interface OneInItems extends LootTableItem { + chance: number; +} export function isArrayOfItemTuples(x: readonly unknown[]): x is [string, (number | number[])?][] { return Array.isArray(x[0]); } @@ -16,6 +47,7 @@ export interface LootTableRollOptions { * item_id droprate will be decreased by percentage%. */ tertiaryItemPercentageChanges?: Map; + targetBank?: Bank; } export default class LootTable { @@ -134,7 +166,7 @@ export default class LootTable { } public add( - item: LootTable | number | string | [string, (number | number[])?][] | LootTableItem[], + item: LootTable | number | string, quantity: number[] | number = 1, weight = 1, options?: LootTableMoreOptions, @@ -146,23 +178,6 @@ export default class LootTable { return this.add(this.resolveName(item), quantity, weight, options); } - // If its an array, but not a LootTableItem[] array. - // i.e, if its directly from the user, and not being internally added. - if (Array.isArray(item) && isArrayOfItemTuples(item)) { - const newItems = []; - const _item = item as [string, (number | number[])?][]; - for (const itemToAdd of _item) { - const resolvedId = this.resolveName(itemToAdd[0]); - this.addToAllItems(resolvedId); - newItems.push({ - item: resolvedId, - quantity: this.determineQuantity(itemToAdd[1]!) || 1, - }); - } - - return this.add(newItems, quantity, weight, options); - } - this.length += 1; this.totalWeight += weight; @@ -178,10 +193,13 @@ export default class LootTable { return this; } - public roll(quantity = 1, options?: LootTableRollOptions): Bank { - const loot = new Bank(); + roll(quantity?: number): Bank; + roll(quantity: number, options: { targetBank?: undefined } & LootTableRollOptions): Bank; + roll(quantity: number, options: { targetBank: Bank } & LootTableRollOptions): null; + public roll(quantity = 1, options: LootTableRollOptions = {}): Bank | null { + const loot = options.targetBank ?? new Bank(); - const effectiveTertiaryItems = options?.tertiaryItemPercentageChanges + const effectiveTertiaryItems = options.tertiaryItemPercentageChanges ? this.tertiaryItems.map(i => { if (typeof i.item !== "number") return i; if (i.options?.freeze === true) return i; @@ -222,7 +240,6 @@ export default class LootTable { for (let i = 0; i < this.table.length; i++) { const item = this.table[i]!; - weight += item.weight!; if (randomWeight <= weight) { result = i; @@ -231,33 +248,30 @@ export default class LootTable { } const chosenItem = this.table[result]; - this.addResultToLoot(chosenItem, loot); + if (chosenItem) { + this.addResultToLoot(chosenItem, loot); + } } + if (options.targetBank) return null; return loot; } - private addResultToLoot(result: LootTableItem | undefined, loot: Bank): void { + private addResultToLoot(result: LootTableItem, loot: Bank): void { if (!result) return; const { item, quantity, options } = result; - const multiply = options?.multiply; - if (Array.isArray(item)) { - for (const singleItem of item) { - this.addResultToLoot(singleItem, loot); - } + if (typeof item === "number") { + loot.add(item, this.determineQuantity(quantity)); return; } - const qty = this.determineQuantity(quantity); - if (item instanceof LootTable) { - if (multiply) loot.add(item.roll(1).multiply(qty)); - else loot.add(item.roll(qty)); + const qty = this.determineQuantity(quantity); + if (options?.multiply) loot.add(item.roll(1).multiply(qty)); + else item.roll(qty, { targetBank: loot }); return; } - - loot.add(item, qty); } protected determineQuantity(quantity: number | number[]): number { diff --git a/src/structures/SimpleMonster.ts b/src/structures/SimpleMonster.ts index da7a9dc19..0db55c4c0 100644 --- a/src/structures/SimpleMonster.ts +++ b/src/structures/SimpleMonster.ts @@ -49,6 +49,7 @@ export default class SimpleMonster extends Monster { const canGetBrimKey = options.onSlayerTask && options.slayerMaster === MonsterSlayerMaster.Konar; const wildySlayer = options.onSlayerTask && options.slayerMaster === MonsterSlayerMaster.Krystilia; const slayerMonster: boolean = Boolean(options.onSlayerTask && this.data.slayerLevelRequired > 1); + const lootTableOptions = { ...options.lootTableOptions, targetBank: loot }; for (let i = 0; i < quantity; i++) { if (canGetBrimKey) { @@ -76,17 +77,17 @@ export default class SimpleMonster extends Monster { if (options.onSlayerTask) { if (wildySlayer && this.wildyCaveTable) { // Roll the monster's wildy slayer cave table - loot.add(this.wildyCaveTable.roll(1, options.lootTableOptions)); + this.wildyCaveTable.roll(1, lootTableOptions); } else if (this.onTaskTable) { // Roll the monster's "on-task" table. - loot.add(this.onTaskTable.roll(1, options.lootTableOptions)); + this.onTaskTable.roll(1, lootTableOptions); } else { // Monster doesn't have a unique on-slayer table - loot.add(this.table?.roll(1, options.lootTableOptions)); + this.table?.roll(1, lootTableOptions); } } else { // Not on slayer task - loot.add(this.table?.roll(1, options.lootTableOptions)); + this.table?.roll(1, lootTableOptions); } if (this.customKillLogic) { this.customKillLogic(options, loot); diff --git a/src/util/smallUtils.ts b/src/util/smallUtils.ts new file mode 100644 index 000000000..89f5b4360 --- /dev/null +++ b/src/util/smallUtils.ts @@ -0,0 +1,28 @@ +import { round } from "e"; + +export function toKMB(number: number): string { + if (number > 999_999_999 || number < -999_999_999) { + return `${round(number / 1_000_000_000)}b`; + } else if (number > 999_999 || number < -999_999) { + return `${round(number / 1_000_000)}m`; + } else if (number > 999 || number < -999) { + return `${round(number / 1000)}k`; + } + return round(number).toString(); +} + +export function fromKMB(number: string): number { + number = number.toLowerCase().replace(/,/g, ""); + const [numberBefore, numberAfter] = number.split(/[.kmb]/g); + + let newNum = numberBefore; + if (number.includes("b")) { + newNum += numberAfter + "0".repeat(9).slice(numberAfter.length); + } else if (number.includes("m")) { + newNum += numberAfter + "0".repeat(6).slice(numberAfter.length); + } else if (number.includes("k")) { + newNum += numberAfter + "0".repeat(3).slice(numberAfter.length); + } + + return Number.parseInt(newNum); +} diff --git a/src/util/util.ts b/src/util/util.ts index 45853a39e..86f0a57a4 100644 --- a/src/util/util.ts +++ b/src/util/util.ts @@ -1,7 +1,7 @@ -import { randFloat, randInt, roll, round } from "e"; +import { randFloat, randInt, roll } from "e"; import { CLUES, MINIGAMES, SKILLS, type hiscoreURLs, mappedBossNames } from "../constants"; -import type { CustomKillLogic, Item, ItemBank, LootBank, MonsterKillOptions } from "../meta/types"; +import type { CustomKillLogic, Item, MonsterKillOptions } from "../meta/types"; import type Bank from "../structures/Bank"; import Items from "../structures/Items"; import LootTable from "../structures/LootTable"; @@ -112,33 +112,6 @@ export function convertXPtoLVL(xp: number, cap = 99): number { return cap; } -export function toKMB(number: number): string { - if (number > 999_999_999 || number < -999_999_999) { - return `${round(number / 1_000_000_000)}b`; - } else if (number > 999_999 || number < -999_999) { - return `${round(number / 1_000_000)}m`; - } else if (number > 999 || number < -999) { - return `${round(number / 1000)}k`; - } - return round(number).toString(); -} - -export function fromKMB(number: string): number { - number = number.toLowerCase().replace(/,/g, ""); - const [numberBefore, numberAfter] = number.split(/[.kmb]/g); - - let newNum = numberBefore; - if (number.includes("b")) { - newNum += numberAfter + "0".repeat(9).slice(numberAfter.length); - } else if (number.includes("m")) { - newNum += numberAfter + "0".repeat(6).slice(numberAfter.length); - } else if (number.includes("k")) { - newNum += numberAfter + "0".repeat(3).slice(numberAfter.length); - } - - return Number.parseInt(newNum); -} - export function getBrimKeyChanceFromCBLevel(combatLevel: number): number { // https://twitter.com/JagexKieren/status/1083781544135847936 if (combatLevel < 100) { @@ -168,15 +141,6 @@ export function JSONClone(object: O): O { return JSON.parse(JSON.stringify(object)); } -export function convertLootBanksToItemBanks(lootResult: LootBank): Record { - const result: { [key: string]: ItemBank } = {}; - for (const [id, loot] of Object.entries(lootResult)) { - result[id] = { ...loot.bank }; - } - - return result; -} - export function getAncientShardChanceFromHP(hitpoints: number): number { return Math.round((500 - hitpoints) / 1.5); } @@ -318,3 +282,13 @@ export function deepResolveItems(itemArray: ArrayItemsResolvable): ArrayItemsRes return newArray; } + +export function itemTupleToTable(items: [string, number | [number, number]][]): LootTable { + const table = new LootTable(); + for (const [item, quantity] of items) { + table.every(item, quantity ?? 1); + } + return table; +} + +export * from "./smallUtils"; diff --git a/test/Bank.test.ts b/test/Bank.test.ts index 6b4d346cf..2c5597cdc 100644 --- a/test/Bank.test.ts +++ b/test/Bank.test.ts @@ -1,8 +1,10 @@ -import { describe, expect, test } from "vitest"; +import { describe, expect, it, test } from "vitest"; +import { EItem } from "../src"; +import type { ItemBank } from "../src/meta/types"; import Bank from "../src/structures/Bank"; import Items from "../src/structures/Items"; -import { addItemToBank, itemID, resolveNameBank } from "../src/util"; +import { addItemToBank, getItemOrThrow, itemID, resolveNameBank } from "../src/util"; describe("Bank", () => { test("convert string bank to number bank", () => { @@ -22,66 +24,56 @@ describe("Bank", () => { test("bank has all items", () => { expect.assertions(2); - const bankToHave = new Bank({ - "Fire rune": 1000, - "Air rune": 1, - "Chaos rune": 101_010, - }); + const bankToHave = new Bank( + resolveNameBank({ + "Fire rune": 1000, + "Air rune": 1, + "Chaos rune": 101_010, + }), + ); - const bankThatShouldntHave = new Bank({ - "Fire rune": 1000, - "Air rune": 1, - "Chaos rune": 1, - }); + const bankThatShouldntHave = new Bank( + resolveNameBank({ + "Fire rune": 1000, + "Air rune": 1, + "Chaos rune": 1, + }), + ); - const bankThatShouldHave = new Bank({ - "Fire rune": 104_200, - "Air rune": 43_432, - "Chaos rune": 121_010, - "Death rune": 121_010, - }); + const bankThatShouldHave = new Bank( + resolveNameBank({ + "Fire rune": 104_200, + "Air rune": 43_432, + "Chaos rune": 121_010, + "Death rune": 121_010, + }), + ); expect(bankThatShouldHave.has(bankToHave)).toBeTruthy(); expect(bankThatShouldntHave.has(bankToHave)).toBeFalsy(); }); - test("remove item from bank", () => { - expect.assertions(3); - const bank = new Bank({ - 45: 9, - 87: 1, - }); - - expect(bank.clone().remove(87).bank).toEqual({ - 45: 9, - }); - - expect(bank.clone().remove(98).bank).toEqual({ - 45: 9, - 87: 1, - }); - - expect(bank.clone().remove(45, 2).bank).toEqual({ - 45: 7, - 87: 1, - }); - }); - test("remove bank from bank", () => { expect.assertions(1); - const sourceBank = new Bank({ - "Fire rune": 100, - "Air rune": 50, - }); + const sourceBank = new Bank( + resolveNameBank({ + "Fire rune": 100, + "Air rune": 50, + }), + ); - const bankToRemove = new Bank({ - "Fire rune": 50, - "Air rune": 50, - }); + const bankToRemove = new Bank( + resolveNameBank({ + "Fire rune": 50, + "Air rune": 50, + }), + ); - const expectedBank = new Bank({ - "Fire rune": 50, - }); + const expectedBank = new Bank( + resolveNameBank({ + "Fire rune": 50, + }), + ); sourceBank.remove(bankToRemove); expect(sourceBank.equals(expectedBank)).toBeTruthy(); @@ -118,7 +110,7 @@ describe("Bank", () => { const expected = { 1: 2, 3: 4 }; - expect(new Bank(bank).add(bank2).bank).toEqual(expected); + expect(new Bank(bank).add(bank2).equals(new Bank(expected))).toBeTruthy(); }); test("add item to bank", () => { @@ -147,64 +139,39 @@ describe("Bank", () => { expect(bank.amount("Egg")).toEqual(100); }); - test("mutate filter", () => { - const bank = new Bank({ - Toolkit: 2, - "Ammo Mould": 4, - Candle: 1, - }); - expect(bank.length).toEqual(3); - const empty = bank.filter(() => false); - expect(bank.length).toEqual(3); - expect(empty.length).toEqual(0); - bank.filter(item => item.name === "Candle", true); - expect(bank.length).toEqual(1); - }); - test("value", () => { - const bank = new Bank({ - Toolkit: 2, - }); + const bank = new Bank( + resolveNameBank({ + Toolkit: 2, + }), + ); expect(bank.value()).toEqual(0); const runePlatebody = Items.get("Rune platebody")!; - const bank2 = new Bank({ - "Rune platebody": 10, - }); + const bank2 = new Bank( + resolveNameBank({ + "Rune platebody": 10, + }), + ); expect(runePlatebody.price).toBeGreaterThan(25_000); expect(bank2.value()).toEqual(runePlatebody.price * 10); - const bank3 = new Bank({ - "Rune platebody": 10, - "Rune platelegs": 10, - "Rune boots": 10, - Toolkit: 1, - "Abyssal book": 10_000, - }); + const bank3 = new Bank( + resolveNameBank({ + "Rune platebody": 10, + "Rune platelegs": 10, + "Rune boots": 10, + Toolkit: 1, + "Abyssal book": 10_000, + }), + ); expect(runePlatebody.price).toBeGreaterThan(25_000); expect(bank3.value()).toEqual( runePlatebody.price * 10 + Items.get("Rune platelegs")!.price * 10 + Items.get("Rune boots")!.price * 10, ); }); - test("init from bank", () => { - const start: any = { 1: 1 }; - let bank = new Bank(start); - const bankToTest = new Bank(bank); - delete start[1]; - delete bank.bank[1]; - start[2] = 1; - bank.bank[2] = 1; - bank = bank.multiply(100); - bank.bank = {}; - expect(bankToTest.amount(1)).toEqual(1); - expect(bankToTest.length).toEqual(1); - }); - test("frozen bank", () => { const bank = new Bank().add("Twisted bow", 73).add("Egg", 5); bank.freeze(); - try { - bank.bank[5] = 1; - } catch {} expect(bank.length).toEqual(2); expect(() => bank.add("Twisted bow")).toThrowError(); try { @@ -213,14 +180,16 @@ describe("Bank", () => { try { bank.addItem(itemID("Twisted bow")); } catch {} + expect(() => bank.removeItem("Twisted bow")).toThrowError(); try { bank.remove(itemID("Twisted bow")); } catch {} try { bank.multiply(5); } catch {} + try { - bank.filter(() => true, true); + bank.set("Twisted bow", 1000); } catch {} expect(bank.amount("Twisted bow")).toEqual(73); }); @@ -263,4 +232,166 @@ describe("Bank", () => { expect(bank.random()).toBeTruthy(); expect(new Bank().random()).toBeFalsy(); }); + + test("set", () => { + const bank = new Bank().add("Twisted bow", 73).add("Egg", 5); + bank.set("Twisted bow", 1); + expect(bank.amount("Twisted bow")).toEqual(1); + bank.set("Twisted bow", 0); + expect(bank.amount("Twisted bow")).toEqual(0); + bank.set("Twisted bow", 1); + expect(bank.amount("Twisted bow")).toEqual(1); + }); + + test("withSanitizedValues", () => { + const badBank: ItemBank = { + [-1]: 1, + 1: Number.NaN, + 2: Number.POSITIVE_INFINITY, + 3: Number.NEGATIVE_INFINITY, + 9: "", + 5: 1, + } as any as ItemBank; + const bank = Bank.withSanitizedValues(badBank); + expect(bank.length).toEqual(1); + expect(bank.amount(5)).toEqual(1); + }); + + function badBank() { + return { + [-1]: 1, + 1: Number.NaN, + 2: Number.POSITIVE_INFINITY, + 3: Number.NEGATIVE_INFINITY, + 9: "", + 5: 1, + } as any as ItemBank; + } + + test("removeInvalidValues", () => { + const bank = new Bank(badBank()); + bank.removeInvalidValues(); + expect(bank.length).toEqual(1); + expect(bank.amount(5)).toEqual(1); + }); + + it("should validate bad bank", () => { + const bank = new Bank(badBank()); + const result = bank.validate(); + expect(result.length).toBeGreaterThan(0); + }); + + it("should validate good bank", () => { + for (const bank of [new Bank(), new Bank().add("Coal", 1)]) { + const result = bank.validate(); + expect(result.length).toEqual(0); + } + }); + + it("should validateOrThrow bad bank", () => { + const bank = new Bank(badBank()); + expect(() => bank.validateOrThrow()).toThrow(); + }); + + it("should validateOrThrow good bank", () => { + for (const bank of [new Bank(), new Bank().add("Coal", 1)]) { + expect(() => bank.validateOrThrow()).not.toThrow(); + } + }); + + test("itemIDs", () => { + expect(new Bank().itemIDs).toEqual([]); + expect(new Bank().add("Coal", 1).itemIDs.sort()).toEqual([itemID("Coal")].sort()); + expect(new Bank().add("Coal", 1).add("Coal", 1).itemIDs.sort()).toEqual([itemID("Coal")].sort()); + expect(new Bank().add("Coal", 1).add("Trout", 1).itemIDs.sort()).toEqual( + [itemID("Coal"), itemID("Trout")].sort(), + ); + }); + + it("clears banks", () => { + expect(new Bank().clear().length).toEqual(0); + for (const bank of [new Bank().add("Coal", 1).add("Trout", 100000), new Bank().add("Coal", 1)]) { + expect(bank.length).toBeGreaterThan(0); + bank.clear(); + expect(bank.length).toEqual(0); + } + }); + + it("doesnt clear bank if frozen", () => { + const bank = new Bank().add("Coal", 1).add("Trout", 100000); + bank.freeze(); + expect(bank.length).toBeGreaterThan(0); + expect(() => bank.clear()).toThrow(); + expect(bank.length).toBeGreaterThan(0); + }); + + it("checks amount", () => { + const bank = new Bank().add(itemID("Coal")); + expect(bank.amount("Coal")).toEqual(1); + expect(bank.amount(itemID("Coal"))).toEqual(1); + expect(bank.amount(EItem.COAL)).toEqual(1); + expect(bank.amount(getItemOrThrow("Coal"))).toEqual(1); + }); + + it("sets and clears items", () => { + const methods = ["Coal", itemID("Coal"), EItem.COAL, getItemOrThrow("Coal")]; + for (const setMethod of methods) { + for (const amountMethod of methods) { + const bank = new Bank().set(setMethod, 5).add("Trout", 100000); + expect(bank.amount(amountMethod)).toEqual(5); + bank.clear(setMethod); + expect(bank.amount(amountMethod)).toEqual(0); + expect(bank.amount("Trout")).toEqual(100000); + } + } + }); + + it("adds itembank", () => { + const bank = new Bank().add("Coal", 100).add("Trout", 100); + const bankToAdd = resolveNameBank({ + Coal: 50, + Trout: 50, + }); + bank.add(bankToAdd); + expect(bank.amount("Coal")).toEqual(150); + expect(bank.amount("Trout")).toEqual(150); + expect(bank.length).toEqual(2); + }); + + it("adds namebank", () => { + const bank = new Bank().add("Coal", 100).add("Trout", 100); + const bankToAdd = { + Coal: 50, + Trout: 50, + }; + bank.add(bankToAdd); + expect(bank.amount("Coal")).toEqual(150); + expect(bank.amount("Trout")).toEqual(150); + expect(bank.length).toEqual(2); + }); + + it("throws if adding invalid name", () => { + const bank = new Bank().add("Coal", 100).add("Trout", 100); + const bankToAdd = { + Casdfoal: 50, + }; + expect(() => bank.add(bankToAdd)).toThrow(); + }); + + it("removes itembank", () => { + const bank = new Bank().add("Coal", 100).add("Trout", 100); + const bankToRemove = resolveNameBank({ + Coal: 50, + Trout: 50, + }); + bank.remove(bankToRemove); + expect(bank.amount("Coal")).toEqual(50); + expect(bank.amount("Trout")).toEqual(50); + expect(bank.length).toEqual(2); + }); + + it("converts to json", () => { + const bank = new Bank().add("Coal", 100).add("Trout", 100); + expect(bank.toJSON()).toEqual(resolveNameBank({ Coal: 100, Trout: 100 })); + }); }); diff --git a/test/BankClass.test.ts b/test/BankClass.test.ts index 5af3be133..f485349cf 100644 --- a/test/BankClass.test.ts +++ b/test/BankClass.test.ts @@ -1,7 +1,7 @@ import { describe, expect, test, vi } from "vitest"; import { Bank, Items, LootTable } from "../src"; -import type { Item, ReturnedLootItem } from "../src/meta/types"; +import type { Item } from "../src/meta/types"; import { getItemOrThrow, itemID, resolveNameBank } from "../src/util"; const TestLootTable = new LootTable().add("Toolkit"); @@ -14,7 +14,8 @@ describe("Bank Class", () => { expect(bank.amount(1)).toBe(2); expect(bank.amount("Toolkit")).toBe(2); expect(bank.amount("Twisted bow")).toBe(0); - expect(bank.bank).toEqual({ 1: 2 }); + expect(bank.amount(1)).toEqual(2); + expect(bank.length).toEqual(1); }); test("removing", () => { @@ -24,11 +25,12 @@ describe("Bank Class", () => { expect(bank.amount(1)).toBe(0); expect(bank.amount("Toolkit")).toBe(0); - expect(bank.bank).toEqual({}); + expect(bank.length).toEqual(0); - bank.add({ Coal: 1, Emerald: 1, Ruby: 1 }); + bank.add(resolveNameBank({ Coal: 1, Emerald: 1, Ruby: 1 })); bank.remove({ Coal: 9999, Emerald: 9999, Toolkit: 10_000 }); - expect(bank.bank).toEqual({ 1603: 1 }); + expect(bank.amount(1603)).toEqual(1); + expect(bank.length).toEqual(1); }); test("chaining", () => { @@ -52,21 +54,6 @@ describe("Bank Class", () => { expect(random).toEqual({ id: 69, qty: 420 }); }); - test("ReturnedLootItem", () => { - const items: ReturnedLootItem[] = [ - { item: 1, quantity: 5 }, - { item: 1, quantity: 5 }, - { item: 1, quantity: 0 }, - { item: 2, quantity: 10 }, - ]; - const bank = new Bank().add(items); - expect(bank.bank).toEqual({ 1: 10, 2: 10 }); - bank.remove(items); - expect(bank.bank).toEqual({}); - expect(bank.amount(1)).toBe(0); - expect(bank.amount(2)).toBe(0); - }); - test("other", () => { const bank = new Bank().add(1).add(1).add(1).add(1); @@ -75,10 +62,10 @@ describe("Bank Class", () => { bank.remove({ 1: 4 }); expect(bank.amount(1)).toBe(0); - bank.add({ Toolkit: 4 }); + bank.add(resolveNameBank({ Toolkit: 4 })); expect(bank.amount(1)).toBe(4); - bank.remove({ Toolkit: 4 }); + bank.remove(resolveNameBank({ Toolkit: 4 })); expect(bank.amount(1)).toBe(0); bank.add(TestLootTable.roll()); @@ -126,10 +113,10 @@ describe("Bank Class", () => { test("toString", () => { const bank = new Bank(resolveNameBank({ Coal: 20, Egg: 5000, Emerald: 1, Ruby: 20_000 })); bank.add("Twisted bow", 0); - expect(bank.toString()).toEqual("20,000x Ruby, 5,000x Egg, 20x Coal, 1x Emerald"); + expect(bank.toString()).toEqual("20x Coal, 5k Egg, 1x Emerald, 20k Ruby"); expect(bank.length).toEqual(4); bank.add("3rd age platebody", 2); - expect(bank.toString()).toEqual("20,000x Ruby, 5,000x Egg, 20x Coal, 2x 3rd age platebody, 1x Emerald"); + expect(bank.toString()).toEqual("2x 3rd age platebody, 20x Coal, 5k Egg, 1x Emerald, 20k Ruby"); expect(bank.length).toEqual(5); expect(new Bank().toString()).toEqual("No items"); expect(new Bank({ 111231231: 1 }).toString()).toEqual("1x Unknown item"); @@ -211,7 +198,7 @@ describe("Bank Class", () => { const bank = new Bank(baseBank); expect(bank.fits(bank)).toEqual(1); - const b1 = new Bank(bank.clone().multiply(2).bank); + const b1 = new Bank(bank.clone().multiply(2)); expect(b1.fits(bank)).toEqual(2); const b2 = new Bank(resolveNameBank({ Coal: 1 })); @@ -251,12 +238,12 @@ describe("Bank Class", () => { const idVersion = resolveNameBank(baseBank); const bank = new Bank(baseBank); expect(bank.amount("Coal")).toEqual(20); - expect(bank.bank).toEqual(idVersion); + expect(new Bank(idVersion).toString()).toEqual(new Bank(bank).toString()); expect(bank.has(idVersion)).toBeTruthy(); const otherBank = new Bank(idVersion); expect(otherBank.amount("Coal")).toEqual(20); - expect(otherBank.bank).toEqual(bank.bank); + expect(new Bank(otherBank).equals(new Bank(bank))).toBeTruthy(); expect(otherBank.has(idVersion)).toBeTruthy(); const base = { @@ -274,17 +261,7 @@ describe("Bank Class", () => { "Blade of saeldor": 0, }; - expect(new Bank(base).bank).toEqual(resolveNameBank(base)); - }); - - test("freeze bank", () => { - const bank = new Bank().add("Twisted bow").freeze(); - - expect(() => bank.add("Coal")).toThrow(); - expect(() => (bank.bank[itemID("Coal")] = 1)).toThrow(); - expect(() => delete bank.bank[itemID("Twisted bow")]).toThrow(); - - expect(bank.bank).toEqual({ [itemID("Twisted bow")]: 1 }); + expect(new Bank(base).equals(new Bank(resolveNameBank(base)))).toBeTruthy(); }); test("has item obj", () => { diff --git a/test/EItem.test.ts b/test/EItem.test.ts index 03bdd399e..4ac9ebf7f 100644 --- a/test/EItem.test.ts +++ b/test/EItem.test.ts @@ -1,6 +1,6 @@ import { expect, test } from "vitest"; -import { EItem } from "../dist/EItem.js"; +import { EItem } from "../src/EItem.js"; test("EItem", async () => { expect(EItem.TWISTED_BOW).toEqual(20997); diff --git a/test/LootTable.test.ts b/test/LootTable.test.ts index ba5611889..3fd361a6d 100644 --- a/test/LootTable.test.ts +++ b/test/LootTable.test.ts @@ -1,13 +1,55 @@ -import { expect, test } from "vitest"; +import { describe, expect, it } from "vitest"; +import { Bank } from "../src"; import LootTable from "../src/structures/LootTable"; -test("LootTable", async () => { - const table1 = new LootTable().every("Coal").tertiary(1, "Coal").add("Coal"); - const table2 = table1.clone(); +describe("LootTable", async () => { + it("should clone", () => { + const table1 = new LootTable().every("Coal").tertiary(1, "Coal").add("Coal"); + const table2 = table1.clone(); - table1.add("Coal").add("Coal").tertiary(1, "Coal").every("Bones"); + table1.add("Coal").add("Coal").tertiary(1, "Coal").every("Bones"); - expect(table2.length).toEqual(1); - expect(table2.tertiaryItems.length).toEqual(1); + expect(table2.length).toEqual(1); + expect(table2.tertiaryItems.length).toEqual(1); + }); + + it("should roll tertiary items", () => { + const table = new LootTable().tertiary(1, "Coal"); + for (const qty of [1, 2, 5]) { + const loot = table.roll(qty); + expect(loot.amount("Coal")).toEqual(qty); + + const loot2 = new Bank(); + table.roll(qty, { targetBank: loot2 }); + expect(loot2.amount("Coal")).toEqual(qty); + } + }); + + it("should roll weight items", () => { + const table = new LootTable().add("Coal", 1); + for (const qty of [1, 2, 5]) { + const loot = table.roll(qty); + expect(loot.amount("Coal")).toEqual(qty); + + const loot2 = new Bank(); + table.roll(qty, { targetBank: loot2 }); + expect(loot2.amount("Coal")).toEqual(qty); + } + }); + + it("should return null if loot passed", () => { + const loot = new Bank(); + const table = new LootTable().add("Coal", 1); + const res = table.roll(1, { targetBank: loot }); + expect(res).toBeNull(); + }); + + it("should return loot if no target passed", () => { + const table = new LootTable().add("Coal", 1); + const res = table.roll(1); + expect(res).toBeInstanceOf(Bank); + expect(res.amount("Coal")).toEqual(1); + expect(res.length).toEqual(1); + }); }); diff --git a/test/Monsters.test.ts b/test/Monsters.test.ts index 619955c2f..1a6032368 100644 --- a/test/Monsters.test.ts +++ b/test/Monsters.test.ts @@ -4,6 +4,7 @@ import { Monsters } from "../src"; import Bank from "../src/structures/Bank"; import LootTable from "../src/structures/LootTable"; import Monster from "../src/structures/Monster"; +import { itemTupleToTable } from "../src/util"; import { checkThreshold } from "./testUtil"; describe("Monsters", () => { @@ -18,7 +19,12 @@ describe("Monsters", () => { .add("Needle") .add("Amethyst") .add("Knife") - .add([["Iron bar"], ["Steel bar"]]) + .add( + itemTupleToTable([ + ["Iron bar", 1], + ["Steel bar", 1], + ]), + ) .add(subSubTable); beforeAll(async () => { diff --git a/test/testUtil.ts b/test/testUtil.ts index c0f6ed04d..64f680b02 100644 --- a/test/testUtil.ts +++ b/test/testUtil.ts @@ -6,20 +6,16 @@ export function withinThreshold(source: number, target: number, epsilon = 5): bo } export function checkThreshold(expectedRates: Record, _result: Bank, numberDone: number): void { - const result = _result.bank; for (const [name, qty] of Object.entries(expectedRates)) { const item = Items.get(name); if (!item) throw new Error(`Missing item: ${name}`); - if (!result[item.id]) { + if (!_result.has(item.id)) { throw new Error(`Was no ${item.name}[${item.id}] in result, should have been.`); } expectedRates[item.id.toString()] = qty; } - for (const [itemID, qty] of Object.entries(result)) { - const item = Items.get(Number.parseInt(itemID)); - if (!item) throw new Error(`Missing item with ID: ${itemID}`); - + for (const [item, qty] of _result.items()) { const { id } = item; const expectedRate = expectedRates[id]; if (!expectedRate) continue; diff --git a/tsconfig.json b/tsconfig.json index 5f93bb009..ce7613de4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,5 +26,6 @@ "esModuleInterop": true, "skipLibCheck": true, "lib": ["ES2023"] - } + }, + "include": ["src/**/*"] } diff --git a/update-history/item-update-2024-9-3.json b/update-history/item-update-2024-9-3.json new file mode 100644 index 000000000..c96dc9cbb --- /dev/null +++ b/update-history/item-update-2024-9-3.json @@ -0,0 +1,2149 @@ +{ + "5374": { + "buy_limit": 100 + }, + "8007": { + "buy_limit": 15000 + }, + "8013": { + "buy_limit": 15000 + }, + "11174": { + "examine": "I can use this to claim a reward from the King, if I get the other half." + }, + "21177": { + "buy_limit": 15000 + }, + "22842": { + "weapon": { + "attack_speed": 4 + } + }, + "22844": { + "weapon": { + "attack_speed": 4 + } + }, + "22846": { + "weapon": { + "attack_speed": 4 + } + }, + "22859": { + "buy_limit": 100 + }, + "23122": { + "weapon": { + "attack_speed": 4 + } + }, + "24613": { + "buy_limit": 15000 + }, + "26243": { + "buy_limit": 8 + }, + "26245": { + "buy_limit": 8 + }, + "27180": { + "id": 27180, + "name": "Guthix chaps", + "equipable": true, + "cost": 10, + "weight": 5, + "examine": "Guthix blessed dragonhide chaps.", + "wiki_name": "Guthix chaps (Last Man Standing)", + "wiki_url": "https://oldschool.runescape.wiki/w/Guthix_chaps_(Last_Man_Standing)", + "equipment": {}, + "price": 0 + }, + "27181": { + "id": 27181, + "name": "Zamorak chaps", + "equipable": true, + "cost": 10, + "weight": 5, + "examine": "Zamorak blessed dragonhide chaps.", + "wiki_name": "Zamorak chaps (Last Man Standing)", + "wiki_url": "https://oldschool.runescape.wiki/w/Zamorak_chaps_(Last_Man_Standing)", + "equipment": {}, + "price": 0 + }, + "27182": { + "id": 27182, + "name": "Saradomin chaps", + "equipable": true, + "cost": 10, + "weight": 5, + "examine": "Saradomin blessed dragonhide chaps.", + "wiki_name": "Saradomin chaps (Last Man Standing)", + "wiki_url": "https://oldschool.runescape.wiki/w/Saradomin_chaps_(Last_Man_Standing)", + "equipment": {}, + "price": 0 + }, + "27194": { + "id": 27194, + "name": "Ancestral robe bottom", + "equipable": true, + "equipable_by_player": true, + "cost": 55, + "weight": 18.14, + "examine": "The robe bottoms of a powerful sorceress from a bygone era.", + "wiki_name": "Ancestral robe bottom (Last Man Standing)", + "wiki_url": "https://oldschool.runescape.wiki/w/Ancestral_robe_bottom_(Last_Man_Standing)", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 26, + "attack_ranged": -7, + "defence_stab": 27, + "defence_slash": 24, + "defence_crush": 30, + "defence_magic": 20, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 4, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "28872": { + "buy_limit": 11000 + }, + "29084": { + "buy_limit": 15 + }, + "29110": { + "buy_limit": 13000 + }, + "29174": { + "buy_limit": 18000 + }, + "29628": { + "buy_limit": 6 + }, + "29686": { + "id": 29686, + "name": "Dni23 torso lightbuttons", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso lightbuttons", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_lightbuttons", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29688": { + "id": 29688, + "name": "Dni23 torso darkbuttons", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "lowalch": 0, + "highalch": 0, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso darkbuttons (Dark Buttons)", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_darkbuttons#Dark_Buttons", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29690": { + "id": 29690, + "name": "Dni23 torso darkbuttons", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "lowalch": 0, + "highalch": 0, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso darkbuttons (Jacket)", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_darkbuttons#Jacket", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29692": { + "id": 29692, + "name": "Dni23 torso shirt", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso shirt", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_shirt", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29694": { + "id": 29694, + "name": "Dni23 torso stitching", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso stitching", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_stitching", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29696": { + "id": 29696, + "name": "Dni23 torso twotoned", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso twotoned", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_twotoned", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29698": { + "id": 29698, + "name": "Dni23 torso princely", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso princely", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_princely", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29700": { + "id": 29700, + "name": "Dni23 torso rippedweskit", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "lowalch": 0, + "highalch": 0, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso rippedweskit (Ripped Weskit)", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_rippedweskit#Ripped_Weskit", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29702": { + "id": 29702, + "name": "Dni23 torso rippedweskit", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "lowalch": 0, + "highalch": 0, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso rippedweskit (Torn Weskit)", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_rippedweskit#Torn_Weskit", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29704": { + "id": 29704, + "name": "Dni23 torso croptops", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso croptops", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_croptops", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29706": { + "id": 29706, + "name": "Dni23 torso poloneck", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso poloneck", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_poloneck", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29708": { + "id": 29708, + "name": "Dni23 torso simple", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso simple", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_simple", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29710": { + "id": 29710, + "name": "Dni23 torso frilly", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso frilly", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_frilly", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29712": { + "id": 29712, + "name": "Dni23 torso corsetry", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso corsetry", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_corsetry", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29714": { + "id": 29714, + "name": "Dni23 torso bodice", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 torso bodice", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_torso_bodice", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "body", + "requirements": null + }, + "price": 0 + }, + "29716": { + "id": 29716, + "name": "Dni23 arms thin", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms thin", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_thin", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29718": { + "id": 29718, + "name": "Dni23 arms shoulderpads", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms shoulderpads", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_shoulderpads", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29720": { + "id": 29720, + "name": "Dni23 arms thickstripe", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms thickstripe", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_thickstripe", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29722": { + "id": 29722, + "name": "Dni23 arms loosesleeves", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms loosesleeves", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_loosesleeves", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29724": { + "id": 29724, + "name": "Dni23 arms princely", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms princely", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_princely", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29726": { + "id": 29726, + "name": "Dni23 arms tattylong", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms tattylong", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_tattylong", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29728": { + "id": 29728, + "name": "Dni23 arms ripped", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms ripped", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_ripped", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29730": { + "id": 29730, + "name": "Dni23 arms bare", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms bare", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_bare", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29732": { + "id": 29732, + "name": "Dni23 arms frilly", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms frilly", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_frilly", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29734": { + "id": 29734, + "name": "Dni23 arms tattyshort", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms tattyshort", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_tattyshort", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29736": { + "id": 29736, + "name": "Dni23 arms bareshoulders", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 arms bareshoulders", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_arms_bareshoulders", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "hands", + "requirements": null + }, + "price": 0 + }, + "29738": { + "id": 29738, + "name": "Dni23 legs shorts", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs shorts", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_shorts", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29740": { + "id": 29740, + "name": "Dni23 legs beach", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs beach", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_beach", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29742": { + "id": 29742, + "name": "Dni23 legs princely", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs princely", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_princely", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29744": { + "id": 29744, + "name": "Dni23 legs leggings", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs leggings", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_leggings", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29746": { + "id": 29746, + "name": "Dni23 legs sidestripes", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs sidestripes", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_sidestripes", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29748": { + "id": 29748, + "name": "Dni23 legs ripped", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs ripped", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_ripped", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29750": { + "id": 29750, + "name": "Dni23 legs patched", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs patched", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_patched", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29752": { + "id": 29752, + "name": "Dni23 legs skirt", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs skirt", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_skirt", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29754": { + "id": 29754, + "name": "Dni23 legs longskirt", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs longskirt", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_longskirt", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29756": { + "id": 29756, + "name": "Dni23 legs longnarrowskirt", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs longnarrowskirt", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_longnarrowskirt", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29758": { + "id": 29758, + "name": "Dni23 legs shortskirt", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs shortskirt", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_shortskirt", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29760": { + "id": 29760, + "name": "Dni23 legs layered", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs layered", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_layered", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29762": { + "id": 29762, + "name": "Dni23 legs sashdots", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs sashdots", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_sashdots", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29764": { + "id": 29764, + "name": "Dni23 legs bighem", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs bighem", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_bighem", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29766": { + "id": 29766, + "name": "Dni23 legs sashtrousers", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs sashtrousers", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_sashtrousers", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29768": { + "id": 29768, + "name": "Dni23 legs patterned", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs patterned", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_patterned", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29770": { + "id": 29770, + "name": "Dni23 legs tornskirt", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs tornskirt", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_tornskirt", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29772": { + "id": 29772, + "name": "Dni23 legs patchedskirt", + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "weight": 0.3, + "release_date": "2024-08-14", + "examine": "Test", + "wiki_name": "Dni23 legs patchedskirt", + "wiki_url": "https://oldschool.runescape.wiki/w/Dni23_legs_patchedskirt", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "legs", + "requirements": null + }, + "price": 0 + }, + "29774": { + "id": 29774, + "name": "Amy's saw (off-hand)", + "members": true, + "equipable": true, + "equipable_by_player": true, + "cost": 280, + "weight": 0.8, + "release_date": "2024-08-14", + "examine": "She saw scope for improvement.", + "wiki_name": "Amy's saw (off-hand)", + "wiki_url": "https://oldschool.runescape.wiki/w/Amy's_saw_(off-hand)", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "shield", + "requirements": null + }, + "price": 0 + }, + "29775": { + "id": 29775, + "name": "Imcando hammer (off-hand)", + "members": true, + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "lowalch": 0, + "highalch": 0, + "weight": 2, + "release_date": "2024-08-14", + "examine": "It's good for holding and hitting things!", + "wiki_name": "Imcando hammer (off-hand)", + "wiki_url": "https://oldschool.runescape.wiki/w/Imcando_hammer_(off-hand)", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "shield", + "requirements": null + }, + "price": 0 + }, + "29777": { + "id": 29777, + "name": "Bruma torch (off-hand)", + "members": true, + "equipable": true, + "equipable_by_player": true, + "cost": 1, + "lowalch": 0, + "highalch": 0, + "weight": 0.003, + "release_date": "2024-08-14", + "examine": "A torch made from the wood of the magical Bruma tree.", + "wiki_name": "Bruma torch (off-hand)", + "wiki_url": "https://oldschool.runescape.wiki/w/Bruma_torch_(off-hand)", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "shield", + "requirements": null + }, + "price": 0 + }, + "29781": { + "id": 29781, + "name": "Coagulated venom", + "members": true, + "cost": 10000, + "weight": 0.19, + "release_date": "2024-08-28", + "examine": "Looks nourishing, to the right beast.", + "wiki_name": "Coagulated venom", + "wiki_url": "https://oldschool.runescape.wiki/w/Coagulated_venom", + "price": 0 + }, + "29782": { + "id": 29782, + "name": "Spider cave teleport", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "stackable": true, + "cost": 10, + "lowalch": 4, + "highalch": 6, + "release_date": "2024-08-28", + "examine": "Teleports you to Morytania Spider Cave.", + "wiki_name": "Spider cave teleport", + "wiki_url": "https://oldschool.runescape.wiki/w/Spider_cave_teleport", + "price": 37695 + }, + "29784": { + "id": 29784, + "name": "Araxyte venom sack", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "stackable": true, + "cost": 500, + "lowalch": 200, + "highalch": 300, + "buy_limit": 11000, + "release_date": "2024-08-28", + "examine": "Looks spicy!", + "wiki_name": "Araxyte venom sack", + "wiki_url": "https://oldschool.runescape.wiki/w/Araxyte_venom_sack", + "price": 1987 + }, + "29786": { + "id": 29786, + "name": "Jar of venom", + "members": true, + "cost": 200000, + "lowalch": 80000, + "highalch": 120000, + "weight": 0.04, + "release_date": "2024-08-28", + "examine": "Handy that this comes in an easily transportable jar.", + "wiki_name": "Jar of venom", + "wiki_url": "https://oldschool.runescape.wiki/w/Jar_of_venom", + "price": 0 + }, + "29788": { + "id": 29788, + "name": "Araxyte head", + "members": true, + "cost": 60000, + "lowalch": 24000, + "highalch": 36000, + "weight": 10, + "release_date": "2024-08-28", + "examine": "Do spiders even have heads?", + "wiki_name": "Araxyte head", + "wiki_url": "https://oldschool.runescape.wiki/w/Araxyte_head", + "price": 0 + }, + "29790": { + "id": 29790, + "name": "Noxious point", + "members": true, + "cost": 160000, + "lowalch": 64000, + "highalch": 96000, + "weight": 0.907, + "release_date": "2024-08-28", + "examine": "A powerful piece of venomous weaponry.", + "wiki_name": "Noxious point", + "wiki_url": "https://oldschool.runescape.wiki/w/Noxious_point", + "price": 0 + }, + "29792": { + "id": 29792, + "name": "Noxious blade", + "members": true, + "cost": 160000, + "lowalch": 64000, + "highalch": 96000, + "weight": 0.907, + "release_date": "2024-08-28", + "examine": "A powerful piece of venomous weaponry.", + "wiki_name": "Noxious blade", + "wiki_url": "https://oldschool.runescape.wiki/w/Noxious_blade", + "price": 0 + }, + "29794": { + "id": 29794, + "name": "Noxious pommel", + "members": true, + "cost": 160000, + "lowalch": 64000, + "highalch": 96000, + "weight": 0.907, + "release_date": "2024-08-28", + "examine": "A powerful piece of venomous weaponry.", + "wiki_name": "Noxious pommel", + "wiki_url": "https://oldschool.runescape.wiki/w/Noxious_pommel", + "price": 0 + }, + "29796": { + "id": 29796, + "name": "Noxious halberd", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "noteable": true, + "equipable": true, + "equipable_by_player": true, + "equipable_weapon": true, + "cost": 500000, + "lowalch": 200000, + "highalch": 300000, + "weight": 2.721, + "release_date": "2024-08-28", + "examine": "A venomous halberd created from the parts of an araxyte.", + "wiki_name": "Noxious halberd", + "wiki_url": "https://oldschool.runescape.wiki/w/Noxious_halberd", + "equipment": { + "attack_stab": 80, + "attack_slash": 132, + "attack_crush": 0, + "attack_magic": 0, + "attack_ranged": 0, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 142, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "2h", + "requirements": null + }, + "weapon": { + "attack_speed": 5, + "weapon_type": "polearm", + "stances": [ + { + "combat_style": "jab", + "attack_type": "stab", + "attack_style": "controlled", + "experience": "shared", + "boosts": null + }, + { + "combat_style": "swipe", + "attack_type": "slash", + "attack_style": "aggressive", + "experience": "strength", + "boosts": null + }, + { + "combat_style": "fend", + "attack_type": "stab", + "attack_style": "defensive", + "experience": "defence", + "boosts": null + } + ] + }, + "price": 25203118 + }, + "29799": { + "id": 29799, + "name": "Araxyte fang", + "members": true, + "cost": 200000, + "lowalch": 80000, + "highalch": 120000, + "weight": 0.001, + "release_date": "2024-08-28", + "examine": "A still dripping venomous fang from a powerful araxyte.", + "wiki_name": "Araxyte fang", + "wiki_url": "https://oldschool.runescape.wiki/w/Araxyte_fang", + "price": 0 + }, + "29801": { + "id": 29801, + "name": "Amulet of rancour", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "noteable": true, + "equipable": true, + "equipable_by_player": true, + "cost": 400000, + "lowalch": 160000, + "highalch": 240000, + "weight": 0.012, + "release_date": "2024-08-28", + "examine": "A very powerful zenyte amulet, infused with araxyte venom.", + "wiki_name": "Amulet of rancour", + "wiki_url": "https://oldschool.runescape.wiki/w/Amulet_of_rancour", + "equipment": { + "attack_stab": 25, + "attack_slash": 25, + "attack_crush": 25, + "attack_magic": -6, + "attack_ranged": -8, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 12, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 2, + "slot": "neck", + "requirements": null + }, + "price": 88500000 + }, + "29804": { + "id": 29804, + "name": "Amulet of rancour (s)", + "members": true, + "equipable": true, + "equipable_by_player": true, + "cost": 400000, + "lowalch": 160000, + "highalch": 240000, + "weight": 0.011, + "release_date": "2024-08-28", + "examine": "A very powerful zenyte amulet, infused with araxyte venom.", + "wiki_name": "Amulet of rancour (s)", + "wiki_url": "https://oldschool.runescape.wiki/w/Amulet_of_rancour_(s)", + "equipment": { + "attack_stab": 25, + "attack_slash": 25, + "attack_crush": 25, + "attack_magic": -6, + "attack_ranged": -8, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 12, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 2, + "slot": "neck", + "requirements": null + }, + "price": 0 + }, + "29806": { + "id": 29806, + "name": "Aranea boots", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "noteable": true, + "equipable": true, + "equipable_by_player": true, + "cost": 150000, + "lowalch": 60000, + "highalch": 90000, + "weight": 1, + "buy_limit": 8, + "release_date": "2024-08-28", + "examine": "Boots that are surrounded with spider folicles. Great web resistance!", + "wiki_name": "Aranea boots", + "wiki_url": "https://oldschool.runescape.wiki/w/Aranea_boots", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 5, + "attack_ranged": 6, + "defence_stab": 0, + "defence_slash": 0, + "defence_crush": 0, + "defence_magic": 0, + "defence_ranged": 0, + "melee_strength": 4, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 1, + "slot": "feet", + "requirements": null + }, + "price": 15322207 + }, + "29809": { + "id": 29809, + "name": "Venom-riddled note", + "members": true, + "cost": 1, + "lowalch": 0, + "highalch": 0, + "weight": 0.003, + "release_date": "2024-08-28", + "examine": "A tatty note found inside the araxyte spider cave.", + "wiki_name": "Venom-riddled note", + "wiki_url": "https://oldschool.runescape.wiki/w/Venom-riddled_note", + "price": 0 + }, + "29816": { + "id": 29816, + "name": "Araxyte slayer helmet", + "members": true, + "equipable": true, + "equipable_by_player": true, + "cost": 40000, + "lowalch": 16000, + "highalch": 24000, + "weight": 2.267, + "release_date": "2024-08-28", + "examine": "You don't want to wear it inside-out.", + "wiki_name": "Araxyte slayer helmet", + "wiki_url": "https://oldschool.runescape.wiki/w/Araxyte_slayer_helmet", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": -6, + "attack_ranged": -2, + "defence_stab": 30, + "defence_slash": 32, + "defence_crush": 27, + "defence_magic": -1, + "defence_ranged": 30, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "head", + "requirements": null + }, + "price": 0 + }, + "29818": { + "id": 29818, + "name": "Araxyte slayer helmet (i)", + "members": true, + "equipable": true, + "equipable_by_player": true, + "cost": 90000, + "lowalch": 36000, + "highalch": 54000, + "weight": 2.267, + "release_date": "2024-08-28", + "examine": "You really don't want to wear it inside-out.", + "wiki_name": "Araxyte slayer helmet (i) (Nightmare Zone)", + "wiki_url": "https://oldschool.runescape.wiki/w/Araxyte_slayer_helmet_(i)#Nightmare_Zone", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 3, + "attack_ranged": 3, + "defence_stab": 30, + "defence_slash": 32, + "defence_crush": 27, + "defence_magic": 10, + "defence_ranged": 30, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "head", + "requirements": null + }, + "price": 0 + }, + "29820": { + "id": 29820, + "name": "Araxyte slayer helmet (i)", + "members": true, + "equipable": true, + "equipable_by_player": true, + "cost": 90000, + "lowalch": 36000, + "highalch": 54000, + "weight": 2.267, + "release_date": "2024-08-28", + "examine": "You really don't want to wear it inside-out.", + "wiki_name": "Araxyte slayer helmet (i) (Soul Wars)", + "wiki_url": "https://oldschool.runescape.wiki/w/Araxyte_slayer_helmet_(i)#Soul_Wars", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 3, + "attack_ranged": 3, + "defence_stab": 30, + "defence_slash": 32, + "defence_crush": 27, + "defence_magic": 10, + "defence_ranged": 30, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "head", + "requirements": null + }, + "price": 0 + }, + "29822": { + "id": 29822, + "name": "Araxyte slayer helmet (i)", + "members": true, + "equipable": true, + "equipable_by_player": true, + "cost": 90000, + "lowalch": 36000, + "highalch": 54000, + "weight": 2.267, + "release_date": "2024-08-28", + "examine": "You really don't want to wear it inside-out.", + "wiki_name": "Araxyte slayer helmet (i) (Emir's Arena)", + "wiki_url": "https://oldschool.runescape.wiki/w/Araxyte_slayer_helmet_(i)#Emir's_Arena", + "equipment": { + "attack_stab": 0, + "attack_slash": 0, + "attack_crush": 0, + "attack_magic": 3, + "attack_ranged": 3, + "defence_stab": 30, + "defence_slash": 32, + "defence_crush": 27, + "defence_magic": 10, + "defence_ranged": 30, + "melee_strength": 0, + "ranged_strength": 0, + "magic_damage": 0, + "prayer": 0, + "slot": "head", + "requirements": null + }, + "price": 0 + }, + "29824": { + "id": 29824, + "name": "Extended anti-venom+(4)", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "noteable": true, + "cost": 444, + "lowalch": 177, + "highalch": 266, + "weight": 0.035, + "release_date": "2024-08-28", + "examine": "4 doses of extended super antivenom potion.", + "wiki_name": "Extended anti-venom+ (4 dose)", + "wiki_url": "https://oldschool.runescape.wiki/w/Extended_anti-venom+#4_dose", + "price": 20360 + }, + "29827": { + "id": 29827, + "name": "Extended anti-venom+(3)", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "noteable": true, + "cost": 333, + "lowalch": 133, + "highalch": 199, + "weight": 0.035, + "release_date": "2024-08-28", + "examine": "3 doses of extended super antivenom potion.", + "wiki_name": "Extended anti-venom+ (3 dose)", + "wiki_url": "https://oldschool.runescape.wiki/w/Extended_anti-venom+#3_dose", + "price": 14678 + }, + "29830": { + "id": 29830, + "name": "Extended anti-venom+(2)", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "noteable": true, + "cost": 222, + "lowalch": 88, + "highalch": 133, + "weight": 0.035, + "release_date": "2024-08-28", + "examine": "2 doses of extended super antivenom potion.", + "wiki_name": "Extended anti-venom+ (2 dose)", + "wiki_url": "https://oldschool.runescape.wiki/w/Extended_anti-venom+#2_dose", + "price": 10282 + }, + "29833": { + "id": 29833, + "name": "Extended anti-venom+(1)", + "members": true, + "tradeable": true, + "tradeable_on_ge": true, + "noteable": true, + "cost": 111, + "lowalch": 44, + "highalch": 66, + "weight": 0.035, + "release_date": "2024-08-28", + "examine": "1 dose of extended super antivenom potion.", + "wiki_name": "Extended anti-venom+ (1 dose)", + "wiki_url": "https://oldschool.runescape.wiki/w/Extended_anti-venom+#1_dose", + "price": 5838 + }, + "29836": { + "id": 29836, + "name": "Nid", + "members": true, + "cost": 1, + "weight": 0.015, + "release_date": "2024-08-28", + "examine": "Looks like a respectable gentleman.", + "wiki_name": "Nid (Nid)", + "wiki_url": "https://oldschool.runescape.wiki/w/Nid#Nid", + "price": 0 + }, + "29838": { + "id": 29838, + "name": "Rax", + "members": true, + "cost": 1, + "lowalch": 0, + "highalch": 0, + "weight": 0.015, + "release_date": "2024-08-28", + "examine": "Really hope she doesn't crawl into my mouth while I sleep.", + "wiki_name": "Nid (Rax)", + "wiki_url": "https://oldschool.runescape.wiki/w/Nid#Rax", + "price": 0 + } +} diff --git a/update-history/item-update-2024-9-3.txt b/update-history/item-update-2024-9-3.txt new file mode 100644 index 000000000..5a3992cab --- /dev/null +++ b/update-history/item-update-2024-9-3.txt @@ -0,0 +1,7 @@ +Updated on 10/09/2024, 10:25:25 pm AEST Sydney / 09/10/2024, 05:25:25 AM PDT California + +Name Changes: + Ring of shadows to Ring of shadows (uncharged) + +New Items: https://chisel.weirdgloop.org/moid/item_id.html#27180,27181,27182,27194,29686,29688,29690,29692,29694,29696,29698,29700,29702,29704,29706,29708,29710,29712,29714,29716,29718,29720,29722,29724,29726,29728,29730,29732,29734,29736,29738,29740,29742,29744,29746,29748,29750,29752,29754,29756,29758,29760,29762,29764,29766,29768,29770,29772,29774,29775,29777,29781,29782,29784,29786,29788,29790,29792,29794,29796,29799,29801,29804,29806,29809,29816,29818,29820,29822,29824,29827,29830,29833,29836,29838 +Deleted Items: No items. diff --git a/vitest.config.mts b/vitest.config.mts index 572e2a43e..ef11a5287 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -3,6 +3,9 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { name: "OldschoolJS", + benchmark: { + include: ["bench/**/*.bench.ts"], + }, include: ["test/**/*.test.ts"], coverage: { provider: "v8",