diff --git a/src/app/components/player/equipment/EquipmentSelect.tsx b/src/app/components/player/equipment/EquipmentSelect.tsx index 3f165567..c7ca623d 100644 --- a/src/app/components/player/equipment/EquipmentSelect.tsx +++ b/src/app/components/player/equipment/EquipmentSelect.tsx @@ -1,11 +1,12 @@ import React, { useMemo } from 'react'; import { useStore } from '@/state'; import { observer } from 'mobx-react-lite'; -import { getCdnImage } from '@/utils'; +import { getCdnImage, isDefined } from '@/utils'; import { EquipmentPiece } from '@/types/Player'; import LazyImage from '@/app/components/generic/LazyImage'; import { cross } from 'd3-array'; import { availableEquipment, equipmentAliases, noStatExceptions } from '@/lib/Equipment'; +import { BLOWPIPE_IDS } from '@/lib/constants'; import Combobox from '../../generic/Combobox'; interface EquipmentOption { @@ -16,29 +17,30 @@ interface EquipmentOption { equipment: EquipmentPiece; } -const BLOWPIPE_IDS: string[] = [ - '12926', // regular - '28688', // blazing -]; - -const DART_IDS: string[] = [ - '806', // bronze - '807', // iron - '808', // steel - '809', // mithril - '810', // adamant - '811', // rune - '3093', // black - '11230', // dragon - '25849', // amethyst -]; +const findDart = (name: string): EquipmentPiece | undefined => { + const eq = availableEquipment.find((e) => e.name === name); + if (!eq) { + console.warn(`Failed to locate dart [${name}] for blowpipe dart entry generation, proceeding without this option.`); + } + return eq; +}; +const DARTS: EquipmentPiece[] = [ + findDart('Bronze dart'), + findDart('Iron dart'), + findDart('Steel dart'), + findDart('Mithril dart'), + findDart('Adamant dart'), + findDart('Rune dart'), + findDart('Black dart'), + findDart('Dragon dart'), + findDart('Amethyst dart'), +].filter(isDefined); const EquipmentSelect: React.FC = observer(() => { const store = useStore(); const options: EquipmentOption[] = useMemo(() => { const blowpipeEntries: EquipmentOption[] = []; - const dartEntries: EquipmentOption[] = []; const entries: EquipmentOption[] = []; for (const v of availableEquipment.filter((eq) => { @@ -67,27 +69,23 @@ const EquipmentSelect: React.FC = observer(() => { equipment: v, }; - if (BLOWPIPE_IDS.includes(e.value)) { + if (BLOWPIPE_IDS.includes(v.id)) { blowpipeEntries.push(e); - } else if (DART_IDS.includes(e.value)) { - dartEntries.push(e); - entries.push(e); } else { entries.push(e); } } - cross(blowpipeEntries, dartEntries).forEach(([blowpipe, dart]) => { - const newStrength = blowpipe.equipment.bonuses.ranged_str + dart.equipment.bonuses.ranged_str; + cross(blowpipeEntries, DARTS).forEach(([blowpipe, dart]) => { entries.push({ ...blowpipe, - label: `${blowpipe.label} (${dart.label.split(' ', 2)[0]})`, - value: `${blowpipe.value}_${dart.value}`, + label: `${blowpipe.label} (${dart.name.replace(' dart', '')})`, + value: `${blowpipe.value}_${dart.id}`, equipment: { ...blowpipe.equipment, - bonuses: { - ...blowpipe.equipment.bonuses, - ranged_str: newStrength, + itemVars: { + ...blowpipe.equipment.itemVars, + blowpipeDartId: dart.id, }, }, }); diff --git a/src/lib/Equipment.ts b/src/lib/Equipment.ts index ff0704a1..48ac49b4 100644 --- a/src/lib/Equipment.ts +++ b/src/lib/Equipment.ts @@ -1,7 +1,7 @@ import { EquipmentPiece, Player, PlayerEquipment } from '@/types/Player'; import { Monster } from '@/types/Monster'; import { keys } from '@/utils'; -import { CAST_STANCES, TOMBS_OF_AMASCUT_MONSTER_IDS } from '@/lib/constants'; +import { BLOWPIPE_IDS, CAST_STANCES, TOMBS_OF_AMASCUT_MONSTER_IDS } from '@/lib/constants'; import { sum } from 'd3-array'; import equipment from '../../cdn/json/equipment.json'; import generatedEquipmentAliases from './EquipmentAliases'; @@ -278,6 +278,15 @@ export const calculateEquipmentBonusesFromGear = (player: Player, monster: Monst }); }); + if (BLOWPIPE_IDS.includes(playerEquipment.weapon?.id || 0)) { + const dart = availableEquipment.find((e) => e.id === playerEquipment.weapon?.itemVars?.blowpipeDartId); + if (dart) { + totals.bonuses.ranged_str += dart.bonuses.ranged_str; + } else { + // todo warn user + } + } + if (playerEquipment.weapon?.name === "Tumeken's shadow" && player.style.stance !== 'Manual Cast') { const factor = TOMBS_OF_AMASCUT_MONSTER_IDS.includes(monster.id) ? 4 : 3; totals.bonuses.magic_str *= factor; diff --git a/src/lib/constants.ts b/src/lib/constants.ts index cef52bad..7b70cdbf 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -1,5 +1,10 @@ import { CombatStyleStance } from '@/types/PlayerCombatStyle'; +export const BLOWPIPE_IDS: number[] = [ + 12926, // regular + 28688, // blazing +]; + export const AKKHA_IDS = [ 11789, 11790, 11791, 11792, 11793, 11794, 11795, 11796, ]; diff --git a/src/state.tsx b/src/state.tsx index 311d55bc..e58a9541 100644 --- a/src/state.tsx +++ b/src/state.tsx @@ -122,7 +122,14 @@ export const parseLoadoutsFromImportedData = (data: ImportableData) => data.load let item: EquipmentPiece | undefined; if (Object.hasOwn(v, 'id')) { item = availableEquipment.find((eq) => eq.id === v.id); - if (!item) console.warn(`[parseLoadoutsFromImportedData] No item found for item ID ${v.id}`); + if (item) { + // include the hidden itemVars inputs that are not present on the availableEquipment store + if (Object.hasOwn(v, 'itemVars')) { + item = { ...item, itemVars: v.itemVars }; + } + } else { + console.warn(`[parseLoadoutsFromImportedData] No item found for item ID ${v.id}`); + } } // The following line will remove the item entirely if it seems to no longer exist. loadout.equipment[k as keyof typeof loadout.equipment] = item || null; diff --git a/src/types/Player.ts b/src/types/Player.ts index 9346cd98..bdb82357 100644 --- a/src/types/Player.ts +++ b/src/types/Player.ts @@ -24,6 +24,9 @@ export interface EquipmentPiece extends EquipmentStats { speed: number; category: EquipmentCategory; isTwoHanded: boolean; + itemVars?: { + blowpipeDartId?: number; + }; } /** diff --git a/src/utils.ts b/src/utils.ts index 8570bef3..75d9175a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -70,6 +70,11 @@ export const generateShortlink = async (data: ImportableData): Promise = return res.data.data; }; +// for type narrowing +export function isDefined(id: T | undefined | null): id is T { + return !!id; +} + /** * Calculates a player's combat level using their skills * @param s