diff --git a/.github/workflows/utils-tauri-build.yml b/.github/workflows/utils-tauri-build.yml index 5bc302840..f01dc3926 100644 --- a/.github/workflows/utils-tauri-build.yml +++ b/.github/workflows/utils-tauri-build.yml @@ -93,6 +93,8 @@ jobs: - name: Setup Rust uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-unknown-unknown - name: Setup Cargo Cache uses: actions/cache@v5 @@ -108,6 +110,9 @@ jobs: ${{ runner.os }}-cargo-tauri- ${{ runner.os }}-cargo- + - name: Install wasm-pack + run: cargo install wasm-pack --locked + - name: Setup Node v24 uses: actions/setup-node@v6 with: @@ -184,6 +189,8 @@ jobs: - name: Setup Rust uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-unknown-unknown - name: Rust Cache uses: Swatinem/rust-cache@v2 @@ -191,6 +198,9 @@ jobs: workspaces: '.' cache-on-failure: true + - name: Install wasm-pack + run: cargo install wasm-pack --locked + - name: Setup Node v24 uses: actions/setup-node@v6 with: @@ -265,6 +275,8 @@ jobs: - name: Setup Rust uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-unknown-unknown - name: Rust Cache uses: Swatinem/rust-cache@v2 @@ -272,6 +284,9 @@ jobs: workspaces: '.' cache-on-failure: true + - name: Install wasm-pack + run: cargo install wasm-pack --locked + - name: Setup Node v24 uses: actions/setup-node@v6 with: @@ -355,18 +370,50 @@ jobs: workspaces: '.' cache-on-failure: true - - name: Install Trunk - run: cargo install trunk --locked + - name: Install wasm-pack + run: cargo install wasm-pack --locked + + - name: Setup Node v24 + uses: actions/setup-node@v6 + with: + node-version: 24 + + - name: Setup pnpm v10 + uses: pnpm/action-setup@v4 + with: + version: 10 + run_install: false + + - name: Get pnpm Store Path + id: pnpm-store + run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT + + - name: Setup pnpm Cache + uses: actions/cache@v5 + with: + path: ${{ steps.pnpm-store.outputs.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml', 'package.json') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install pnpm Dependencies + run: pnpm install + + - name: Install project-local npm dependencies + working-directory: ${{ inputs.project_path }} + run: npm install - - name: Build WASM - working-directory: ${{ inputs.project_path }}/src-tauri - run: trunk build --release + - name: Build WASM + Frontend + working-directory: ${{ inputs.project_path }} + run: pnpm build + env: + CI: true - name: Upload WASM artifacts uses: actions/upload-artifact@v7 with: name: ${{ inputs.app_name }}-wasm - path: ${{ inputs.project_path }}/dist-wasm/ + path: ${{ inputs.project_path }}/dist/ if-no-files-found: warn retention-days: 14 diff --git a/Cargo.lock b/Cargo.lock index 36d6f6c96..d47b76b45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1064,7 +1064,7 @@ dependencies = [ [[package]] name = "axum-kbve" -version = "1.0.39" +version = "1.0.40" dependencies = [ "anyhow", "askama", @@ -1835,6 +1835,40 @@ dependencies = [ "bevy_winit", ] +[[package]] +name = "bevy_inventory" +version = "0.1.0" +dependencies = [ + "bevy", + "serde", + "serde_json", +] + +[[package]] +name = "bevy_kbve_camera" +version = "0.1.0" +dependencies = [ + "bevy", +] + +[[package]] +name = "bevy_kbve_player" +version = "0.1.0" +dependencies = [ + "bevy", + "bevy_rapier3d", + "serde", +] + +[[package]] +name = "bevy_kbve_state" +version = "0.1.0" +dependencies = [ + "bevy", + "serde", + "serde_json", +] + [[package]] name = "bevy_light" version = "0.18.1" diff --git a/Cargo.toml b/Cargo.toml index 9f3e0c79d..416d05e66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,10 @@ members = [ 'apps/kbve/axum-kbve', 'apps/cryptothrone/axum-cryptothrone', 'apps/kbve/isometric/src-tauri', + 'packages/rust/bevy/bevy_kbve_inventory', + 'packages/rust/bevy/bevy_kbve_state', + 'packages/rust/bevy/bevy_kbve_player', + 'packages/rust/bevy/bevy_kbve_camera', ] [profile.dev] diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/OSRS.md b/apps/kbve/astro-kbve/data/osrs-overrides/OSRS.md index 7f2baca90..97180ada7 100644 --- a/apps/kbve/astro-kbve/data/osrs-overrides/OSRS.md +++ b/apps/kbve/astro-kbve/data/osrs-overrides/OSRS.md @@ -272,6 +272,120 @@ Focus on items with clear processing chains and market flip potential. | 3051 | Grimy snapdragon | Herb cleaning | | 12695 | Super combat(4) | Best melee potion | +### Dragon Armour (Implemented - Mar 2026) + +| ID | Item | Override Focus | +| ----- | ---------------- | ------------------ | +| 1149 | Dragon med helm | 60 Def helm | +| 3140 | Dragon chainbody | 60 Def body | +| 4087 | Dragon platelegs | 60 Def legs | +| 11335 | Dragon full helm | Ultra-rare helm | +| 21892 | Dragon platebody | DS2 craftable body | + +### Melee Helms (Implemented - Mar 2026) + +| ID | Item | Override Focus | +| ----- | ----------------- | ----------------- | +| 10828 | Helm of neitiznot | Iconic melee helm | + +### Magic Armour (Implemented - Mar 2026) + +| ID | Item | Override Focus | +| ---- | ------------------ | ----------------- | +| 4091 | Mystic robe top | Budget magic body | +| 4093 | Mystic robe bottom | Budget magic legs | + +### Ring Crafting Chain (Implemented - Mar 2026) + +| ID | Item | Override Focus | +| ----- | ---------------- | ---------------------- | +| 1637 | Sapphire ring | Ring of recoil base | +| 1639 | Emerald ring | Ring of dueling base | +| 1641 | Ruby ring | Ring of forging base | +| 1643 | Diamond ring | Ring of life base | +| 1645 | Dragonstone ring | Ring of wealth base | +| 6575 | Onyx ring | Ring of stone base | +| 19538 | Zenyte ring | Ring of suffering base | + +### Teleport Jewelry (Implemented - Mar 2026) + +| ID | Item | Override Focus | +| ----- | --------------- | ------------------ | +| 11113 | Skills necklace | Skilling teleports | +| 11126 | Combat bracelet | Combat teleports | + +### Rune/Obsidian Weapons (Implemented - Mar 2026) + +| ID | Item | Override Focus | +| ---- | -------------- | ---------------------- | +| 1289 | Rune sword | F2P stab weapon | +| 1373 | Rune battleaxe | F2P strength weapon | +| 6523 | Toktz-xil-ak | Obsidian sword | +| 1347 | Rune warhammer | F2P crush weapon | +| 1432 | Rune mace | F2P prayer weapon | +| 6522 | Toktz-xil-ul | Obsidian throwing ring | + +### Granite Gear (Implemented - Mar 2026) + +| ID | Item | Override Focus | +| ----- | -------------- | ----------------- | +| 10589 | Granite helm | 50 Def/Str helm | +| 3122 | Granite shield | 50 Def/Str shield | +| 6809 | Granite legs | 50 Def/Str legs | + +### Battlestaves (Implemented - Mar 2026) + +| ID | Item | Override Focus | +| ---- | ------------------ | ----------------------- | +| 6562 | Mud battlestaff | Water+Earth combo (DKs) | +| 3053 | Lava battlestaff | Earth+Fire combo | +| 1401 | Mystic fire staff | Fire elemental mystic | +| 1403 | Mystic water staff | Water elemental mystic | +| 1407 | Mystic earth staff | Earth elemental mystic | +| 6563 | Mystic mud staff | Water+Earth mystic | +| 3054 | Mystic lava staff | Earth+Fire mystic | + +### Splitbark Armour (Implemented - Mar 2026) + +| ID | Item | Override Focus | +| ---- | -------------- | ----------------- | +| 3385 | Splitbark helm | 40 Def/Magic helm | +| 3387 | Splitbark body | 40 Def/Magic body | +| 3389 | Splitbark legs | 40 Def/Magic legs | + +### Skeletal Armour (Implemented - Mar 2026) + +| ID | Item | Override Focus | +| ---- | ---------------- | -------------------- | +| 6137 | Skeletal helm | Fremennik magic helm | +| 6139 | Skeletal top | Fremennik magic body | +| 6141 | Skeletal bottoms | Fremennik magic legs | + +### Rock-shell Armour (Implemented - Mar 2026) + +| ID | Item | Override Focus | +| ---- | ---------------- | -------------------- | +| 6128 | Rock-shell helm | Fremennik melee helm | +| 6129 | Rock-shell plate | Fremennik melee body | +| 6130 | Rock-shell legs | Fremennik melee legs | + +### Snakeskin/Frog Leather (Implemented - Mar 2026) + +| ID | Item | Override Focus | +| ----- | ----------------- | ------------------- | +| 6322 | Snakeskin body | 30 Ranged/Def body | +| 6324 | Snakeskin chaps | 30 Ranged/Def legs | +| 6328 | Snakeskin boots | Budget ranged boots | +| 10954 | Frog-leather body | 25 Ranged/Def body | + +### Misc Weapons/Jewelry (Implemented - Mar 2026) + +| ID | Item | Override Focus | +| ----- | ----------------- | ---------------------- | +| 11037 | Brine sabre | Niche 40 Attack weapon | +| 6724 | Seercull | DK Supreme bow, spec | +| 1729 | Amulet of defence | Defensive amulet | + --- ## Future Override Priorities diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_10589.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_10589.mdx new file mode 100644 index 000000000..10893a3cc --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_10589.mdx @@ -0,0 +1,110 @@ +--- +equipment: + slot: "head" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: -9 + ranged: -7 + defence_bonus: + stab: 31 + slash: 33 + crush: 29 + magic: -1 + ranged: 39 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + defence: 50 + strength: 50 + weight: 4.535 +drop_table: + primary_source: "Terror dog" + best_drop_rate: "1/128" + sources: + - source: "Terror dog" + combat_level: 110 + quantity: "1" + rarity: "rare" + drop_rate: "1/128" + members_only: true +related_items: + - item_id: 1149 + item_name: "Dragon med helm" + slug: "dragon-med-helm" + relationship: "alternative" + description: "60 Def helm, no Strength req" + - item_id: 10828 + item_name: "Helm of neitiznot" + slug: "helm-of-neitiznot" + relationship: "upgrade" + description: "Superior with Strength/Prayer bonus" + - item_id: 3122 + item_name: "Granite shield" + slug: "granite-shield" + relationship: "set" + description: "Granite set piece" +--- + +## Obtaining + +- **Terror dogs** (Level 100/110) — 1/128 drop rate + - Located in **Lair of Tarn Razorlor** (Haunted Mine quest area) +- **Barbarian Assault** High Level Gamble — 1/32 + +> *"A stone helmet."* + +## Stats + +| Defence | Value | +|---------|-------| +| Stab | +31 | +| Slash | +33 | +| Crush | +29 | +| Ranged | **+39** | +| Magic | -1 | + +**Requirements:** 50 Defence, 50 Strength + +## Comparison + +| Stat | Granite Helm | Rune Full Helm | Dragon Med | +|------|-------------|----------------|------------| +| Stab Def | +31 | +30 | +33 | +| Slash Def | +33 | +32 | +35 | +| Crush Def | +29 | +27 | +36 | +| Ranged Def | **+39** | +30 | +34 | +| Magic Def | -1 | -1 | -1 | +| Str Bonus | 0 | 0 | 0 | +| Prayer | 0 | 0 | 0 | +| Req | 50 Def/Str | 40 Def | 60 Def | + +Slightly better than rune full helm but slightly worse than dragon med helm overall. Notably provides **superior ranged defence** (+39) compared to both alternatives. + +## Usage + +Mid-tier melee helm: +- Best ranged defence of any mid-tier helm +- Sits between rune full helm and dragon med helm +- Budget option before [Helm of neitiznot](/osrs/helm-of-neitiznot/) (which adds +3 Str, +3 Prayer) +- Part of the granite fashionscape set + +## Market Strategy + +**Buy Limit:** 70 per 4 hours | **High Alch:** 27,600 GP | **Volume:** ~491/day + +**Tips:** +- Terror dogs require Haunted Mine access +- BA High Gamble is an alternative source (1/32) +- GE price near high alch value + +## Related Items + +- [Helm of neitiznot](/osrs/helm-of-neitiznot/) - Superior with +3 Str/Prayer +- [Dragon med helm](/osrs/dragon-med-helm/) - 60 Def, no Strength req +- [Granite shield](/osrs/granite-shield/) - Matching set piece +- [Granite legs](/osrs/granite-legs/) - Matching set piece diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_10828.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_10828.mdx new file mode 100644 index 000000000..ba0e75ea7 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_10828.mdx @@ -0,0 +1,82 @@ +--- +equipment: + slot: "head" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 0 + ranged: 0 + defence_bonus: + stab: 31 + slash: 29 + crush: 34 + magic: 3 + ranged: 30 + other_bonus: + melee_strength: 3 + ranged_strength: 0 + magic_damage: 0 + prayer: 3 + requirements: + defence: 55 +related_items: + - item_id: 24268 + item_name: "Neitiznot faceguard" + slug: "neitiznot-faceguard" + relationship: "upgrade" + description: "BiS melee helm upgrade with Basilisk jaw" + - item_id: 3748 + item_name: "Berserker helm" + slug: "berserker-helm" + relationship: "alternative" + description: "Fremennik helm for lower Defence" + - item_id: 3751 + item_name: "Warrior helm" + slug: "warrior-helm" + relationship: "alternative" + description: "Alternative Fremennik helm" +--- + +## Obtaining + +Reward from **The Fremennik Isles** quest. + +**Requirements:** +- The Fremennik Trials quest +- 46 Construction (boostable) +- 56 Crafting (boostable) + +## Stats + +| Defence | Value | +|---------|-------| +| Stab | +31 | +| Slash | +29 | +| Crush | +34 | +| Magic | +3 | +| Ranged | +30 | + +| Other | Value | +|-------|-------| +| Strength | +3 | +| Prayer | +3 | + +## Usage + +Best free melee helm until Neitiznot faceguard: +- Slayer tasks +- General PvM +- Budget melee setup +- PvP (commonly used) + +**Upgrade:** Attach a [Basilisk jaw](/osrs/basilisk-jaw/) to create the Neitiznot faceguard. + +## Market Strategy + +**Untradeable** - Quest reward only. + +## Related Items + +- [Neitiznot faceguard](/osrs/neitiznot-faceguard/) - BiS melee helm upgrade +- [Berserker helm](/osrs/berserker-helm/) - Fremennik helm alternative diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_10954.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_10954.mdx new file mode 100644 index 000000000..5ec1201ef --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_10954.mdx @@ -0,0 +1,103 @@ +--- +equipment: + slot: "body" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: -5 + ranged: 10 + defence_bonus: + stab: 23 + slash: 26 + crush: 30 + magic: 15 + ranged: 32 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + ranged: 25 + defence: 25 + weight: 5.0 +related_items: + - item_id: 6322 + item_name: "Snakeskin body" + slug: "snakeskin-body" + relationship: "upgrade" + description: "30 Ranged/Def body" + - item_id: 1135 + item_name: "Green d'hide body" + slug: "green-dhide-body" + relationship: "upgrade" + description: "40 Ranged body" +--- + +## Obtaining + +- **Reldak's Leather Armour** shop in Dorgesh-Kaan (1,000 coins) +- **Dorgesh-Kaan chests** (52-78 Thieving) + +**Cannot be crafted** — shop/chest only. + +Requires access to Dorgesh-Kaan (after **Death to the Dorgeshuun** quest). + +> *"A leather body made of frog skin. Not very popular with the Dorgeshuun."* + +## Stats + +| Ranged | Value | +|--------|-------| +| Ranged Attack | +10 | + +| Defence | Value | +|---------|-------| +| Stab | +23 | +| Slash | +26 | +| Crush | +30 | +| Magic | **+15** | +| Ranged | +32 | + +**Requirements:** 25 Ranged, 25 Defence + +## Lowest Requirement Ranged Body + +The frog-leather body has the lowest combined requirements of any ranged body with meaningful stats. At 25 Ranged/25 Defence, it fills the gap between leather (no req) and snakeskin (30 Ranged/Def). + +## Comparison + +| Stat | Frog-leather | Studded Body | Snakeskin Body | +|------|-------------|--------------|----------------| +| Ranged Atk | +10 | +8 | +12 | +| Stab Def | +23 | +22 | +25 | +| Magic Def | **+15** | +4 | +15 | +| Req | 25 Rng/Def | 20 Rng/Def | 30 Rng/Def | + +Surprisingly strong for its level — comparable magic defence to snakeskin body despite lower requirements. Better ranged attack than studded body. + +## Dorgesh-Kaan Access + +Dorgesh-Kaan is the cave goblin city accessed after Death to the Dorgeshuun. The shop is run by Reldak in the southern market area. The body costs only 1,000 coins. + +## Usage + +- Best-in-slot ranged body for 25 Defence pures +- Niche PvP bracket item +- Quest-locked but very cheap +- Not craftable — must visit Dorgesh-Kaan + +## Market Strategy + +**Buy Limit:** 70 per 4 hours | **High Alch:** 600 GP + +**Tips:** +- Shop-only source (1,000 coins in Dorgesh-Kaan) +- Very niche demand — low trade volume +- Quest requirement limits supply + +## Related Items + +- [Snakeskin body](/osrs/snakeskin-body/) - 30 Ranged/Def upgrade +- [Green d'hide body](/osrs/green-dhide-body/) - 40 Ranged upgrade diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_11037.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_11037.mdx new file mode 100644 index 000000000..361a1c5ef --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_11037.mdx @@ -0,0 +1,118 @@ +--- +equipment: + slot: "weapon" + attack_speed: 4 + attack_bonus: + stab: 7 + slash: 47 + crush: -2 + magic: 0 + ranged: 0 + defence_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 0 + ranged: 0 + other_bonus: + melee_strength: 46 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + attack: 40 + weight: 1.814 +special_attack: + name: "Liquify" + energy: 75 + description: "Doubles accuracy and boosts Attack, Strength, and Defence by 25% of damage dealt. Only usable underwater." +drop_table: + primary_source: "Brine rat" + best_drop_rate: "1/512" + sources: + - source: "Brine rat" + combat_level: 70 + quantity: "1" + rarity: "rare" + drop_rate: "1/512" + members_only: true +related_items: + - item_id: 4587 + item_name: "Dragon scimitar" + slug: "dragon-scimitar" + relationship: "upgrade" + description: "60 Attack scimitar upgrade" + - item_id: 1333 + item_name: "Rune scimitar" + slug: "rune-scimitar" + relationship: "alternative" + description: "F2P slash weapon" +--- + +## Obtaining + +**Brine rats** (Level 70) — 1/512 drop rate + +Located in a small cave accessed during **Olaf's Quest**. The cave is reached via the windswept tree on the east coast of Rellekka. Brine rats are rarely killed, making this weapon significantly more expensive than its stats would suggest. + +Also obtainable from **Master Treasure Trails** (emote clue). + +## Stats + +| Attack | Value | +|--------|-------| +| Slash | +47 | +| Stab | +7 | + +| Other | Value | +|-------|-------| +| Strength | +46 | + +**Requirements:** 40 Attack | **Speed:** 4 tick (2.4s) + +> *"A salty sword."* + +## Comparison vs Rune Scimitar + +| Stat | Brine Sabre | Rune Scimitar | +|------|-------------|---------------| +| Slash | +47 | +45 | +| Strength | +46 | +44 | +| Price | ~390K | ~15K | + +The brine sabre has **+2 higher slash and strength** than the rune scimitar, but costs ~25x more. The granite hammer (+56 crush, +49 str) has since surpassed it as best-in-slot for 40 Attack pures. + +## Special Attack + +**Liquify** (75% energy): +- Doubles accuracy +- Boosts Attack, Strength, and Defence by 25% of damage dealt +- **Only usable underwater** — extremely niche + +## Unique Interactions + +**Rockslug finishing:** Can finish off rockslugs without needing a bag of salt. Also works on rocklings automatically. + +**Lobstrosities:** One of the few weapons that can kill lobstrosities (underwater creatures). + +## Usage + +- Former best-in-slot for **40 Attack pures** on members worlds +- Now surpassed by granite hammer for pure DPS +- Collector's item due to rare, remote source +- Niche underwater content + +## Market Strategy + +**Buy Limit:** 8 per 4 hours | **High Alch:** 15,600 GP + +**Tips:** +- Extremely low supply (brine rats are rarely killed) +- Price driven by rarity, not combat utility +- 40 Attack PvP bracket demand + +## Related Items + +- [Dragon scimitar](/osrs/dragon-scimitar/) - 60 Attack upgrade +- [Rune scimitar](/osrs/rune-scimitar/) - Budget F2P alternative +- [Granite hammer](/osrs/granite-hammer/) - Better 50 Attack option diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_11113.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_11113.mdx new file mode 100644 index 000000000..7e448f793 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_11113.mdx @@ -0,0 +1,64 @@ +--- +equipment: + slot: "neck" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 0 + ranged: 0 + defence_bonus: + stab: 3 + slash: 3 + crush: 3 + magic: 3 + ranged: 3 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + crafting: 72 +related_items: + - item_id: 11126 + item_name: "Combat bracelet" + slug: "combat-bracelet" + relationship: "alternative" + description: "Combat teleport jewelry" + - item_id: 1704 + item_name: "Amulet of glory" + slug: "amulet-of-glory" + relationship: "alternative" + description: "Popular teleport amulet" +--- + +## Creation + +Made by enchanting a dragon necklace (Lvl-5 Enchant, Magic 68). + +**Base:** Dragon necklace (Crafting 72, Dragonstone + Gold bar) + +## Usage + +Teleport jewelry with 4 charges: +- Fishing Guild +- Mining Guild +- Crafting Guild +- Farming Guild +- Cooking Guild +- Woodcutting Guild + +## Market Strategy + +**Buy Limit:** 10,000 per 4 hours + +**Tips:** +- Rechargeable at Legends' Guild +- Popular for guild access +- Useful for skilling teleports + +## Related Items + +- [Combat bracelet](/osrs/combat-bracelet/) - Combat teleports +- [Amulet of glory](/osrs/amulet-of-glory/) - General teleports diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_11126.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_11126.mdx new file mode 100644 index 000000000..9c467eabf --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_11126.mdx @@ -0,0 +1,74 @@ +--- +equipment: + slot: "hands" + attack_bonus: + stab: 3 + slash: 3 + crush: 3 + magic: 3 + ranged: 3 + defence_bonus: + stab: 3 + slash: 3 + crush: 3 + magic: 3 + ranged: 3 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + crafting: 58 +recipes: + - skill: "crafting" + level: 58 + xp: 100 + inputs: + - item_id: 1615 + item_name: "Dragonstone" + quantity: 1 + - item_id: 2357 + item_name: "Gold bar" + quantity: 1 + output_quantity: 1 +related_items: + - item_id: 11113 + item_name: "Skills necklace" + slug: "skills-necklace" + relationship: "alternative" + description: "Skilling teleport jewelry" + - item_id: 1704 + item_name: "Amulet of glory" + slug: "amulet-of-glory" + relationship: "alternative" + description: "Popular teleport amulet" +--- + +## Creation + +Made by enchanting a dragonstone bracelet (Lvl-5 Enchant, Magic 68). + +**Base:** Dragon bracelet (Crafting 58, Dragonstone + Gold bar) + +## Usage + +Teleport jewelry with 4 charges: +- Warriors' Guild +- Champions' Guild +- Edgeville Monastery +- Ranging Guild + +## Market Strategy + +**Buy Limit:** 10,000 per 4 hours + +**Tips:** +- Rechargeable at Legends' Guild +- Popular for Warriors' Guild access +- Dragonstone price drives cost + +## Related Items + +- [Skills necklace](/osrs/skills-necklace/) - Skilling teleports +- [Amulet of glory](/osrs/amulet-of-glory/) - General teleports diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_11335.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_11335.mdx new file mode 100644 index 000000000..b82fb3180 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_11335.mdx @@ -0,0 +1,83 @@ +--- +equipment: + slot: "head" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: -3 + ranged: -1 + defence_bonus: + stab: 45 + slash: 48 + crush: 51 + magic: -1 + ranged: 46 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + defence: 60 +drop_table: + primary_source: "Mithril dragon" + best_drop_rate: "1/32768" + sources: + - source: "Mithril dragon" + combat_level: 304 + quantity: "1" + rarity: "very rare" + drop_rate: "1/32768" + members_only: true +related_items: + - item_id: 1149 + item_name: "Dragon med helm" + slug: "dragon-med-helm" + relationship: "downgrade" + description: "Common dragon helm" + - item_id: 10828 + item_name: "Helm of neitiznot" + slug: "helm-of-neitiznot" + relationship: "alternative" + description: "Better stats with Strength/Prayer bonus" +--- + +## Obtaining + +Extremely rare drop from Mithril dragons (1/32,768). + +One of the rarest tradeable drops in the game. + +## Stats + +| Defence | Value | +|---------|-------| +| Stab | +45 | +| Slash | +48 | +| Crush | +51 | +| Magic | -1 | +| Ranged | +46 | + +**Requirements:** 60 Defence + +## Usage + +Primarily a fashionscape and collection item: +- High Defence stats but no offensive bonuses +- Helm of neitiznot is preferred for melee +- Status symbol due to extreme rarity + +## Market Strategy + +**Buy Limit:** 8 per 4 hours + +**Tips:** +- Extremely rare (1/32,768) +- Price driven by rarity, not utility +- Collector's item + +## Related Items + +- [Dragon med helm](/osrs/dragon-med-helm/) - Common dragon helm +- [Helm of neitiznot](/osrs/helm-of-neitiznot/) - Better for combat diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_1149.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_1149.mdx new file mode 100644 index 000000000..aeb860245 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_1149.mdx @@ -0,0 +1,94 @@ +--- +equipment: + slot: "head" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: -3 + ranged: -1 + defence_bonus: + stab: 33 + slash: 35 + crush: 36 + magic: -1 + ranged: 34 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + defence: 60 +drop_table: + primary_source: "King Black Dragon" + best_drop_rate: "1/128" + sources: + - source: "King Black Dragon" + combat_level: 276 + quantity: "1" + rarity: "uncommon" + drop_rate: "1/128" + members_only: true + - source: "Brutal black dragon" + combat_level: 318 + quantity: "1" + rarity: "rare" + drop_rate: "1/512" + members_only: true +related_items: + - item_id: 11335 + item_name: "Dragon full helm" + slug: "dragon-full-helm" + relationship: "upgrade" + description: "Rare full helm upgrade" + - item_id: 10828 + item_name: "Helm of neitiznot" + slug: "helm-of-neitiznot" + relationship: "alternative" + description: "Better combat stats" + - item_id: 1163 + item_name: "Rune full helm" + slug: "rune-full-helm" + relationship: "downgrade" + description: "F2P alternative" +--- + +## Obtaining + +Dropped by dragons: +- King Black Dragon (1/128) +- Brutal black dragons (1/512) +- Other high-level dragons + +## Stats + +| Defence | Value | +|---------|-------| +| Stab | +33 | +| Slash | +35 | +| Crush | +36 | +| Ranged | +34 | + +**Requirements:** 60 Defence + +## Usage + +Mid-tier melee helm: +- Decent Defence stats +- No Strength or Prayer bonus +- Helm of neitiznot is generally preferred + +## Market Strategy + +**Buy Limit:** 70 per 4 hours + +**Tips:** +- Common dragon drop +- Low value due to better alternatives +- Entry-level dragon gear + +## Related Items + +- [Dragon full helm](/osrs/dragon-full-helm/) - Rare upgrade +- [Helm of neitiznot](/osrs/helm-of-neitiznot/) - Better for combat diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_1289.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_1289.mdx new file mode 100644 index 000000000..33386e7e1 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_1289.mdx @@ -0,0 +1,67 @@ +--- +equipment: + slot: "weapon" + attack_bonus: + stab: 38 + slash: 26 + crush: -2 + magic: 0 + ranged: 0 + defence_bonus: + stab: 0 + slash: 1 + crush: 0 + magic: 0 + ranged: 0 + other_bonus: + melee_strength: 39 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + attack: 40 + attack_speed: 4 +related_items: + - item_id: 1333 + item_name: "Rune scimitar" + slug: "rune-scimitar" + relationship: "alternative" + description: "Higher slash, preferred for training" + - item_id: 1373 + item_name: "Rune battleaxe" + slug: "rune-battleaxe" + relationship: "alternative" + description: "Higher strength, slower speed" + - item_id: 1347 + item_name: "Rune warhammer" + slug: "rune-warhammer" + relationship: "alternative" + description: "Crush alternative" +--- + +## Obtaining + +- Smithing: 2 Runite bars at 89 Smithing +- Monster drops (various) +- Grand Exchange + +## Usage + +Rune-tier stab weapon: +- Best F2P stab weapon +- Useful against stab-weak monsters +- Generally outclassed by rune scimitar for training + +## Market Strategy + +**Buy Limit:** 70 per 4 hours + +**Tips:** +- Commonly smithed for XP +- Low demand vs rune scimitar +- High alch value matters + +## Related Items + +- [Rune scimitar](/osrs/rune-scimitar/) - Preferred for training +- [Rune battleaxe](/osrs/rune-battleaxe/) - Strength training option diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_1347.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_1347.mdx new file mode 100644 index 000000000..f8001a0dc --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_1347.mdx @@ -0,0 +1,97 @@ +--- +equipment: + slot: "weapon" + attack_speed: 6 + attack_bonus: + stab: -4 + slash: -4 + crush: 53 + magic: -4 + ranged: 0 + defence_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 0 + ranged: 0 + other_bonus: + melee_strength: 62 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + strength: 40 + weight: 1.814 +related_items: + - item_id: 13576 + item_name: "Dragon warhammer" + slug: "dragon-warhammer" + relationship: "upgrade" + description: "Superior crush weapon with Defence-drain spec" + - item_id: 1373 + item_name: "Rune battleaxe" + slug: "rune-battleaxe" + relationship: "alternative" + description: "Slash-based strength weapon" + - item_id: 1333 + item_name: "Rune scimitar" + slug: "rune-scimitar" + relationship: "alternative" + description: "Faster training weapon" +--- + +## Obtaining + +- **Smithing:** Level 94 using 3 Runite bars (225 XP, 5 ticks) +- **Shop:** Skulgrimen's Battle Gear, Rellekka (53,950 coins) +- **Notable drops:** Lizardman Shaman (16/500), Cave Kraken (4/200), Rune Dragon (7/127), Revenant Dragon (1/49.75) + +> *"I don't think it's intended for joinery."* + +## Stats + +| Attack | Value | +|--------|-------| +| Crush | +53 | +| Stab/Slash | -4 | + +| Other | Value | +|-------|-------| +| Strength | +62 | + +**Requirements:** 40 Strength (no Attack req) | **Speed:** 6 tick (3.6s) + +## Unique Properties + +**Strength-only requirement:** Unlike most rune weapons that need 40 Attack, the warhammer only requires 40 Strength. This makes it the weapon of choice for **F2P Strength pures**. + +**Bandos GWD:** Functions as a substitute hammer for opening the big door at Bandos' Stronghold in the God Wars Dungeon — saves an inventory slot. + +## Balance History + +**April 2021:** Major buff — requirement changed from Attack to Strength, and Strength bonus increased from +48 to **+62**. This modernized its viability significantly. + +## Smithing Economics + +| Component | Cost | +|-----------|------| +| 3 Runite bars | ~37,000 GP | +| High Alch | 24,900 GP | +| GE Price | ~24,300 GP | + +Currently a **loss of ~12,500 GP** to smith and sell. Better as an alch target. + +## Market Strategy + +**Buy Limit:** 70 per 4 hours | **High Alch:** 24,900 GP | **Volume:** ~118K/day + +**Tips:** +- Very high daily volume (popular alch target) +- GE price tracks near high alch value +- F2P Strength pure knockout weapon demand + +## Related Items + +- [Dragon warhammer](/osrs/dragon-warhammer/) - Superior crush with Defence-drain spec +- [Rune battleaxe](/osrs/rune-battleaxe/) - Slash strength alternative +- [Rune scimitar](/osrs/rune-scimitar/) - Faster training weapon diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_1373.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_1373.mdx new file mode 100644 index 000000000..60dbe7348 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_1373.mdx @@ -0,0 +1,68 @@ +--- +equipment: + slot: "weapon" + attack_bonus: + stab: -2 + slash: 48 + crush: 42 + magic: 0 + ranged: 0 + defence_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 0 + ranged: 0 + other_bonus: + melee_strength: 64 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + attack: 40 + attack_speed: 6 +related_items: + - item_id: 1333 + item_name: "Rune scimitar" + slug: "rune-scimitar" + relationship: "alternative" + description: "Faster, preferred for general training" + - item_id: 1289 + item_name: "Rune sword" + slug: "rune-sword" + relationship: "alternative" + description: "Stab alternative" + - item_id: 1347 + item_name: "Rune warhammer" + slug: "rune-warhammer" + relationship: "alternative" + description: "Crush alternative" +--- + +## Obtaining + +- Smithing: 3 Runite bars at 95 Smithing +- Monster drops (various) +- Grand Exchange + +## Usage + +Rune-tier Strength weapon: +- Highest Strength bonus of rune weapons (+64) +- Very slow attack speed (6 tick / 3.6s) +- F2P Strength training option +- Outclassed by faster weapons for DPS + +## Market Strategy + +**Buy Limit:** 70 per 4 hours + +**Tips:** +- Smithing training product +- High alch target +- Niche F2P Strength training + +## Related Items + +- [Rune scimitar](/osrs/rune-scimitar/) - Better DPS for training +- [Rune warhammer](/osrs/rune-warhammer/) - Crush alternative diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_1401.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_1401.mdx new file mode 100644 index 000000000..6eb078bc9 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_1401.mdx @@ -0,0 +1,106 @@ +--- +equipment: + slot: "weapon" + attack_speed: 5 + attack_bonus: + stab: 10 + slash: -1 + crush: 40 + magic: 14 + ranged: 0 + defence_bonus: + stab: 2 + slash: 3 + crush: 1 + magic: 14 + ranged: 0 + other_bonus: + melee_strength: 50 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + attack: 40 + magic: 40 + weight: 2.267 +related_items: + - item_id: 1403 + item_name: "Mystic water staff" + slug: "mystic-water-staff" + relationship: "set" + description: "Water elemental mystic staff" + - item_id: 1407 + item_name: "Mystic earth staff" + slug: "mystic-earth-staff" + relationship: "set" + description: "Earth elemental mystic staff" + - item_id: 3054 + item_name: "Mystic lava staff" + slug: "mystic-lava-staff" + relationship: "upgrade" + description: "Earth+Fire combination staff" +--- + +## Obtaining + +- **Thormac** (after Scorpion Catcher): Fire battlestaff + 40,000 coins + - Hard Kandarin Diary: 30,000 coins + - Elite Kandarin Diary: 20,000 coins +- **Player-owned House** (66 Construction): Convert from other mystic staff + 1,000 fire runes +- **Drops:** Chaos Elemental, Vet'ion, Thermonuclear Smoke Devil, Alchemical Hydra + +> *"It's a slightly magical stick."* + +## Stats + +| Attack | Value | +|--------|-------| +| Magic | +14 | +| Crush | +40 | + +| Defence | Value | +|---------|-------| +| Magic | +14 | + +| Other | Value | +|-------|-------| +| Strength | +50 | + +**Requirements:** 40 Attack, 40 Magic + +## Fire Rune Provision + +Provides unlimited **fire runes** — the most universally useful elemental staff: +- **High Level Alchemy** — saves 5 fire runes per cast (most popular use) +- **Superheat Item** — saves 4 fire runes per cast +- **All fire spells** — saves runes for combat magic +- Autocast with standard spellbook + +## All Mystic Staves + +| Staff | Element | Notable Use | +|-------|---------|-------------| +| Mystic fire | Fire | High Alch, fire spells | +| Mystic water | Water | Water spells, ice barrage combo | +| Mystic earth | Earth | Earth spells | +| Mystic air | Air | Air spells, Charge Orb | +| Mystic mud | Water+Earth | Lunar spells, barrage | +| Mystic lava | Earth+Fire | Superheat, Enchant | + +All share identical stats (+14 magic atk/def, +40 crush, +50 str). The difference is which rune(s) they provide. + +## Market Strategy + +**Buy Limit:** 18,000 per 4 hours | **High Alch:** 25,500 GP + +**Tips:** +- Thormac upgrade profitable with diary discounts +- Multiple boss drop sources +- Most popular mystic staff due to fire rune utility +- High volume — frequently alched + +## Related Items + +- [Mystic water staff](/osrs/mystic-water-staff/) - Water elemental version +- [Mystic earth staff](/osrs/mystic-earth-staff/) - Earth elemental version +- [Mystic lava staff](/osrs/mystic-lava-staff/) - Earth+Fire combination diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_1403.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_1403.mdx new file mode 100644 index 000000000..2c72c3984 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_1403.mdx @@ -0,0 +1,101 @@ +--- +equipment: + slot: "weapon" + attack_speed: 5 + attack_bonus: + stab: 10 + slash: -1 + crush: 40 + magic: 14 + ranged: 0 + defence_bonus: + stab: 2 + slash: 3 + crush: 1 + magic: 14 + ranged: 0 + other_bonus: + melee_strength: 50 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + attack: 40 + magic: 40 + weight: 2.267 +related_items: + - item_id: 1401 + item_name: "Mystic fire staff" + slug: "mystic-fire-staff" + relationship: "set" + description: "Fire elemental mystic staff" + - item_id: 1407 + item_name: "Mystic earth staff" + slug: "mystic-earth-staff" + relationship: "set" + description: "Earth elemental mystic staff" + - item_id: 6563 + item_name: "Mystic mud staff" + slug: "mystic-mud-staff" + relationship: "upgrade" + description: "Water+Earth combination staff" +--- + +## Obtaining + +- **Thormac** (after Scorpion Catcher): Water battlestaff + 40,000 coins + - Hard Kandarin Diary: 30,000 coins + - Elite Kandarin Diary: 20,000 coins +- **Player-owned House** (66 Construction): Convert from other mystic staff + 1,000 water runes + +> *"It's a slightly magical stick."* + +## Stats + +| Attack | Value | +|--------|-------| +| Magic | +14 | +| Crush | +40 | + +| Defence | Value | +|---------|-------| +| Magic | +14 | + +| Other | Value | +|-------|-------| +| Strength | +50 | + +**Requirements:** 40 Attack, 40 Magic + +## Water Rune Provision + +Provides unlimited **water runes**. Useful for: +- **Water spells** (Water Blast through Water Surge) +- **Charge Water Orb** — saves water runes for crafting +- **Humidify** (Lunar spellbook) — requires water runes +- Autocast with standard spellbook + +## Thormac Upgrade Economics + +| Diary Tier | Cost | Battlestaff GE | Profit | +|-----------|------|----------------|--------| +| None | 40,000 | ~8,500 | ~-6,000 | +| Hard Kandarin | 30,000 | ~8,500 | ~4,000 | +| Elite Kandarin | 20,000 | ~8,500 | ~14,000 | + +The Kandarin diary discount makes mystic staff upgrades progressively more profitable. + +## Market Strategy + +**Buy Limit:** 18,000 per 4 hours | **High Alch:** 25,500 GP + +**Tips:** +- Thormac upgrade profitable with Hard+ diary +- High volume item +- GE price tracks near high alch value + +## Related Items + +- [Mystic fire staff](/osrs/mystic-fire-staff/) - Fire elemental version +- [Mystic earth staff](/osrs/mystic-earth-staff/) - Earth elemental version +- [Mystic mud staff](/osrs/mystic-mud-staff/) - Water+Earth combination diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_1407.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_1407.mdx new file mode 100644 index 000000000..a4c6b98c6 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_1407.mdx @@ -0,0 +1,96 @@ +--- +equipment: + slot: "weapon" + attack_speed: 5 + attack_bonus: + stab: 10 + slash: -1 + crush: 40 + magic: 14 + ranged: 0 + defence_bonus: + stab: 2 + slash: 3 + crush: 1 + magic: 14 + ranged: 0 + other_bonus: + melee_strength: 50 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + attack: 40 + magic: 40 + weight: 2.267 +related_items: + - item_id: 1403 + item_name: "Mystic water staff" + slug: "mystic-water-staff" + relationship: "set" + description: "Water elemental mystic staff" + - item_id: 1401 + item_name: "Mystic fire staff" + slug: "mystic-fire-staff" + relationship: "set" + description: "Fire elemental mystic staff" + - item_id: 6563 + item_name: "Mystic mud staff" + slug: "mystic-mud-staff" + relationship: "upgrade" + description: "Water+Earth combination staff" +--- + +## Obtaining + +- **Thormac** (after Scorpion Catcher): Earth battlestaff + 40,000 coins + - Hard Kandarin Diary: 30,000 coins + - Elite Kandarin Diary: 20,000 coins +- **Player-owned House** (66 Construction): Convert from other mystic staff + 1,000 earth runes + +> *"It's a slightly magical stick."* + +## Stats + +| Attack | Value | +|--------|-------| +| Magic | +14 | +| Crush | +40 | + +| Defence | Value | +|---------|-------| +| Magic | +14 | + +| Other | Value | +|-------|-------| +| Strength | +50 | + +**Requirements:** 40 Attack, 40 Magic + +## Earth Rune Provision + +Provides unlimited **earth runes**. Useful for: +- **Earth spells** (Earth Blast through Earth Surge) +- **Bones to Bananas/Peaches** — requires earth runes +- **Charge Earth Orb** — saves earth runes +- Autocast with standard spellbook + +## Usage Notes + +Earth runes are among the cheapest runes, so the cost-saving benefit is modest compared to fire or water staves. The primary use case is convenience — not needing to carry earth runes in the inventory. + +## Market Strategy + +**Buy Limit:** 18,000 per 4 hours | **High Alch:** 25,500 GP + +**Tips:** +- Thormac upgrade profitable with diary discounts +- High volume item — popular alch target +- GE price tracks near high alch value +- Least practical of the elemental mystic staves + +## Related Items + +- [Mystic water staff](/osrs/mystic-water-staff/) - Water elemental version +- [Mystic fire staff](/osrs/mystic-fire-staff/) - Fire elemental version +- [Mystic mud staff](/osrs/mystic-mud-staff/) - Water+Earth combination diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_1432.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_1432.mdx new file mode 100644 index 000000000..399da68db --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_1432.mdx @@ -0,0 +1,99 @@ +--- +equipment: + slot: "weapon" + attack_speed: 4 + attack_bonus: + stab: 20 + slash: -2 + crush: 39 + magic: 0 + ranged: 0 + defence_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 0 + ranged: 0 + other_bonus: + melee_strength: 36 + ranged_strength: 0 + magic_damage: 0 + prayer: 4 + requirements: + attack: 40 + weight: 1.814 +related_items: + - item_id: 1333 + item_name: "Rune scimitar" + slug: "rune-scimitar" + relationship: "alternative" + description: "Higher DPS for training" + - item_id: 1347 + item_name: "Rune warhammer" + slug: "rune-warhammer" + relationship: "alternative" + description: "Higher crush bonus" +--- + +## Obtaining + +- **Smithing:** Level 87 using 1 Runite bar (75 XP) +- **Shop:** Scavvo's Rune Store, Champions' Guild (14,400 coins, 32 QP required) +- **Drops:** Greater demon, Cockathrice, Shadow warrior, various others + +> *"A spiky mace."* + +## Stats + +| Attack | Value | +|--------|-------| +| Stab | +20 | +| Crush | +39 | + +| Other | Value | +|-------|-------| +| Strength | +36 | +| Prayer | **+4** | + +**Requirements:** 40 Attack | **Speed:** 4 tick (2.4s) + +## Prayer Bonus + +The **+4 Prayer bonus** is the rune mace's defining feature. In F2P, only the rune mace and its upgrade (bone mace) provide Prayer from a weapon slot. This makes it valuable for: +- Extended trips where Prayer matters (e.g., Protection prayers at bosses) +- F2P PKing with Protect Item + +## Bone Mace Upgrade + +Combine with **Scurrius' spine** (from the Scurrius boss) to create a **Bone mace** — a direct upgrade with higher stats and retained prayer bonus. Scurrius is located beneath Varrock in the Scurrius' Lair. + +## Comparison + +| Stat | Rune Mace | Rune Scimitar | Bone Mace | +|------|-----------|---------------|-----------| +| Slash | -2 | +45 | — | +| Crush | +39 | -2 | +52 | +| Strength | +36 | +44 | +46 | +| Prayer | **+4** | 0 | **+4** | +| Speed | 4 tick | 4 tick | 4 tick | + +Lower DPS than rune scimitar, but the prayer bonus and crush style offer niche advantages — especially against Scurrius (weak to crush) and in prayer-dependent F2P scenarios. + +## Balance History + +**April 2021:** Attack speed buffed from 5-tick (3.0s) to **4-tick (2.4s)**, matching the rune scimitar's speed. This significantly improved its viability. + +## Market Strategy + +**Buy Limit:** 70 per 4 hours | **High Alch:** 8,640 GP + +**Tips:** +- Low-value smithing product (1 runite bar) +- Demand driven by bone mace upgrade path +- F2P prayer weapon niche + +## Related Items + +- [Rune scimitar](/osrs/rune-scimitar/) - Higher DPS training weapon +- [Rune warhammer](/osrs/rune-warhammer/) - Better crush weapon +- [Bone mace](/osrs/bone-mace/) - Direct upgrade via Scurrius' spine diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_1637.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_1637.mdx new file mode 100644 index 000000000..8ed6faed9 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_1637.mdx @@ -0,0 +1,53 @@ +--- +equipment: + slot: "ring" + requirements: + crafting: 20 +recipes: + - skill: "crafting" + level: 20 + xp: 40 + inputs: + - item_id: 1656 + item_name: "Sapphire" + quantity: 1 + - item_id: 2357 + item_name: "Gold bar" + quantity: 1 + output_quantity: 1 +related_items: + - item_id: 1639 + item_name: "Emerald ring" + slug: "emerald-ring" + relationship: "upgrade" + description: "Next tier ring" + - item_id: 2550 + item_name: "Ring of recoil" + slug: "ring-of-recoil" + relationship: "enchanted" + description: "Enchanted version (reflects damage)" +--- + +## Creation + +| Input | Skill | Level | XP | +|-------|-------|-------|----| +| [Sapphire](/osrs/sapphire/) + [Gold bar](/osrs/gold-bar/) | Crafting | 20 | 40 | + +## Enchanting + +Enchant with Lvl-1 Enchant (Magic 7) to create [Ring of recoil](/osrs/ring-of-recoil/). + +## Market Strategy + +**Buy Limit:** 10,000 per 4 hours + +**Tips:** +- Crafting training material +- Ring of recoil is highly consumed +- Check enchant profit margins + +## Related Items + +- [Ring of recoil](/osrs/ring-of-recoil/) - Enchanted version +- [Emerald ring](/osrs/emerald-ring/) - Next tier diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_1639.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_1639.mdx new file mode 100644 index 000000000..bf319416e --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_1639.mdx @@ -0,0 +1,58 @@ +--- +equipment: + slot: "ring" + requirements: + crafting: 27 +recipes: + - skill: "crafting" + level: 27 + xp: 55 + inputs: + - item_id: 1658 + item_name: "Emerald" + quantity: 1 + - item_id: 2357 + item_name: "Gold bar" + quantity: 1 + output_quantity: 1 +related_items: + - item_id: 1637 + item_name: "Sapphire ring" + slug: "sapphire-ring" + relationship: "downgrade" + description: "Previous tier ring" + - item_id: 1641 + item_name: "Ruby ring" + slug: "ruby-ring" + relationship: "upgrade" + description: "Next tier ring" + - item_id: 2552 + item_name: "Ring of dueling(8)" + slug: "ring-of-dueling-8" + relationship: "enchanted" + description: "Enchanted version (teleports)" +--- + +## Creation + +| Input | Skill | Level | XP | +|-------|-------|-------|----| +| [Emerald](/osrs/emerald/) + [Gold bar](/osrs/gold-bar/) | Crafting | 27 | 55 | + +## Enchanting + +Enchant with Lvl-2 Enchant (Magic 27) to create Ring of dueling. + +## Market Strategy + +**Buy Limit:** 10,000 per 4 hours + +**Tips:** +- Ring of dueling is consumed on use +- Steady demand for teleports +- Check enchant profit margins + +## Related Items + +- [Sapphire ring](/osrs/sapphire-ring/) - Previous tier +- [Ruby ring](/osrs/ruby-ring/) - Next tier diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_1641.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_1641.mdx new file mode 100644 index 000000000..456d7cd33 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_1641.mdx @@ -0,0 +1,59 @@ +--- +equipment: + slot: "ring" + requirements: + crafting: 34 +recipes: + - skill: "crafting" + level: 34 + xp: 70 + inputs: + - item_id: 1660 + item_name: "Ruby" + quantity: 1 + - item_id: 2357 + item_name: "Gold bar" + quantity: 1 + output_quantity: 1 +related_items: + - item_id: 1639 + item_name: "Emerald ring" + slug: "emerald-ring" + relationship: "downgrade" + description: "Previous tier ring" + - item_id: 1643 + item_name: "Diamond ring" + slug: "diamond-ring" + relationship: "upgrade" + description: "Next tier ring" + - item_id: 2568 + item_name: "Ring of forging" + slug: "ring-of-forging" + relationship: "enchanted" + description: "Enchanted version (100% smelt success)" +--- + +## Creation + +| Input | Skill | Level | XP | +|-------|-------|-------|----| +| [Ruby](/osrs/ruby/) + [Gold bar](/osrs/gold-bar/) | Crafting | 34 | 70 | + +## Enchanting + +Enchant with Lvl-3 Enchant (Magic 49) to create Ring of forging (140 charges, 100% iron ore smelting). + +## Market Strategy + +**Buy Limit:** 10,000 per 4 hours + +**Tips:** +- Ring of forging consumed in Blast Furnace +- Check enchant profit margins +- Iron smelting meta drives demand + +## Related Items + +- [Emerald ring](/osrs/emerald-ring/) - Previous tier +- [Diamond ring](/osrs/diamond-ring/) - Next tier +- [Ring of forging](/osrs/ring-of-forging/) - Enchanted version diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_1643.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_1643.mdx new file mode 100644 index 000000000..d913147ba --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_1643.mdx @@ -0,0 +1,59 @@ +--- +equipment: + slot: "ring" + requirements: + crafting: 43 +recipes: + - skill: "crafting" + level: 43 + xp: 85 + inputs: + - item_id: 1662 + item_name: "Diamond" + quantity: 1 + - item_id: 2357 + item_name: "Gold bar" + quantity: 1 + output_quantity: 1 +related_items: + - item_id: 1641 + item_name: "Ruby ring" + slug: "ruby-ring" + relationship: "downgrade" + description: "Previous tier ring" + - item_id: 1645 + item_name: "Dragonstone ring" + slug: "dragonstone-ring" + relationship: "upgrade" + description: "Next tier ring" + - item_id: 2570 + item_name: "Ring of life" + slug: "ring-of-life" + relationship: "enchanted" + description: "Enchanted version (auto-teleport at low HP)" +--- + +## Creation + +| Input | Skill | Level | XP | +|-------|-------|-------|----| +| [Diamond](/osrs/diamond/) + [Gold bar](/osrs/gold-bar/) | Crafting | 43 | 85 | + +## Enchanting + +Enchant with Lvl-4 Enchant (Magic 57) to create Ring of life (teleports to spawn when below 10% HP). + +## Market Strategy + +**Buy Limit:** 10,000 per 4 hours + +**Tips:** +- Ring of life consumed on activation +- Popular safety item for PvM +- Steady enchant demand + +## Related Items + +- [Ruby ring](/osrs/ruby-ring/) - Previous tier +- [Dragonstone ring](/osrs/dragonstone-ring/) - Next tier +- [Ring of life](/osrs/ring-of-life/) - Enchanted version diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_1645.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_1645.mdx new file mode 100644 index 000000000..1474452d4 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_1645.mdx @@ -0,0 +1,59 @@ +--- +equipment: + slot: "ring" + requirements: + crafting: 55 +recipes: + - skill: "crafting" + level: 55 + xp: 100 + inputs: + - item_id: 1615 + item_name: "Dragonstone" + quantity: 1 + - item_id: 2357 + item_name: "Gold bar" + quantity: 1 + output_quantity: 1 +related_items: + - item_id: 1643 + item_name: "Diamond ring" + slug: "diamond-ring" + relationship: "downgrade" + description: "Previous tier ring" + - item_id: 6575 + item_name: "Onyx ring" + slug: "onyx-ring" + relationship: "upgrade" + description: "Next tier ring" + - item_id: 2572 + item_name: "Ring of wealth" + slug: "ring-of-wealth" + relationship: "enchanted" + description: "Enchanted version (improves RDT)" +--- + +## Creation + +| Input | Skill | Level | XP | +|-------|-------|-------|----| +| [Dragonstone](/osrs/dragonstone/) + [Gold bar](/osrs/gold-bar/) | Crafting | 55 | 100 | + +## Enchanting + +Enchant with Lvl-5 Enchant (Magic 68) to create [Ring of wealth](/osrs/ring-of-wealth/) (improved Rare Drop Table access). + +## Market Strategy + +**Buy Limit:** 10,000 per 4 hours + +**Tips:** +- Ring of wealth is widely used +- Dragonstone cost drives price +- Enchant margin varies + +## Related Items + +- [Diamond ring](/osrs/diamond-ring/) - Previous tier +- [Onyx ring](/osrs/onyx-ring/) - Next tier +- [Ring of wealth](/osrs/ring-of-wealth/) - Enchanted version diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_1729.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_1729.mdx new file mode 100644 index 000000000..8ea544401 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_1729.mdx @@ -0,0 +1,104 @@ +--- +equipment: + slot: "neck" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 0 + ranged: 0 + defence_bonus: + stab: 7 + slash: 7 + crush: 7 + magic: 7 + ranged: 7 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: {} + weight: 0.01 +recipes: + - skill: "magic" + level: 27 + xp: 37 + inputs: + - item_name: "Emerald amulet" + quantity: 1 + - item_name: "Cosmic rune" + quantity: 1 + - item_name: "Air rune" + quantity: 3 + output_quantity: 1 +related_items: + - item_id: 1725 + item_name: "Amulet of power" + slug: "amulet-of-power" + relationship: "upgrade" + description: "All-round offensive amulet" + - item_id: 1704 + item_name: "Amulet of glory" + slug: "amulet-of-glory" + relationship: "upgrade" + description: "Superior all-round amulet" +--- + +## Obtaining + +**Lvl-2 Enchant** (27 Magic): Emerald amulet + 1 cosmic rune + 3 air runes (37 XP) + +Also dropped by various monsters and available from shops. + +> *"An enchanted emerald amulet of protection."* + +## Stats + +| Defence | Value | +|---------|-------| +| All styles | **+7** | + +**Requirements:** None | **Weight:** 0.01 kg + +No attack bonuses of any kind — purely defensive. + +## F2P Significance + +One of the few neck slot items available in F2P that provides defence bonuses. The amulet of power (+6 all attack, +1 all defence, +6 str, +1 prayer) is generally preferred, but the amulet of defence offers **+6 more defence** per style — making it niche for tanking. + +## Ornament Kit + +An **Amulet of defence (t)** variant exists, obtained from beginner Treasure Trail caskets. This trimmed version is purely cosmetic with identical stats, but trades at a premium for fashionscape. + +## Wilderness Skilling + +The amulet of defence is useful for **Wilderness skilling** (e.g., black chinchompa hunting, Wilderness Agility Course) where players want cheap defensive stats without risking an expensive amulet. + +## Enchanting Economics + +| Component | Cost | +|-----------|------| +| Emerald amulet | ~1,200 GP | +| 1 Cosmic rune | ~130 GP | +| 3 Air runes | ~15 GP | +| **Total cost** | ~1,345 GP | +| GE sell price | ~1,800 GP | +| **Profit** | ~455 GP | + +Enchanting emerald amulets into amulets of defence can be marginally profitable and provides decent Magic XP at low levels. + +## Market Strategy + +**Buy Limit:** 125 per 4 hours | **High Alch:** 765 GP + +**Tips:** +- Steady F2P demand +- Enchanting can be marginally profitable +- (t) variant from beginner clues trades at premium +- Budget Wilderness risk amulet + +## Related Items + +- [Amulet of power](/osrs/amulet-of-power/) - Offensive all-round upgrade +- [Amulet of glory](/osrs/amulet-of-glory/) - Superior all-round amulet diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_19538.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_19538.mdx new file mode 100644 index 000000000..86cce0f57 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_19538.mdx @@ -0,0 +1,59 @@ +--- +equipment: + slot: "ring" + requirements: + crafting: 89 +recipes: + - skill: "crafting" + level: 89 + xp: 150 + inputs: + - item_id: 19493 + item_name: "Zenyte" + quantity: 1 + - item_id: 2357 + item_name: "Gold bar" + quantity: 1 + output_quantity: 1 +related_items: + - item_id: 6575 + item_name: "Onyx ring" + slug: "onyx-ring" + relationship: "downgrade" + description: "Previous tier ring" + - item_id: 19550 + item_name: "Ring of suffering" + slug: "ring-of-suffering" + relationship: "enchanted" + description: "Enchanted version (BiS defensive ring)" + - item_id: 19493 + item_name: "Zenyte" + slug: "zenyte" + relationship: "component" + description: "Key component from demonic gorillas" +--- + +## Creation + +| Input | Skill | Level | XP | +|-------|-------|-------|----| +| [Zenyte](/osrs/zenyte/) + [Gold bar](/osrs/gold-bar/) | Crafting | 89 | 150 | + +## Enchanting + +Enchant with Lvl-7 Enchant (Magic 93) to create [Ring of suffering](/osrs/ring-of-suffering/) (BiS defensive ring, stores recoils). + +## Market Strategy + +**Buy Limit:** 8 per 4 hours + +**Tips:** +- Zenyte shard from demonic gorillas + onyx +- Ring of suffering is BiS defensive +- High-value enchant + +## Related Items + +- [Onyx ring](/osrs/onyx-ring/) - Previous tier +- [Ring of suffering](/osrs/ring-of-suffering/) - Enchanted version +- [Zenyte](/osrs/zenyte/) - Key component diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_20724.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_20724.mdx index b2f0aba55..dd2398c25 100644 --- a/apps/kbve/astro-kbve/data/osrs-overrides/_20724.mdx +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_20724.mdx @@ -16,7 +16,7 @@ drop_table: rate: "1/200" - source: "Other superiors" rate: "1/200 - 1/1,376" -truemarket: +market: buy_limit: 8 price_estimate: 29000000 related_items: diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_21892.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_21892.mdx new file mode 100644 index 000000000..11fc317ba --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_21892.mdx @@ -0,0 +1,83 @@ +--- +equipment: + slot: "body" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: -15 + ranged: 0 + defence_bonus: + stab: 117 + slash: 120 + crush: 113 + magic: -6 + ranged: 113 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + defence: 60 +related_items: + - item_id: 3140 + item_name: "Dragon chainbody" + slug: "dragon-chainbody" + relationship: "downgrade" + description: "Easier to obtain dragon body" + - item_id: 11832 + item_name: "Bandos chestplate" + slug: "bandos-chestplate" + relationship: "upgrade" + description: "BiS melee body with strength bonus" + - item_id: 1127 + item_name: "Rune platebody" + slug: "rune-platebody" + relationship: "downgrade" + description: "F2P alternative" +--- + +## Obtaining + +Created during **Dragon Slayer II** quest. + +Combine at the Lithkren Vault: +- Dragon metal shard (Vorkath drop, 1/5000 from other dragons) +- Dragon metal slice (Adamant/Rune dragons) +- Dragon metal lump (Adamant/Rune dragons) + +## Stats + +| Defence | Value | +|---------|-------| +| Stab | +117 | +| Slash | +120 | +| Crush | +113 | +| Magic | -6 | +| Ranged | +113 | + +**Requirements:** 60 Defence, Dragon Slayer II + +## Usage + +Best dragon body armour: +- Highest defensive stats for 60 Defence +- Does not degrade +- Cosmetically popular + +**Note:** Bandos chestplate is preferred for the +4 Strength bonus despite lower Defence. + +## Market Strategy + +**Buy Limit:** 8 per 4 hours + +**Tips:** +- Components are rare drops +- Popular for fashionscape +- Niche use where pure Defence matters + +## Related Items + +- [Dragon chainbody](/osrs/dragon-chainbody/) - Easier to obtain +- [Bandos chestplate](/osrs/bandos-chestplate/) - BiS with strength bonus diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_3053.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_3053.mdx new file mode 100644 index 000000000..2818dc5bd --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_3053.mdx @@ -0,0 +1,106 @@ +--- +equipment: + slot: "weapon" + attack_speed: 5 + attack_bonus: + stab: 7 + slash: -1 + crush: 28 + magic: 12 + ranged: 0 + defence_bonus: + stab: 2 + slash: 3 + crush: 1 + magic: 12 + ranged: 0 + other_bonus: + melee_strength: 35 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + attack: 30 + magic: 30 + weight: 2.267 +related_items: + - item_id: 3054 + item_name: "Mystic lava staff" + slug: "mystic-lava-staff" + relationship: "upgrade" + description: "Mystic version via Thormac" + - item_id: 6562 + item_name: "Mud battlestaff" + slug: "mud-battlestaff" + relationship: "alternative" + description: "Water+Earth combination staff" +--- + +## Obtaining + +- **TzHaar creatures** — various drops (TzHaar-Hur, TzHaar-Ket, TzHaar-Xil, TzHaar-Mej) +- **Abyssal Sire** — 16/128 (common) +- **Various spectres** — uncommon drop +- **Barbarian Assault** — High Level Gamble +- **Elite Treasure Trails** — reward + +> *"A magical staff that harnesses the power of fire and earth."* + +## Stats + +| Attack | Value | +|--------|-------| +| Magic | +12 | +| Crush | +28 | + +| Defence | Value | +|---------|-------| +| Magic | +12 | + +| Other | Value | +|-------|-------| +| Strength | +35 | + +**Requirements:** 30 Attack, 30 Magic + +## Combination Staff + +Provides unlimited **earth and fire runes** simultaneously. This enables: +- **Lvl-6 Enchant** (Onyx) — saves fire + earth runes +- **Shadow Veil** (Arceuus spellbook) — requires earth + fire runes +- **Battlefront Teleport** — earth + fire runes +- **Superheat Item** — saves fire runes (with earth already covered) +- Autocast with standard spellbook + +## Upgrade Path + +| Option | Cost | Result | +|--------|------|--------| +| Thormac (base) | 40,000 coins | Mystic lava staff | +| Thormac (Hard Kandarin) | 30,000 coins | Mystic lava staff | +| Thormac (Elite Kandarin) | 20,000 coins | Mystic lava staff | + +## Comparison vs Mud Battlestaff + +| Stat | Lava Bstaff | Mud Bstaff | +|------|-------------|------------| +| Elements | Earth + Fire | Water + Earth | +| Rarity | Common | Rare (DK Prime) | +| GE Price | ~10K | ~35K+ | +| Best spells | Fire spells, Superheat | Lunar spells, Barrage | + +Lava is much more common and cheaper, but mud is more versatile for high-level spellcasting. + +## Market Strategy + +**Buy Limit:** 8 per 4 hours | **High Alch:** 10,200 GP + +**Tips:** +- Multiple drop sources (easier to obtain than mud) +- Thormac upgrade can be profitable with diary discounts +- Useful for specific spell combinations + +## Related Items + +- [Mystic lava staff](/osrs/mystic-lava-staff/) - Upgraded version via Thormac +- [Mud battlestaff](/osrs/mud-battlestaff/) - Water+Earth alternative diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_3054.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_3054.mdx new file mode 100644 index 000000000..a0fcb9b18 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_3054.mdx @@ -0,0 +1,102 @@ +--- +equipment: + slot: "weapon" + attack_speed: 5 + attack_bonus: + stab: 10 + slash: -1 + crush: 40 + magic: 14 + ranged: 0 + defence_bonus: + stab: 2 + slash: 3 + crush: 1 + magic: 14 + ranged: 0 + other_bonus: + melee_strength: 50 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + attack: 40 + magic: 40 + weight: 2.267 +related_items: + - item_id: 3053 + item_name: "Lava battlestaff" + slug: "lava-battlestaff" + relationship: "component" + description: "Base staff for upgrade" + - item_id: 6563 + item_name: "Mystic mud staff" + slug: "mystic-mud-staff" + relationship: "alternative" + description: "Water+Earth combination" +--- + +## Obtaining + +**Thormac** (after Scorpion Catcher quest): Lava battlestaff + coins +- Base cost: 40,000 coins +- Hard Kandarin Diary: 30,000 coins +- Elite Kandarin Diary: 20,000 coins + +> *"It's a slightly magical stick."* + +## Stats + +| Attack | Value | +|--------|-------| +| Magic | +14 | +| Crush | +40 | + +| Defence | Value | +|---------|-------| +| Magic | +14 | + +| Other | Value | +|-------|-------| +| Strength | +50 | + +**Requirements:** 40 Attack, 40 Magic + +## Dual Rune Provision + +Provides unlimited **earth and fire runes** simultaneously: +- **Lvl-6 Enchant** (Onyx) — saves earth + fire runes +- **Superheat Item** — saves fire runes with earth covered +- **Shadow Veil** (Arceuus) — requires earth + fire runes +- **Fire spells** — saves fire runes with earth as backup +- Autocast with standard spellbook + +## Cosmetic Upgrade + +A **Lava staff upgrade kit** (from Master Treasure Trails) can be applied for a cosmetic variant. Purely visual — no stat changes. + +## Comparison vs Mystic Mud Staff + +| Feature | Mystic Lava | Mystic Mud | +|---------|-------------|------------| +| Elements | Earth + Fire | Water + Earth | +| Base staff cost | ~10K | ~35K+ | +| Best for | Superheat, Enchant | Barrage, Lunar | +| Buy limit | 8/4h | 8/4h | + +Both are combination mystic staves with identical combat stats. The mud staff is more expensive and generally more useful for endgame content. + +## Market Strategy + +**Buy Limit:** 8 per 4 hours | **High Alch:** 27,000 GP + +**Tips:** +- Lava battlestaff is cheap and common +- Thormac cost varies with Kandarin diary tier +- Cosmetic upgrade kit available from Master clues +- Less demand than mystic mud staff + +## Related Items + +- [Lava battlestaff](/osrs/lava-battlestaff/) - Base staff +- [Mystic mud staff](/osrs/mystic-mud-staff/) - Water+Earth combination diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_3122.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_3122.mdx new file mode 100644 index 000000000..ec4d3e8cb --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_3122.mdx @@ -0,0 +1,113 @@ +--- +equipment: + slot: "shield" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: -12 + ranged: -8 + defence_bonus: + stab: 40 + slash: 42 + crush: 38 + magic: 0 + ranged: 65 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + defence: 50 + strength: 50 + weight: 6.803 +drop_table: + primary_source: "Ice troll" + best_drop_rate: "1/128" + sources: + - source: "Ice troll" + combat_level: 120 + quantity: "1" + rarity: "rare" + drop_rate: "1/128" + members_only: true + - source: "Troll general" + combat_level: 113 + quantity: "1" + rarity: "rare" + drop_rate: "1/128" + members_only: true +related_items: + - item_id: 10589 + item_name: "Granite helm" + slug: "granite-helm" + relationship: "set" + description: "Granite set piece" + - item_id: 6809 + item_name: "Granite legs" + slug: "granite-legs" + relationship: "set" + description: "Granite set piece" +--- + +## Obtaining + +- **Ice trolls** (Jatizso/Neitiznot) — 1/128 drop rate +- **Troll generals** — 1/128 drop rate +- **Barbarian Assault** High Level Gamble — 1/32 +- **Elite Treasure Trails** — emote clue reward + +> *"A solid stone shield."* + +## Stats + +| Defence | Value | +|---------|-------| +| Stab | +40 | +| Slash | +42 | +| Crush | +38 | +| Ranged | **+65** | +| Magic | 0 | + +**Requirements:** 50 Defence, 50 Strength | **Weight:** 6.8 kg + +## Comparison + +| Stat | Granite Shield | Toktz-ket-xil | Rune Kiteshield | +|------|---------------|---------------|-----------------| +| Stab Def | +40 | +40 | +44 | +| Slash Def | +42 | +42 | +48 | +| Crush Def | +38 | +38 | +42 | +| Ranged Def | **+65** | **+65** | +46 | +| Str Bonus | 0 | **+5** | 0 | +| Prayer | 0 | 0 | 0 | +| Req | 50 Def/Str | 60 Def | 40 Def | + +Statistically identical to the Toktz-ket-xil (obsidian shield) in defensive stats, but the obsidian shield provides **+5 Strength bonus** and only requires 60 Defence (no Strength req). The granite shield's main advantage is lower level requirements for the ranged defence. + +## Trivia + +The granite shield's model bears a resemblance to the island of **Gran Canaria** in the Canary Islands, which may be an intentional reference to "granite." + +## Usage + +- Best ranged defence shield at 50 Defence +- Part of granite fashionscape set +- Budget alternative before dragon/crystal shields +- No negative Prayer bonus (unlike some alternatives) + +## Market Strategy + +**Buy Limit:** 70 per 4 hours | **High Alch:** 33,600 GP + +**Tips:** +- Dropped by trolls on Jatizso during The Fremennik Isles +- GE price near high alch value +- Low trade volume + +## Related Items + +- [Granite helm](/osrs/granite-helm/) - Matching set piece +- [Granite legs](/osrs/granite-legs/) - Matching set piece +- [Toktz-ket-xil](/osrs/toktz-ket-xil/) - Equal stats with +5 Str diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_3140.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_3140.mdx new file mode 100644 index 000000000..c9b5c20ae --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_3140.mdx @@ -0,0 +1,94 @@ +--- +equipment: + slot: "body" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: -15 + ranged: 0 + defence_bonus: + stab: 81 + slash: 93 + crush: 83 + magic: -3 + ranged: 80 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + defence: 60 +drop_table: + primary_source: "Kalphite Queen" + best_drop_rate: "1/128" + sources: + - source: "Kalphite Queen" + combat_level: 333 + quantity: "1" + rarity: "rare" + drop_rate: "1/128" + members_only: true + - source: "Thermonuclear smoke devil" + combat_level: 301 + quantity: "1" + rarity: "very rare" + drop_rate: "1/2000" + members_only: true +related_items: + - item_id: 21892 + item_name: "Dragon platebody" + slug: "dragon-platebody" + relationship: "upgrade" + description: "Superior dragon body armour" + - item_id: 1127 + item_name: "Rune platebody" + slug: "rune-platebody" + relationship: "downgrade" + description: "Budget alternative" + - item_id: 11832 + item_name: "Bandos chestplate" + slug: "bandos-chestplate" + relationship: "upgrade" + description: "BiS melee body (non-degradable)" +--- + +## Obtaining + +Dropped by Kalphite Queen (1/128). + +**Other sources:** +- Thermonuclear smoke devil (1/2000) + +## Stats + +| Defence | Value | +|---------|-------| +| Stab | +81 | +| Slash | +93 | +| Crush | +83 | +| Magic | -3 | +| Ranged | +80 | + +## Usage + +Mid-tier melee body armour: +- Budget alternative to Bandos chestplate +- 60 Defence requirement +- Does not degrade + +## Market Strategy + +**Buy Limit:** 8 per 4 hours + +**Tips:** +- Dropped by Kalphite Queen +- Budget melee option before BCP +- Cosmetic appeal for fashionscape + +## Related Items + +- [Dragon platebody](/osrs/dragon-platebody/) - Superior dragon armour +- [Rune platebody](/osrs/rune-platebody/) - Budget alternative +- [Bandos chestplate](/osrs/bandos-chestplate/) - BiS melee body diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_3385.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_3385.mdx new file mode 100644 index 000000000..6d1e7465b --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_3385.mdx @@ -0,0 +1,121 @@ +--- +equipment: + slot: "head" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 3 + ranged: -2 + defence_bonus: + stab: 10 + slash: 9 + crush: 11 + magic: 3 + ranged: 0 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + magic: 40 + defence: 40 + weight: 0.907 +recipes: + - skill: "crafting" + level: 61 + xp: 124 + inputs: + - item_name: "Bark" + quantity: 2 + - item_name: "Fine cloth" + quantity: 2 + output_quantity: 1 +related_items: + - item_id: 3387 + item_name: "Splitbark body" + slug: "splitbark-body" + relationship: "set" + description: "Matching body piece" + - item_id: 3389 + item_name: "Splitbark legs" + slug: "splitbark-legs" + relationship: "set" + description: "Matching leg piece" + - item_id: 4091 + item_name: "Mystic robe top" + slug: "mystic-robe-top" + relationship: "alternative" + description: "Higher magic bonus, no defence" +--- + +## Obtaining + +- **Crafting** (Level 61): 2 bark + 2 fine cloth + needle/thread (124 XP) +- **Wizard Jalarast** (Wizards' Tower basement): 2 bark + 2 fine cloth + 6,000 coins +- **Ninja implings** — 1/19 rarity + +> *"A helm made from magical bark."* + +## Stats + +| Attack | Value | +|--------|-------| +| Magic | +3 | + +| Defence | Value | +|---------|-------| +| Stab | +10 | +| Slash | +9 | +| Crush | +11 | +| Magic | +3 | + +**Requirements:** 40 Magic, 40 Defence + +## Material Sourcing + +- **Bark:** Obtained from Hollow Trees in the Haunted Woods (east of Canifis). Requires a knife. Each tree gives 1 bark. +- **Fine cloth:** Obtained from the Shades of Mort'ton minigame by burning remains on pyres and looting the chest. + +## Comparison vs Mystic Hat + +| Stat | Splitbark Helm | Mystic Hat | +|------|---------------|------------| +| Magic Atk | +3 | +4 | +| Stab Def | **+10** | 0 | +| Slash Def | **+9** | 0 | +| Crush Def | **+11** | 0 | +| Magic Def | +3 | +4 | + +Trades 1 Magic attack for significant melee defence. Better for hybrid setups where you expect melee hits. + +## Upgraded Variants + +- **Swampbark helm:** Requires Nature Spirit quest. Adds prayer-boosting effects to Earth spells. +- **Bloodbark helm:** Requires A Taste of Hope quest. Adds blood spell healing effects. + +## Set Totals (Full Splitbark) + +| Stat | Total | +|------|-------| +| Magic Atk | **+20** | +| Stab Def | +68 | +| Slash Def | +55 | +| Crush Def | +78 | +| Magic Def | +28 | +| Materials | 9 bark + 9 fine cloth | + +## Market Strategy + +**Buy Limit:** 70 per 4 hours | **High Alch:** 6,000 GP + +**Tips:** +- Material sourcing is the bottleneck (fine cloth from Shades of Mort'ton) +- Craftable for XP and slight profit +- Niche hybrid demand + +## Related Items + +- [Splitbark body](/osrs/splitbark-body/) - Matching body +- [Splitbark legs](/osrs/splitbark-legs/) - Matching legs diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_3387.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_3387.mdx new file mode 100644 index 000000000..9146f3d55 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_3387.mdx @@ -0,0 +1,115 @@ +--- +equipment: + slot: "body" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 10 + ranged: -10 + defence_bonus: + stab: 36 + slash: 26 + crush: 42 + magic: 15 + ranged: 0 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + magic: 40 + defence: 40 + weight: 4.535 +recipes: + - skill: "crafting" + level: 62 + xp: 248 + inputs: + - item_name: "Bark" + quantity: 4 + - item_name: "Fine cloth" + quantity: 4 + output_quantity: 1 +drop_table: + sources: + - source: "Chaos Fanatic" + combat_level: 202 + quantity: "1" + rarity: "rare" + drop_rate: "1/128" + members_only: true +related_items: + - item_id: 3385 + item_name: "Splitbark helm" + slug: "splitbark-helm" + relationship: "set" + description: "Matching helm piece" + - item_id: 3389 + item_name: "Splitbark legs" + slug: "splitbark-legs" + relationship: "set" + description: "Matching leg piece" +--- + +## Obtaining + +- **Crafting** (Level 62): 4 bark + 4 fine cloth + needle/thread (248 XP) +- **Wizard Jalarast** (Wizards' Tower basement): 4 bark + 4 fine cloth + 37,000 coins +- **Chaos Fanatic** — 1/128 drop rate +- **Treasure Trails** — Elite/Master rewards + +> *"A body made from magical bark."* + +## Stats + +| Attack | Value | +|--------|-------| +| Magic | +10 | + +| Defence | Value | +|---------|-------| +| Stab | +36 | +| Slash | +26 | +| Crush | +42 | +| Magic | +15 | + +**Requirements:** 40 Magic, 40 Defence + +## Comparison vs Mystic Robe Top + +| Stat | Splitbark Body | Mystic Robe Top | +|------|---------------|-----------------| +| Magic Atk | +10 | **+20** | +| Stab Def | **+36** | 0 | +| Slash Def | **+26** | 0 | +| Crush Def | **+42** | 0 | +| Magic Def | +15 | **+20** | + +Half the magic attack bonus of mystic, but provides substantial melee defence. The body piece shows the largest stat trade-off in the set — 10 less magic attack for +36/+26/+42 melee defence. + +## Material Sourcing + +- **Bark:** Hollow Trees in the Haunted Woods (east of Canifis) +- **Fine cloth:** Shades of Mort'ton minigame — burn shade remains on pyres +- The body requires the most materials of any splitbark piece (4 bark + 4 fine cloth) + +## Upgraded Variants + +- **Swampbark body:** Earth spell prayer effect variant +- **Bloodbark body:** Blood spell healing effect variant + +## Market Strategy + +**Buy Limit:** 70 per 4 hours | **High Alch:** 27,000 GP + +**Tips:** +- Chaos Fanatic is a Wilderness boss — PvP risk for drops +- Material-intensive to craft (4+4) +- Niche hybrid and fashionscape demand + +## Related Items + +- [Splitbark helm](/osrs/splitbark-helm/) - Matching helm +- [Splitbark legs](/osrs/splitbark-legs/) - Matching legs diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_3389.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_3389.mdx new file mode 100644 index 000000000..c323daaf3 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_3389.mdx @@ -0,0 +1,109 @@ +--- +equipment: + slot: "legs" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 7 + ranged: -7 + defence_bonus: + stab: 22 + slash: 20 + crush: 25 + magic: 10 + ranged: 0 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + magic: 40 + defence: 40 + weight: 3.628 +recipes: + - skill: "crafting" + level: 62 + xp: 186 + inputs: + - item_name: "Bark" + quantity: 3 + - item_name: "Fine cloth" + quantity: 3 + output_quantity: 1 +drop_table: + sources: + - source: "Chaos Fanatic" + combat_level: 202 + quantity: "1" + rarity: "rare" + drop_rate: "1/128" + members_only: true +related_items: + - item_id: 3385 + item_name: "Splitbark helm" + slug: "splitbark-helm" + relationship: "set" + description: "Matching helm piece" + - item_id: 3387 + item_name: "Splitbark body" + slug: "splitbark-body" + relationship: "set" + description: "Matching body piece" +--- + +## Obtaining + +- **Crafting** (Level 62): 3 bark + 3 fine cloth + needle/thread (186 XP) +- **Wizard Jalarast** (Wizards' Tower basement): 3 bark + 3 fine cloth + 32,000 coins +- **Chaos Fanatic** — 1/128 drop rate +- **Treasure Trails** — Elite emote clue reward + +> *"A pair of leg armour made from magical bark."* + +## Stats + +| Attack | Value | +|--------|-------| +| Magic | +7 | + +| Defence | Value | +|---------|-------| +| Stab | +22 | +| Slash | +20 | +| Crush | +25 | +| Magic | +10 | + +**Requirements:** 40 Magic, 40 Defence + +## Comparison vs Mystic Robe Bottom + +| Stat | Splitbark Legs | Mystic Robe Bottom | +|------|---------------|-------------------| +| Magic Atk | +7 | **+15** | +| Stab Def | **+22** | 0 | +| Slash Def | **+20** | 0 | +| Crush Def | **+25** | 0 | +| Magic Def | +10 | **+15** | + +Trades 8 Magic attack for melee defence. A reasonable trade-off for hybridding or tanking in Wilderness scenarios. + +## Upgraded Variants + +- **Swampbark legs:** Earth spell prayer effect variant +- **Bloodbark legs:** Blood spell healing effect variant + +## Market Strategy + +**Buy Limit:** 70 per 4 hours | **High Alch:** 24,000 GP + +**Tips:** +- Chaos Fanatic is the main drop source (Wilderness boss) +- Requires 3 bark + 3 fine cloth to craft +- Low trade volume — niche item + +## Related Items + +- [Splitbark helm](/osrs/splitbark-helm/) - Matching helm +- [Splitbark body](/osrs/splitbark-body/) - Matching body diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_4087.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_4087.mdx new file mode 100644 index 000000000..08c73aa2e --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_4087.mdx @@ -0,0 +1,92 @@ +--- +equipment: + slot: "legs" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: -21 + ranged: -7 + defence_bonus: + stab: 68 + slash: 66 + crush: 63 + magic: -4 + ranged: 65 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + defence: 60 +drop_table: + primary_source: "Bronze dragon" + best_drop_rate: "1/256" + sources: + - source: "Bronze dragon" + combat_level: 131 + quantity: "1" + rarity: "rare" + drop_rate: "1/256" + members_only: true + - source: "Iron dragon" + combat_level: 189 + quantity: "1" + rarity: "rare" + drop_rate: "1/256" + members_only: true + - source: "Steel dragon" + combat_level: 246 + quantity: "1" + rarity: "rare" + drop_rate: "1/256" + members_only: true +related_items: + - item_id: 4585 + item_name: "Dragon plateskirt" + slug: "dragon-plateskirt" + relationship: "variant" + description: "Cosmetic alternative (same stats)" + - item_id: 11834 + item_name: "Bandos tassets" + slug: "bandos-tassets" + relationship: "upgrade" + description: "BiS melee legs (non-degradable)" + - item_id: 1079 + item_name: "Rune platelegs" + slug: "rune-platelegs" + relationship: "downgrade" + description: "Budget alternative" +--- + +## Obtaining + +Dropped by metal dragons (1/256 each): +- Bronze dragons +- Iron dragons +- Steel dragons + +Also available from Rare Drop Table. + +## Usage + +Mid-tier melee leg armour: +- 60 Defence requirement +- Does not degrade +- Budget option before Bandos tassets + +## Market Strategy + +**Buy Limit:** 70 per 4 hours + +**Tips:** +- High supply from Slayer tasks +- Dragon plateskirt has identical stats +- Budget melee legs before tassets + +## Related Items + +- [Bandos tassets](/osrs/bandos-tassets/) - BiS melee legs upgrade +- [Dragon plateskirt](/osrs/dragon-plateskirt/) - Same stats, cosmetic alternative +- [Rune platelegs](/osrs/rune-platelegs/) - Budget alternative diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_4091.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_4091.mdx new file mode 100644 index 000000000..3211f2028 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_4091.mdx @@ -0,0 +1,61 @@ +--- +equipment: + slot: "body" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 20 + ranged: 0 + defence_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 20 + ranged: 0 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + magic: 40 + defence: 20 +related_items: + - item_id: 4101 + item_name: "Mystic robe top (dark)" + slug: "mystic-robe-top-dark" + relationship: "variant" + description: "Dark recolor (same stats)" + - item_id: 4093 + item_name: "Mystic robe bottom" + slug: "mystic-robe-bottom" + relationship: "set" + description: "Matching leg piece" +--- + +## Obtaining + +- Slayer Tower creatures (Infernal Mages) +- Various boss drops +- Purchasable from Wizard Beniami in Wizards' Guild (99 Magic) + +## Usage + +Budget magic top: +- Entry-level magic armour +- Affordable for new players +- Commonly used for Barrows + +## Market Strategy + +**Buy Limit:** 70 per 4 hours + +**Tips:** +- High supply from Slayer +- Budget magic option +- Multiple color variants available + +## Related Items + +- [Mystic robe bottom](/osrs/mystic-robe-bottom/) - Matching leg piece diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_4093.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_4093.mdx new file mode 100644 index 000000000..8aaf7627d --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_4093.mdx @@ -0,0 +1,61 @@ +--- +equipment: + slot: "legs" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 15 + ranged: 0 + defence_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 15 + ranged: 0 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + magic: 40 + defence: 20 +related_items: + - item_id: 4091 + item_name: "Mystic robe top" + slug: "mystic-robe-top" + relationship: "set" + description: "Matching body piece" + - item_id: 4103 + item_name: "Mystic robe bottom (dark)" + slug: "mystic-robe-bottom-dark" + relationship: "variant" + description: "Dark recolor (same stats)" +--- + +## Obtaining + +- Slayer Tower creatures (Infernal Mages) +- Various boss drops +- Purchasable from Wizard Beniami in Wizards' Guild + +## Usage + +Budget magic legs: +- Entry-level magic armour +- Affordable for new players +- Commonly paired with Mystic robe top + +## Market Strategy + +**Buy Limit:** 70 per 4 hours + +**Tips:** +- High supply from Slayer +- Budget magic option +- Multiple color variants + +## Related Items + +- [Mystic robe top](/osrs/mystic-robe-top/) - Matching body piece diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_6128.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_6128.mdx new file mode 100644 index 000000000..3fea2b1a0 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_6128.mdx @@ -0,0 +1,104 @@ +--- +equipment: + slot: "head" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: -6 + ranged: -2 + defence_bonus: + stab: 30 + slash: 32 + crush: 27 + magic: -1 + ranged: 30 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + defence: 40 + weight: 2.721 +related_items: + - item_id: 6129 + item_name: "Rock-shell plate" + slug: "rock-shell-plate" + relationship: "set" + description: "Matching body piece" + - item_id: 6130 + item_name: "Rock-shell legs" + slug: "rock-shell-legs" + relationship: "set" + description: "Matching leg piece" + - item_id: 10828 + item_name: "Helm of neitiznot" + slug: "helm-of-neitiznot" + relationship: "upgrade" + description: "Superior with Strength/Prayer bonus" +--- + +## Obtaining + +**Skulgrimen** in Rellekka — bring materials: +- 1 Dagannoth hide +- 1 Rock-shell chunk (from Rock crabs or Sand crabs) +- 5,000 coins + +Requires completion of **The Fremennik Trials**. + +> *"A helm made from the shell of a rock crab."* + +## Stats + +| Defence | Value | +|---------|-------| +| Stab | +30 | +| Slash | +32 | +| Crush | +27 | +| Ranged | +30 | +| Magic | -1 | + +**Requirements:** 40 Defence + +## Comparison + +| Stat | Rock-shell Helm | Rune Full Helm | Berserker Helm | +|------|----------------|----------------|----------------| +| Stab Def | +30 | +30 | +30 | +| Slash Def | +32 | +32 | +32 | +| Crush Def | +27 | +27 | +27 | +| Ranged Def | +30 | +30 | +30 | +| Str Bonus | 0 | 0 | **+3** | +| Prayer | 0 | 0 | 0 | +| Req | 40 Def | 40 Def | 45 Def + quest | + +Identical defensive stats to a rune full helm. The **berserker helm** (from The Fremennik Trials) is the preferred choice at this tier due to its +3 Strength bonus — making the rock-shell helm largely obsolete. + +## Fremennik Armour System + +The rock-shell set is the **melee variant** of the three Fremennik armour sets: +- **Rock-shell** — melee (Skulgrimen) +- **Skeletal** — magic (Peer the Seer) +- **Spined** — ranged (Sigli the Huntsman) + +## Storage + +Storable in a **Costume Room armour case** as part of the complete rock-shell set. + +## Market Strategy + +**Buy Limit:** 70 per 4 hours | **High Alch:** 21,120 GP + +**Tips:** +- Rock-shell chunks from Rock crabs (common drop) +- Outclassed by berserker helm and Helm of neitiznot +- Collector's/fashionscape piece +- Low trade volume + +## Related Items + +- [Rock-shell plate](/osrs/rock-shell-plate/) - Matching body +- [Rock-shell legs](/osrs/rock-shell-legs/) - Matching legs +- [Helm of neitiznot](/osrs/helm-of-neitiznot/) - Superior upgrade (+3 Str, +3 Prayer) diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_6129.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_6129.mdx new file mode 100644 index 000000000..218816240 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_6129.mdx @@ -0,0 +1,107 @@ +--- +equipment: + slot: "body" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: -30 + ranged: -10 + defence_bonus: + stab: 82 + slash: 80 + crush: 72 + magic: -6 + ranged: 80 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + defence: 40 + weight: 9.979 +drop_table: + sources: + - source: "Dagannoth Rex" + combat_level: 303 + quantity: "1" + rarity: "uncommon" + drop_rate: "1/128" + members_only: true +related_items: + - item_id: 6128 + item_name: "Rock-shell helm" + slug: "rock-shell-helm" + relationship: "set" + description: "Matching helm piece" + - item_id: 6130 + item_name: "Rock-shell legs" + slug: "rock-shell-legs" + relationship: "set" + description: "Matching leg piece" + - item_id: 1127 + item_name: "Rune platebody" + slug: "rune-platebody" + relationship: "alternative" + description: "Similar stats, easier to obtain" +--- + +## Obtaining + +- **Skulgrimen** in Rellekka: 3 dagannoth hides + 1 rock-shell shard + 10,000 coins + - Total crafting cost: ~29,900 coins | Profit: ~8,200 coins + - Crafting time: 1 tick (0.6s) +- **Dagannoth Rex** (Level 303) — 1/128 drop rate + +Requires completion of **The Fremennik Trials**. + +> *"A sturdy body armour made from rock crab pieces."* + +## Stats + +| Defence | Value | +|---------|-------| +| Stab | +82 | +| Slash | +80 | +| Crush | +72 | +| Ranged | +80 | +| Magic | -6 | + +**Requirements:** 40 Defence | **Weight:** 10 kg + +## Comparison vs Rune Platebody + +| Stat | Rock-shell | Rune Platebody | +|------|------------|----------------| +| Stab Def | +82 | +82 | +| Slash Def | +80 | +80 | +| Crush Def | +72 | +72 | +| Ranged Def | **+80** | +63 | +| Quest Req | Fremennik Trials | Dragon Slayer I | + +Key differences: +- **+17 Ranged Defence** over rune platebody +- **No Dragon Slayer I requirement** — accessible earlier +- **Compatible with Ava's device** (not a metal platebody) — arrows/bolts are not blocked + +## Usage + +**Range Tank PvP:** The Ava's device compatibility is the defining feature. Players can wear this for melee defence while still benefiting from Ava's ammo-saving effect, making it viable for range tank builds at 40 Defence. + +**Storage:** Can be stored in costume room armour case as part of complete Rock-shell set. + +## Market Strategy + +**Buy Limit:** 70 per 4 hours | **High Alch:** 39,000 GP | **Volume:** ~253/day + +**Tips:** +- Crafting via Skulgrimen is profitable (~8K profit) +- Ava's compatibility drives niche PvP demand +- Dagannoth Rex also drops it directly + +## Related Items + +- [Rock-shell helm](/osrs/rock-shell-helm/) - Matching helm +- [Rock-shell legs](/osrs/rock-shell-legs/) - Matching legs +- [Rune platebody](/osrs/rune-platebody/) - Similar stats, no Ava's compat diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_6130.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_6130.mdx new file mode 100644 index 000000000..66c38a5d9 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_6130.mdx @@ -0,0 +1,99 @@ +--- +equipment: + slot: "legs" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: -21 + ranged: -7 + defence_bonus: + stab: 51 + slash: 49 + crush: 47 + magic: -4 + ranged: 49 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + defence: 40 + weight: 9.071 +drop_table: + sources: + - source: "Dagannoth Rex" + combat_level: 303 + quantity: "1" + rarity: "uncommon" + drop_rate: "1/128" + members_only: true +related_items: + - item_id: 6128 + item_name: "Rock-shell helm" + slug: "rock-shell-helm" + relationship: "set" + description: "Matching helm piece" + - item_id: 6129 + item_name: "Rock-shell plate" + slug: "rock-shell-plate" + relationship: "set" + description: "Matching body piece" +--- + +## Obtaining + +- **Skulgrimen** in Rellekka: 2 dagannoth hides + 1 rock-shell splinter + 7,500 coins +- **Dagannoth Rex** (Level 303) — 1/128 drop rate + +Requires completion of **The Fremennik Trials**. + +> *"A pair of leg armour made from the shell of a rock crab."* + +## Stats + +| Defence | Value | +|---------|-------| +| Stab | +51 | +| Slash | +49 | +| Crush | +47 | +| Ranged | **+49** | +| Magic | -4 | + +**Requirements:** 40 Defence | **Weight:** 9.1 kg + +## Comparison vs Rune Platelegs + +| Stat | Rock-shell Legs | Rune Platelegs | +|------|----------------|----------------| +| Stab Def | +51 | +51 | +| Slash Def | +49 | +49 | +| Crush Def | +47 | +47 | +| Ranged Def | **+49** | +45 | +| Magic | -4 | -21 | + +Identical melee defence to rune platelegs with **+4 more ranged defence** and significantly less magic penalty (-4 vs -21). Like the rock-shell plate, this is slightly superior to its rune counterpart. + +## Dagannoth Rex + +Rex (melee DK) drops the legs at 1/128. Players farming all three DKs will accumulate these passively alongside other rock-shell and skeletal pieces. + +## Storage + +Storable in a **Costume Room armour case** as part of the complete rock-shell set. + +## Market Strategy + +**Buy Limit:** 70 per 4 hours | **High Alch:** 38,400 GP + +**Tips:** +- GE price near high alch value +- Dagannoth Rex drop (passive from DK farming) +- Slight upgrade over rune platelegs for ranged defence +- Moderate trade volume + +## Related Items + +- [Rock-shell helm](/osrs/rock-shell-helm/) - Matching helm +- [Rock-shell plate](/osrs/rock-shell-plate/) - Matching body diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_6137.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_6137.mdx new file mode 100644 index 000000000..b246b2ed8 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_6137.mdx @@ -0,0 +1,107 @@ +--- +equipment: + slot: "head" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 2 + ranged: -2 + defence_bonus: + stab: 10 + slash: 9 + crush: 11 + magic: 3 + ranged: 0 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + defence: 40 + magic: 40 + weight: 1.36 +related_items: + - item_id: 6139 + item_name: "Skeletal top" + slug: "skeletal-top" + relationship: "set" + description: "Matching body piece" + - item_id: 6141 + item_name: "Skeletal bottoms" + slug: "skeletal-bottoms" + relationship: "set" + description: "Matching leg piece" + - item_id: 3385 + item_name: "Splitbark helm" + slug: "splitbark-helm" + relationship: "alternative" + description: "Craftable alternative with similar stats" +--- + +## Obtaining + +**Peer the Seer** in Rellekka — bring materials: +- 1 Dagannoth hide +- 1 Skull piece (from Wallasalki in Waterbirth Island Dungeon) +- 5,000 coins + +Requires completion of **The Fremennik Trials**. + +> *"A helm made from the bones and hides of the Dagannoth."* + +## Stats + +| Attack | Value | +|--------|-------| +| Magic | +2 | + +| Defence | Value | +|---------|-------| +| Stab | +10 | +| Slash | +9 | +| Crush | +11 | +| Magic | +3 | + +**Requirements:** 40 Defence, 40 Magic + +## Comparison vs Splitbark Helm + +| Stat | Skeletal Helm | Splitbark Helm | +|------|--------------|----------------| +| Magic Atk | +2 | **+3** | +| Stab Def | +10 | +10 | +| Slash Def | +9 | +9 | +| Crush Def | +11 | +11 | +| Magic Def | +3 | +3 | + +Nearly identical — splitbark has **+1 more Magic attack**. Skeletal requires Fremennik Trials and DK materials, making splitbark generally easier to obtain. + +## Fremennik Armour System + +The skeletal set is the **magic variant** of the three Fremennik armour sets: +- **Rock-shell** — melee (Skulgrimen) +- **Skeletal** — magic (Peer the Seer) +- **Spined** — ranged (Sigli the Huntsman) + +All require The Fremennik Trials and dagannoth hides. + +## Storage + +Can be stored in a **Costume Room armour case** in a Player-owned House as part of the complete skeletal set. + +## Market Strategy + +**Buy Limit:** 70 per 4 hours | **High Alch:** 6,000 GP + +**Tips:** +- Skull pieces from Wallasalki (Waterbirth Island Dungeon) +- Rarely used — splitbark is easier to obtain and slightly better +- Collector's/fashionscape piece + +## Related Items + +- [Skeletal top](/osrs/skeletal-top/) - Matching body +- [Skeletal bottoms](/osrs/skeletal-bottoms/) - Matching legs +- [Splitbark helm](/osrs/splitbark-helm/) - Craftable alternative (+1 magic atk) diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_6139.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_6139.mdx new file mode 100644 index 000000000..7c02f691a --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_6139.mdx @@ -0,0 +1,108 @@ +--- +equipment: + slot: "body" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 8 + ranged: -10 + defence_bonus: + stab: 35 + slash: 25 + crush: 42 + magic: 15 + ranged: 0 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + defence: 40 + magic: 40 + weight: 4.989 +drop_table: + sources: + - source: "Dagannoth Prime" + combat_level: 303 + quantity: "1" + rarity: "uncommon" + drop_rate: "1/128" + members_only: true +related_items: + - item_id: 6137 + item_name: "Skeletal helm" + slug: "skeletal-helm" + relationship: "set" + description: "Matching helm piece" + - item_id: 6141 + item_name: "Skeletal bottoms" + slug: "skeletal-bottoms" + relationship: "set" + description: "Matching leg piece" + - item_id: 3387 + item_name: "Splitbark body" + slug: "splitbark-body" + relationship: "alternative" + description: "Slightly superior alternative" +--- + +## Obtaining + +- **Peer the Seer** in Rellekka: 3 dagannoth hides + 1 ribcage piece + 10,000 coins +- **Dagannoth Prime** (Level 303) — 1/128 drop rate + +Requires completion of **The Fremennik Trials**. + +> *"A body armour made from the bones and hides of the Dagannoth."* + +## Stats + +| Attack | Value | +|--------|-------| +| Magic | +8 | + +| Defence | Value | +|---------|-------| +| Stab | +35 | +| Slash | +25 | +| Crush | +42 | +| Magic | +15 | + +**Requirements:** 40 Defence, 40 Magic + +## Comparison vs Splitbark Body + +| Stat | Skeletal Top | Splitbark Body | +|------|-------------|----------------| +| Magic Atk | +8 | **+10** | +| Stab Def | +35 | **+36** | +| Slash Def | +25 | **+26** | +| Crush Def | +42 | +42 | +| Magic Def | +15 | +15 | + +Slightly inferior across the board — splitbark body has +2 more magic attack and +1-2 more melee defence. The skeletal top exists mainly as a Dagannoth Prime drop and Fremennik lore piece. + +## Dagannoth Prime + +Prime (mage DK) drops the skeletal top at 1/128. Players killing DKs will accumulate these passively. Since splitbark requires Shades of Mort'ton materials, the skeletal top can be easier to obtain for players already farming DKs. + +## Storage + +Storable in a **Costume Room armour case** as part of the complete skeletal set. + +## Market Strategy + +**Buy Limit:** 70 per 4 hours | **High Alch:** 27,000 GP + +**Tips:** +- Dagannoth Prime drop (passive from DK farming) +- Splitbark body is generally preferred for stats +- Low trade volume — niche item + +## Related Items + +- [Skeletal helm](/osrs/skeletal-helm/) - Matching helm +- [Skeletal bottoms](/osrs/skeletal-bottoms/) - Matching legs +- [Splitbark body](/osrs/splitbark-body/) - Slightly superior alternative diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_6141.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_6141.mdx new file mode 100644 index 000000000..4e6d06df1 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_6141.mdx @@ -0,0 +1,112 @@ +--- +equipment: + slot: "legs" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 6 + ranged: -7 + defence_bonus: + stab: 22 + slash: 20 + crush: 24 + magic: 10 + ranged: 0 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + defence: 40 + magic: 40 + weight: 4.082 +drop_table: + sources: + - source: "Dagannoth Prime" + combat_level: 303 + quantity: "1" + rarity: "uncommon" + drop_rate: "1/128" + members_only: true +related_items: + - item_id: 6137 + item_name: "Skeletal helm" + slug: "skeletal-helm" + relationship: "set" + description: "Matching helm piece" + - item_id: 6139 + item_name: "Skeletal top" + slug: "skeletal-top" + relationship: "set" + description: "Matching body piece" + - item_id: 3389 + item_name: "Splitbark legs" + slug: "splitbark-legs" + relationship: "alternative" + description: "Craftable alternative" +--- + +## Obtaining + +- **Peer the Seer** in Rellekka: 2 dagannoth hides + 1 fibula piece + 7,500 coins +- **Dagannoth Prime** (Level 303) — 1/128 drop rate + +Requires completion of **The Fremennik Trials**. + +> *"A pair of leg armour made from the bones and hides of the Dagannoth."* + +## Stats + +| Attack | Value | +|--------|-------| +| Magic | +6 | + +| Defence | Value | +|---------|-------| +| Stab | +22 | +| Slash | +20 | +| Crush | +24 | +| Magic | +10 | + +**Requirements:** 40 Defence, 40 Magic + +## Comparison vs Splitbark Legs + +| Stat | Skeletal Bottoms | Splitbark Legs | +|------|-----------------|----------------| +| Magic Atk | +6 | **+7** | +| Stab Def | +22 | +22 | +| Slash Def | +20 | +20 | +| Crush Def | +24 | **+25** | +| Magic Def | +10 | +10 | + +Nearly identical — splitbark has +1 magic attack and +1 crush defence. The difference is negligible. + +## Full Set Totals (Skeletal) + +| Stat | Total | +|------|-------| +| Magic Atk | **+16** | +| Stab Def | +67 | +| Slash Def | +54 | +| Crush Def | +77 | +| Magic Def | +28 | + +Compare to full splitbark: +20 magic attack, +68/+55/+78 melee def, +28 magic def. Splitbark is marginally better across the board. + +## Market Strategy + +**Buy Limit:** 70 per 4 hours | **High Alch:** 24,000 GP + +**Tips:** +- Dagannoth Prime drop (passive from DK farming) +- Low trade volume +- Collector's piece — splitbark preferred for stats + +## Related Items + +- [Skeletal helm](/osrs/skeletal-helm/) - Matching helm +- [Skeletal top](/osrs/skeletal-top/) - Matching body +- [Splitbark legs](/osrs/splitbark-legs/) - Slightly superior alternative diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_6322.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_6322.mdx new file mode 100644 index 000000000..9a81df82e --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_6322.mdx @@ -0,0 +1,110 @@ +--- +equipment: + slot: "body" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: -5 + ranged: 12 + defence_bonus: + stab: 25 + slash: 28 + crush: 32 + magic: 15 + ranged: 35 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + ranged: 30 + defence: 30 + weight: 6.803 +recipes: + - skill: "crafting" + level: 53 + xp: 55 + inputs: + - item_name: "Snakeskin" + quantity: 15 + - item_name: "Thread" + quantity: 1 + output_quantity: 1 +related_items: + - item_id: 6324 + item_name: "Snakeskin chaps" + slug: "snakeskin-chaps" + relationship: "set" + description: "Matching leg piece" + - item_id: 6328 + item_name: "Snakeskin boots" + slug: "snakeskin-boots" + relationship: "set" + description: "Matching boots" + - item_id: 1135 + item_name: "Green d'hide body" + slug: "green-dhide-body" + relationship: "upgrade" + description: "40 Ranged body upgrade" +--- + +## Obtaining + +**Crafting** (Level 53): 15 snakeskin + needle/thread (55 XP) + +> *"A body made out of snakeskin."* + +## Stats + +| Ranged | Value | +|--------|-------| +| Ranged Attack | +12 | + +| Defence | Value | +|---------|-------| +| Stab | +25 | +| Slash | +28 | +| Crush | +32 | +| Magic | **+15** | +| Ranged | +35 | + +**Requirements:** 30 Ranged, 30 Defence + +## Comparison + +| Stat | Snakeskin Body | Studded Body | Green D'hide Body | +|------|---------------|--------------|-------------------| +| Ranged Atk | +12 | +8 | +15 | +| Stab Def | +25 | +22 | +40 | +| Magic Def | **+15** | +4 | +20 | +| Req | 30 Rng/Def | 20 Rng/Def | 40 Rng/Def | + +Bridges the gap between studded leather (20 Ranged) and green d'hide (40 Ranged). Notable for its +15 Magic defence at only 30 Defence. + +## Material Cost + +Requires 15 snakeskin — the most material-intensive piece in the snakeskin set. Snakeskin is cheap on the GE (~50 GP each), making the body cost under 1,000 GP in materials. + +## Usage + +- Mid-tier ranged body for 30 Def bracket +- Popular with ironmen leveling through ranged tiers +- Crafting training product (53-57 range) +- Niche for 30 Def pures in PvP + +## Market Strategy + +**Buy Limit:** 125 per 4 hours | **High Alch:** 750 GP + +**Tips:** +- Low demand — green d'hide body preferred at 40+ +- Crafting training byproduct +- Niche for 30 Def bracket builds + +## Related Items + +- [Snakeskin chaps](/osrs/snakeskin-chaps/) - Matching legs +- [Snakeskin boots](/osrs/snakeskin-boots/) - Matching boots +- [Green d'hide body](/osrs/green-dhide-body/) - 40 Ranged upgrade diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_6324.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_6324.mdx new file mode 100644 index 000000000..8deb7d565 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_6324.mdx @@ -0,0 +1,99 @@ +--- +equipment: + slot: "legs" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: -5 + ranged: 6 + defence_bonus: + stab: 8 + slash: 8 + crush: 10 + magic: 4 + ranged: 10 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + ranged: 30 + defence: 30 + weight: 3.628 +recipes: + - skill: "crafting" + level: 51 + xp: 50 + inputs: + - item_name: "Snakeskin" + quantity: 12 + - item_name: "Thread" + quantity: 1 + output_quantity: 1 +related_items: + - item_id: 6322 + item_name: "Snakeskin body" + slug: "snakeskin-body" + relationship: "set" + description: "Matching body piece" + - item_id: 6328 + item_name: "Snakeskin boots" + slug: "snakeskin-boots" + relationship: "set" + description: "Matching boots" +--- + +## Obtaining + +**Crafting** (Level 51): 12 snakeskin + needle/thread (50 XP) + +> *"Some chaps made out of snakeskin."* + +## Stats + +| Ranged | Value | +|--------|-------| +| Ranged Attack | +6 | + +| Defence | Value | +|---------|-------| +| Stab/Slash | +8 | +| Crush | +10 | +| Magic | +4 | +| Ranged | +10 | + +**Requirements:** 30 Ranged, 30 Defence + +## Comparison + +| Stat | Snakeskin Chaps | Studded Chaps | Green D'hide Chaps | +|------|----------------|---------------|---------------------| +| Ranged Atk | +6 | +6 | +8 | +| Stab Def | +8 | +6 | +22 | +| Magic Def | +4 | +2 | +8 | +| Req | 30 Rng/Def | 20 Rng/Def | 40 Rng/Def | + +Moderate upgrade from studded chaps. The ranged attack bonus is the same, but snakeskin chaps offer slightly better defensive stats across the board. + +## Usage + +- Budget ranged legs for 30 Def bracket +- Crafting training product (51-53 range) +- Ironman progression piece +- 12 snakeskin per pair (~600 GP in materials) + +## Market Strategy + +**Buy Limit:** 125 per 4 hours | **High Alch:** 705 GP + +**Tips:** +- Low demand — quickly replaced by green d'hide chaps +- Crafting XP is the main reason to make these +- Low margins + +## Related Items + +- [Snakeskin body](/osrs/snakeskin-body/) - Matching body +- [Snakeskin boots](/osrs/snakeskin-boots/) - Matching boots diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_6328.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_6328.mdx new file mode 100644 index 000000000..984b77e97 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_6328.mdx @@ -0,0 +1,101 @@ +--- +equipment: + slot: "feet" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: -10 + ranged: 3 + defence_bonus: + stab: 1 + slash: 1 + crush: 2 + magic: 1 + ranged: 0 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + ranged: 30 + defence: 30 + weight: 0.34 +recipes: + - skill: "crafting" + level: 45 + xp: 30 + inputs: + - item_name: "Snakeskin" + quantity: 6 + - item_name: "Thread" + quantity: 1 + output_quantity: 1 +related_items: + - item_id: 6322 + item_name: "Snakeskin body" + slug: "snakeskin-body" + relationship: "set" + description: "Matching body piece" + - item_id: 6324 + item_name: "Snakeskin chaps" + slug: "snakeskin-chaps" + relationship: "set" + description: "Matching leg piece" +--- + +## Obtaining + +**Crafting** (Level 45): 6 snakeskin + needle/thread (30 XP) + +Snakeskin is obtained by killing snakes on Mos Le'Harmless (after Cabin Fever quest) or buying from the GE. + +> *"Some nice boots made from snakeskin."* + +## Stats + +| Ranged | Value | +|--------|-------| +| Ranged Attack | +3 | + +| Defence | Value | +|---------|-------| +| Stab/Slash | +1 | +| Crush | +2 | +| Magic | +1 | + +**Requirements:** 30 Ranged, 30 Defence + +## Boots Progression + +| Boots | Ranged Req | Ranged Atk | Other | +|-------|-----------|------------|-------| +| Leather boots | 1 | 0 | Free | +| Snakeskin boots | 30 | +3 | Budget | +| Blessed d'hide boots | 40 | +7 | +1 Prayer | +| Ranger boots | 40 | **+8** | ~35M GP | +| Pegasian boots | 75 | **+12** | ~30M GP | + +Snakeskin boots fill the gap between leather and blessed d'hide boots. At only 30 Ranged/Defence, they're the earliest meaningful ranged footwear upgrade. + +## Usage + +- Cheapest ranged boots with actual stats +- Used by ironmen before blessed d'hide boots +- Crafting training product (6 snakeskin per pair) +- Budget option for players saving for blessed boots + +## Market Strategy + +**Buy Limit:** 125 per 4 hours | **High Alch:** 150 GP + +**Tips:** +- Very cheap to craft (30 XP per pair) +- Steady demand from mid-level rangers and ironmen +- Low margins — better as crafting XP than profit + +## Related Items + +- [Snakeskin body](/osrs/snakeskin-body/) - Matching body +- [Snakeskin chaps](/osrs/snakeskin-chaps/) - Matching legs diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_6522.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_6522.mdx new file mode 100644 index 000000000..8f389c528 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_6522.mdx @@ -0,0 +1,90 @@ +--- +equipment: + slot: "weapon" + attack_speed: 3 + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 0 + ranged: 69 + defence_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 0 + ranged: 0 + other_bonus: + melee_strength: 0 + ranged_strength: 49 + magic_damage: 0 + prayer: 0 + requirements: + ranged: 60 + weight: 0 +related_items: + - item_id: 6523 + item_name: "Toktz-xil-ak" + slug: "toktz-xil-ak" + relationship: "alternative" + description: "Obsidian melee sword" + - item_id: 6528 + item_name: "Tzhaar-ket-om" + slug: "tzhaar-ket-om" + relationship: "alternative" + description: "Obsidian maul (crush)" +--- + +## Obtaining + +- **Shop:** TzHaar-Hur-Tel's Equipment Store (375 Tokkul each, 325 with Karamja gloves 1+) +- **Drop:** TzHaar-Xil (4/2,048 rarity) +- **Grand Exchange** + +> *"A razor sharp ring of obsidian."* + +## Stats + +| Ranged | Value | +|--------|-------| +| Ranged Attack | +69 | +| Ranged Strength | +49 | + +**Requirements:** 60 Ranged | **Speed:** 3 tick (1.8s) on Rapid | **Weight:** 0 kg + +## One-Handed Advantage + +Unlike bows, the Toktz-xil-ul is **one-handed** — allowing a shield in the off-hand. This makes it uniquely versatile: +- Equip a book of balance or unholy book for prayer +- Use a defender for accuracy +- Tank with a shield while still dealing ranged damage + +## DPS Comparison + +| Weapon | Speed | Ranged Atk | Ranged Str | Hands | +|--------|-------|------------|------------|-------| +| Toktz-xil-ul | 3 tick | +69 | +49 | 1H | +| MSB (i) + Rune arrows | 4 tick | +75 | +49 | 2H | +| Rune knives | 3 tick | +25 | +36 | 1H | +| Rune darts | 3 tick | +18 | +29 | 1H | + +Roughly equivalent DPS to a magic shortbow (i) with rune arrows, while being one-handed and having zero weight. Significantly outperforms rune knives and darts. + +## Construction Use + +Used as a material in **Player-owned Houses**: 25 Toktz-xil-ul are needed to build an **obsidian fence** decoration. + +## Market Strategy + +**Buy Limit:** 11,000 per 4 hours | **High Alch:** 150 GP + +**Tips:** +- Very cheap from TzHaar shop (375 Tokkul, 325 with Karamja gloves) +- High GE volume — popular ranged option +- Zero weight makes them convenient for any activity +- Expendable (consumed on use) — buy in bulk + +## Related Items + +- [Toktz-xil-ak](/osrs/toktz-xil-ak/) - Obsidian melee sword +- [Tzhaar-ket-om](/osrs/tzhaar-ket-om/) - Obsidian maul diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_6523.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_6523.mdx new file mode 100644 index 000000000..e388a7e98 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_6523.mdx @@ -0,0 +1,68 @@ +--- +equipment: + slot: "weapon" + attack_bonus: + stab: 36 + slash: 55 + crush: -2 + magic: 0 + ranged: 0 + defence_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 0 + ranged: 0 + other_bonus: + melee_strength: 49 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + attack: 60 +related_items: + - item_id: 6525 + item_name: "Toktz-xil-ek" + slug: "toktz-xil-ek" + relationship: "alternative" + description: "Obsidian knife (ranged)" + - item_id: 6527 + item_name: "Toktz-xil-ul" + slug: "toktz-xil-ul" + relationship: "alternative" + description: "Obsidian throwing ring" + - item_id: 6528 + item_name: "Tzhaar-ket-om" + slug: "tzhaar-ket-om" + relationship: "alternative" + description: "Obsidian maul (crush)" +--- + +## Obtaining + +Purchased from TzHaar-Hur-Tel in Mor Ul Rek for 60,000 Tokkul. + +Also dropped by TzTok-Jad and TzHaar creatures. + +## Usage + +Strong 60 Attack weapon: +- Benefits from Obsidian armour set effect (+10% accuracy/damage) +- Good Strength training weapon +- Popular for low-level PvP + +**With full Obsidian set + Berserker necklace:** Competitive DPS for training. + +## Market Strategy + +**Buy Limit:** 70 per 4 hours + +**Tips:** +- Tokkul farmable from TzHaar Fight Cave +- Popular for Strength training meta +- Obsidian set synergy drives demand + +## Related Items + +- [Tzhaar-ket-om](/osrs/tzhaar-ket-om/) - Obsidian maul +- [Berserker necklace](/osrs/berserker-necklace/) - Obsidian damage boost diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_6562.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_6562.mdx new file mode 100644 index 000000000..de7a9c96a --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_6562.mdx @@ -0,0 +1,125 @@ +--- +equipment: + slot: "weapon" + attack_speed: 5 + attack_bonus: + stab: 7 + slash: -1 + crush: 28 + magic: 12 + ranged: 0 + defence_bonus: + stab: 2 + slash: 3 + crush: 1 + magic: 12 + ranged: 0 + other_bonus: + melee_strength: 35 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + attack: 30 + magic: 30 + weight: 2.267 +drop_table: + primary_source: "Dagannoth Prime" + best_drop_rate: "1/128" + sources: + - source: "Dagannoth Prime" + combat_level: 303 + quantity: "1" + rarity: "uncommon" + drop_rate: "1/128" + members_only: true +related_items: + - item_id: 6563 + item_name: "Mystic mud staff" + slug: "mystic-mud-staff" + relationship: "upgrade" + description: "Mystic version via Thormac" + - item_id: 3053 + item_name: "Lava battlestaff" + slug: "lava-battlestaff" + relationship: "alternative" + description: "Earth+Fire combination staff" +--- + +## Obtaining + +**Dagannoth Prime** (Level 303) — 1/128 drop rate on Waterbirth Island. + +This is the **only tradeable source** — no other monster drops it. + +## Stats + +| Attack | Value | +|--------|-------| +| Magic | +12 | +| Crush | +28 | +| Stab | +7 | + +| Defence | Value | +|---------|-------| +| Magic | +12 | + +| Other | Value | +|-------|-------| +| Strength | +35 | + +**Requirements:** 30 Attack, 30 Magic | **Speed:** 5 tick (3.0s) + +## Rune Provision + +Provides unlimited **water and earth runes** simultaneously. This uniquely benefits: + +**Curse Spells** (almost all use both water + earth runes): +- Confuse, Weaken, Curse +- Vulnerability, Enfeeble, Stun +- Bind, Snare, Entangle + +**Utility Spells:** +- Bones to Bananas / Bones to Peaches +- Lvl-5 Enchant +- Draynor Manor Teleport + +**Lunar Spells:** +- String Jewellery +- Stat Restore Pot Share +- Boost Potion Share + +## Upgrade Path + +Upgrade to [Mystic mud staff](/osrs/mystic-mud-staff/) via **Thormac** (after Scorpion Catcher): + +| Diary Completion | Cost | +|-----------------|------| +| None | 40,000 coins | +| Hard Kandarin | 30,000 coins | +| Elite Kandarin | 20,000 coins | + +The Mystic version gains +2 Magic attack/defence and +15 Strength bonus. + +## Ironman Notes + +If unable to obtain this drop, alternatives include pairing an earth staff with Tome of Water (or water staff with earth runes). Tome-based setups avoid charge depletion during non-combat spells like Bones to Peaches. + +## Balance History + +September 2021: Magic Attack and Defence bonuses increased from +10 to +12. + +## Market Strategy + +**Buy Limit:** 8 per 4 hours | **High Alch:** 10,200 GP | **Volume:** ~410/day + +**Tips:** +- Only from Dagannoth Prime (must trio or solo DKs) +- Upgrade to Mystic for profit (check Thormac cost vs GE spread) +- Low daily volume — prices can be volatile + +## Related Items + +- [Mystic mud staff](/osrs/mystic-mud-staff/) - Upgraded version (+14 Magic) +- [Lava battlestaff](/osrs/lava-battlestaff/) - Earth+Fire combination +- [Kodai wand](/osrs/kodai-wand/) - BiS autocast staff diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_6563.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_6563.mdx new file mode 100644 index 000000000..8f25decaf --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_6563.mdx @@ -0,0 +1,95 @@ +--- +equipment: + slot: "weapon" + attack_speed: 5 + attack_bonus: + stab: 10 + slash: -1 + crush: 40 + magic: 14 + ranged: 0 + defence_bonus: + stab: 2 + slash: 3 + crush: 1 + magic: 14 + ranged: 0 + other_bonus: + melee_strength: 50 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + attack: 40 + magic: 40 + weight: 2.267 +related_items: + - item_id: 6562 + item_name: "Mud battlestaff" + slug: "mud-battlestaff" + relationship: "component" + description: "Base staff for upgrade" + - item_id: 3054 + item_name: "Mystic lava staff" + slug: "mystic-lava-staff" + relationship: "alternative" + description: "Earth+Fire combination" +--- + +## Obtaining + +**Thormac** (after Scorpion Catcher quest): Mud battlestaff + coins +- Base cost: 40,000 coins +- Hard Kandarin Diary: 30,000 coins +- Elite Kandarin Diary: 20,000 coins + +> *"It's a slightly magical stick."* + +## Stats + +| Attack | Value | +|--------|-------| +| Magic | +14 | +| Crush | +40 | + +| Defence | Value | +|---------|-------| +| Magic | +14 | + +| Other | Value | +|-------|-------| +| Strength | +50 | + +**Requirements:** 40 Attack, 40 Magic + +## Dual Rune Provision + +Provides unlimited **water and earth runes** simultaneously — the most valuable combination staff: +- **Ice Barrage** — saves water runes (death + blood still needed) +- **Lunar spells** — many require water + earth (Humidify, Fertile Soil, etc.) +- **Mage Training Arena** — saves runes during Alchemy room +- **Charge Water/Earth Orb** — saves respective runes +- Autocast with standard spellbook + +## Cosmetic Upgrade + +A **Mystic mud staff upgrade kit** (from Master Treasure Trails) can be applied for a cosmetic variant. The kit is purely visual — no stat changes. + +## Why It's Expensive + +The mud battlestaff (base component) is a rare drop from **Dagannoth Prime** (1/128). Combined with Thormac's fee, the total cost is significantly higher than other mystic staves. The water+earth combination is also the most useful for high-level PvM. + +## Market Strategy + +**Buy Limit:** 8 per 4 hours | **High Alch:** 27,000 GP + +**Tips:** +- Mud battlestaff is the expensive component (DK Prime 1/128) +- Thormac cost varies with Kandarin diary tier +- Higher value than other mystic staves due to rarity + utility +- Used in endgame PvM (Barrage tasks, Lunar spells) + +## Related Items + +- [Mud battlestaff](/osrs/mud-battlestaff/) - Base staff (from Dagannoth Prime) +- [Mystic lava staff](/osrs/mystic-lava-staff/) - Earth+Fire combination diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_6575.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_6575.mdx new file mode 100644 index 000000000..8b25bcfe3 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_6575.mdx @@ -0,0 +1,54 @@ +--- +equipment: + slot: "ring" + requirements: + crafting: 67 +recipes: + - skill: "crafting" + level: 67 + xp: 115 + inputs: + - item_id: 6573 + item_name: "Onyx" + quantity: 1 + - item_id: 2357 + item_name: "Gold bar" + quantity: 1 + output_quantity: 1 +related_items: + - item_id: 1645 + item_name: "Dragonstone ring" + slug: "dragonstone-ring" + relationship: "downgrade" + description: "Previous tier ring" + - item_id: 19538 + item_name: "Zenyte ring" + slug: "zenyte-ring" + relationship: "upgrade" + description: "Next tier ring (zenyte)" +--- + +## Creation + +| Input | Skill | Level | XP | +|-------|-------|-------|----| +| [Onyx](/osrs/onyx/) + [Gold bar](/osrs/gold-bar/) | Crafting | 67 | 115 | + +## Enchanting + +Enchant with Lvl-6 Enchant (Magic 87) to create Ring of stone (cosmetic transformation). + +## Market Strategy + +**Buy Limit:** 8 per 4 hours + +**Tips:** +- Onyx is expensive (~2.5M) +- Ring of stone is cosmetic only +- Niche crafting XP method + +## Related Items + +- [Dragonstone ring](/osrs/dragonstone-ring/) - Previous tier +- [Zenyte ring](/osrs/zenyte-ring/) - Next tier +- [Onyx](/osrs/onyx/) - Key component diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_6724.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_6724.mdx new file mode 100644 index 000000000..f1edb94f5 --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_6724.mdx @@ -0,0 +1,120 @@ +--- +equipment: + slot: "weapon" + two_handed: true + attack_speed: 5 + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 0 + ranged: 69 + defence_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: 0 + ranged: 0 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + ranged: 50 + weight: 1.36 +special_attack: + name: "Soulshot" + energy: 100 + description: "Guaranteed hit that lowers opponent's Magic level equal to damage dealt. Damage boosted only by ammunition and visible Ranged level." +drop_table: + primary_source: "Dagannoth Supreme" + best_drop_rate: "1/128" + sources: + - source: "Dagannoth Supreme" + combat_level: 303 + quantity: "1" + rarity: "uncommon" + drop_rate: "1/128" + members_only: true +related_items: + - item_id: 861 + item_name: "Magic shortbow" + slug: "magic-shortbow" + relationship: "alternative" + description: "More practical ranged bow" + - item_id: 11235 + item_name: "Dark bow" + slug: "dark-bow" + relationship: "alternative" + description: "Slow but powerful spec bow" +--- + +## Obtaining + +**Dagannoth Supreme** (Level 303) — 1/128 drop rate on Waterbirth Island. + +Also obtainable from **Elite Treasure Trails**. + +> *"An ancient Fremennik bow that was once used to battle the Moon Clan."* + +## Stats + +| Ranged | Value | +|--------|-------| +| Ranged Attack | +69 | +| Ranged Strength | +0 | + +**Requirements:** 50 Ranged | **Speed:** 5 tick (3.0s) | **Two-handed** + +Fires arrows up to **Amethyst arrows** (+55 ranged strength). + +## Special Attack — Soulshot + +**Cost:** 100% special attack energy | **Guaranteed hit** + +Lowers opponent's **Magic level** equal to damage dealt. + +| Factor | Affects Damage? | +|--------|----------------| +| Ammunition ranged strength | Yes | +| Visible Ranged level | Yes | +| Ranged prayers (Eagle Eye, Rigour) | **No** | +| Equipment ranged strength | **No** | +| Void Knight bonuses | **No** | +| Salve amulet / Slayer helm | **No** | + +**Key:** Magic drain only applies if the target's Magic isn't already reduced. Consecutive uses won't stack. + +## Name Etymology + +"Seer" (mage) + "cull" (kill) = **"mage-killer"**. + +## PvP Applications + +Draining a mage's Magic level prevents spellcasting until they restore. Effective against: +- Mage pures +- Tribrid switches +- Players without super restores + +## Comparison + +| Weapon | Speed | Ranged Atk | Special | +|--------|-------|------------|---------| +| Seercull | 5 tick | +69 | Magic drain (100%) | +| Magic shortbow | 4 tick | +69 | 2 rapid shots (55%) | +| Dark bow | 8 tick | +95 | +50% damage (55%) | + +## Market Strategy + +**Buy Limit:** 8 per 4 hours | **High Alch:** 4,800 GP + +**Tips:** +- Supply tied to DK farming +- Niche PvP anti-mage demand +- Low practical use keeps price modest + +## Related Items + +- [Magic shortbow](/osrs/magic-shortbow/) - Faster, better general DPS +- [Dark bow](/osrs/dark-bow/) - High burst damage spec diff --git a/apps/kbve/astro-kbve/data/osrs-overrides/_6809.mdx b/apps/kbve/astro-kbve/data/osrs-overrides/_6809.mdx new file mode 100644 index 000000000..54d4ed96a --- /dev/null +++ b/apps/kbve/astro-kbve/data/osrs-overrides/_6809.mdx @@ -0,0 +1,111 @@ +--- +equipment: + slot: "legs" + attack_bonus: + stab: 0 + slash: 0 + crush: 0 + magic: -31 + ranged: -18 + defence_bonus: + stab: 43 + slash: 45 + crush: 41 + magic: -4 + ranged: 68 + other_bonus: + melee_strength: 0 + ranged_strength: 0 + magic_damage: 0 + prayer: 0 + requirements: + defence: 50 + strength: 50 + weight: 15.875 +drop_table: + primary_source: "Skeletal Wyvern" + best_drop_rate: "1/512" + sources: + - source: "Skeletal Wyvern" + combat_level: 140 + quantity: "1" + rarity: "rare" + drop_rate: "1/512" + members_only: true +related_items: + - item_id: 10589 + item_name: "Granite helm" + slug: "granite-helm" + relationship: "set" + description: "Granite set piece" + - item_id: 3122 + item_name: "Granite shield" + slug: "granite-shield" + relationship: "set" + description: "Granite set piece" + - item_id: 4087 + item_name: "Dragon platelegs" + slug: "dragon-platelegs" + relationship: "upgrade" + description: "60 Def alternative" +--- + +## Obtaining + +**Skeletal Wyverns** (Level 140) — 1/512 drop rate + +Requires **72 Slayer** to kill Skeletal Wyverns. Located in the Asgarnian Ice Dungeon. + +> *"These look pretty heavy."* + +## Stats + +| Defence | Value | +|---------|-------| +| Stab | +43 | +| Slash | +45 | +| Crush | +41 | +| Ranged | **+68** | +| Magic | -4 | + +**Requirements:** 50 Defence, 50 Strength | **Weight:** 15.9 kg + +## Comparison + +| Stat | Granite Legs | Rune Platelegs | Dragon Platelegs | +|------|-------------|----------------|------------------| +| Stab Def | +43 | +51 | +68 | +| Slash Def | +45 | +49 | +66 | +| Crush Def | +41 | +47 | +63 | +| Ranged Def | **+68** | +45 | +65 | +| Magic | -4 | -21 | -4 | +| Weight | 15.9 kg | 9.1 kg | 9.1 kg | + +Notably provides **superior ranged defence** (+68) compared to rune platelegs (+45), but has lower melee defence stats. The heavy weight of 15.9 kg significantly impacts run energy. + +## Splashing + +The **-31 Magic attack** penalty makes granite legs popular for splashing — providing 10 more negative magic than rune platelegs (-21). This helps players intentionally miss spells for AFK Magic XP. + +## Usage + +- Best ranged defence legs at 50 Defence +- Popular for splash training (high negative magic) +- Used in some Dagannoth Kings guides for ranged defence +- Budget option before dragon platelegs +- Part of granite fashionscape set + +## Market Strategy + +**Buy Limit:** 70 per 4 hours | **High Alch:** 39,600 GP + +**Tips:** +- Only obtainable from Skeletal Wyverns (72 Slayer) +- GE price near high alch value +- Heavy item (15.9 kg) — significant run energy impact + +## Related Items + +- [Dragon platelegs](/osrs/dragon-platelegs/) - 60 Def upgrade +- [Granite helm](/osrs/granite-helm/) - Matching set piece +- [Granite shield](/osrs/granite-shield/) - Matching set piece diff --git a/apps/kbve/astro-kbve/public/isometric/assets/index.js b/apps/kbve/astro-kbve/public/isometric/assets/index.js index f62e4f9d7..32bc17b8e 100644 --- a/apps/kbve/astro-kbve/public/isometric/assets/index.js +++ b/apps/kbve/astro-kbve/public/isometric/assets/index.js @@ -49,9 +49,9 @@ const scriptRel = 'modulepreload', __vitePreload = function (c, i, f) { let _ = Promise.resolve(); if (i && i.length > 0) { - let y = function (p) { + let y = function (O) { return Promise.all( - p.map((z) => + O.map((z) => Promise.resolve(z).then( (H) => ({ status: 'fulfilled', value: H }), (H) => ({ status: 'rejected', reason: H }), @@ -65,18 +65,18 @@ const scriptRel = 'modulepreload', (S == null ? void 0 : S.nonce) || (S == null ? void 0 : S.getAttribute('nonce')); _ = y( - i.map((p) => { - if (((p = assetsURL(p)), p in seen)) return; - seen[p] = true; - const z = p.endsWith('.css'), + i.map((O) => { + if (((O = assetsURL(O)), O in seen)) return; + seen[O] = true; + const z = O.endsWith('.css'), H = z ? '[rel="stylesheet"]' : ''; - if (document.querySelector(`link[href="${p}"]${H}`)) return; + if (document.querySelector(`link[href="${O}"]${H}`)) return; const J = document.createElement('link'); if ( ((J.rel = z ? 'stylesheet' : scriptRel), z || (J.as = 'script'), (J.crossOrigin = ''), - (J.href = p), + (J.href = O), x && J.setAttribute('nonce', x), document.head.appendChild(J), z) @@ -86,7 +86,7 @@ const scriptRel = 'modulepreload', J.addEventListener('error', () => De( new Error( - `Unable to preload CSS for ${p}`, + `Unable to preload CSS for ${O}`, ), ), ); @@ -191,7 +191,7 @@ function requireReact_production() { y = Symbol.for('react.context'), S = Symbol.for('react.forward_ref'), x = Symbol.for('react.suspense'), - p = Symbol.for('react.memo'), + O = Symbol.for('react.memo'), z = Symbol.for('react.lazy'), H = Symbol.for('react.activity'), J = Symbol.iterator; @@ -240,7 +240,7 @@ function requireReact_production() { (at.constructor = Re), ze(at, Ke.prototype), (at.isPureReactComponent = true); - var pt = Array.isArray; + var Ot = Array.isArray; function Ue() {} var k = { H: null, A: null, T: null, S: null }, Be = Object.prototype.hasOwnProperty; @@ -269,7 +269,7 @@ function requireReact_production() { }) ); } - var pn = /\/+/g; + var On = /\/+/g; function Et(m, D) { return typeof m == 'object' && m !== null && m.key != null ? Ge('' + m.key) @@ -336,9 +336,9 @@ function requireReact_production() { return ( (V = V(m)), (te = U === '' ? '.' + Et(m, 0) : U), - pt(V) + Ot(V) ? ((C = ''), - te != null && (C = te.replace(pn, '$&/') + '/'), + te != null && (C = te.replace(On, '$&/') + '/'), A(V, D, C, '', function (va) { return va; })) @@ -349,7 +349,7 @@ function requireReact_production() { C + (V.key == null || (m && m.key === V.key) ? '' - : ('' + V.key).replace(pn, '$&/') + + : ('' + V.key).replace(On, '$&/') + '/') + te, )), @@ -358,7 +358,7 @@ function requireReact_production() { ); te = 0; var He = U === '' ? '.' : U + ':'; - if (pt(m)) + if (Ot(m)) for (var ge = 0; ge < m.length; ge++) (U = m[ge]), (Q = He + Et(U, ge)), (te += A(U, D, C, Q, V)); else if (((ge = Ce(m)), typeof ge == 'function')) @@ -579,7 +579,7 @@ function requireReact_production() { }; }), (react_production.memo = function (m, D) { - return { $$typeof: p, type: m, compare: D === void 0 ? null : D }; + return { $$typeof: O, type: m, compare: D === void 0 ? null : D }; }), (react_production.startTransition = function (m) { var D = k.T, @@ -752,7 +752,7 @@ function requireScheduler_production() { }; } var x = [], - p = [], + O = [], z = 1, H = null, J = 3, @@ -765,21 +765,21 @@ function requireScheduler_production() { typeof clearTimeout == 'function' ? clearTimeout : null, Re = typeof setImmediate < 'u' ? setImmediate : null; function at(A) { - for (var R = i(p); R !== null; ) { - if (R.callback === null) f(p); + for (var R = i(O); R !== null; ) { + if (R.callback === null) f(O); else if (R.startTime <= A) - f(p), (R.sortIndex = R.expirationTime), c(x, R); + f(O), (R.sortIndex = R.expirationTime), c(x, R); else break; - R = i(p); + R = i(O); } } - function pt(A) { + function Ot(A) { if (((ze = false), at(A), !De)) if (i(x) !== null) (De = true), Ue || ((Ue = true), Ge()); else { - var R = i(p); - R !== null && dt(pt, R.startTime - A); + var R = i(O); + R !== null && dt(Ot, R.startTime - A); } } var Ue = false, @@ -830,9 +830,9 @@ function requireScheduler_production() { } if (H !== null) R = true; else { - var m = i(p); + var m = i(O); m !== null && - dt(pt, m.startTime - A), + dt(Ot, m.startTime - A), (R = false); } } @@ -853,9 +853,9 @@ function requireScheduler_production() { Re(jt); }; else if (typeof MessageChannel < 'u') { - var pn = new MessageChannel(), - Et = pn.port2; - (pn.port1.onmessage = jt), + var On = new MessageChannel(), + Et = On.port2; + (On.port1.onmessage = jt), (Ge = function () { Et.postMessage(null); }); @@ -966,11 +966,11 @@ function requireScheduler_production() { }), q > ce ? ((A.sortIndex = q), - c(p, A), + c(O, A), i(x) === null && - A === i(p) && + A === i(O) && (ze ? (Zt(k), (k = -1)) : (ze = true), - dt(pt, q - ce))) + dt(Ot, q - ce))) : ((A.sortIndex = re), c(x, A), De || @@ -1023,17 +1023,17 @@ function requireReactDom_production() { hasRequiredReactDom_production = 1; var a = requireReact(); function c(x) { - var p = 'https://react.dev/errors/' + x; + var O = 'https://react.dev/errors/' + x; if (1 < arguments.length) { - p += '?args[]=' + encodeURIComponent(arguments[1]); + O += '?args[]=' + encodeURIComponent(arguments[1]); for (var z = 2; z < arguments.length; z++) - p += '&args[]=' + encodeURIComponent(arguments[z]); + O += '&args[]=' + encodeURIComponent(arguments[z]); } return ( 'Minified React error #' + x + '; visit ' + - p + + O + ' for the full message or use the non-minified dev environment for full errors and additional helpful warnings.' ); } @@ -1056,7 +1056,7 @@ function requireReactDom_production() { findDOMNode: null, }, _ = Symbol.for('react.portal'); - function d(x, p, z) { + function d(x, O, z) { var H = 3 < arguments.length && arguments[3] !== void 0 ? arguments[3] @@ -1065,69 +1065,69 @@ function requireReactDom_production() { $$typeof: _, key: H == null ? null : '' + H, children: x, - containerInfo: p, + containerInfo: O, implementation: z, }; } var y = a.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE; - function S(x, p) { + function S(x, O) { if (x === 'font') return ''; - if (typeof p == 'string') return p === 'use-credentials' ? p : ''; + if (typeof O == 'string') return O === 'use-credentials' ? O : ''; } return ( (reactDom_production.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE = f), - (reactDom_production.createPortal = function (x, p) { + (reactDom_production.createPortal = function (x, O) { var z = 2 < arguments.length && arguments[2] !== void 0 ? arguments[2] : null; if ( - !p || - (p.nodeType !== 1 && p.nodeType !== 9 && p.nodeType !== 11) + !O || + (O.nodeType !== 1 && O.nodeType !== 9 && O.nodeType !== 11) ) throw Error(c(299)); - return d(x, p, null, z); + return d(x, O, null, z); }), (reactDom_production.flushSync = function (x) { - var p = y.T, + var O = y.T, z = f.p; try { if (((y.T = null), (f.p = 2), x)) return x(); } finally { - (y.T = p), (f.p = z), f.d.f(); + (y.T = O), (f.p = z), f.d.f(); } }), - (reactDom_production.preconnect = function (x, p) { + (reactDom_production.preconnect = function (x, O) { typeof x == 'string' && - (p - ? ((p = p.crossOrigin), - (p = - typeof p == 'string' - ? p === 'use-credentials' - ? p + (O + ? ((O = O.crossOrigin), + (O = + typeof O == 'string' + ? O === 'use-credentials' + ? O : '' : void 0)) - : (p = null), - f.d.C(x, p)); + : (O = null), + f.d.C(x, O)); }), (reactDom_production.prefetchDNS = function (x) { typeof x == 'string' && f.d.D(x); }), - (reactDom_production.preinit = function (x, p) { - if (typeof x == 'string' && p && typeof p.as == 'string') { - var z = p.as, - H = S(z, p.crossOrigin), - J = typeof p.integrity == 'string' ? p.integrity : void 0, + (reactDom_production.preinit = function (x, O) { + if (typeof x == 'string' && O && typeof O.as == 'string') { + var z = O.as, + H = S(z, O.crossOrigin), + J = typeof O.integrity == 'string' ? O.integrity : void 0, Ce = - typeof p.fetchPriority == 'string' - ? p.fetchPriority + typeof O.fetchPriority == 'string' + ? O.fetchPriority : void 0; z === 'style' ? f.d.S( x, - typeof p.precedence == 'string' - ? p.precedence + typeof O.precedence == 'string' + ? O.precedence : void 0, { crossOrigin: H, integrity: J, fetchPriority: Ce }, ) @@ -1137,73 +1137,73 @@ function requireReactDom_production() { integrity: J, fetchPriority: Ce, nonce: - typeof p.nonce == 'string' ? p.nonce : void 0, + typeof O.nonce == 'string' ? O.nonce : void 0, }); } }), - (reactDom_production.preinitModule = function (x, p) { + (reactDom_production.preinitModule = function (x, O) { if (typeof x == 'string') - if (typeof p == 'object' && p !== null) { - if (p.as == null || p.as === 'script') { - var z = S(p.as, p.crossOrigin); + if (typeof O == 'object' && O !== null) { + if (O.as == null || O.as === 'script') { + var z = S(O.as, O.crossOrigin); f.d.M(x, { crossOrigin: z, integrity: - typeof p.integrity == 'string' - ? p.integrity + typeof O.integrity == 'string' + ? O.integrity : void 0, nonce: - typeof p.nonce == 'string' ? p.nonce : void 0, + typeof O.nonce == 'string' ? O.nonce : void 0, }); } - } else p == null && f.d.M(x); + } else O == null && f.d.M(x); }), - (reactDom_production.preload = function (x, p) { + (reactDom_production.preload = function (x, O) { if ( typeof x == 'string' && - typeof p == 'object' && - p !== null && - typeof p.as == 'string' + typeof O == 'object' && + O !== null && + typeof O.as == 'string' ) { - var z = p.as, - H = S(z, p.crossOrigin); + var z = O.as, + H = S(z, O.crossOrigin); f.d.L(x, z, { crossOrigin: H, integrity: - typeof p.integrity == 'string' ? p.integrity : void 0, - nonce: typeof p.nonce == 'string' ? p.nonce : void 0, - type: typeof p.type == 'string' ? p.type : void 0, + typeof O.integrity == 'string' ? O.integrity : void 0, + nonce: typeof O.nonce == 'string' ? O.nonce : void 0, + type: typeof O.type == 'string' ? O.type : void 0, fetchPriority: - typeof p.fetchPriority == 'string' - ? p.fetchPriority + typeof O.fetchPriority == 'string' + ? O.fetchPriority : void 0, referrerPolicy: - typeof p.referrerPolicy == 'string' - ? p.referrerPolicy + typeof O.referrerPolicy == 'string' + ? O.referrerPolicy : void 0, imageSrcSet: - typeof p.imageSrcSet == 'string' - ? p.imageSrcSet + typeof O.imageSrcSet == 'string' + ? O.imageSrcSet : void 0, imageSizes: - typeof p.imageSizes == 'string' ? p.imageSizes : void 0, - media: typeof p.media == 'string' ? p.media : void 0, + typeof O.imageSizes == 'string' ? O.imageSizes : void 0, + media: typeof O.media == 'string' ? O.media : void 0, }); } }), - (reactDom_production.preloadModule = function (x, p) { + (reactDom_production.preloadModule = function (x, O) { if (typeof x == 'string') - if (p) { - var z = S(p.as, p.crossOrigin); + if (O) { + var z = S(O.as, O.crossOrigin); f.d.m(x, { as: - typeof p.as == 'string' && p.as !== 'script' - ? p.as + typeof O.as == 'string' && O.as !== 'script' + ? O.as : void 0, crossOrigin: z, integrity: - typeof p.integrity == 'string' - ? p.integrity + typeof O.integrity == 'string' + ? O.integrity : void 0, }); } else f.d.m(x); @@ -1211,11 +1211,11 @@ function requireReactDom_production() { (reactDom_production.requestFormReset = function (x) { f.d.r(x); }), - (reactDom_production.unstable_batchedUpdates = function (x, p) { - return x(p); + (reactDom_production.unstable_batchedUpdates = function (x, O) { + return x(O); }), - (reactDom_production.useFormState = function (x, p, z) { - return y.H.useFormState(x, p, z); + (reactDom_production.useFormState = function (x, O, z) { + return y.H.useFormState(x, O, z); }), (reactDom_production.useFormStatus = function () { return y.H.useHostTransitionStatus(); @@ -1323,7 +1323,7 @@ function requireReactDomClient_production() { function x(e) { if (d(e) !== e) throw Error(f(188)); } - function p(e) { + function O(e) { var t = e.alternate; if (!t) { if (((t = d(e)), t === null)) throw Error(f(188)); @@ -1400,7 +1400,7 @@ function requireReactDomClient_production() { Zt = Symbol.for('react.consumer'), Re = Symbol.for('react.context'), at = Symbol.for('react.forward_ref'), - pt = Symbol.for('react.suspense'), + Ot = Symbol.for('react.suspense'), Ue = Symbol.for('react.suspense_list'), k = Symbol.for('react.memo'), Be = Symbol.for('react.lazy'), @@ -1413,11 +1413,11 @@ function requireReactDomClient_production() { : ((e = (jt && e[jt]) || e['@@iterator']), typeof e == 'function' ? e : null); } - var pn = Symbol.for('react.client.reference'); + var On = Symbol.for('react.client.reference'); function Et(e) { if (e == null) return null; if (typeof e == 'function') - return e.$$typeof === pn ? null : e.displayName || e.name || null; + return e.$$typeof === On ? null : e.displayName || e.name || null; if (typeof e == 'string') return e; switch (e) { case ze: @@ -1426,7 +1426,7 @@ function requireReactDomClient_production() { return 'Profiler'; case St: return 'StrictMode'; - case pt: + case Ot: return 'Suspense'; case Ue: return 'SuspenseList'; @@ -1720,7 +1720,7 @@ Error generating stack: ` + cs = a.unstable_getCurrentPriorityLevel, er = a.unstable_ImmediatePriority, tr = a.unstable_UserBlockingPriority, - Oc = a.unstable_NormalPriority, + pc = a.unstable_NormalPriority, us = a.unstable_LowPriority, nr = a.unstable_IdlePriority, ls = a.log, @@ -1742,7 +1742,7 @@ Error generating stack: ` + function os(e) { return (e >>>= 0), e === 0 ? 32 : (31 - ((rs(e) / fs) | 0)) | 0; } - var pc = 256, + var Oc = 256, yc = 262144, jc = 4194304; function jn(e) { @@ -2167,7 +2167,7 @@ Error generating stack: ` + (t === 'checkbox' || t === 'radio') ); } - function Os(e, t, n) { + function ps(e, t, n) { var u = Object.getOwnPropertyDescriptor(e.constructor.prototype, t); if ( !e.hasOwnProperty(t) && @@ -2205,7 +2205,7 @@ Error generating stack: ` + function Zu(e) { if (!e._valueTracker) { var t = dr(e) ? 'checked' : 'value'; - e._valueTracker = Os(e, t, '' + e[t]); + e._valueTracker = ps(e, t, '' + e[t]); } } function gr(e) { @@ -2232,9 +2232,9 @@ Error generating stack: ` + return e.body; } } - var ps = /[\n"\\]/g; + var Os = /[\n"\\]/g; function ut(e) { - return e.replace(ps, function (t) { + return e.replace(Os, function (t) { return '\\' + t.charCodeAt(0).toString(16) + ' '; }); } @@ -2322,7 +2322,7 @@ Error generating stack: ` + t !== null && (t.selected = true); } } - function Or(e, t, n) { + function pr(e, t, n) { if ( t != null && ((t = '' + ct(t)), t !== e.value && (e.value = t), n == null) @@ -2332,7 +2332,7 @@ Error generating stack: ` + } e.defaultValue = n != null ? '' + ct(n) : ''; } - function pr(e, t, n, u) { + function Or(e, t, n, u) { if (t == null) { if (u != null) { if (n != null) throw Error(f(92)); @@ -2562,7 +2562,7 @@ Error generating stack: ` + } break e; case 'textarea': - Or(e, n.value, n.defaultValue); + pr(e, n.value, n.defaultValue); break e; case 'select': (t = n.value), t != null && Qn(e, !!n.multiple, t, false); @@ -3041,7 +3041,7 @@ Error generating stack: ` + var Ha = null, Na = null; function Is(e) { - p_(e, 0); + O_(e, 0); } function Rc(e) { var t = Ta(e); @@ -3497,10 +3497,10 @@ Error generating stack: ` + (e.lanes = r), e ); - case pt: + case Ot: return ( (e = Ie(13, n, t, l)), - (e.elementType = pt), + (e.elementType = Ot), (e.lanes = r), e ); @@ -3604,10 +3604,10 @@ Error generating stack: ` + (wt = r + e); } else (ht = (1 << r) | (n << l) | u), (wt = e); } - function Ol(e) { + function pl(e) { e.return !== null && (Rt(e, 1), tf(e, 1, 0)); } - function pl(e) { + function Ol(e) { for (; e === Bc; ) (Bc = Pn[--ea]), (Pn[ea] = null), (Ga = Pn[--ea]), (Pn[ea] = null); for (; e === Jt; ) @@ -3689,7 +3689,7 @@ Error generating stack: ` + K('invalid', t); break; case 'textarea': - K('invalid', t), pr(t, u.value, u.defaultValue, u.children); + K('invalid', t), Or(t, u.value, u.defaultValue, u.children); } (n = u.children), (typeof n != 'string' && @@ -3918,7 +3918,7 @@ Error generating stack: ` + }, fb = a.unstable_scheduleCallback, ob = a.unstable_NormalPriority, - pe = { + Oe = { $$typeof: Re, Consumer: null, Provider: null, @@ -4014,7 +4014,7 @@ Error generating stack: ` + } function ff() { var e = El(); - return e === null ? null : { parent: pe._currentValue, pool: e }; + return e === null ? null : { parent: Oe._currentValue, pool: e }; } var ua = Error(f(460)), Al = Error(f(474)), @@ -4110,189 +4110,189 @@ Error generating stack: ` + )); } function df(e) { - function t(O, g) { + function t(p, g) { if (e) { - var j = O.deletions; - j === null ? ((O.deletions = [g]), (O.flags |= 16)) : j.push(g); + var j = p.deletions; + j === null ? ((p.deletions = [g]), (p.flags |= 16)) : j.push(g); } } - function n(O, g) { + function n(p, g) { if (!e) return null; - for (; g !== null; ) t(O, g), (g = g.sibling); + for (; g !== null; ) t(p, g), (g = g.sibling); return null; } - function u(O) { - for (var g = /* @__PURE__ */ new Map(); O !== null; ) - O.key !== null ? g.set(O.key, O) : g.set(O.index, O), - (O = O.sibling); + function u(p) { + for (var g = /* @__PURE__ */ new Map(); p !== null; ) + p.key !== null ? g.set(p.key, p) : g.set(p.index, p), + (p = p.sibling); return g; } - function l(O, g) { - return (O = Dt(O, g)), (O.index = 0), (O.sibling = null), O; + function l(p, g) { + return (p = Dt(p, g)), (p.index = 0), (p.sibling = null), p; } - function r(O, g, j) { + function r(p, g, j) { return ( - (O.index = j), + (p.index = j), e - ? ((j = O.alternate), + ? ((j = p.alternate), j !== null ? ((j = j.index), - j < g ? ((O.flags |= 67108866), g) : j) - : ((O.flags |= 67108866), g)) - : ((O.flags |= 1048576), g) + j < g ? ((p.flags |= 67108866), g) : j) + : ((p.flags |= 67108866), g)) + : ((p.flags |= 1048576), g) ); } - function o(O) { - return e && O.alternate === null && (O.flags |= 67108866), O; + function o(p) { + return e && p.alternate === null && (p.flags |= 67108866), p; } - function s(O, g, j, T) { + function s(p, g, j, T) { return g === null || g.tag !== 6 - ? ((g = gl(j, O.mode, T)), (g.return = O), g) - : ((g = l(g, j)), (g.return = O), g); + ? ((g = gl(j, p.mode, T)), (g.return = p), g) + : ((g = l(g, j)), (g.return = p), g); } - function b(O, g, j, T) { + function b(p, g, j, T) { var G = j.type; return G === ze - ? E(O, g, j.props.children, T, j.key) + ? E(p, g, j.props.children, T, j.key) : g !== null && (g.elementType === G || (typeof G == 'object' && G !== null && G.$$typeof === Be && Rn(G) === g.type)) - ? ((g = l(g, j.props)), Xa(g, j), (g.return = O), g) - : ((g = Uc(j.type, j.key, j.props, null, O.mode, T)), + ? ((g = l(g, j.props)), Xa(g, j), (g.return = p), g) + : ((g = Uc(j.type, j.key, j.props, null, p.mode, T)), Xa(g, j), - (g.return = O), + (g.return = p), g); } - function h(O, g, j, T) { + function h(p, g, j, T) { return g === null || g.tag !== 4 || g.stateNode.containerInfo !== j.containerInfo || g.stateNode.implementation !== j.implementation - ? ((g = ml(j, O.mode, T)), (g.return = O), g) - : ((g = l(g, j.children || [])), (g.return = O), g); + ? ((g = ml(j, p.mode, T)), (g.return = p), g) + : ((g = l(g, j.children || [])), (g.return = p), g); } - function E(O, g, j, T, G) { + function E(p, g, j, T, G) { return g === null || g.tag !== 7 - ? ((g = En(j, O.mode, T, G)), (g.return = O), g) - : ((g = l(g, j)), (g.return = O), g); + ? ((g = En(j, p.mode, T, G)), (g.return = p), g) + : ((g = l(g, j)), (g.return = p), g); } - function M(O, g, j) { + function M(p, g, j) { if ( (typeof g == 'string' && g !== '') || typeof g == 'number' || typeof g == 'bigint' ) - return (g = gl('' + g, O.mode, j)), (g.return = O), g; + return (g = gl('' + g, p.mode, j)), (g.return = p), g; if (typeof g == 'object' && g !== null) { switch (g.$$typeof) { case Ce: return ( - (j = Uc(g.type, g.key, g.props, null, O.mode, j)), + (j = Uc(g.type, g.key, g.props, null, p.mode, j)), Xa(j, g), - (j.return = O), + (j.return = p), j ); case De: - return (g = ml(g, O.mode, j)), (g.return = O), g; + return (g = ml(g, p.mode, j)), (g.return = p), g; case Be: - return (g = Rn(g)), M(O, g, j); + return (g = Rn(g)), M(p, g, j); } if (dt(g) || Ge(g)) - return (g = En(g, O.mode, j, null)), (g.return = O), g; - if (typeof g.then == 'function') return M(O, Xc(g), j); - if (g.$$typeof === Re) return M(O, Lc(O, g), j); - Qc(O, g); + return (g = En(g, p.mode, j, null)), (g.return = p), g; + if (typeof g.then == 'function') return M(p, Xc(g), j); + if (g.$$typeof === Re) return M(p, Lc(p, g), j); + Qc(p, g); } return null; } - function w(O, g, j, T) { + function w(p, g, j, T) { var G = g !== null ? g.key : null; if ( (typeof j == 'string' && j !== '') || typeof j == 'number' || typeof j == 'bigint' ) - return G !== null ? null : s(O, g, '' + j, T); + return G !== null ? null : s(p, g, '' + j, T); if (typeof j == 'object' && j !== null) { switch (j.$$typeof) { case Ce: - return j.key === G ? b(O, g, j, T) : null; + return j.key === G ? b(p, g, j, T) : null; case De: - return j.key === G ? h(O, g, j, T) : null; + return j.key === G ? h(p, g, j, T) : null; case Be: - return (j = Rn(j)), w(O, g, j, T); + return (j = Rn(j)), w(p, g, j, T); } if (dt(j) || Ge(j)) - return G !== null ? null : E(O, g, j, T, null); - if (typeof j.then == 'function') return w(O, g, Xc(j), T); - if (j.$$typeof === Re) return w(O, g, Lc(O, j), T); - Qc(O, j); + return G !== null ? null : E(p, g, j, T, null); + if (typeof j.then == 'function') return w(p, g, Xc(j), T); + if (j.$$typeof === Re) return w(p, g, Lc(p, j), T); + Qc(p, j); } return null; } - function v(O, g, j, T, G) { + function v(p, g, j, T, G) { if ( (typeof T == 'string' && T !== '') || typeof T == 'number' || typeof T == 'bigint' ) - return (O = O.get(j) || null), s(g, O, '' + T, G); + return (p = p.get(j) || null), s(g, p, '' + T, G); if (typeof T == 'object' && T !== null) { switch (T.$$typeof) { case Ce: return ( - (O = O.get(T.key === null ? j : T.key) || null), - b(g, O, T, G) + (p = p.get(T.key === null ? j : T.key) || null), + b(g, p, T, G) ); case De: return ( - (O = O.get(T.key === null ? j : T.key) || null), - h(g, O, T, G) + (p = p.get(T.key === null ? j : T.key) || null), + h(g, p, T, G) ); case Be: - return (T = Rn(T)), v(O, g, j, T, G); + return (T = Rn(T)), v(p, g, j, T, G); } if (dt(T) || Ge(T)) - return (O = O.get(j) || null), E(g, O, T, G, null); - if (typeof T.then == 'function') return v(O, g, j, Xc(T), G); - if (T.$$typeof === Re) return v(O, g, j, Lc(g, T), G); + return (p = p.get(j) || null), E(g, p, T, G, null); + if (typeof T.then == 'function') return v(p, g, j, Xc(T), G); + if (T.$$typeof === Re) return v(p, g, j, Lc(g, T), G); Qc(g, T); } return null; } - function N(O, g, j, T) { + function N(p, g, j, T) { for ( var G = null, $ = null, B = g, X = (g = 0), W = null; B !== null && X < j.length; X++ ) { B.index > X ? ((W = B), (B = null)) : (W = B.sibling); - var P = w(O, B, j[X], T); + var P = w(p, B, j[X], T); if (P === null) { B === null && (B = W); break; } - e && B && P.alternate === null && t(O, B), + e && B && P.alternate === null && t(p, B), (g = r(P, g, X)), $ === null ? (G = P) : ($.sibling = P), ($ = P), (B = W); } - if (X === j.length) return n(O, B), I && Rt(O, X), G; + if (X === j.length) return n(p, B), I && Rt(p, X), G; if (B === null) { for (; X < j.length; X++) - (B = M(O, j[X], T)), + (B = M(p, j[X], T)), B !== null && ((g = r(B, g, X)), $ === null ? (G = B) : ($.sibling = B), ($ = B)); - return I && Rt(O, X), G; + return I && Rt(p, X), G; } for (B = u(B); X < j.length; X++) - (W = v(B, O, X, j[X], T)), + (W = v(B, p, X, j[X], T)), W !== null && (e && W.alternate !== null && @@ -4302,14 +4302,14 @@ Error generating stack: ` + ($ = W)); return ( e && - B.forEach(function (On) { - return t(O, On); + B.forEach(function (pn) { + return t(p, pn); }), - I && Rt(O, X), + I && Rt(p, X), G ); } - function L(O, g, j, T) { + function L(p, g, j, T) { if (j == null) throw Error(f(151)); for ( var G = null, @@ -4322,29 +4322,29 @@ Error generating stack: ` + X++, P = j.next() ) { B.index > X ? ((W = B), (B = null)) : (W = B.sibling); - var On = w(O, B, P.value, T); - if (On === null) { + var pn = w(p, B, P.value, T); + if (pn === null) { B === null && (B = W); break; } - e && B && On.alternate === null && t(O, B), - (g = r(On, g, X)), - $ === null ? (G = On) : ($.sibling = On), - ($ = On), + e && B && pn.alternate === null && t(p, B), + (g = r(pn, g, X)), + $ === null ? (G = pn) : ($.sibling = pn), + ($ = pn), (B = W); } - if (P.done) return n(O, B), I && Rt(O, X), G; + if (P.done) return n(p, B), I && Rt(p, X), G; if (B === null) { for (; !P.done; X++, P = j.next()) - (P = M(O, P.value, T)), + (P = M(p, P.value, T)), P !== null && ((g = r(P, g, X)), $ === null ? (G = P) : ($.sibling = P), ($ = P)); - return I && Rt(O, X), G; + return I && Rt(p, X), G; } for (B = u(B); !P.done; X++, P = j.next()) - (P = v(B, O, X, P.value, T)), + (P = v(B, p, X, P.value, T)), P !== null && (e && P.alternate !== null && @@ -4355,13 +4355,13 @@ Error generating stack: ` + return ( e && B.forEach(function (vd) { - return t(O, vd); + return t(p, vd); }), - I && Rt(O, X), + I && Rt(p, X), G ); } - function ie(O, g, j, T) { + function ie(p, g, j, T) { if ( (typeof j == 'object' && j !== null && @@ -4377,10 +4377,10 @@ Error generating stack: ` + if (g.key === G) { if (((G = j.type), G === ze)) { if (g.tag === 7) { - n(O, g.sibling), + n(p, g.sibling), (T = l(g, j.props.children)), - (T.return = O), - (O = T); + (T.return = p), + (p = T); break e; } } else if ( @@ -4390,35 +4390,35 @@ Error generating stack: ` + G.$$typeof === Be && Rn(G) === g.type) ) { - n(O, g.sibling), + n(p, g.sibling), (T = l(g, j.props)), Xa(T, j), - (T.return = O), - (O = T); + (T.return = p), + (p = T); break e; } - n(O, g); + n(p, g); break; - } else t(O, g); + } else t(p, g); g = g.sibling; } j.type === ze - ? ((T = En(j.props.children, O.mode, T, j.key)), - (T.return = O), - (O = T)) + ? ((T = En(j.props.children, p.mode, T, j.key)), + (T.return = p), + (p = T)) : ((T = Uc( j.type, j.key, j.props, null, - O.mode, + p.mode, T, )), Xa(T, j), - (T.return = O), - (O = T)); + (T.return = p), + (p = T)); } - return o(O); + return o(p); case De: e: { for (G = j.key; g !== null; ) { @@ -4430,59 +4430,59 @@ Error generating stack: ` + g.stateNode.implementation === j.implementation ) { - n(O, g.sibling), + n(p, g.sibling), (T = l(g, j.children || [])), - (T.return = O), - (O = T); + (T.return = p), + (p = T); break e; } else { - n(O, g); + n(p, g); break; } - else t(O, g); + else t(p, g); g = g.sibling; } - (T = ml(j, O.mode, T)), (T.return = O), (O = T); + (T = ml(j, p.mode, T)), (T.return = p), (p = T); } - return o(O); + return o(p); case Be: - return (j = Rn(j)), ie(O, g, j, T); + return (j = Rn(j)), ie(p, g, j, T); } - if (dt(j)) return N(O, g, j, T); + if (dt(j)) return N(p, g, j, T); if (Ge(j)) { if (((G = Ge(j)), typeof G != 'function')) throw Error(f(150)); - return (j = G.call(j)), L(O, g, j, T); + return (j = G.call(j)), L(p, g, j, T); } - if (typeof j.then == 'function') return ie(O, g, Xc(j), T); - if (j.$$typeof === Re) return ie(O, g, Lc(O, j), T); - Qc(O, j); + if (typeof j.then == 'function') return ie(p, g, Xc(j), T); + if (j.$$typeof === Re) return ie(p, g, Lc(p, j), T); + Qc(p, j); } return (typeof j == 'string' && j !== '') || typeof j == 'number' || typeof j == 'bigint' ? ((j = '' + j), g !== null && g.tag === 6 - ? (n(O, g.sibling), + ? (n(p, g.sibling), (T = l(g, j)), - (T.return = O), - (O = T)) - : (n(O, g), - (T = gl(j, O.mode, T)), - (T.return = O), - (O = T)), - o(O)) - : n(O, g); + (T.return = p), + (p = T)) + : (n(p, g), + (T = gl(j, p.mode, T)), + (T.return = p), + (p = T)), + o(p)) + : n(p, g); } - return function (O, g, j, T) { + return function (p, g, j, T) { try { Ya = 0; - var G = ie(O, g, j, T); + var G = ie(p, g, j, T); return (la = null), G; } catch (B) { if (B === ua || B === Vc) throw B; - var $ = Ie(29, B, null, O.mode); - return ($.lanes = T), ($.return = O), $; + var $ = Ie(29, B, null, p.mode); + return ($.lanes = T), ($.return = p), $; } finally { } }; @@ -4684,14 +4684,14 @@ Error generating stack: ` + if (typeof e != 'function') throw Error(f(191, e)); e.call(t); } - function Of(e, t) { + function pf(e, t) { var n = e.callbacks; if (n !== null) for (e.callbacks = null, e = 0; e < n.length; e++) mf(n[e], t); } var ia = m(null), Zc = m(0); - function pf(e, t) { + function Of(e, t) { (e = Yt), C(Zc, e), C(ia, t), (Yt = e | t.baseLanes); } function Cl() { @@ -4870,7 +4870,7 @@ Error generating stack: ` + ye === null ? (Y.memoizedState = ye = e) : (ye = ye.next = e), ye ); } - function Oe() { + function pe() { if (ue === null) { var e = Y.alternate; e = e !== null ? e.memoizedState : null; @@ -4951,7 +4951,7 @@ Error generating stack: ` + return typeof t == 'function' ? t(e) : t; } function Ic(e) { - var t = Oe(); + var t = pe(); return Vl(t, ue, e); } function Vl(e, t, n) { @@ -5041,7 +5041,7 @@ Error generating stack: ` + return l === null && (u.lanes = 0), [e.memoizedState, u.dispatch]; } function Yl(e) { - var t = Oe(), + var t = pe(), n = t.queue; if (n === null) throw Error(f(311)); n.lastRenderedReducer = e; @@ -5062,7 +5062,7 @@ Error generating stack: ` + } function wf(e, t, n) { var u = Y, - l = Oe(), + l = pe(), r = I; if (r) { if (n === void 0) throw Error(f(407)); @@ -5293,7 +5293,7 @@ Error generating stack: ` + ); } function Nf(e) { - var t = Oe(); + var t = pe(); return Uf(t, ue, e); } function Uf(e, t, n) { @@ -5308,7 +5308,7 @@ Error generating stack: ` + throw o === ua ? Vc : o; } else u = t; - t = Oe(); + t = pe(); var l = t.queue, r = l.dispatch; return ( @@ -5322,10 +5322,10 @@ Error generating stack: ` + e.action = t; } function Bf(e) { - var t = Oe(), + var t = pe(), n = ue; if (n !== null) return Uf(t, n, e); - Oe(), (t = t.memoizedState), (n = Oe()); + pe(), (t = t.memoizedState), (n = pe()); var u = n.queue.dispatch; return (n.memoizedState = e), [t, u, false]; } @@ -5345,7 +5345,7 @@ Error generating stack: ` + ); } function Gf() { - return Oe().memoizedState; + return pe().memoizedState; } function $c(e, t, n, u) { var l = Ne(); @@ -5358,7 +5358,7 @@ Error generating stack: ` + )); } function Pc(e, t, n, u) { - var l = Oe(); + var l = pe(); u = u === void 0 ? null : u; var r = l.memoizedState.inst; ue !== null && u !== null && Nl(u, ue.memoizedState.deps) @@ -5371,7 +5371,7 @@ Error generating stack: ` + function Zl(e, t) { Pc(2048, 8, e, t); } - function Ob(e) { + function pb(e) { Y.flags |= 4; var t = Y.updateQueue; if (t === null) (t = Jc()), (Y.updateQueue = t), (t.events = [e]); @@ -5381,9 +5381,9 @@ Error generating stack: ` + } } function qf(e) { - var t = Oe().memoizedState; + var t = pe().memoizedState; return ( - Ob({ ref: t, nextImpl: e }), + pb({ ref: t, nextImpl: e }), function () { if ((ee & 2) !== 0) throw Error(f(440)); return t.impl.apply(void 0, arguments); @@ -5419,7 +5419,7 @@ Error generating stack: ` + } function Kl() {} function Zf(e, t) { - var n = Oe(); + var n = pe(); t = t === void 0 ? null : t; var u = n.memoizedState; return t !== null && Nl(t, u[1]) @@ -5427,7 +5427,7 @@ Error generating stack: ` + : ((n.memoizedState = [e, t]), e); } function Kf(e, t) { - var n = Oe(); + var n = pe(); t = t === void 0 ? null : t; var u = n.memoizedState; if (t !== null && Nl(t, u[1])) return u[0]; @@ -5487,7 +5487,7 @@ Error generating stack: ` + (A.T = o); } } - function pb() {} + function Ob() {} function Wl(e, t, n, u) { if (e.tag !== 5) throw Error(f(476)); var l = Jf(e).queue; @@ -5497,7 +5497,7 @@ Error generating stack: ` + t, q, n === null - ? pb + ? Ob : function () { return kf(e), n(u); }, @@ -5549,10 +5549,10 @@ Error generating stack: ` + return Ae(sc); } function If() { - return Oe().memoizedState; + return pe().memoizedState; } function $f() { - return Oe().memoizedState; + return pe().memoizedState; } function yb(e) { for (var t = e.return; t !== null; ) { @@ -5850,12 +5850,12 @@ Error generating stack: ` + }, useDebugValue: Kl, useDeferredValue: function (e, t) { - var n = Oe(); + var n = pe(); return Ff(n, ue.memoizedState, e, t); }, useTransition: function () { var e = Ic(Nt)[0], - t = Oe().memoizedState; + t = pe().memoizedState; return [typeof e == 'boolean' ? e : Wa(e), t]; }, useSyncExternalStore: wf, @@ -5864,7 +5864,7 @@ Error generating stack: ` + useFormState: Nf, useActionState: Nf, useOptimistic: function (e, t) { - var n = Oe(); + var n = pe(); return Tf(n, ue, e, t); }, useMemoCache: ql, @@ -5888,12 +5888,12 @@ Error generating stack: ` + }, useDebugValue: Kl, useDeferredValue: function (e, t) { - var n = Oe(); + var n = pe(); return ue === null ? Fl(n, e, t) : Ff(n, ue.memoizedState, e, t); }, useTransition: function () { var e = Yl(Nt)[0], - t = Oe().memoizedState; + t = pe().memoizedState; return [typeof e == 'boolean' ? e : Wa(e), t]; }, useSyncExternalStore: wf, @@ -5902,7 +5902,7 @@ Error generating stack: ` + useFormState: Bf, useActionState: Bf, useOptimistic: function (e, t) { - var n = Oe(); + var n = pe(); return ue !== null ? Tf(n, ue, e, t) : ((n.baseState = e), [e, n.queue.dispatch]); @@ -6195,7 +6195,7 @@ Error generating stack: ` + (s = Bl()), e !== null && !je ? (Gl(e, t, l), Ut(e, t, l)) - : (I && s && Ol(t), (t.flags |= 1), Te(e, t, u, l), t.child) + : (I && s && pl(t), (t.flags |= 1), Te(e, t, u, l), t.child) ); } function bo(e, t, n, u, l) { @@ -6258,21 +6258,21 @@ Error generating stack: ` + (l = l | u.lanes | u.childLanes), (u = u.sibling); u = l & ~r; } else (u = 0), (t.child = null); - return Oo(e, t, r, n, u); + return po(e, t, r, n, u); } if ((n & 536870912) !== 0) (t.memoizedState = { baseLanes: 0, cachePool: null }), e !== null && qc(t, r !== null ? r.cachePool : null), - r !== null ? pf(t, r) : Cl(), + r !== null ? Of(t, r) : Cl(), yf(t); else return ( (u = t.lanes = 536870912), - Oo(e, t, r !== null ? r.baseLanes | n : n, n, u) + po(e, t, r !== null ? r.baseLanes | n : n, n, u) ); } else r !== null - ? (qc(t, r.cachePool), pf(t, r), an(), (t.memoizedState = null)) + ? (qc(t, r.cachePool), Of(t, r), an(), (t.memoizedState = null)) : (e !== null && qc(t, null), Cl(), an()); return Te(e, t, l, n), t.child; } @@ -6289,10 +6289,10 @@ Error generating stack: ` + t.sibling ); } - function Oo(e, t, n, u, l) { + function po(e, t, n, u, l) { var r = El(); return ( - (r = r === null ? null : { parent: pe._currentValue, pool: r }), + (r = r === null ? null : { parent: Oe._currentValue, pool: r }), (t.memoizedState = { baseLanes: n, cachePool: r }), e !== null && qc(t, null), Cl(), @@ -6311,7 +6311,7 @@ Error generating stack: ` + t ); } - function po(e, t, n) { + function Oo(e, t, n) { return ( zn(t, e.child, null, n), (e = nu(t, t.pendingProps)), @@ -6360,7 +6360,7 @@ Error generating stack: ` + if (r !== null) { var o = r.dehydrated; if ((Hl(t), l)) - if (t.flags & 256) (t.flags &= -257), (t = po(e, t, n)); + if (t.flags & 256) (t.flags &= -257), (t = Oo(e, t, n)); else if (t.memoizedState !== null) (t.child = e.child), (t.flags |= 128), (t = null); else throw Error(f(558)); @@ -6375,7 +6375,7 @@ Error generating stack: ` + ((o = lr(u, n)), o !== 0 && o !== r.retryLane)) ) throw ((r.retryLane = o), Sn(e, o), Ze(u, e, o), ti); - bu(), (t = po(e, t, n)); + bu(), (t = Oo(e, t, n)); } else (e = r.treeContext), (oe = st(o.nextSibling)), @@ -6412,7 +6412,7 @@ Error generating stack: ` + (u = Bl()), e !== null && !je ? (Gl(e, t, l), Ut(e, t, l)) - : (I && u && Ol(t), (t.flags |= 1), Te(e, t, n, l), t.child) + : (I && u && pl(t), (t.flags |= 1), Te(e, t, n, l), t.child) ); } function yo(e, t, n, u, l, r) { @@ -6424,7 +6424,7 @@ Error generating stack: ` + (u = Bl()), e !== null && !je ? (Gl(e, t, r), Ut(e, t, r)) - : (I && u && Ol(t), (t.flags |= 1), Te(e, t, n, r), t.child) + : (I && u && pl(t), (t.flags |= 1), Te(e, t, n, r), t.child) ); } function jo(e, t, n, u, l) { @@ -6772,7 +6772,7 @@ Error generating stack: ` + ? (s = ci(n)) : ((l = s.cachePool), l !== null - ? ((b = pe._currentValue), + ? ((b = Oe._currentValue), (l = l.parent !== b ? { parent: b, pool: b } @@ -6936,7 +6936,7 @@ Error generating stack: ` + switch (t.tag) { case 3: He(t, t.stateNode.containerInfo), - $t(t, pe, e.memoizedState.cache), + $t(t, Oe, e.memoizedState.cache), An(); break; case 27: @@ -6989,7 +6989,7 @@ Error generating stack: ` + case 22: return (t.lanes = 0), mo(e, t, n, t.pendingProps); case 24: - $t(t, pe, e.memoizedState.cache); + $t(t, Oe, e.memoizedState.cache); } return Ut(e, t, n); } @@ -7047,8 +7047,8 @@ Error generating stack: ` + var o = t.memoizedState; if ( ((u = o.cache), - $t(t, pe, u), - u !== r.cache && vl(t, [pe], n, true), + $t(t, Oe, u), + u !== r.cache && vl(t, [Oe], n, true), Za(), (u = o.element), r.isDehydrated) @@ -7240,7 +7240,7 @@ Error generating stack: ` + case 24: return ( Mn(t), - (u = Ae(pe)), + (u = Ae(Oe)), e === null ? ((l = El()), l === null && @@ -7252,7 +7252,7 @@ Error generating stack: ` + (l = r)), (t.memoizedState = { parent: u, cache: l }), Tl(t), - $t(t, pe, l)) + $t(t, Oe, l)) : ((e.lanes & n) !== 0 && (Ml(e, t), Ka(t, null, null, n), Za()), (l = e.memoizedState), @@ -7264,10 +7264,10 @@ Error generating stack: ` + (t.memoizedState = t.updateQueue.baseState = l), - $t(t, pe, u)) + $t(t, Oe, u)) : ((u = r.cache), - $t(t, pe, u), - u !== l.cache && vl(t, [pe], n, true))), + $t(t, Oe, u), + u !== l.cache && vl(t, [Oe], n, true))), Te(e, t, t.pendingProps.children, n), t.child ); @@ -7343,7 +7343,7 @@ Error generating stack: ` + } function xb(e, t, n) { var u = t.pendingProps; - switch ((pl(t), t.tag)) { + switch ((Ol(t), t.tag)) { case 16: case 15: case 0: @@ -7362,7 +7362,7 @@ Error generating stack: ` + (u = null), e !== null && (u = e.memoizedState.cache), t.memoizedState.cache !== u && (t.flags |= 2048), - zt(pe), + zt(Oe), ge(), n.pendingContext && ((n.context = n.pendingContext), @@ -7769,7 +7769,7 @@ Error generating stack: ` + (n = null), e !== null && (n = e.memoizedState.cache), t.memoizedState.cache !== n && (t.flags |= 2048), - zt(pe), + zt(Oe), _e(t), null ); @@ -7781,7 +7781,7 @@ Error generating stack: ` + throw Error(f(156, t.tag)); } function Sb(e, t) { - switch ((pl(t), t.tag)) { + switch ((Ol(t), t.tag)) { case 1: return ( (e = t.flags), @@ -7789,7 +7789,7 @@ Error generating stack: ` + ); case 3: return ( - zt(pe), + zt(Oe), ge(), (e = t.flags), (e & 65536) !== 0 && (e & 128) === 0 @@ -7838,7 +7838,7 @@ Error generating stack: ` + e & 65536 ? ((t.flags = (e & -65537) | 128), t) : null ); case 24: - return zt(pe), null; + return zt(Oe), null; case 25: return null; default: @@ -7846,9 +7846,9 @@ Error generating stack: ` + } } function Ao(e, t) { - switch ((pl(t), t.tag)) { + switch ((Ol(t), t.tag)) { case 3: - zt(pe), ge(); + zt(Oe), ge(); break; case 26: case 27: @@ -7875,7 +7875,7 @@ Error generating stack: ` + Pe(t), zl(), e !== null && D(Dn); break; case 24: - zt(pe); + zt(Oe); } } function Pa(e, t) { @@ -7933,7 +7933,7 @@ Error generating stack: ` + if (t !== null) { var n = e.stateNode; try { - Of(t, n); + pf(t, n); } catch (u) { ae(e, e.return, u); } @@ -8294,7 +8294,7 @@ Error generating stack: ` + t = n.child.stateNode; } try { - Of(e, t); + pf(e, t); } catch (o) { ae(n, n.return, o); } @@ -9131,7 +9131,7 @@ Error generating stack: ` + (t = t.memoizedState.cache), t !== e && (t.refCount++, e != null && qa(e)); } - function Ot(e, t, n, u) { + function pt(e, t, n, u) { if (t.subtreeFlags & 10256) for (t = t.child; t !== null; ) Vo(e, t, n, u), (t = t.sibling); } @@ -9141,13 +9141,13 @@ Error generating stack: ` + case 0: case 11: case 15: - Ot(e, t, n, u), l & 2048 && Pa(9, t); + pt(e, t, n, u), l & 2048 && Pa(9, t); break; case 1: - Ot(e, t, n, u); + pt(e, t, n, u); break; case 3: - Ot(e, t, n, u), + pt(e, t, n, u), l & 2048 && ((e = null), t.alternate !== null && @@ -9157,7 +9157,7 @@ Error generating stack: ` + break; case 12: if (l & 2048) { - Ot(e, t, n, u), (e = t.stateNode); + pt(e, t, n, u), (e = t.stateNode); try { var r = t.memoizedProps, o = r.id, @@ -9172,13 +9172,13 @@ Error generating stack: ` + } catch (b) { ae(t, t.return, b); } - } else Ot(e, t, n, u); + } else pt(e, t, n, u); break; case 31: - Ot(e, t, n, u); + pt(e, t, n, u); break; case 13: - Ot(e, t, n, u); + pt(e, t, n, u); break; case 23: break; @@ -9187,10 +9187,10 @@ Error generating stack: ` + (o = t.alternate), t.memoizedState !== null ? r._visibility & 2 - ? Ot(e, t, n, u) + ? pt(e, t, n, u) : tc(e, t) : r._visibility & 2 - ? Ot(e, t, n, u) + ? pt(e, t, n, u) : ((r._visibility |= 2), _a( e, @@ -9202,10 +9202,10 @@ Error generating stack: ` + l & 2048 && gi(o, t); break; case 24: - Ot(e, t, n, u), l & 2048 && mi(t.alternate, t); + pt(e, t, n, u), l & 2048 && mi(t.alternate, t); break; default: - Ot(e, t, n, u); + pt(e, t, n, u); } } function _a(e, t, n, u, l) { @@ -9411,12 +9411,12 @@ Error generating stack: ` + } var Tb = { getCacheForType: function (e) { - var t = Ae(pe), + var t = Ae(Oe), n = t.data.get(e); return n === void 0 && ((n = e()), t.data.set(e, n)), n; }, cacheSignal: function () { - return Ae(pe).controller.signal; + return Ae(Oe).controller.signal; }, }, Mb = typeof WeakMap == 'function' ? WeakMap : Map, @@ -9428,12 +9428,12 @@ Error generating stack: ` + et = null, un = false, ba = false, - Oi = false, + pi = false, Yt = 0, de = 0, ln = 0, Bn = 0, - pi = 0, + Oi = 0, tt = 0, da = 0, cc = null, @@ -9507,7 +9507,7 @@ Error generating stack: ` + (o = xi(s, o, false)), o !== 2) ) { - if (Oi && !b) { + if (pi && !b) { (s.errorRecoveryDisabledLanes |= r), (Bn |= r), (l = 4); @@ -9666,7 +9666,7 @@ Error generating stack: ` + return true; } function on(e, t, n, u) { - (t &= ~pi), + (t &= ~Oi), (t &= ~Bn), (e.suspendedLanes |= t), (e.pingedLanes &= ~t), @@ -9710,8 +9710,8 @@ Error generating stack: ` + (et = null), (un = false), (ba = Sa(e, t)), - (Oi = false), - (da = tt = pi = Bn = ln = de = 0), + (pi = false), + (da = tt = Oi = Bn = ln = de = 0), (Qe = cc = null), (yi = false), (t & 8) !== 0 && (t |= t & 32); @@ -9790,14 +9790,14 @@ Error generating stack: ` + $e.current === null && (t = true); var h = ne; if ( - ((ne = 0), (et = null), Oa(e, s, b, h), n && ba) + ((ne = 0), (et = null), pa(e, s, b, h), n && ba) ) { o = 0; break e; } break; default: - (h = ne), (ne = 0), (et = null), Oa(e, s, b, h); + (h = ne), (ne = 0), (et = null), pa(e, s, b, h); } } Rb(), (o = de); @@ -9834,7 +9834,7 @@ Error generating stack: ` + var r = et; t: switch (ne) { case 1: - (ne = 0), (et = null), Oa(e, t, r, 1); + (ne = 0), (et = null), pa(e, t, r, 1); break; case 2: case 9: @@ -9857,7 +9857,7 @@ Error generating stack: ` + case 7: of(r) ? ((ne = 0), (et = null), n_(t)) - : ((ne = 0), (et = null), Oa(e, t, r, 7)); + : ((ne = 0), (et = null), pa(e, t, r, 7)); break; case 5: var o = null; @@ -9880,10 +9880,10 @@ Error generating stack: ` + break t; } } - (ne = 0), (et = null), Oa(e, t, r, 5); + (ne = 0), (et = null), pa(e, t, r, 5); break; case 6: - (ne = 0), (et = null), Oa(e, t, r, 6); + (ne = 0), (et = null), pa(e, t, r, 6); break; case 8: vi(), (de = 6); @@ -9931,7 +9931,7 @@ Error generating stack: ` + } (e.memoizedProps = e.pendingProps), t === null ? du(e) : (Z = t); } - function Oa(e, t, n, u) { + function pa(e, t, n, u) { (Ct = Tn = null), Ll(t), (la = null), (Ya = 0); var l = t.return; try { @@ -10022,7 +10022,7 @@ Error generating stack: ` + (t.subtreeFlags & 10256) !== 0 || (t.flags & 10256) !== 0 ? ((e.callbackNode = null), (e.callbackPriority = 0), - Bb(Oc, function () { + Bb(pc, function () { return f_(), null; })) : ((e.callbackNode = null), (e.callbackPriority = 0)), @@ -10093,19 +10093,19 @@ Error generating stack: ` + !v.extend && L > ie && ((o = ie), (ie = L), (L = o)); - var O = Vr(s, L), + var p = Vr(s, L), g = Vr(s, ie); if ( - O && + p && g && (v.rangeCount !== 1 || - v.anchorNode !== O.node || - v.anchorOffset !== O.offset || + v.anchorNode !== p.node || + v.anchorOffset !== p.offset || v.focusNode !== g.node || v.focusOffset !== g.offset) ) { var j = M.createRange(); - j.setStart(O.node, O.offset), + j.setStart(p.node, p.offset), v.removeAllRanges(), L > ie ? (v.addRange(j), @@ -10291,7 +10291,7 @@ Error generating stack: ` + (l = u.get(t)), l === void 0 && ((l = /* @__PURE__ */ new Set()), u.set(t, l)); l.has(n) || - ((Oi = true), l.add(n), (e = Hb.bind(null, e, t, n)), t.then(e, e)); + ((pi = true), l.add(n), (e = Hb.bind(null, e, t, n)), t.then(e, e)); } function Hb(e, t, n) { var u = e.pingCache; @@ -10303,7 +10303,7 @@ Error generating stack: ` + (de === 4 || (de === 3 && (F & 62914560) === F && 300 > Fe() - fu) ? (ee & 2) === 0 && ma(e, 0) - : (pi |= n), + : (Oi |= n), da === F && (da = 0)), xt(e); } @@ -10339,20 +10339,20 @@ Error generating stack: ` + return Gu(e, t); } var mu = null, - pa = null, + Oa = null, Ei = false, - Ou = false, + pu = false, Ai = false, _n = 0; function xt(e) { - e !== pa && + e !== Oa && e.next === null && - (pa === null ? (mu = pa = e) : (pa = pa.next = e)), - (Ou = true), + (Oa === null ? (mu = Oa = e) : (Oa = Oa.next = e)), + (pu = true), Ei || ((Ei = true), Lb()); } function lc(e, t) { - if (!Ai && Ou) { + if (!Ai && pu) { Ai = true; do for (var n = false, u = mu; u !== null; ) { @@ -10391,7 +10391,7 @@ Error generating stack: ` + s_(); } function s_() { - Ou = Ei = false; + pu = Ei = false; var e = 0; _n !== 0 && Jb() && (e = _n); for (var t = Fe(), n = null, u = mu; u !== null; ) { @@ -10400,8 +10400,8 @@ Error generating stack: ` + r === 0 ? ((u.next = null), n === null ? (mu = l) : (n.next = l), - l === null && (pa = n)) - : ((n = u), (e !== 0 || (r & 3) !== 0) && (Ou = true)), + l === null && (Oa = n)) + : ((n = u), (e !== 0 || (r & 3) !== 0) && (pu = true)), (u = l); } (we !== 0 && we !== 5) || lc(e), _n !== 0 && (_n = 0); @@ -10449,13 +10449,13 @@ Error generating stack: ` + n = tr; break; case 32: - n = Oc; + n = pc; break; case 268435456: n = nr; break; default: - n = Oc; + n = pc; } return ( (u = d_.bind(null, e)), @@ -10506,7 +10506,7 @@ Error generating stack: ` + if (_n === 0) { var e = aa; e === 0 && - ((e = pc), (pc <<= 1), (pc & 261888) === 0 && (pc = 256)), + ((e = Oc), (Oc <<= 1), (Oc & 261888) === 0 && (Oc = 256)), (_n = e); } return _n; @@ -10518,7 +10518,7 @@ Error generating stack: ` + ? e : Sc('' + e); } - function O_(e, t) { + function p_(e, t) { var n = t.ownerDocument.createElement('input'); return ( (n.name = t.name), @@ -10548,7 +10548,7 @@ Error generating stack: ` + listener: function () { if (u.defaultPrevented) { if (_n !== 0) { - var b = o ? O_(l, o) : new FormData(l); + var b = o ? p_(l, o) : new FormData(l); Wl( n, { @@ -10564,7 +10564,7 @@ Error generating stack: ` + } else typeof r == 'function' && (s.preventDefault(), - (b = o ? O_(l, o) : new FormData(l)), + (b = o ? p_(l, o) : new FormData(l)), Wl( n, { @@ -10648,7 +10648,7 @@ Error generating stack: ` + .split(' ') .concat(ic), ); - function p_(e, t) { + function O_(e, t) { t = (t & 4) !== 0; for (var n = 0; n < e.length; n++) { var u = e[n], @@ -10705,27 +10705,27 @@ Error generating stack: ` + var u = 0; t && (u |= 4), y_(n, e, u, t); } - var pu = '_reactListening' + Math.random().toString(36).slice(2); + var Ou = '_reactListening' + Math.random().toString(36).slice(2); function Ci(e) { - if (!e[pu]) { - (e[pu] = true), + if (!e[Ou]) { + (e[Ou] = true), or.forEach(function (n) { n !== 'selectionchange' && (Xb.has(n) || Ri(n, false, e), Ri(n, true, e)); }); var t = e.nodeType === 9 ? e : e.ownerDocument; t === null || - t[pu] || - ((t[pu] = true), Ri('selectionchange', false, t)); + t[Ou] || + ((t[Ou] = true), Ri('selectionchange', false, t)); } } function y_(e, t, n, u) { switch (W_(t)) { case 2: - var l = Od; + var l = pd; break; case 8: - l = pd; + l = Od; break; default: l = Fi; @@ -10866,7 +10866,7 @@ Error generating stack: ` + } var L = (t & 4) !== 0, ie = !L && (e === 'scroll' || e === 'scrollend'), - O = L ? (w !== null ? w + 'Capture' : null) : w; + p = L ? (w !== null ? w + 'Capture' : null) : w; L = []; for (var g = h, j; g !== null; ) { var T = g; @@ -10875,8 +10875,8 @@ Error generating stack: ` + (T = T.tag), (T !== 5 && T !== 26 && T !== 27) || j === null || - O === null || - ((T = Ma(g, O)), + p === null || + ((T = Ma(g, p)), T != null && L.push(rc(g, T, j))), ie) ) @@ -10923,12 +10923,12 @@ Error generating stack: ` + if ( ((L = Sr), (T = 'onMouseLeave'), - (O = 'onMouseEnter'), + (p = 'onMouseEnter'), (g = 'mouse'), (e === 'pointerout' || e === 'pointerover') && ((L = Ar), (T = 'onPointerLeave'), - (O = 'onPointerEnter'), + (p = 'onPointerEnter'), (g = 'pointer')), (ie = v == null ? w : Ta(v)), (j = N == null ? w : Ta(N)), @@ -10937,7 +10937,7 @@ Error generating stack: ` + (w.relatedTarget = j), (T = null), qn(E) === h && - ((L = new L(O, g + 'enter', N, n, E)), + ((L = new L(p, g + 'enter', N, n, E)), (L.target = j), (L.relatedTarget = ie), (T = L)), @@ -10946,24 +10946,24 @@ Error generating stack: ` + ) t: { for ( - L = Qb, O = v, g = N, j = 0, T = O; + L = Qb, p = v, g = N, j = 0, T = p; T; T = L(T) ) j++; T = 0; for (var G = g; G; G = L(G)) T++; - for (; 0 < j - T; ) (O = L(O)), j--; + for (; 0 < j - T; ) (p = L(p)), j--; for (; 0 < T - j; ) (g = L(g)), T--; for (; j--; ) { if ( - O === g || - (g !== null && O === g.alternate) + p === g || + (g !== null && p === g.alternate) ) { - L = O; + L = p; break t; } - (O = L(O)), (g = L(g)); + (p = L(p)), (g = L(g)); } L = null; } @@ -11077,7 +11077,7 @@ Error generating stack: ` + (B.data = X))), qb(M, e, h, n, E); } - p_(M, t); + O_(M, t); }); } function rc(e, t, n) { @@ -11567,7 +11567,7 @@ Error generating stack: ` + default: le(e, t, o, s, n, null); } - pr(e, u, l, r); + Or(e, u, l, r); return; case 'option': for (b in n) @@ -11787,7 +11787,7 @@ Error generating stack: ` + default: l !== r && le(e, t, o, l, u, r); } - Or(e, w, v); + pr(e, w, v); return; case 'option': for (var N in n) @@ -11879,12 +11879,12 @@ Error generating stack: ` + return; } } - for (var O in n) - (w = n[O]), - n.hasOwnProperty(O) && + for (var p in n) + (w = n[p]), + n.hasOwnProperty(p) && w != null && - !u.hasOwnProperty(O) && - le(e, t, O, null, u, w); + !u.hasOwnProperty(p) && + le(e, t, p, null, u, w); for (M in u) (w = u[M]), (v = n[M]), @@ -13048,7 +13048,7 @@ Error generating stack: ` + } } var Au = true; - function Od(e, t, n, u) { + function pd(e, t, n, u) { var l = A.T; A.T = null; var r = R.p; @@ -13058,7 +13058,7 @@ Error generating stack: ` + (R.p = r), (A.T = l); } } - function pd(e, t, n, u) { + function Od(e, t, n, u) { var l = A.T; A.T = null; var r = R.p; @@ -13225,7 +13225,7 @@ Error generating stack: ` + return 2; case tr: return 8; - case Oc: + case pc: case us: return 32; case nr: @@ -13531,7 +13531,7 @@ Error generating stack: ` + ? Error(f(188)) : ((e = Object.keys(e).join(',')), Error(f(268, e))); return ( - (e = p(t)), + (e = O(t)), (e = e !== null ? z(e) : null), (e = e === null ? null : e.stateNode), e @@ -13784,14 +13784,14 @@ function ModalProvider({ children: a }) { reactExports.useEffect(() => { const f = gameEvents.on( 'modal:open', - ({ id: d, title: y, content: S, size: x, onClose: p }) => { + ({ id: d, title: y, content: S, size: x, onClose: O }) => { i({ type: 'OPEN', modal: { id: d ?? crypto.randomUUID(), title: y, content: S, - onClose: p, + onClose: O, closeOnOverlayClick: true, closeOnEscape: true, size: x ?? 'md', @@ -14519,7 +14519,7 @@ function __wbg_get_imports() { y, S, x, - p, + O, z, ) { getObject(a).blitFramebuffer( @@ -14531,7 +14531,7 @@ function __wbg_get_imports() { y, S, x, - p >>> 0, + O >>> 0, z >>> 0, ); }, @@ -14729,7 +14729,7 @@ function __wbg_get_imports() { y, S, x, - p, + O, ) { getObject(a).compressedTexSubImage2D( c >>> 0, @@ -14740,7 +14740,7 @@ function __wbg_get_imports() { y, S >>> 0, x, - p, + O, ); }, __wbg_compressedTexSubImage3D_77a6ab77487aa211: function ( @@ -14753,7 +14753,7 @@ function __wbg_get_imports() { y, S, x, - p, + O, z, H, ) { @@ -14766,7 +14766,7 @@ function __wbg_get_imports() { y, S, x, - p >>> 0, + O >>> 0, z, H, ); @@ -14781,7 +14781,7 @@ function __wbg_get_imports() { y, S, x, - p, + O, z, ) { getObject(a).compressedTexSubImage3D( @@ -14793,7 +14793,7 @@ function __wbg_get_imports() { y, S, x, - p >>> 0, + O >>> 0, getObject(z), ); }, @@ -14878,9 +14878,9 @@ function __wbg_get_imports() { y, S, x, - p, + O, ) { - getObject(a).copyTexSubImage3D(c >>> 0, i, f, _, d, y, S, x, p); + getObject(a).copyTexSubImage3D(c >>> 0, i, f, _, d, y, S, x, O); }, __wbg_copyTextureToBuffer_f5501895b13306e1: function () { return handleError(function (a, c, i, f) { @@ -15904,10 +15904,10 @@ function __wbg_get_imports() { return getObject(a).location; }, __wbg_log_0c201ade58bb55e1: function (a, c, i, f, _, d, y, S) { - let x, p; + let x, O; try { (x = a), - (p = c), + (O = c), console.log( getStringFromWasm0(a, c), getStringFromWasm0(i, f), @@ -15915,7 +15915,7 @@ function __wbg_get_imports() { getStringFromWasm0(y, S), ); } finally { - wasm.__wbindgen_export4(x, p, 1); + wasm.__wbindgen_export4(x, O, 1); } }, __wbg_log_70972330cfc941dd: function (a, c, i, f) { @@ -17309,7 +17309,7 @@ function __wbg_get_imports() { getObject(a).submit(getObject(c)); }, __wbg_texImage2D_32ed4220040ca614: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p) { + return handleError(function (a, c, i, f, _, d, y, S, x, O) { getObject(a).texImage2D( c >>> 0, i, @@ -17319,12 +17319,12 @@ function __wbg_get_imports() { y, S >>> 0, x >>> 0, - getObject(p), + getObject(O), ); }, arguments); }, __wbg_texImage2D_d8c284c813952313: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p) { + return handleError(function (a, c, i, f, _, d, y, S, x, O) { getObject(a).texImage2D( c >>> 0, i, @@ -17334,12 +17334,12 @@ function __wbg_get_imports() { y, S >>> 0, x >>> 0, - p, + O, ); }, arguments); }, __wbg_texImage2D_f4ae6c314a9a4bbe: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p) { + return handleError(function (a, c, i, f, _, d, y, S, x, O) { getObject(a).texImage2D( c >>> 0, i, @@ -17349,12 +17349,12 @@ function __wbg_get_imports() { y, S >>> 0, x >>> 0, - getObject(p), + getObject(O), ); }, arguments); }, __wbg_texImage3D_88ff1fa41be127b9: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p, z) { + return handleError(function (a, c, i, f, _, d, y, S, x, O, z) { getObject(a).texImage3D( c >>> 0, i, @@ -17364,13 +17364,13 @@ function __wbg_get_imports() { y, S, x >>> 0, - p >>> 0, + O >>> 0, getObject(z), ); }, arguments); }, __wbg_texImage3D_9a207e0459a4f276: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p, z) { + return handleError(function (a, c, i, f, _, d, y, S, x, O, z) { getObject(a).texImage3D( c >>> 0, i, @@ -17380,7 +17380,7 @@ function __wbg_get_imports() { y, S, x >>> 0, - p >>> 0, + O >>> 0, z, ); }, arguments); @@ -17398,7 +17398,7 @@ function __wbg_get_imports() { getObject(a).texStorage3D(c >>> 0, i, f >>> 0, _, d, y); }, __wbg_texSubImage2D_1e7d6febf82b9bed: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p) { + return handleError(function (a, c, i, f, _, d, y, S, x, O) { getObject(a).texSubImage2D( c >>> 0, i, @@ -17408,12 +17408,12 @@ function __wbg_get_imports() { y, S >>> 0, x >>> 0, - getObject(p), + getObject(O), ); }, arguments); }, __wbg_texSubImage2D_271ffedb47424d0d: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p) { + return handleError(function (a, c, i, f, _, d, y, S, x, O) { getObject(a).texSubImage2D( c >>> 0, i, @@ -17423,12 +17423,12 @@ function __wbg_get_imports() { y, S >>> 0, x >>> 0, - getObject(p), + getObject(O), ); }, arguments); }, __wbg_texSubImage2D_3bb41b987f2bfe39: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p) { + return handleError(function (a, c, i, f, _, d, y, S, x, O) { getObject(a).texSubImage2D( c >>> 0, i, @@ -17438,12 +17438,12 @@ function __wbg_get_imports() { y, S >>> 0, x >>> 0, - getObject(p), + getObject(O), ); }, arguments); }, __wbg_texSubImage2D_68e0413824eddc12: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p) { + return handleError(function (a, c, i, f, _, d, y, S, x, O) { getObject(a).texSubImage2D( c >>> 0, i, @@ -17453,12 +17453,12 @@ function __wbg_get_imports() { y, S >>> 0, x >>> 0, - getObject(p), + getObject(O), ); }, arguments); }, __wbg_texSubImage2D_b6cdbbe62097211a: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p) { + return handleError(function (a, c, i, f, _, d, y, S, x, O) { getObject(a).texSubImage2D( c >>> 0, i, @@ -17468,12 +17468,12 @@ function __wbg_get_imports() { y, S >>> 0, x >>> 0, - getObject(p), + getObject(O), ); }, arguments); }, __wbg_texSubImage2D_c8919d8f32f723da: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p) { + return handleError(function (a, c, i, f, _, d, y, S, x, O) { getObject(a).texSubImage2D( c >>> 0, i, @@ -17483,12 +17483,12 @@ function __wbg_get_imports() { y, S >>> 0, x >>> 0, - getObject(p), + getObject(O), ); }, arguments); }, __wbg_texSubImage2D_d784df0b813dc1ab: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p) { + return handleError(function (a, c, i, f, _, d, y, S, x, O) { getObject(a).texSubImage2D( c >>> 0, i, @@ -17498,12 +17498,12 @@ function __wbg_get_imports() { y, S >>> 0, x >>> 0, - p, + O, ); }, arguments); }, __wbg_texSubImage2D_dd1d50234b61de4b: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p) { + return handleError(function (a, c, i, f, _, d, y, S, x, O) { getObject(a).texSubImage2D( c >>> 0, i, @@ -17513,12 +17513,12 @@ function __wbg_get_imports() { y, S >>> 0, x >>> 0, - getObject(p), + getObject(O), ); }, arguments); }, __wbg_texSubImage3D_09cc863aedf44a21: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p, z, H) { + return handleError(function (a, c, i, f, _, d, y, S, x, O, z, H) { getObject(a).texSubImage3D( c >>> 0, i, @@ -17528,14 +17528,14 @@ function __wbg_get_imports() { y, S, x, - p >>> 0, + O >>> 0, z >>> 0, getObject(H), ); }, arguments); }, __wbg_texSubImage3D_4665e67a8f0f7806: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p, z, H) { + return handleError(function (a, c, i, f, _, d, y, S, x, O, z, H) { getObject(a).texSubImage3D( c >>> 0, i, @@ -17545,14 +17545,14 @@ function __wbg_get_imports() { y, S, x, - p >>> 0, + O >>> 0, z >>> 0, getObject(H), ); }, arguments); }, __wbg_texSubImage3D_61ed187f3ec11ecc: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p, z, H) { + return handleError(function (a, c, i, f, _, d, y, S, x, O, z, H) { getObject(a).texSubImage3D( c >>> 0, i, @@ -17562,14 +17562,14 @@ function __wbg_get_imports() { y, S, x, - p >>> 0, + O >>> 0, z >>> 0, getObject(H), ); }, arguments); }, __wbg_texSubImage3D_6a46981af8bc8e49: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p, z, H) { + return handleError(function (a, c, i, f, _, d, y, S, x, O, z, H) { getObject(a).texSubImage3D( c >>> 0, i, @@ -17579,14 +17579,14 @@ function __wbg_get_imports() { y, S, x, - p >>> 0, + O >>> 0, z >>> 0, getObject(H), ); }, arguments); }, __wbg_texSubImage3D_9eca35d234d51b8a: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p, z, H) { + return handleError(function (a, c, i, f, _, d, y, S, x, O, z, H) { getObject(a).texSubImage3D( c >>> 0, i, @@ -17596,14 +17596,14 @@ function __wbg_get_imports() { y, S, x, - p >>> 0, + O >>> 0, z >>> 0, getObject(H), ); }, arguments); }, __wbg_texSubImage3D_b3cbbb79fe54da6d: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p, z, H) { + return handleError(function (a, c, i, f, _, d, y, S, x, O, z, H) { getObject(a).texSubImage3D( c >>> 0, i, @@ -17613,14 +17613,14 @@ function __wbg_get_imports() { y, S, x, - p >>> 0, + O >>> 0, z >>> 0, H, ); }, arguments); }, __wbg_texSubImage3D_f9c3af789162846a: function () { - return handleError(function (a, c, i, f, _, d, y, S, x, p, z, H) { + return handleError(function (a, c, i, f, _, d, y, S, x, O, z, H) { getObject(a).texSubImage3D( c >>> 0, i, @@ -17630,7 +17630,7 @@ function __wbg_get_imports() { y, S, x, - p >>> 0, + O >>> 0, z >>> 0, getObject(H), ); @@ -18890,10 +18890,10 @@ function useInventory() { const y = /* @__PURE__ */ new Map(); for (const x of d.items) y.set(x.kind, x.quantity); const S = i.current; - for (const [x, p] of y) { + for (const [x, O] of y) { const z = S.get(x) ?? 0; - if (p > z) { - const H = p - z, + if (O > z) { + const H = O - z, J = ITEM_NAMES[x] ?? x; gameEvents.emit('toast:show', { message: `+${H} ${J}`, @@ -19092,10 +19092,10 @@ function worldToScreen(a, c, i, f) { y = i / f, S = d * y, x = dot(_, AXES.right) / S, - p = dot(_, AXES.up) / d; - return Math.abs(x) > 1.2 || Math.abs(p) > 1.2 + O = dot(_, AXES.up) / d; + return Math.abs(x) > 1.2 || Math.abs(O) > 1.2 ? null - : { x: ((x + 1) / 2) * i, y: ((1 - p) / 2) * f }; + : { x: ((x + 1) / 2) * i, y: ((1 - O) / 2) * f }; } const OBJECT_NAMES = { tree: 'Tree', @@ -19155,13 +19155,13 @@ function ObjectLabel() { y.position[2] + CAMERA_OFFSET[2], ], x = [_.position[0], _.position[1] + 1.5, _.position[2]], - p = worldToScreen( + O = worldToScreen( x, S, window.innerWidth, window.innerHeight, ); - if (!p) { + if (!O) { c(null); return; } @@ -19175,7 +19175,7 @@ function ObjectLabel() { _.kind === 'mushroom' && _.sub_kind && (z = MUSHROOM_NAMES[_.sub_kind] ?? z), - c({ name: z, screenX: p.x, screenY: p.y }); + c({ name: z, screenX: O.x, screenY: O.y }); } catch { c(null); } @@ -19486,25 +19486,51 @@ function App() { }) ); } +function supportsDecompression(a) { + try { + return new DecompressionStream(a), true; + } catch { + return false; + } +} +async function fetchCompressedWasm(a) { + const c = []; + supportsDecompression('brotli') && + c.push({ ext: '.wasm.br', encoding: 'brotli' }), + c.push({ ext: '.wasm.gz', encoding: 'gzip' }); + for (const { ext: i, encoding: f } of c) + try { + const _ = await fetch(new URL('isometric_game_bg' + i, a)); + if (!_.ok) continue; + const d = _.body.pipeThrough(new DecompressionStream(f)), + y = await new Response(d).arrayBuffer(); + return WebAssembly.compile(y); + } catch { + continue; + } + throw new Error('Failed to load compressed WASM'); +} async function bootstrap() { if (!navigator.gpu) { - const c = document.getElementById('root'); - c && - ((c.innerHTML = + const f = document.getElementById('root'); + f && + ((f.innerHTML = '

WebGPU Not Available

This browser does not support WebGPU (Chrome 113+, Edge 113+, Safari 18+).

'), - (c.style.pointerEvents = 'auto')); + (f.style.pointerEvents = 'auto')); return; } const { default: a } = await __vitePreload( - async () => { - const { default: c } = await Promise.resolve().then( - () => isometric_game, - ); - return { default: c }; - }, - void 0, - ); - await a(), + async () => { + const { default: f } = await Promise.resolve().then( + () => isometric_game, + ); + return { default: f }; + }, + void 0, + ), + c = new URL('/isometric/assets/', window.location.origin), + i = await fetchCompressedWasm(c); + await a(i), ReactDOM.createRoot(document.getElementById('root')).render( jsxRuntimeExports.jsx(React.StrictMode, { children: jsxRuntimeExports.jsx(GameUIProvider, { diff --git a/apps/kbve/astro-kbve/public/isometric/assets/isometric_game_bg.wasm b/apps/kbve/astro-kbve/public/isometric/assets/isometric_game_bg.wasm deleted file mode 100644 index 351e5a237..000000000 Binary files a/apps/kbve/astro-kbve/public/isometric/assets/isometric_game_bg.wasm and /dev/null differ diff --git a/apps/kbve/astro-kbve/public/isometric/assets/isometric_game_bg.wasm.br b/apps/kbve/astro-kbve/public/isometric/assets/isometric_game_bg.wasm.br new file mode 100644 index 000000000..2cf1f92dd Binary files /dev/null and b/apps/kbve/astro-kbve/public/isometric/assets/isometric_game_bg.wasm.br differ diff --git a/apps/kbve/astro-kbve/public/isometric/assets/isometric_game_bg.wasm.gz b/apps/kbve/astro-kbve/public/isometric/assets/isometric_game_bg.wasm.gz new file mode 100644 index 000000000..ffb8950c6 Binary files /dev/null and b/apps/kbve/astro-kbve/public/isometric/assets/isometric_game_bg.wasm.gz differ diff --git a/apps/kbve/axum-kbve/Cargo.toml b/apps/kbve/axum-kbve/Cargo.toml index 2508baaae..fd7cc3c00 100644 --- a/apps/kbve/axum-kbve/Cargo.toml +++ b/apps/kbve/axum-kbve/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "axum-kbve" authors = ["kbve", "h0lybyte"] -version = "1.0.39" +version = "1.0.40" edition = "2021" publish = false diff --git a/apps/kbve/axum-kbve/src/transport/https.rs b/apps/kbve/axum-kbve/src/transport/https.rs index 368b75c81..9a013f84d 100644 --- a/apps/kbve/axum-kbve/src/transport/https.rs +++ b/apps/kbve/axum-kbve/src/transport/https.rs @@ -39,6 +39,16 @@ pub struct AppStateInner { } impl AppState { + pub fn new() -> Self { + Self { + inner: Arc::new(AppStateInner { + start_time: std::time::Instant::now(), + version: env!("CARGO_PKG_VERSION"), + game: None, + }), + } + } + pub fn new_with_gameserver(game: crate::gameserver::GameServerState) -> Self { Self { inner: Arc::new(AppStateInner { diff --git a/apps/kbve/isometric/package.json b/apps/kbve/isometric/package.json index 91b875101..0ad8f8316 100644 --- a/apps/kbve/isometric/package.json +++ b/apps/kbve/isometric/package.json @@ -23,7 +23,8 @@ "tailwindcss": "^4.1.3", "typescript": "^5.0.0", "vite": "^6.0.0", - "vite-plugin-wasm": "^3.3.0", - "vite-plugin-top-level-await": "^1.4.4" + "vite-plugin-compression2": "^2.5.0", + "vite-plugin-top-level-await": "^1.4.4", + "vite-plugin-wasm": "^3.3.0" } } diff --git a/apps/kbve/isometric/src-tauri/Cargo.toml b/apps/kbve/isometric/src-tauri/Cargo.toml index 418259d94..d54342e11 100644 --- a/apps/kbve/isometric/src-tauri/Cargo.toml +++ b/apps/kbve/isometric/src-tauri/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [package.metadata.wasm-pack.profile.release] -wasm-opt = false +wasm-opt = ["-Oz", "--enable-bulk-memory", "--enable-nontrapping-float-to-int", "--enable-sign-ext", "--enable-mutable-globals"] [package.metadata.wasm-pack.profile.dev] wasm-opt = false @@ -66,7 +66,7 @@ features = ["webgpu"] # --- WASM-only dependencies --- [target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen = "0.2" +wasm-bindgen = "=0.2.114" wasm-bindgen-futures = "0.4" web-sys = { version = "0.3", features = ["Document", "Window", "HtmlCanvasElement"] } console_error_panic_hook = "0.1" diff --git a/apps/kbve/isometric/src/main.tsx b/apps/kbve/isometric/src/main.tsx index 0bb70c48f..b0eff7058 100644 --- a/apps/kbve/isometric/src/main.tsx +++ b/apps/kbve/isometric/src/main.tsx @@ -4,6 +4,40 @@ import './app.css'; import { GameUIProvider } from './ui/provider/GameUIProvider'; import App from './App'; +function supportsDecompression(encoding: CompressionFormat | string): boolean { + try { + new DecompressionStream(encoding as CompressionFormat); + return true; + } catch { + return false; + } +} + +async function fetchCompressedWasm(base: URL): Promise { + const variants: Array<{ + ext: string; + encoding: CompressionFormat | string; + }> = []; + if (supportsDecompression('brotli')) + variants.push({ ext: '.wasm.br', encoding: 'brotli' }); + variants.push({ ext: '.wasm.gz', encoding: 'gzip' }); + + for (const { ext, encoding } of variants) { + try { + const res = await fetch(new URL('isometric_game_bg' + ext, base)); + if (!res.ok) continue; + const decompressed = res.body!.pipeThrough( + new DecompressionStream(encoding as CompressionFormat), + ); + const bytes = await new Response(decompressed).arrayBuffer(); + return WebAssembly.compile(bytes); + } catch { + continue; + } + } + throw new Error('Failed to load compressed WASM'); +} + async function bootstrap() { // Verify WebGPU is available before loading the WASM game if (!(navigator as unknown as { gpu?: unknown }).gpu) { @@ -21,7 +55,11 @@ async function bootstrap() { // Load and initialize Bevy WASM — starts the game loop (non-blocking) const { default: init } = await import('../wasm-pkg/isometric_game.js'); - await init(); + + // Fetch pre-compressed WASM and decompress client-side (brotli → gzip fallback) + const wasmBase = new URL('/isometric/assets/', window.location.origin); + const wasmBytes = await fetchCompressedWasm(wasmBase); + await init(wasmBytes); // Render React UI overlay ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( diff --git a/apps/kbve/isometric/vite.config.ts b/apps/kbve/isometric/vite.config.ts index 66de39b44..fefcc614d 100644 --- a/apps/kbve/isometric/vite.config.ts +++ b/apps/kbve/isometric/vite.config.ts @@ -3,11 +3,27 @@ import react from '@vitejs/plugin-react'; import tailwindcss from '@tailwindcss/vite'; import wasm from 'vite-plugin-wasm'; import topLevelAwait from 'vite-plugin-top-level-await'; +import { compression } from 'vite-plugin-compression2'; const host = process.env.TAURI_DEV_HOST; export default defineConfig(async () => ({ - plugins: [react(), tailwindcss(), wasm(), topLevelAwait()], + plugins: [ + react(), + tailwindcss(), + wasm(), + topLevelAwait(), + compression({ + include: /\.wasm$/i, + algorithm: 'brotliCompress', + deleteOriginalAssets: false, + }), + compression({ + include: /\.wasm$/i, + algorithm: 'gzip', + deleteOriginalAssets: true, + }), + ], base: '/isometric/', clearScreen: false, build: { diff --git a/packages/rust/bevy/bevy_kbve_camera/Cargo.toml b/packages/rust/bevy/bevy_kbve_camera/Cargo.toml new file mode 100644 index 000000000..0821b7e72 --- /dev/null +++ b/packages/rust/bevy/bevy_kbve_camera/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "bevy_kbve_camera" +authors = ["kbve", "h0lybyte"] +version = "0.1.0" +edition = "2021" +license = "MIT" +description = "Bevy isometric camera plugin with pixel snapping, smooth zoom, and two-stage render-to-texture pipeline." +homepage = "https://kbve.com/" +repository = "https://github.com/KBVE/kbve/tree/main/packages/rust/bevy/bevy_kbve_camera" + +[dependencies] +bevy = { version = "0.18", default-features = false, features = [ + "bevy_state", + "bevy_asset", + "bevy_render", + "bevy_core_pipeline", + "bevy_pbr", + "bevy_winit", +] } diff --git a/packages/rust/bevy/bevy_kbve_camera/src/lib.rs b/packages/rust/bevy/bevy_kbve_camera/src/lib.rs new file mode 100644 index 000000000..dfc9121cb --- /dev/null +++ b/packages/rust/bevy/bevy_kbve_camera/src/lib.rs @@ -0,0 +1,347 @@ +//! # bevy_kbve_camera +//! +//! Isometric camera plugin for Bevy with pixel snapping, smooth zoom, and +//! a two-stage render-to-texture pipeline for crisp pixel-art rendering. +//! +//! ## Usage +//! +//! ```ignore +//! use bevy_kbve_camera::{IsometricCameraPlugin, CameraConfig}; +//! +//! app.add_plugins(IsometricCameraPlugin::new(CameraConfig { +//! offset: Vec3::new(15.0, 20.0, 15.0), +//! viewport_height: 20.0, +//! pixel_density: 32, +//! ..default() +//! })); +//! ``` + +use bevy::camera::ScalingMode; +use bevy::camera::visibility::RenderLayers; +use bevy::core_pipeline::tonemapping::Tonemapping; +use bevy::ecs::message::MessageReader; +use bevy::image::ImageSampler; +use bevy::input::mouse::MouseWheel; +use bevy::prelude::*; +use bevy::render::render_resource::TextureFormat; +use bevy::window::PrimaryWindow; + +// ── Configuration ─────────────────────────────────────────────────────── + +/// Camera configuration — set once at plugin creation. +#[derive(Resource, Debug, Clone)] +pub struct CameraConfig { + /// Offset from the follow target to the camera position. + pub offset: Vec3, + /// Orthographic viewport height in world units. + pub viewport_height: f32, + /// Render pixels per world unit (controls pixel-art resolution). + pub pixel_density: u32, + /// RenderLayer index for the display quad (must differ from scene layer 0). + pub display_layer: usize, + /// Minimum zoom level (< 1.0 = zoomed in). + pub zoom_min: f32, + /// Maximum zoom level (> 1.0 = zoomed out). + pub zoom_max: f32, + /// Zoom speed multiplier per scroll tick. + pub zoom_speed: f32, + /// Zoom interpolation smoothing factor. + pub zoom_smoothing: f32, +} + +impl Default for CameraConfig { + fn default() -> Self { + Self { + offset: Vec3::new(15.0, 20.0, 15.0), + viewport_height: 20.0, + pixel_density: 32, + display_layer: 1, + zoom_min: 0.5, + zoom_max: 2.0, + zoom_speed: 0.10, + zoom_smoothing: 8.0, + } + } +} + +// ── Components ────────────────────────────────────────────────────────── + +/// Marker for the scene camera that renders the 3D world. +#[derive(Component)] +pub struct IsometricCamera; + +/// Marker for the follow target entity. Attach this to the player or +/// any entity the camera should track. +#[derive(Component)] +pub struct CameraFollowTarget; + +/// Marker for the display quad. +#[derive(Component)] +struct DisplayQuad; + +// ── Resources ─────────────────────────────────────────────────────────── + +/// Runtime zoom state. +#[derive(Resource)] +pub struct CameraZoom { + pub target: f32, + pub current: f32, +} + +impl Default for CameraZoom { + fn default() -> Self { + Self { + target: 1.0, + current: 1.0, + } + } +} + +/// Precomputed stable camera axes to avoid frame-to-frame drift from +/// reading the mutated transform (causes pixel jitter). +struct StableCameraAxes { + right: Vec3, + up: Vec3, + forward: Vec3, +} + +impl StableCameraAxes { + fn from_offset(offset: Vec3) -> Self { + let tf = Transform::from_translation(offset).looking_at(Vec3::ZERO, Vec3::Y); + Self { + right: tf.right().as_vec3(), + up: tf.up().as_vec3(), + forward: tf.forward().as_vec3(), + } + } +} + +// ── System set ────────────────────────────────────────────────────────── + +/// System set for camera systems — use for ordering constraints. +#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] +pub struct CameraUpdate; + +// ── Plugin ────────────────────────────────────────────────────────────── + +pub struct IsometricCameraPlugin { + config: CameraConfig, +} + +impl IsometricCameraPlugin { + pub fn new(config: CameraConfig) -> Self { + Self { config } + } +} + +impl Default for IsometricCameraPlugin { + fn default() -> Self { + Self { + config: CameraConfig::default(), + } + } +} + +impl Plugin for IsometricCameraPlugin { + fn build(&self, app: &mut App) { + let axes = StableCameraAxes::from_offset(self.config.offset); + + app.insert_resource(self.config.clone()); + app.init_resource::(); + app.insert_resource(StableAxesResource { + right: axes.right, + up: axes.up, + forward: axes.forward, + }); + app.add_systems(Startup, setup_camera); + app.add_systems(Update, handle_zoom_input); + app.add_systems( + PostUpdate, + (camera_follow_target, apply_camera_zoom) + .chain() + .in_set(CameraUpdate), + ); + } +} + +/// Internal resource holding precomputed axes. +#[derive(Resource)] +struct StableAxesResource { + right: Vec3, + up: Vec3, + forward: Vec3, +} + +// ── Systems ───────────────────────────────────────────────────────────── + +fn setup_camera( + mut commands: Commands, + mut images: ResMut>, + mut meshes: ResMut>, + mut materials: ResMut>, + windows: Query<&Window, With>, + config: Res, +) { + let Ok(window) = windows.single() else { return }; + let aspect = window.width() / window.height(); + + let render_h = (config.viewport_height * config.pixel_density as f32) as u32; + let render_w = (render_h as f32 * aspect) as u32; + + // Low-res render target with nearest-neighbor sampling + let mut render_img = + Image::new_target_texture(render_w, render_h, TextureFormat::Bgra8UnormSrgb, None); + render_img.sampler = ImageSampler::nearest(); + let render_handle = images.add(render_img); + + // Stage 1: Scene camera → low-res texture + commands.spawn(( + Camera3d::default(), + Camera { + order: -1, + ..default() + }, + Msaa::Off, + Tonemapping::None, + bevy::camera::RenderTarget::Image(render_handle.clone().into()), + Projection::from(OrthographicProjection { + scaling_mode: ScalingMode::FixedVertical { + viewport_height: config.viewport_height, + }, + ..OrthographicProjection::default_3d() + }), + Transform::from_translation(config.offset).looking_at(Vec3::ZERO, Vec3::Y), + IsometricCamera, + )); + + // Stage 2: Display camera → window + commands.spawn(( + Camera3d::default(), + Camera { + order: 0, + ..default() + }, + Msaa::Off, + Tonemapping::None, + Projection::from(OrthographicProjection { + scaling_mode: ScalingMode::FixedVertical { + viewport_height: 1.0, + }, + ..OrthographicProjection::default_3d() + }), + Transform::from_xyz(0.0, 0.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y), + RenderLayers::layer(config.display_layer), + )); + + // Fullscreen quad with render texture (slightly oversized to avoid edge artifacts) + let texel_pad = 2.0 / render_h as f32; + let quad_material = materials.add(StandardMaterial { + base_color_texture: Some(render_handle), + unlit: true, + ..default() + }); + commands.spawn(( + Mesh3d(meshes.add(Rectangle::new(aspect + texel_pad * aspect, 1.0 + texel_pad))), + MeshMaterial3d(quad_material), + Transform::default(), + RenderLayers::layer(config.display_layer), + DisplayQuad, + )); +} + +fn camera_follow_target( + target_query: Query<&Transform, (With, Without)>, + mut camera_query: Query<&mut Transform, (With, Without)>, + config: Res, + axes: Res, +) { + let Ok(target_tf) = target_query.single() else { + return; + }; + let Ok(mut camera_tf) = camera_query.single_mut() else { + return; + }; + + let desired = target_tf.translation + config.offset; + let pixel_step = 1.0 / config.pixel_density as f32; + + // Snap to pixel grid on all axes + let right_proj = desired.dot(axes.right); + let up_proj = desired.dot(axes.up); + let forward_proj = desired.dot(axes.forward); + + let snapped_right = (right_proj / pixel_step).round() * pixel_step; + let snapped_up = (up_proj / pixel_step).round() * pixel_step; + let snapped_forward = (forward_proj / pixel_step).round() * pixel_step; + + camera_tf.translation = + snapped_right * axes.right + snapped_up * axes.up + snapped_forward * axes.forward; +} + +fn handle_zoom_input( + mut scroll_evr: MessageReader, + mut zoom: ResMut, + config: Res, +) { + for ev in scroll_evr.read() { + zoom.target = + (zoom.target - ev.y * config.zoom_speed).clamp(config.zoom_min, config.zoom_max); + } +} + +fn apply_camera_zoom( + time: Res