diff --git a/src/lib/data/buyables/bsoBuyables.ts b/src/lib/data/buyables/bsoBuyables.ts index c220ef6ed1..25c7ba201b 100644 --- a/src/lib/data/buyables/bsoBuyables.ts +++ b/src/lib/data/buyables/bsoBuyables.ts @@ -3,7 +3,7 @@ import { Time } from 'e'; import { Bank } from 'oldschooljs'; import { calculateCompCapeProgress } from '../../bso/calculateCompCapeProgress'; -import { allMasterCapesBank } from '../../skilling/skillcapes'; +import { compCapeCreatableBank } from '../../skilling/skillcapes'; import type { Buyable } from './buyables'; import { circusBuyables } from './circusBuyables'; import { fistOfGuthixBuyables } from './fistOfGuthixBuyables'; @@ -103,7 +103,7 @@ export const bsoBuyables: Buyable[] = [ { name: 'Completionist cape', outputItems: new Bank().add('Completionist cape').add('Completionist hood'), - itemCost: allMasterCapesBank, + itemCost: compCapeCreatableBank, customReq: async user => { const { totalPercentUntrimmed } = await calculateCompCapeProgress(user); if (totalPercentUntrimmed < 100) { diff --git a/src/lib/data/creatables/bsoItems.ts b/src/lib/data/creatables/bsoItems.ts index ea65fe85d9..974363a5b6 100644 --- a/src/lib/data/creatables/bsoItems.ts +++ b/src/lib/data/creatables/bsoItems.ts @@ -1329,6 +1329,7 @@ export const BsoCreateables: Createable[] = [ { name: 'Revert completionist cape', outputItems: user => { + // check compCapeCreatableBank in skillcapes.ts to ensure all capes are being refunded const refundBank = new Bank(); for (const { masterCape } of Skillcapes) { if (user.cl.has(masterCape.id)) { @@ -1336,7 +1337,7 @@ export const BsoCreateables: Createable[] = [ } } refundBank.add('Master quest cape'); - refundBank.add('Achievement diary cape(t)'); + refundBank.add('Achievement diary cape (t)'); refundBank.add('Music cape (t)'); return refundBank; }, diff --git a/src/lib/data/similarItems.ts b/src/lib/data/similarItems.ts index 3c6910907e..3977366767 100644 --- a/src/lib/data/similarItems.ts +++ b/src/lib/data/similarItems.ts @@ -1,6 +1,7 @@ import { resolveItems } from 'oldschooljs/dist/util/util'; import { dyedItems } from '../dyedItems'; -import { allMasterCapesBank } from '../skilling/skillcapes'; +import skillcapes from '../skilling/skillcapes'; +import getOSItem from '../util/getOSItem'; import itemID from '../util/itemID'; import { gracefulCapes, @@ -90,6 +91,12 @@ const source: [string, (string | number)[]][] = [ ['Rune axe', ['Rune felling axe', 'Gilded axe']], ['Dragon axe', ['Dragon felling axe', '3rd age axe', '3rd age felling axe']], ['Crystal axe', ['Crystal felling axe']], + ['Lumberjack hat', ['Forestry hat']], + ['Lumberjack top', ['Forestry top']], + ['Lumberjack legs', ['Forestry legs']], + ['Lumberjack boots', ['Forestry boots']], + ['Log basket', ['Forestry basket']], + ['Forestry kit', ['Forestry basket']], ['Rune pickaxe', ['Gilded pickaxe']], ['Dragon full helm', ['Dragon full helm (g)']], ['Dragon chainbody', ['Dragon chainbody (g)']], @@ -212,7 +219,6 @@ const source: [string, (string | number)[]][] = [ ] ], ['Mythical cape', ['Mythical max cape']], - ['Achievement diary cape', ['Achievement diary cape(t)']], [ 'Imbued guthix cape', [ @@ -278,7 +284,6 @@ const source: [string, (string | number)[]][] = [ ] ] ], - ['Slayer cape', ['Slayer cape (t)']], ['Nose peg', slayerHelmSimilar], ['Earmuffs', slayerHelmSimilar], ['Spiny helmet', slayerHelmSimilar], @@ -304,33 +309,6 @@ const source: [string, (string | number)[]][] = [ 'Void staff (u)' ] ], - ['Attack cape', ['Max cape', 'Attack cape(t)']], - ['Farming cape', ['Max cape', 'Farming cape(t)']], - ['Agility master cape', ['Support cape']], - ['Dungeoneering master cape', ['Support cape']], - ['Thieving master cape', ['Support cape']], - ['Slayer master cape', ['Support cape']], - ['Farming master cape', ["Gatherer's cape"]], - ['Fishing master cape', ["Gatherer's cape"]], - ['Hunter master cape', ["Gatherer's cape"]], - ['Mining master cape', ["Gatherer's cape"]], - ['Woodcutting master cape', ["Gatherer's cape"]], - ['Divination master cape', ["Gatherer's cape"]], - ['Attack master cape', ["Combatant's cape"]], - ['Hitpoints master cape', ["Combatant's cape"]], - ['Defence master cape', ["Combatant's cape"]], - ['Magic master cape', ["Combatant's cape"]], - ['Prayer master cape', ["Combatant's cape"]], - ['Ranged master cape', ["Combatant's cape"]], - ['Strength master cape', ["Combatant's cape"]], - ['Crafting master cape', ["Artisan's cape"]], - ['Construction master cape', ["Artisan's cape"]], - ['Cooking master cape', ["Artisan's cape"]], - ['Firemaking master cape', ["Artisan's cape"]], - ['Fletching master cape', ["Artisan's cape"]], - ['Herblore master cape', ["Artisan's cape"]], - ['Runecraft master cape', ["Artisan's cape"]], - ['Smithing master cape', ["Artisan's cape"]], ['Torva full helm', ['Gorajan warrior helmet', 'Infernal slayer helmet(i)']], ['Torva platebody', ['Gorajan warrior top']], ['Torva platelegs', ['Gorajan warrior legs']], @@ -372,30 +350,6 @@ const source: [string, (string | number)[]][] = [ ['Scythe of vitur (uncharged)', ['Sanguine scythe of vitur (uncharged)', 'Holy scythe of vitur (uncharged)']], ['Sanguinesti staff', ['Holy sanguinesti staff']], ['Sanguinesti staff (uncharged)', ['Holy sanguinesti staff (uncharged)']], - ['Runecraft cape', ['Max cape', 'Runecraft cape(t)']], - ['Agility cape', ['Max cape', 'Agility cape(t)']], - ['Attack cape', ['Max cape', 'Attack cape(t)']], - ['Construct. cape', ['Max cape', 'Construct. cape(t)']], - ['Cooking cape', ['Max cape', 'Cooking cape(t)']], - ['Crafting cape', ['Max cape', 'Crafting cape(t)']], - ['Defence cape', ['Max cape', 'Defence cape(t)']], - ['Farming cape', ['Max cape', 'Farming cape(t)']], - ['Firemaking cape', ['Max cape', 'Firemaking cape(t)']], - ['Fishing cape', ['Max cape', 'Fishing cape(t)']], - ['Fletching cape', ['Max cape', 'Fletching cape(t)']], - ['Herblore cape', ['Max cape', 'Herblore cape(t)']], - ['Hitpoints cape', ['Max cape', 'Hitpoints cape(t)']], - ['Hunter cape', ['Max cape', 'Hunter cape(t)']], - ['Magic cape', ['Max cape', 'Magic cape(t)']], - ['Mining cape', ['Max cape', 'Mining cape(t)']], - ['Prayer cape', ['Max cape', 'Prayer cape(t)']], - ['Ranging cape', ['Max cape', 'Ranging cape(t)']], - ['Runecraft cape', ['Max cape', 'Runecraft cape(t)']], - ['Slayer cape', ['Max cape', 'Slayer cape(t)']], - ['Smithing cape', ['Max cape', 'Smithing cape(t)']], - ['Strength cape', ['Max cape', 'Strength cape(t)']], - ['Thieving cape', ['Max cape', 'Thieving cape(t)']], - ['Woodcutting cape', ['Max cape', 'Woodcut. cape(t)']], ['Salve amulet', ['Salve amulet(ei)', 'Salve amulet(i)', 'Salve amulet (e)']], ['Salve amulet (e)', ['Salve amulet(ei)']], ['Salve amulet(i)', ['Salve amulet(ei)']], @@ -441,50 +395,70 @@ const source: [string, (string | number)[]][] = [ ['Archers ring', ['Archers ring (i)']], ['Ignis ring', ['Ignis ring (i)']], ['Ring of piercing', ['Ring of piercing (i)']], - - // Inventions - ['Inferno adze', ['Superior inferno adze']], - ['Gorajan bonecrusher', ['Superior bonecrusher']], - ['Magic secateurs', ['Arcane harvester']], ["Karil's coif", ['Armadyl helmet', 'Masori mask (f)', 'Masori mask']], ["Karil's leathertop", ['Armadyl chestplate', 'Masori body (f)', 'Masori body']], ["Karil's leatherskirt", ['Armadyl chainskirt', 'Masori chaps (f)', 'Masori chaps']], ['Armadyl helmet', ['Masori mask (f)', 'Masori mask']], ['Armadyl chestplate', ['Armadyl chestplate', 'Masori body (f)', 'Masori body']], ['Armadyl chainskirt', ['Masori chaps (f)', 'Masori chaps']], - ['Music cape', ['Music cape (t)']], ['Imbued heart', ['Saturated heart']], ["Craw's bow", ['Webweaver bow']], ["Viggora's chainmace", ['Ursine chainmace']], ["Thammaron's sceptre", ['Accursed sceptre']], - ['Lumberjack hat', ['Forestry hat']], - ['Lumberjack top', ['Forestry top']], - ['Lumberjack legs', ['Forestry legs']], - ['Lumberjack boots', ['Forestry boots']], + ['Ring of stone', ['Ring of coins', 'Crate ring', 'Ring of nature', 'Snowman ring', 'Ring of 3rd age']], + ['Ring of suffering (i)', ['Ring of suffering (ri)']], + ['Amulet of rancour', ['Amulet of rancour (s)']], + + // Tame gear ['Abyssal jibwings', ['Abyssal jibwings (e)']], ['3rd age jibwings', ['3rd age jibwings (e)']], ['Demonic jibwings', ['Demonic jibwings (e)']], - ['Completionist cape', ['Completionist cape (t)']], + // Inventions + ['Inferno adze', ['Superior inferno adze']], + ['Gorajan bonecrusher', ['Superior bonecrusher']], + ['Magic secateurs', ['Arcane harvester']], + ['Dwarven greataxe', ['Drygore axe']], + + // Mastery capes + ['Music cape', ['Music cape (t)', 'Completionist cape', 'Completionist cape (t)']], + ['Achievement diary cape', ['Achievement diary cape(t)', 'Completionist cape', 'Completionist cape (t)']], + ['Master quest cape', ['Completionist cape', 'Completionist cape (t)']], ["Combatant's cape", ['Completionist cape', 'Completionist cape (t)']], ["Gatherer's cape", ['Completionist cape', 'Completionist cape (t)']], ['Support cape', ['Completionist cape', 'Completionist cape (t)']], - ["Artisan's cape", ['Completionist cape', 'Completionist cape (t)']], - ['Log basket', ['Forestry basket']], - ['Forestry kit', ['Forestry basket']], - ['Dwarven greataxe', ['Drygore axe']], - ['Ring of stone', ['Ring of coins', 'Crate ring', 'Ring of nature', 'Snowman ring', 'Ring of 3rd age']], - ['Ring of suffering (i)', ['Ring of suffering (ri)']], - ['Amulet of rancour', ['Amulet of rancour (s)']] + ["Artisan's cape", ['Completionist cape', 'Completionist cape (t)']] ]; -// Make max cape count as all master capes -for (const [cape] of allMasterCapesBank.items()) { - const existingSimilarItem = source.find(s => s[0] === cape.name); - if (existingSimilarItem) { - existingSimilarItem[1].push('Completionist cape', 'Completionist cape (t)'); +// Build skill cape & master cape similar items. This also handles comp and comp(t) receiving all skillcape and master cape perks. +for (const cape of skillcapes) { + const untrimmedCape = getOSItem(cape.untrimmed).name; + const trimmedCape = getOSItem(cape.trimmed).name; + const masterCape = getOSItem(cape.masterCape.id).name; + const expertCape = cape.expertCape ? getOSItem(cape.expertCape.id).name : null; + + const skillCapeList = [trimmedCape, 'Max cape', masterCape, 'Completionist cape', 'Completionist cape (t)']; + const masterCapeList = ['Completionist cape', 'Completionist cape (t)']; + + if (expertCape !== null) { + skillCapeList.push(expertCape); + masterCapeList.push(expertCape); + } + + // Skill cape + const existingSkillCape = source.find(s => s[0] === untrimmedCape); + if (existingSkillCape) { + existingSkillCape[1].push(...skillCapeList); + } else { + source.push([untrimmedCape, skillCapeList]); + } + + // Master cape + const existingMasterCape = source.find(s => s[0] === masterCape); + if (existingMasterCape) { + existingMasterCape[1].push(...masterCapeList); } else { - source.push([cape.name, ['Completionist cape', 'Completionist cape (t)']]); + source.push([masterCape, masterCapeList]); } } diff --git a/src/lib/skilling/skillcapes.ts b/src/lib/skilling/skillcapes.ts index 018876882f..d26009d29f 100644 --- a/src/lib/skilling/skillcapes.ts +++ b/src/lib/skilling/skillcapes.ts @@ -11,6 +11,7 @@ interface Skillcape { untrimmed: number; trimmed: number; masterCape: Item; + expertCape?: Item; } const Skillcapes: Skillcape[] = [ @@ -19,168 +20,192 @@ const Skillcapes: Skillcape[] = [ hood: itemID('Mining hood'), untrimmed: itemID('Mining cape'), trimmed: itemID('Mining cape(t)'), - masterCape: getOSItem('Mining master cape') + masterCape: getOSItem('Mining master cape'), + expertCape: getOSItem("Gatherer's cape") }, { skill: SkillsEnum.Smithing, hood: itemID('Smithing hood'), untrimmed: itemID('Smithing cape'), trimmed: itemID('Smithing cape(t)'), - masterCape: getOSItem('Smithing master cape') + masterCape: getOSItem('Smithing master cape'), + expertCape: getOSItem("Artisan's cape") }, { skill: SkillsEnum.Woodcutting, hood: itemID('Woodcutting hood'), untrimmed: itemID('Woodcutting cape'), trimmed: itemID('Woodcut. cape(t)'), - masterCape: getOSItem('Woodcutting master cape') + masterCape: getOSItem('Woodcutting master cape'), + expertCape: getOSItem("Gatherer's cape") }, { skill: SkillsEnum.Firemaking, hood: itemID('Firemaking hood'), untrimmed: itemID('Firemaking cape'), trimmed: itemID('Firemaking cape(t)'), - masterCape: getOSItem('Firemaking master cape') + masterCape: getOSItem('Firemaking master cape'), + expertCape: getOSItem("Artisan's cape") }, { skill: SkillsEnum.Agility, hood: itemID('Agility hood'), untrimmed: itemID('Agility cape'), trimmed: itemID('Agility cape(t)'), - masterCape: getOSItem('Agility master cape') + masterCape: getOSItem('Agility master cape'), + expertCape: getOSItem('Support cape') }, { skill: SkillsEnum.Fishing, hood: itemID('Fishing hood'), untrimmed: itemID('Fishing cape'), trimmed: itemID('Fishing cape(t)'), - masterCape: getOSItem('Fishing master cape') + masterCape: getOSItem('Fishing master cape'), + expertCape: getOSItem("Gatherer's cape") }, { skill: SkillsEnum.Runecraft, hood: itemID('Runecraft hood'), untrimmed: itemID('Runecraft cape'), trimmed: itemID('Runecraft cape(t)'), - masterCape: getOSItem('Runecraft master cape') + masterCape: getOSItem('Runecraft master cape'), + expertCape: getOSItem("Artisan's cape") }, { skill: SkillsEnum.Cooking, hood: itemID('Cooking hood'), untrimmed: itemID('Cooking cape'), trimmed: itemID('Cooking cape(t)'), - masterCape: getOSItem('Cooking master cape') + masterCape: getOSItem('Cooking master cape'), + expertCape: getOSItem("Artisan's cape") }, { skill: SkillsEnum.Crafting, hood: itemID('Crafting hood'), untrimmed: itemID('Crafting cape'), trimmed: itemID('Crafting cape(t)'), - masterCape: getOSItem('Crafting master cape') + masterCape: getOSItem('Crafting master cape'), + expertCape: getOSItem("Artisan's cape") }, { skill: SkillsEnum.Prayer, hood: itemID('Prayer hood'), untrimmed: itemID('Prayer cape'), trimmed: itemID('Prayer cape(t)'), - masterCape: getOSItem('Prayer master cape') + masterCape: getOSItem('Prayer master cape'), + expertCape: getOSItem("Combatant's cape") }, { skill: SkillsEnum.Fletching, hood: itemID('Fletching hood'), untrimmed: itemID('Fletching cape'), trimmed: itemID('Fletching cape(t)'), - masterCape: getOSItem('Fletching master cape') + masterCape: getOSItem('Fletching master cape'), + expertCape: getOSItem("Artisan's cape") }, { skill: SkillsEnum.Thieving, hood: itemID('Thieving hood'), untrimmed: itemID('Thieving cape'), trimmed: itemID('Thieving cape(t)'), - masterCape: getOSItem('Thieving master cape') + masterCape: getOSItem('Thieving master cape'), + expertCape: getOSItem('Support cape') }, { skill: SkillsEnum.Farming, hood: itemID('Farming hood'), untrimmed: itemID('Farming cape'), trimmed: itemID('Farming cape(t)'), - masterCape: getOSItem('Farming master cape') + masterCape: getOSItem('Farming master cape'), + expertCape: getOSItem("Gatherer's cape") }, { skill: SkillsEnum.Herblore, hood: itemID('Herblore hood'), untrimmed: itemID('Herblore cape'), trimmed: itemID('Herblore cape(t)'), - masterCape: getOSItem('Herblore master cape') + masterCape: getOSItem('Herblore master cape'), + expertCape: getOSItem("Artisan's cape") }, { skill: SkillsEnum.Hunter, hood: itemID('Hunter hood'), untrimmed: itemID('Hunter cape'), trimmed: itemID('Hunter cape(t)'), - masterCape: getOSItem('Hunter master cape') + masterCape: getOSItem('Hunter master cape'), + expertCape: getOSItem("Gatherer's cape") }, { skill: SkillsEnum.Construction, hood: itemID('Construct. hood'), untrimmed: itemID('Construct. cape'), trimmed: itemID('Construct. cape(t)'), - masterCape: getOSItem('Construction master cape') + masterCape: getOSItem('Construction master cape'), + expertCape: getOSItem("Artisan's cape") }, { skill: SkillsEnum.Magic, hood: itemID('Magic hood'), untrimmed: itemID('Magic cape'), trimmed: itemID('Magic cape(t)'), - masterCape: getOSItem('Magic master cape') + masterCape: getOSItem('Magic master cape'), + expertCape: getOSItem("Combatant's cape") }, { skill: SkillsEnum.Attack, hood: itemID('Attack hood'), untrimmed: itemID('Attack cape'), trimmed: itemID('Attack cape(t)'), - masterCape: getOSItem('Attack master cape') + masterCape: getOSItem('Attack master cape'), + expertCape: getOSItem("Combatant's cape") }, { skill: SkillsEnum.Strength, hood: itemID('Strength hood'), untrimmed: itemID('Strength cape'), trimmed: itemID('Strength cape(t)'), - masterCape: getOSItem('Strength master cape') + masterCape: getOSItem('Strength master cape'), + expertCape: getOSItem("Combatant's cape") }, { skill: SkillsEnum.Defence, hood: itemID('Defence hood'), untrimmed: itemID('Defence cape'), trimmed: itemID('Defence cape(t)'), - masterCape: getOSItem('Defence master cape') + masterCape: getOSItem('Defence master cape'), + expertCape: getOSItem("Combatant's cape") }, { skill: SkillsEnum.Ranged, hood: itemID('Ranging hood'), untrimmed: itemID('Ranging cape'), trimmed: itemID('Ranging cape(t)'), - masterCape: getOSItem('Ranged master cape') + masterCape: getOSItem('Ranged master cape'), + expertCape: getOSItem("Combatant's cape") }, { skill: SkillsEnum.Hitpoints, hood: itemID('Hitpoints hood'), untrimmed: itemID('Hitpoints cape'), trimmed: itemID('Hitpoints cape(t)'), - masterCape: getOSItem('Hitpoints master cape') + masterCape: getOSItem('Hitpoints master cape'), + expertCape: getOSItem("Combatant's cape") }, { skill: SkillsEnum.Slayer, hood: itemID('Slayer hood'), untrimmed: itemID('Slayer cape'), trimmed: itemID('Slayer cape(t)'), - masterCape: getOSItem('Slayer master cape') + masterCape: getOSItem('Slayer master cape'), + expertCape: getOSItem('Support cape') }, { skill: SkillsEnum.Dungeoneering, hood: itemID('Dungeoneering hood'), untrimmed: itemID('Dungeoneering cape'), trimmed: itemID('Dungeoneering cape(t)'), - masterCape: getOSItem('Dungeoneering master cape') + masterCape: getOSItem('Dungeoneering master cape'), + expertCape: getOSItem('Support cape') }, { skill: SkillsEnum.Invention, @@ -188,23 +213,26 @@ const Skillcapes: Skillcape[] = [ untrimmed: itemID('Invention cape'), trimmed: itemID('Invention cape(t)'), masterCape: getOSItem('Invention master cape') + // No expert cape for Invention }, { skill: SkillsEnum.Divination, hood: itemID('Divination hood'), untrimmed: itemID('Divination cape'), trimmed: itemID('Divination cape(t)'), - masterCape: getOSItem('Divination master cape') + masterCape: getOSItem('Divination master cape'), + expertCape: getOSItem("Gatherer's cape") } ]; export default Skillcapes; -export const allMasterCapesBank = new Bank(); + +export const compCapeCreatableBank = new Bank(); for (const cape of Skillcapes) { - allMasterCapesBank.add(cape.masterCape.id); + compCapeCreatableBank.add(cape.masterCape.id); } -allMasterCapesBank.add('Master quest cape'); -allMasterCapesBank.add('Achievement diary cape(t)'); -allMasterCapesBank.add('Music cape (t)'); +compCapeCreatableBank.add('Master quest cape'); +compCapeCreatableBank.add('Achievement diary cape (t)'); +compCapeCreatableBank.add('Music cape (t)'); -allMasterCapesBank.freeze(); +compCapeCreatableBank.freeze(); diff --git a/tests/unit/Gear.test.ts b/tests/unit/Gear.test.ts index fac279c5cb..2cc912a451 100644 --- a/tests/unit/Gear.test.ts +++ b/tests/unit/Gear.test.ts @@ -44,6 +44,8 @@ describe('Gear', () => { 'Cooking cape', 'Crafting cape', 'Defence cape', + 'Divination cape', + 'Dungeoneering cape', 'Farming cape', 'Firemaking cape', 'Fishing cape', @@ -51,6 +53,7 @@ describe('Gear', () => { 'Herblore cape', 'Hitpoints cape', 'Hunter cape', + 'Invention cape', 'Magic cape', 'Mining cape', 'Prayer cape', diff --git a/tests/unit/similarItems.test.ts b/tests/unit/similarItems.test.ts index 17fe165e56..6fb69e0aa5 100644 --- a/tests/unit/similarItems.test.ts +++ b/tests/unit/similarItems.test.ts @@ -274,7 +274,8 @@ describe('Gear', () => { gear.equip('Completionist cape'); expect(gear.hasEquipped('Invention master cape')).toEqual(true); expect(gear.hasEquipped('Master quest cape')).toEqual(true); - expect(gear.hasEquipped('Achievement diary cape (t)')).toEqual(true); + expect(gear.hasEquipped('Achievement diary cape')).toEqual(true); + expect(gear.hasEquipped('Music cape')).toEqual(true); expect(gear.hasEquipped('Slayer master cape')).toEqual(true); expect(gear.hasEquipped('Attack master cape')).toEqual(true); expect(gear.hasEquipped('Strength master cape')).toEqual(true);