From ff5a0a675de9bea17ff44b21a58cc123c12140f1 Mon Sep 17 00:00:00 2001 From: Henning Berge Date: Fri, 24 Jan 2025 22:50:09 +0100 Subject: [PATCH 1/3] add items configs for mining/wc, and make use of them --- data/config/items/equipment/amulets.json | 54 ++++++++ data/config/items/logs.json | 7 ++ data/config/items/quests/lost-city.json | 9 ++ data/config/items/skills/crafting/gems.json | 44 +++++-- data/config/items/skills/mining.json | 119 +++++++++++++++--- src/engine/world/config/harvestable-object.ts | 104 ++++++++++----- src/engine/world/skill-util/glory-boost.ts | 21 +++- src/engine/world/skill-util/harvest-roll.ts | 60 +++++---- src/engine/world/skill-util/harvest-skill.ts | 18 ++- src/plugins/skills/mining/mining-task.ts | 56 +++++++-- .../skills/woodcutting/woodcutting-task.ts | 36 +++++- 11 files changed, 411 insertions(+), 117 deletions(-) create mode 100644 data/config/items/equipment/amulets.json create mode 100644 data/config/items/quests/lost-city.json diff --git a/data/config/items/equipment/amulets.json b/data/config/items/equipment/amulets.json new file mode 100644 index 000000000..c423a1402 --- /dev/null +++ b/data/config/items/equipment/amulets.json @@ -0,0 +1,54 @@ +{ + "rs:amulet_of_glory": { + "game_id": 1704, + "examine": "A very powerful dragonstone amulet.", + "tradable": true, + "weight": 0.01, + "equippable": true, + "equipment_data": { + "equipment_slot": "neck", + "offensive_bonuses": { + "stab": 10, + "slash": 10, + "crush": 10, + "magic": 10, + "ranged": 10 + }, + "defensive_bonuses": { + "stab": 3, + "slash": 3, + "crush": 3, + "magic": 3, + "ranged": 3 + }, + "skill_bonuses": { + "prayer": 3, + "strength": 6, + "ranged": 0, + "magic": 0 + } + }, + "variations": [ + { + "game_id": 1706, + "examine": "A dragonstone amulet with 1 magic charge.", + "suffix": "charged_1" + }, + { + "game_id": 1708, + "examine": "A dragonstone amulet with 2 magic charges.", + "suffix": "charged_2" + }, + { + "game_id": 1710, + "examine": "A dragonstone amulet with 3 magic charges.", + "suffix": "charged_3" + }, + { + "game_id": 1712, + "examine": "A dragonstone amulet with 4 magic charges.", + "suffix": "charged_4" + } + ] + } +} diff --git a/data/config/items/logs.json b/data/config/items/logs.json index 96c86189a..242b814df 100644 --- a/data/config/items/logs.json +++ b/data/config/items/logs.json @@ -73,5 +73,12 @@ "rs:magic_pyre_logs": { "extends": "rs:log", "game_id": 1513 + }, + "rs:bark": { + "game_id": 3239, + "examine": "Bark from a hollow tree.", + "tradable": true, + "weight": 1, + "equippable": false } } diff --git a/data/config/items/quests/lost-city.json b/data/config/items/quests/lost-city.json new file mode 100644 index 000000000..496cf1f0c --- /dev/null +++ b/data/config/items/quests/lost-city.json @@ -0,0 +1,9 @@ +{ + "rs:dramen_branch": { + "game_id": 771, + "examine": "A limb of the fabled Dramen tree.", + "tradable": false, + "weight": 2.267, + "equippable": false + } +} diff --git a/data/config/items/skills/crafting/gems.json b/data/config/items/skills/crafting/gems.json index c2fb39b54..cfc9f3e80 100644 --- a/data/config/items/skills/crafting/gems.json +++ b/data/config/items/skills/crafting/gems.json @@ -3,15 +3,28 @@ "rs:uncut_gem_base": { "examine": "This would be worth more cut.", "weight": 0.003, - "tradable": true + "tradable": true, + "equippable": false }, - "rs:precious_gem_base": { - "examine": "This looks valuable.", + "rs:cut_gem_base": { + "examine": "A precious gem.", "weight": 0.002, - "tradable": true + "tradable": true, + "equippable": false } }, - + "rs:uncut_opal": { + "extends": "rs:uncut_gem_base", + "game_id": 1625 + }, + "rs:uncut_jade": { + "extends": "rs:uncut_gem_base", + "game_id": 1627 + }, + "rs:uncut_red_topaz": { + "extends": "rs:uncut_gem_base", + "game_id": 1629 + }, "rs:uncut_sapphire": { "extends": "rs:uncut_gem_base", "game_id": 1623 @@ -28,21 +41,32 @@ "extends": "rs:uncut_gem_base", "game_id": 1617 }, - + "rs:opal": { + "extends": "rs:cut_gem_base", + "game_id": 1609 + }, + "rs:jade": { + "extends": "rs:cut_gem_base", + "game_id": 1611 + }, + "rs:red_topaz": { + "extends": "rs:cut_gem_base", + "game_id": 1613 + }, "rs:sapphire": { - "extends": "rs:precious_gem_base", + "extends": "rs:cut_gem_base", "game_id": 1607 }, "rs:emerald": { - "extends": "rs:precious_gem_base", + "extends": "rs:cut_gem_base", "game_id": 1605 }, "rs:ruby": { - "extends": "rs:precious_gem_base", + "extends": "rs:cut_gem_base", "game_id": 1603 }, "rs:diamond": { - "extends": "rs:precious_gem_base", + "extends": "rs:cut_gem_base", "game_id": 1601 } } diff --git a/data/config/items/skills/mining.json b/data/config/items/skills/mining.json index fd116b668..afccc0a75 100644 --- a/data/config/items/skills/mining.json +++ b/data/config/items/skills/mining.json @@ -1,47 +1,128 @@ { "rs:rune_essence": { - "game_id": 1436 + "game_id": 1436, + "examine": "An unimbued rune.", + "tradable": true, + "weight": 0.002, + "equippable": false }, "rs:clay": { - "game_id": 434 + "game_id": 434, + "examine": "Some hard dry clay.", + "tradable": true, + "weight": 1, + "equippable": false }, "rs:copper_ore": { - "game_id": 436 + "game_id": 436, + "examine": "This needs refining.", + "tradable": true, + "weight": 2.267, + "equippable": false }, "rs:tin_ore": { - "game_id": 438 + "game_id": 438, + "examine": "This needs refining.", + "tradable": true, + "weight": 2.267, + "equippable": false }, "rs:blurite_ore": { - "game_id": 668 + "game_id": 668, + "examine": "Definitely blue.", + "tradable": false, + "weight": 2.267, + "equippable": false }, "rs:limestone": { - "game_id": 3211 + "game_id": 3211, + "examine": "Some limestone.", + "tradable": true, + "weight": 2.267, + "equippable": false }, "rs:iron_ore": { - "game_id": 440 + "game_id": 440, + "examine": "This needs refining.", + "tradable": true, + "weight": 2.267, + "equippable": false }, "rs:silver_ore": { - "game_id": 442 + "game_id": 442, + "examine": "This needs refining.", + "tradable": true, + "weight": 2.267, + "equippable": false }, - "rs:coal": { - "game_id": 453 + "rs:coal":{ + "game_id": 453, + "examine": "Hmm a non-renewable energy source!", + "tradable": true, + "weight": 2.267, + "equippable": false }, "rs:gold_ore": { - "game_id": 444 - }, - "rs:granite": { - "game_id": 6983 + "game_id": 444, + "examine": "This needs refining.", + "tradable": true, + "weight": 2.267, + "equippable": false }, "rs:mithril_ore": { - "game_id": 447 + "game_id": 447, + "examine": "This needs refining.", + "tradable": true, + "weight": 1.814, + "equippable": false }, "rs:adamantite_ore": { - "game_id": 1761 + "game_id": 449, + "examine": "This needs refining.", + "tradable": true, + "weight": 2.721, + "equippable": false }, - "rs:soft_clay": { - "game_id": 1761 + "rs:soft_clay":{ + "game_id": 1761, + "examine": "Clay soft enough to mould.", + "tradable": true, + "weight": 0.907, + "equippable": false }, "rs:runite_ore": { - "game_id": 451 + "game_id": 451, + "examine": "This needs refining.", + "tradable": true, + "weight": 2.267, + "equippable": false + }, + "rs:pure_essence": { + "game_id": 7936, + "examine": "An unimbued rune of extra capability.", + "tradable": true, + "weight": 0.002, + "equippable": false + }, + "rs:granite": { + "game_id": 6979, + "examine": "A tiny chunk of granite.", + "tradable": true, + "weight": 0.5, + "equippable": false, + "variations": [ + { + "game_id": 6981, + "examine": "A small chunk of granite.", + "weight": 2, + "suffix": "2kg" + }, + { + "game_id": 6983, + "examine": "A medium-sized chunk of granite.", + "weight": 5, + "suffix": "5kg" + } + ] } } diff --git a/src/engine/world/config/harvestable-object.ts b/src/engine/world/config/harvestable-object.ts index f936432d2..15c42cadf 100644 --- a/src/engine/world/config/harvestable-object.ts +++ b/src/engine/world/config/harvestable-object.ts @@ -1,9 +1,14 @@ import { objectIds } from '@engine/world/config/object-ids'; -import { itemIds } from './item-ids'; +import { randomBetween } from "@engine/util"; + +interface WeightedItem { + itemConfigId: string; + weight: number; +} export interface IHarvestable { objects: Map; - itemId: number; + items: string | WeightedItem[]; level: number; experience: number; respawnLow: number; @@ -131,13 +136,29 @@ export enum Tree { MAHOGANY, YEW, MAGIC, + HOLLOW, + DRAMEN, } +export function selectWeightedItem(items: WeightedItem[]): string { + const totalWeight = items.reduce((sum, item) => sum + item.weight, 0); + let random = randomBetween(1, totalWeight); + + for (const item of items) { + random -= item.weight; + if (random <= 0) { + return item.itemConfigId; + } + } + + return items[0].itemConfigId; // Fallback to first item +} + const Ores: IHarvestable[] = [ { objects: CLAY_OBJECTS, - itemId: itemIds.ores.clay, + items: 'rs:clay', level: 1, experience: 5.0, respawnLow: 5, @@ -147,7 +168,7 @@ const Ores: IHarvestable[] = [ }, { objects: COPPER_OBJECTS, - itemId: itemIds.ores.copper, + items: 'rs:copper_ore', level: 1, experience: 17.5, respawnLow: 10, @@ -157,7 +178,7 @@ const Ores: IHarvestable[] = [ }, { objects: TIN_OBJECTS, - itemId: itemIds.ores.tin, + items: 'rs:tin_ore', level: 1, experience: 17.5, respawnLow: 10, @@ -167,7 +188,7 @@ const Ores: IHarvestable[] = [ }, { objects: IRON_OBJECTS, - itemId: itemIds.ores.iron, + items: 'rs:iron_ore', level: 15, experience: 35.0, respawnLow: 9, @@ -177,7 +198,7 @@ const Ores: IHarvestable[] = [ }, { objects: COAL_OBJECTS, - itemId: itemIds.ores.coal, + items: 'rs:coal', level: 30, experience: 50.0, respawnLow: 20, @@ -187,7 +208,7 @@ const Ores: IHarvestable[] = [ }, { objects: SILVER_OBJECTS, - itemId: itemIds.ores.silver, + items: 'rs:silver_ore', level: 20, experience: 40.0, respawnLow: 30, @@ -197,7 +218,7 @@ const Ores: IHarvestable[] = [ }, { objects: GOLD_OBJECTS, - itemId: itemIds.ores.gold, + items: 'rs:gold_ore', level: 40, experience: 65.0, respawnLow: 50, @@ -207,7 +228,7 @@ const Ores: IHarvestable[] = [ }, { objects: MITHRIL_OBJECTS, - itemId: itemIds.ores.mithril, + items: 'rs:mithril_ore', level: 55, experience: 65.0, respawnLow: 90, @@ -217,7 +238,7 @@ const Ores: IHarvestable[] = [ }, { objects: ADAMANT_OBJECTS, - itemId: itemIds.ores.adamantite, + items: 'rs:adamantite_ore', level: 70, experience: 95.0, respawnLow: 200, @@ -227,7 +248,7 @@ const Ores: IHarvestable[] = [ }, { objects: RUNITE_OBJECTS, - itemId: itemIds.ores.runite, + items: 'rs:runite_ore', level: 85, experience: 125.0, respawnLow: 1200, @@ -236,21 +257,29 @@ const Ores: IHarvestable[] = [ break: 100 }, { - objects: new Map([[2111, 450]]), // Gem rocks - itemId: 1625, + objects: new Map([[2111, 450]]), + items: [ + { itemConfigId: 'rs:uncut_opal', weight: 60 }, // 60/128 + { itemConfigId: 'rs:uncut_jade', weight: 30 }, // 30/128 + { itemConfigId: 'rs:uncut_red_topaz', weight: 15 }, // 15/128 + { itemConfigId: 'rs:uncut_sapphire', weight: 9 }, // 9/128 + { itemConfigId: 'rs:uncut_emerald', weight: 5 }, // 5/128 + { itemConfigId: 'rs:uncut_ruby', weight: 5 }, // 5/128 + { itemConfigId: 'rs:uncut_diamond', weight: 4 } // 4/128 + ], level: 40, experience: 65.0, respawnLow: 200, respawnHigh: 400, - baseChance: 30, - break: 100 + baseChance: 28, // Base success chance at level 40 + break: 100 // Always depletes after successful mining } ]; const Trees: IHarvestable[] = [ { objects: NORMAL_OBJECTS, - itemId: itemIds.logs.normal, + items: 'rs:logs', level: 1, experience: 25, respawnLow: 59, @@ -260,7 +289,7 @@ const Trees: IHarvestable[] = [ }, { objects: ACHEY_OBJECTS, - itemId: itemIds.logs.achey, + items: 'rs:achey_logs', level: 1, experience: 25, respawnLow: 59, @@ -270,7 +299,7 @@ const Trees: IHarvestable[] = [ }, { objects: OAK_OBJECTS, - itemId: itemIds.logs.oak, + items: 'rs:oak_logs', level: 15, experience: 37.5, respawnLow: 14, @@ -280,7 +309,7 @@ const Trees: IHarvestable[] = [ }, { objects: WILLOW_OBJECTS, - itemId: itemIds.logs.willow, + items: 'rs:willow_logs', level: 30, experience: 67.5, respawnLow: 14, @@ -290,7 +319,7 @@ const Trees: IHarvestable[] = [ }, { objects: TEAK_OBJECTS, - itemId: itemIds.logs.teak, + items: 'rs:teak_logs', level: 35, experience: 85, respawnLow: 15, @@ -298,10 +327,9 @@ const Trees: IHarvestable[] = [ baseChance: 0, break: 100 / 8 }, - { objects: DRAMEN_OBJECTS, - itemId: itemIds.logs.dramenbranch, + items: 'rs:dramen_branch', // You'll need to add this to logs.json level: 36, experience: 0, respawnLow: 0, @@ -311,7 +339,7 @@ const Trees: IHarvestable[] = [ }, { objects: MAPLE_OBJECTS, - itemId: itemIds.logs.maple, + items: 'rs:maple_logs', level: 45, experience: 100, respawnLow: 59, @@ -321,7 +349,7 @@ const Trees: IHarvestable[] = [ }, { objects: HOLLOW_OBJECTS, - itemId: itemIds.logs.bark, + items: 'rs:bark', // You'll need to add this to logs.json level: 45, experience: 82.5, respawnLow: 43, @@ -331,7 +359,7 @@ const Trees: IHarvestable[] = [ }, { objects: MAHOGANY_OBJECTS, - itemId: itemIds.logs.mahogany, + items: 'rs:mahogany_logs', level: 50, experience: 125, respawnLow: 14, @@ -341,7 +369,7 @@ const Trees: IHarvestable[] = [ }, { objects: YEW_OBJECTS, - itemId: itemIds.logs.yew, + items: 'rs:yew_logs', level: 60, experience: 175, respawnLow: 99, @@ -351,7 +379,7 @@ const Trees: IHarvestable[] = [ }, { objects: MAGIC_OBJECTS, - itemId: itemIds.logs.magic, + items: 'rs:magic_logs', level: 75, experience: 250, respawnLow: 199, @@ -359,6 +387,26 @@ const Trees: IHarvestable[] = [ baseChance: -25, break: 100 / 8 }, + { + objects: DRAMEN_OBJECTS, + items: 'rs:dramen_branch', + level: 36, + experience: 0, + respawnLow: 0, + respawnHigh: 0, + baseChance: 100, + break: 0 + }, + { + objects: HOLLOW_OBJECTS, + items: 'rs:bark', + level: 45, + experience: 82.5, + respawnLow: 43, + respawnHigh: 44, + baseChance: 0, + break: 100 / 8 + }, ]; export function getOre(ore: Ore): IHarvestable { diff --git a/src/engine/world/skill-util/glory-boost.ts b/src/engine/world/skill-util/glory-boost.ts index f64767d25..1d5bbdaa1 100644 --- a/src/engine/world/skill-util/glory-boost.ts +++ b/src/engine/world/skill-util/glory-boost.ts @@ -1,11 +1,20 @@ import { Player } from '@engine/world/actor/player/player'; +import { equipmentIndices, findItem } from '@engine/config'; + export function checkForGemBoost(player: Player): number { - if (player.isItemEquipped(1706) || - player.isItemEquipped(1708) || - player.isItemEquipped(1710) || - player.isItemEquipped(1712)) { - return 86; + // Check if any charged glory is equipped + const neckSlotIndex = equipmentIndices['neck']; + const neckItem = player.equipment.items[neckSlotIndex]; + + if (!neckItem) { + return 256; } - return 256; + + const itemConfig = findItem(neckItem.itemId); + if (!itemConfig || !itemConfig.key.startsWith('rs:amulet_of_glory:charged_')) { + return 256; + } + + return 86; } diff --git a/src/engine/world/skill-util/harvest-roll.ts b/src/engine/world/skill-util/harvest-roll.ts index c02b48d50..c40520bb1 100644 --- a/src/engine/world/skill-util/harvest-roll.ts +++ b/src/engine/world/skill-util/harvest-roll.ts @@ -1,59 +1,55 @@ // Note if adding hunter, Strung rabbit foot makes this out of 94 instead of 99 import { randomBetween } from '@engine/util/num'; import { Item } from '@engine/world/items/item'; +import { findItem } from '@engine/config'; export function rollBirdsNestType(): Item { const roll = randomBetween(0, 99); - let itemId; + let itemConfigId; + if (roll > 3) { // Bird egg if (roll === 0) { - itemId = 5070; // Red egg + itemConfigId = 'rs:birds_egg_red'; } else if (roll === 1) { - itemId = 5071; // Green egg + itemConfigId = 'rs:birds_egg_green'; } else { - itemId = 5072; // blue egg + itemConfigId = 'rs:birds_egg_blue'; } } else if (roll > 34) { - itemId = 5074; // Ring + itemConfigId = 'rs:birds_nest_ring'; } else { - itemId = 5073; // Seeds + itemConfigId = 'rs:birds_nest_seed'; + } + + const item = findItem(itemConfigId); + if (!item) { + throw new Error(`Could not find item config for ${itemConfigId}`); } - return { itemId: itemId, amount: 1 }; + + return { itemId: item.gameId, amount: 1 }; } + export function rollGemType(): Item { const roll = randomBetween(0, 3); - let itemId; + let itemConfigId; + if (roll === 0) { - itemId = 1617; // Uncut Diamond + itemConfigId = 'rs:uncut_diamond'; } else if (roll === 1) { - itemId = 1619; // Uncut ruby + itemConfigId = 'rs:uncut_ruby'; } else if (roll === 2) { - itemId = 1621; // uncut emerald + itemConfigId = 'rs:uncut_emerald'; } else { - itemId = 1623; // uncut sapphire + itemConfigId = 'rs:uncut_sapphire'; } - return { itemId: itemId, amount: 1 }; -} -export function rollGemRockResult(): Item { - const roll = randomBetween(0, 127); - let itemId; - if (roll < 60) { - itemId = 1625; // Uncut Opal - } else if (roll < 90) { - itemId = 1627; // Uncut Jade - } else if (roll < 105) { - itemId = 1629; // uncut topaz - } else if (roll < 114) { - itemId = 1623; // uncut sapphire - } else if (roll < 119) { - itemId = 1621; // uncut emerald - } else if (roll < 124) { - itemId = 1619; // uncut ruby - } else { - itemId = 1617; // uncut diamond + const item = findItem(itemConfigId); + if (!item) { + throw new Error(`Could not find item config for ${itemConfigId}`); } - return { itemId: itemId, amount: 1 }; + + return { itemId: item.gameId, amount: 1 }; } + diff --git a/src/engine/world/skill-util/harvest-skill.ts b/src/engine/world/skill-util/harvest-skill.ts index 6850f634b..a98c6abe3 100644 --- a/src/engine/world/skill-util/harvest-skill.ts +++ b/src/engine/world/skill-util/harvest-skill.ts @@ -12,12 +12,11 @@ import { logger } from '@runejs/common'; * @returns a {@link HarvestTool} if the player can harvest the object, or undefined if they cannot. */ export function canInitiateHarvest(player: Player, target: IHarvestable, skill: Skill): undefined | HarvestTool { - - - const item = findItem(target.itemId); + const itemConfigId = typeof target.items === 'string' ? target.items : target.items[0].itemConfigId; + const item = findItem(itemConfigId); if (!item) { - logger.error(`Could not find item with id ${target.itemId} for harvestable object.`); + logger.error(`Could not find item with config id ${itemConfigId} for harvestable object.`); player.sendMessage('Sorry, there was an error. Please contact a developer.'); return; } @@ -30,8 +29,7 @@ export function canInitiateHarvest(player: Player, target: IHarvestable, skill: break; } - - // Check player level against the required level + // Rest of the function remains the same... if (!player.skills.hasLevel(skill, target.level)) { switch (skill) { case Skill.WOODCUTTING: @@ -40,13 +38,14 @@ export function canInitiateHarvest(player: Player, target: IHarvestable, skill: } return; } - // Check the players equipment and inventory for a tool + let tool; switch (skill) { case Skill.WOODCUTTING: tool = getBestAxe(player); break; } + if (tool == null) { switch (skill) { case Skill.WOODCUTTING: @@ -55,13 +54,12 @@ export function canInitiateHarvest(player: Player, target: IHarvestable, skill: } return; } - // Check if the players inventory is full, and notify them if its full. + if (!player.inventory.hasSpace()) { player.sendMessage(`Your inventory is too full to hold any more ${targetName}.`, true); player.playSound(soundIds.inventoryFull); return; } - return tool; - + return tool; } diff --git a/src/plugins/skills/mining/mining-task.ts b/src/plugins/skills/mining/mining-task.ts index 7d127f90e..b9372a335 100644 --- a/src/plugins/skills/mining/mining-task.ts +++ b/src/plugins/skills/mining/mining-task.ts @@ -1,9 +1,9 @@ import { LandscapeObject } from '@runejs/filestore'; -import { findItem } from '@engine/config'; +import { equipmentIndices, findItem } from '@engine/config'; import { ActorLandscapeObjectInteractionTask } from '@engine/task/impl'; import { colors, colorText, randomBetween } from '@engine/util'; import { Player, Skill } from '@engine/world/actor'; -import { HarvestTool, IHarvestable, soundIds } from '@engine/world/config'; +import { HarvestTool, IHarvestable, selectWeightedItem, soundIds } from '@engine/world/config'; import { checkForGemBoost } from '@engine/world/skill-util/glory-boost'; import { rollGemType } from '@engine/world/skill-util/harvest-roll'; import { canMine } from './chance'; @@ -30,18 +30,37 @@ export class MiningTask extends ActorLandscapeObjectInteractionTask { */ private targetItemName: string; + constructor(player: Player, landscapeObject: LandscapeObject, private readonly ore: IHarvestable, private readonly tool: HarvestTool) { super(player, landscapeObject); - const item = findItem(ore.itemId); + const itemConfigId = typeof ore.items === 'string' ? ore.items : selectWeightedItem(ore.items); + const item = findItem(itemConfigId); if (!item) { - throw new Error(`Could not find item with ID ${ore.itemId}`); + throw new Error(`Could not find item with ID ${itemConfigId}`); } this.targetItemName = item.name.toLowerCase().replace(' ore', '') } + private isGemRock(): boolean { + return this.landscapeObject?.objectId === 2111; + } + + private hasChargedGlory(): boolean { + const neckSlotIndex = equipmentIndices['neck']; // This is 2 + const neckItem = this.actor.equipment.items[neckSlotIndex]; + if (!neckItem) { + return false; + } + const itemConfig = findItem(neckItem.itemId); + if (!itemConfig) { + return false; + } + return itemConfig.key.startsWith('rs:amulet_of_glory:charged_'); + } + public execute(): void { const taskIteration = this.elapsedTicks++; @@ -83,7 +102,11 @@ export class MiningTask extends ActorLandscapeObjectInteractionTask { } // roll for success - const succeeds = canMine(this.ore, toolLevel, this.actor.skills.mining.level); + const succeeds = canMine( + { ...this.ore, baseChance: this.getGemMiningChance() }, + toolLevel, + this.actor.skills.mining.level + ); if(!succeeds) { return; } @@ -94,8 +117,10 @@ export class MiningTask extends ActorLandscapeObjectInteractionTask { this.actor.giveItem(rollGemType()); } else { this.actor.sendMessage(`You manage to mine some ${this.targetItemName}.`); - this.actor.giveItem(this.ore.itemId); - + const itemToGive = typeof this.ore.items === 'string' ? + this.ore.items : + selectWeightedItem(this.ore.items); + this.actor.giveItem(itemToGive); // TODO (Jameskmonger) handle Gem rocks and Pure essence rocks // if (itemToAdd === 1436 && details.player.skills.hasLevel(Skill.MINING, 30)) { // itemToAdd = 7936; @@ -133,6 +158,23 @@ export class MiningTask extends ActorLandscapeObjectInteractionTask { return this.actor.inventory.has(this.tool.itemId); } + private getGemMiningChance(): number { + if (!this.isGemRock()) { + return this.ore.baseChance; + } + + // Base chance scaling from 28 to 70 based on level + let chance = this.ore.baseChance + + (this.actor.skills.mining.level - this.ore.level) * (70 - 28) / (99 - 40); + + // Glory multiplies chance by 3 (from 28-70 to 84-210) + if (this.hasChargedGlory()) { + chance *= 3; + } + + return chance; + } + /** * Check that the player still has the level to mine the ore. * diff --git a/src/plugins/skills/woodcutting/woodcutting-task.ts b/src/plugins/skills/woodcutting/woodcutting-task.ts index d9615fbee..25fc99668 100644 --- a/src/plugins/skills/woodcutting/woodcutting-task.ts +++ b/src/plugins/skills/woodcutting/woodcutting-task.ts @@ -58,6 +58,26 @@ class WoodcuttingTask extends ActorLandscapeObjectInteractionTask { } } + private getItemToAdd(): string | null { + this.actor.sendMessage(`Looking for item ${this.treeInfo.items}`); + if (typeof this.treeInfo.items === 'string') { + return this.treeInfo.items; + } + + // Handle weighted items + const totalWeight = this.treeInfo.items.reduce((sum, item) => sum + item.weight, 0); + let random = randomBetween(1, totalWeight); + + for (const item of this.treeInfo.items) { + random -= item.weight; + if (random <= 0) { + return item.itemConfigId; + } + } + + return null; + } + /** * Execute the main woodcutting task loop. This method is called every game tick until the task is completed. * @@ -113,10 +133,17 @@ class WoodcuttingTask extends ActorLandscapeObjectInteractionTask { return; } - const logItem = findObject(this.treeInfo.itemId); + const itemConfigId = this.getItemToAdd(); + if (!itemConfigId) { + logger.error('Could not determine item to add from tree'); + this.actor.sendMessage('Sorry, an error occurred. Please report this to a developer.'); + this.stop(); + return; + } - if(!logItem) { - logger.error(`Could not find log item with id ${this.treeInfo.itemId}`); + const logItem = findItem(itemConfigId); + if (!logItem) { + logger.error(`Could not find log item with id ${itemConfigId}`); this.actor.sendMessage('Sorry, an error occurred. Please report this to a developer.'); this.stop(); return; @@ -132,7 +159,6 @@ class WoodcuttingTask extends ActorLandscapeObjectInteractionTask { return; } - const itemToAdd = this.treeInfo.itemId; const roll = randomBetween(1, 256); // roll for bird nest chance if(roll === 1) { @@ -141,7 +167,7 @@ class WoodcuttingTask extends ActorLandscapeObjectInteractionTask { { owner: this.actor || null, expires: 300 }); } else { // Standard log chopper this.actor.sendMessage(`You manage to chop some ${targetName}.`); - this.actor.giveItem(itemToAdd); + this.actor.giveItem(itemConfigId); } this.actor.skills.woodcutting.addExp(this.treeInfo.experience); From db455c381343130f1514c6dc908ac9c1cb603ac4 Mon Sep 17 00:00:00 2001 From: Henning Berge Date: Fri, 24 Jan 2025 23:01:27 +0100 Subject: [PATCH 2/3] lint --- src/engine/world/config/harvestable-object.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/world/config/harvestable-object.ts b/src/engine/world/config/harvestable-object.ts index 15c42cadf..101ad1e1c 100644 --- a/src/engine/world/config/harvestable-object.ts +++ b/src/engine/world/config/harvestable-object.ts @@ -1,5 +1,5 @@ import { objectIds } from '@engine/world/config/object-ids'; -import { randomBetween } from "@engine/util"; +import { randomBetween } from '@engine/util'; interface WeightedItem { itemConfigId: string; From 4af50c51336f6dc33c263035b2d6b8db4e19e041 Mon Sep 17 00:00:00 2001 From: Henning Berge Date: Fri, 24 Jan 2025 23:04:28 +0100 Subject: [PATCH 3/3] prooooooospect --- src/plugins/skills/mining/prospecting.plugin.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/skills/mining/prospecting.plugin.ts b/src/plugins/skills/mining/prospecting.plugin.ts index 2d56f939c..67d281a5b 100644 --- a/src/plugins/skills/mining/prospecting.plugin.ts +++ b/src/plugins/skills/mining/prospecting.plugin.ts @@ -10,7 +10,8 @@ const action: objectInteractionActionHandler = (details) => { const ore = getOreFromRock(details.object.objectId); details.player.playSound(soundIds.oreEmpty, 7, 0); - const oreItem = findItem(ore.itemId); + const itemConfigId = typeof ore.items === 'string' ? ore.items : ore.items[0].itemConfigId; + const oreItem = findItem(itemConfigId); if (!oreItem) { details.player.sendMessage('Sorry, something went wrong. Please report this to a developer.');