From d37c2705f2d5f9e9c0d8e975500c5b16cc79e824 Mon Sep 17 00:00:00 2001 From: Cooper Morris Date: Sat, 25 Jan 2025 00:20:26 -0800 Subject: [PATCH 1/2] Removing leagues 5 code --- scripts/generateEquipment.py | 18 + src/app/components/generic/GridItem.tsx | 9 +- src/app/components/player/EchoesLeague.tsx | 123 ----- .../player/PlayerInnerContainer.tsx | 5 - .../components/results/HitDistribution.tsx | 9 - src/enums/EquipmentCategory.ts | 1 - src/lib/BaseCalc.ts | 16 +- src/lib/CalcDetails.ts | 5 - src/lib/Equipment.ts | 25 - src/lib/LeaguesV.ts | 195 -------- src/lib/NPCVsPlayerCalc.ts | 3 +- src/lib/PlayerVsNPCCalc.ts | 438 ++---------------- src/lib/constants.ts | 17 - src/lib/dists/bolts.ts | 6 +- src/public/img/league/magic_1.png | Bin 739 -> 0 bytes src/public/img/league/magic_2.png | Bin 732 -> 0 bytes src/public/img/league/magic_3.png | Bin 770 -> 0 bytes src/public/img/league/magic_4.png | Bin 776 -> 0 bytes src/public/img/league/magic_5.png | Bin 755 -> 0 bytes src/public/img/league/magic_6.png | Bin 772 -> 0 bytes src/public/img/league/melee_1.png | Bin 741 -> 0 bytes src/public/img/league/melee_2.png | Bin 744 -> 0 bytes src/public/img/league/melee_3.png | Bin 755 -> 0 bytes src/public/img/league/melee_4.png | Bin 760 -> 0 bytes src/public/img/league/melee_5.png | Bin 750 -> 0 bytes src/public/img/league/melee_6.png | Bin 758 -> 0 bytes src/public/img/league/ranged_1.png | Bin 747 -> 0 bytes src/public/img/league/ranged_2.png | Bin 752 -> 0 bytes src/public/img/league/ranged_3.png | Bin 763 -> 0 bytes src/public/img/league/ranged_4.png | Bin 748 -> 0 bytes src/public/img/league/ranged_5.png | Bin 761 -> 0 bytes src/public/img/league/ranged_6.png | Bin 769 -> 0 bytes src/state.tsx | 43 -- src/types/Player.ts | 5 - src/types/PlayerCombatStyle.ts | 1 - src/utils.ts | 12 - src/worker/worker.ts | 8 +- 37 files changed, 74 insertions(+), 865 deletions(-) delete mode 100644 src/app/components/player/EchoesLeague.tsx delete mode 100644 src/lib/LeaguesV.ts delete mode 100644 src/public/img/league/magic_1.png delete mode 100644 src/public/img/league/magic_2.png delete mode 100644 src/public/img/league/magic_3.png delete mode 100644 src/public/img/league/magic_4.png delete mode 100644 src/public/img/league/magic_5.png delete mode 100644 src/public/img/league/magic_6.png delete mode 100644 src/public/img/league/melee_1.png delete mode 100644 src/public/img/league/melee_2.png delete mode 100644 src/public/img/league/melee_3.png delete mode 100644 src/public/img/league/melee_4.png delete mode 100644 src/public/img/league/melee_5.png delete mode 100644 src/public/img/league/melee_6.png delete mode 100644 src/public/img/league/ranged_1.png delete mode 100644 src/public/img/league/ranged_2.png delete mode 100644 src/public/img/league/ranged_3.png delete mode 100644 src/public/img/league/ranged_4.png delete mode 100644 src/public/img/league/ranged_5.png delete mode 100644 src/public/img/league/ranged_6.png diff --git a/scripts/generateEquipment.py b/scripts/generateEquipment.py index 792ac4fa..e7393e4d 100644 --- a/scripts/generateEquipment.py +++ b/scripts/generateEquipment.py @@ -41,6 +41,21 @@ 'Combat style' ] +ITEMS_TO_SKIP = [ + 'The dogsword', + 'Drygore blowpipe', + 'Amulet of the monarchs', + 'Emperor ring', + 'Devil\'s element', + 'Nature\'s reprisal', + 'Gloves of the damned', + 'Crystal blessing', + 'Sunlight spear', + 'Sunlit bracers', + 'Thunder khopesh', + 'Thousand-dragon ward' +] + def getEquipmentData(): equipment = {} offset = 0 @@ -147,6 +162,9 @@ def main(): if "(Last Man Standing)" in equipment['name']: continue + if equipment['name'] in ITEMS_TO_SKIP: + continue + # Append the current equipment item to the calc's equipment list data.append(equipment) diff --git a/src/app/components/generic/GridItem.tsx b/src/app/components/generic/GridItem.tsx index 8b29b19d..5b608eaf 100644 --- a/src/app/components/generic/GridItem.tsx +++ b/src/app/components/generic/GridItem.tsx @@ -9,9 +9,6 @@ interface IGridItemProps { image: string | StaticImageData; onClick: (item: T) => void; active: boolean; - width?: number; - height?: number; - className?: string; } /** @@ -19,7 +16,7 @@ interface IGridItemProps { */ const GridItem: React.FC> = observer((props: IGridItemProps) => { const { - item, name, image, active, onClick, width, height, className, + item, name, image, active, onClick, } = props; return ( @@ -28,7 +25,7 @@ const GridItem: React.FC> = observer((p data-tooltip-id="tooltip" data-tooltip-content={name} onClick={() => onClick(item)} - className={`cursor-pointer flex justify-center items-center ${className}`} + className="cursor-pointer w-[28px] h-[23px] flex justify-center items-center" >
{active && ( @@ -36,7 +33,7 @@ const GridItem: React.FC> = observer((p className="filter drop-shadow absolute top-[-10px] left-[-12px] text-green-400 dark:text-green-200 w-5" /> )} - {name} + {name}
); diff --git a/src/app/components/player/EchoesLeague.tsx b/src/app/components/player/EchoesLeague.tsx deleted file mode 100644 index 854f075a..00000000 --- a/src/app/components/player/EchoesLeague.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import React from 'react'; -import { observer } from 'mobx-react-lite'; -import { useStore } from '@/state'; -import GridItem from '@/app/components/generic/GridItem'; -import { - MAGIC_MASTERIES, - MELEE_MASTERIES, - RANGED_MASTERIES, - MasteryStyle, - MasteryUiData, -} from '@/lib/LeaguesV'; -import NumberInput from '@/app/components/generic/NumberInput'; -import magic_2 from '@/public/img/league/magic_2.png'; - -const MasteryButton: React.FC> = observer((props) => { - const { - masteryStyle, - mastery, - name, - image, - } = props; - - const store = useStore(); - const { five: data } = store.player.leagues; - - let ix = 0; - if (masteryStyle === 'ranged') { - ix += 6; - } else if (masteryStyle === 'magic') { - ix += 12; - } - - const onClick = () => { - if (store.player.leagues.five[masteryStyle] === mastery) { - store.updatePlayer({ leagues: { five: { [masteryStyle]: 0 } } }); - } else { - store.updatePlayer({ leagues: { five: { [masteryStyle]: mastery } } }); - } - }; - - return ( - = mastery} - width={40} - height={40} - /> - ); -}); - -const EchoesLeague: React.FC = observer(() => { - const store = useStore(); - const { five: data } = store.player.leagues; - - const passives = []; - const maxMastery = Math.max(data.melee, data.ranged, data.magic); - if (maxMastery >= 3) { - passives.push('+100% accuracy'); - } - if (maxMastery >= 6) { - passives.push('+60% prayer penetration'); - } - - return ( -
-

- Masteries -

- -
- {MELEE_MASTERIES.map((r) => ())} - {RANGED_MASTERIES.map((r) => ())} - {MAGIC_MASTERIES.map((r) => ())} -
- - {data.magic >= 2 && ( -
-
- store.updatePlayer({ leagues: { five: { ticksDelayed: v } } })} - /> - - - {' '} - Extra delay ticks - {' '} - - ? - - -
-
- )} - - {(passives.length > 0) && ( -
- Passives: -
    - {passives.map((passive, i) => ( - // each index is always the same string - // eslint-disable-next-line react/no-array-index-key -
  • {passive}
  • - ))} -
-
- )} -
- ); -}); - -export default EchoesLeague; diff --git a/src/app/components/player/PlayerInnerContainer.tsx b/src/app/components/player/PlayerInnerContainer.tsx index e3b93f6d..1beb64a2 100644 --- a/src/app/components/player/PlayerInnerContainer.tsx +++ b/src/app/components/player/PlayerInnerContainer.tsx @@ -5,8 +5,6 @@ import options from '@/public/img/tabs/options.webp'; import prayer from '@/public/img/tabs/prayer.png'; import React, { useState } from 'react'; import PlayerTab from '@/app/components/player/PlayerTab'; -import league from '@/public/img/tabs/league.png'; -import EchoesLeague from '@/app/components/player/EchoesLeague'; import Equipment from './Equipment'; import Combat from './Combat'; import Skills from './Skills'; @@ -30,8 +28,6 @@ const PlayerInnerContainer: React.FC = () => { return ; case 'options': return ; - case 'league': - return ; default: break; } @@ -47,7 +43,6 @@ const PlayerInnerContainer: React.FC = () => { setSelected('equipment')} /> setSelected('prayer')} /> setSelected('options')} /> - setSelected('league')} /> {renderSelected()} diff --git a/src/app/components/results/HitDistribution.tsx b/src/app/components/results/HitDistribution.tsx index a2ddea4f..4e0678c0 100644 --- a/src/app/components/results/HitDistribution.tsx +++ b/src/app/components/results/HitDistribution.tsx @@ -14,7 +14,6 @@ import { observer } from 'mobx-react-lite'; import { max } from 'd3-array'; import { toJS } from 'mobx'; import { isDefined } from '@/utils'; -import { MeleeMastery } from '@/lib/LeaguesV'; const CustomTooltip: React.FC> = ({ active, payload, label }) => { if (active && payload && payload.length) { @@ -138,14 +137,6 @@ const HitDistribution: React.FC = observer(() => { - {store.loadouts[selectedLoadout].leagues.five.melee >= MeleeMastery.MELEE_2 && ( -

- - Echoes are intentionally omitted from the above graph for clarity, - but are included in all other calculations. - -

- )} ); diff --git a/src/enums/EquipmentCategory.ts b/src/enums/EquipmentCategory.ts index 8f7c45b4..78b5f700 100644 --- a/src/enums/EquipmentCategory.ts +++ b/src/enums/EquipmentCategory.ts @@ -18,7 +18,6 @@ export enum EquipmentCategory { CROSSBOW = 'Crossbow', DAGGER = 'Dagger', GUN = 'Gun', - MULTISTYLE = 'Multi-Style', PARTISAN = 'Partisan', PICKAXE = 'Pickaxe', POLEARM = 'Polearm', diff --git a/src/lib/BaseCalc.ts b/src/lib/BaseCalc.ts index d2bc4902..99a67a0a 100644 --- a/src/lib/BaseCalc.ts +++ b/src/lib/BaseCalc.ts @@ -331,7 +331,7 @@ export default class BaseCalc { } protected isWearingGodsword(): boolean { - return this.wearing(['Ancient godsword', 'Armadyl godsword', 'Bandos godsword', 'Saradomin godsword', 'Zamorak godsword', 'The dogsword']); + return this.wearing(['Ancient godsword', 'Armadyl godsword', 'Bandos godsword', 'Saradomin godsword', 'Zamorak godsword']); } /** @@ -382,7 +382,7 @@ export default class BaseCalc { * @see https://oldschool.runescape.wiki/w/Karil_the_Tainted%27s_equipment */ protected isWearingKarils(): boolean { - return this.wearingAll(["Karil's coif", "Karil's leathertop", "Karil's leatherskirt", "Karil's crossbow"]) && this.isWearingAnyDamnedItems(); + return this.wearingAll(["Karil's coif", "Karil's leathertop", "Karil's leatherskirt", "Karil's crossbow", 'Amulet of the damned']); } /** @@ -391,7 +391,7 @@ export default class BaseCalc { */ protected isWearingAhrims(): boolean { - return this.wearingAll(["Ahrim's staff", "Ahrim's hood", "Ahrim's robetop", "Ahrim's robeskirt"]) && this.isWearingAnyDamnedItems(); + return this.wearingAll(["Ahrim's staff", "Ahrim's hood", "Ahrim's robetop", "Ahrim's robeskirt", 'Amulet of the damned']); } /** @@ -399,11 +399,7 @@ export default class BaseCalc { * @see https://oldschool.runescape.wiki/w/Torag_the_Corrupted%27s_equipment */ protected isWearingTorags(): boolean { - return this.wearingAll(["Torag's helm", "Torag's platebody", "Torag's platelegs", "Torag's hammers"]) && this.isWearingAnyDamnedItems(); - } - - protected isWearingAnyDamnedItems(): boolean { - return this.wearing('Amulet of the damned') || this.wearing('Gloves of the damned'); + return this.wearingAll(["Torag's helm", "Torag's platebody", "Torag's platelegs", "Torag's hammers", 'Amulet of the damned']); } protected isWearingBloodMoonSet(): boolean { @@ -523,10 +519,6 @@ export default class BaseCalc { return true; } - if (this.wearing('Thunder khopesh')) { - return true; - } - return false; } diff --git a/src/lib/CalcDetails.ts b/src/lib/CalcDetails.ts index 06c532b2..3b580ff9 100644 --- a/src/lib/CalcDetails.ts +++ b/src/lib/CalcDetails.ts @@ -95,11 +95,6 @@ export enum DetailKey { NPC_ACCURACY_ROLL_BONUS = 'NPC accuracy bonus', NPC_ACCURACY_ROLL_FINAL = 'NPC accuracy roll', - // leagues relics - PLAYER_ACCURACY_LEAGUES_5_PASSIVE = 'Leagues V passive accuracy', - MAX_HIT_FOCUS_BLASTS = 'Focus blasts max hit', - MAX_HIT_ADRENALINE_CHARGES = 'Adrenaline charges max hit', - MAX_HIT_REPEAT_SHOOTER = 'Repeat shooter max hit', } export interface DetailEntry { diff --git a/src/lib/Equipment.ts b/src/lib/Equipment.ts index 75694d25..a93ddee1 100644 --- a/src/lib/Equipment.ts +++ b/src/lib/Equipment.ts @@ -254,29 +254,6 @@ export const calculateAttackSpeed = (player: Player, monster: Monster): number = } } - let activeRelic: number; - if (player.style.type === 'ranged') { - activeRelic = player.leagues.five.ranged; - } else if (player.style.type === 'magic') { - activeRelic = player.leagues.five.magic; - } else { - activeRelic = player.leagues.five.melee; - } - - if (activeRelic >= 5) { - if (attackSpeed >= 5) { - attackSpeed = Math.trunc(attackSpeed / 2); - } else { - attackSpeed = Math.ceil(attackSpeed / 2); - } - } else if (activeRelic >= 3) { - attackSpeed = Math.trunc(attackSpeed * 4 / 5); - } - - if (player.style.type === 'magic' && activeRelic >= 2) { - attackSpeed += player.leagues.five.ticksDelayed; - } - return Math.max(attackSpeed, 1); }; @@ -422,8 +399,6 @@ export const WEAPON_SPEC_COSTS: { [canonicalName: string]: number } = { 'Armadyl godsword': 50, 'Zamorak godsword': 50, 'Abyssal bludgeon': 50, - 'The dogsword': 50, - 'Thunder khopesh': 50, 'Magic shortbow': 55, 'Dark bow': 55, diff --git a/src/lib/LeaguesV.ts b/src/lib/LeaguesV.ts deleted file mode 100644 index 38ef24cb..00000000 --- a/src/lib/LeaguesV.ts +++ /dev/null @@ -1,195 +0,0 @@ -import melee_1 from '@/public/img/league/melee_1.png'; -import melee_2 from '@/public/img/league/melee_2.png'; -import melee_3 from '@/public/img/league/melee_3.png'; -import melee_4 from '@/public/img/league/melee_4.png'; -import melee_5 from '@/public/img/league/melee_5.png'; -import melee_6 from '@/public/img/league/melee_6.png'; -import ranged_1 from '@/public/img/league/ranged_1.png'; -import ranged_2 from '@/public/img/league/ranged_2.png'; -import ranged_3 from '@/public/img/league/ranged_3.png'; -import ranged_4 from '@/public/img/league/ranged_4.png'; -import ranged_5 from '@/public/img/league/ranged_5.png'; -import ranged_6 from '@/public/img/league/ranged_6.png'; -import magic_1 from '@/public/img/league/magic_1.png'; -import magic_2 from '@/public/img/league/magic_2.png'; -import magic_3 from '@/public/img/league/magic_3.png'; -import magic_4 from '@/public/img/league/magic_4.png'; -import magic_5 from '@/public/img/league/magic_5.png'; -import magic_6 from '@/public/img/league/magic_6.png'; -import { StaticImageData } from 'next/image'; - -export enum MeleeMastery { - NONE = 0, - MELEE_1, - MELEE_2, - MELEE_3, - MELEE_4, - MELEE_5, - MELEE_6, -} - -export enum RangedMastery { - NONE = 0, - RANGED_1, - RANGED_2, - RANGED_3, - RANGED_4, - RANGED_5, - RANGED_6, -} - -export enum MagicMastery { - NONE = 0, - MAGIC_1, - MAGIC_2, - MAGIC_3, - MAGIC_4, - MAGIC_5, - MAGIC_6, -} - -export interface LeaguesState { - melee: MeleeMastery; - ranged: RangedMastery; - magic: MagicMastery; - - /** for {@link MagicMastery.MAGIC_2} */ - ticksDelayed: number; - - /** for {@link RangedMastery.RANGED_2}. not shown in ui anymore but still stateful in calculations */ - attackCount: number; -} - -export type MasteryStyle = keyof Pick; - -export const defaultLeaguesState = (): LeaguesState => ({ - melee: MeleeMastery.NONE, - ranged: RangedMastery.NONE, - magic: MagicMastery.NONE, - ticksDelayed: 0, - attackCount: 2, // average, -1 relative to ui so this is 3/5 -}); - -export interface MasteryUiData { - masteryStyle: S; - mastery: LeaguesState[S]; - name: string; - image: StaticImageData; -} - -export const MELEE_MASTERIES: MasteryUiData<'melee'>[] = [ - { - name: 'Melee I', - mastery: MeleeMastery.MELEE_1, - masteryStyle: 'melee', - image: melee_1, - }, - { - name: 'Melee II', - mastery: MeleeMastery.MELEE_2, - masteryStyle: 'melee', - image: melee_2, - }, - { - name: 'Melee III', - mastery: MeleeMastery.MELEE_3, - masteryStyle: 'melee', - image: melee_3, - }, - { - name: 'Melee IV', - mastery: MeleeMastery.MELEE_4, - masteryStyle: 'melee', - image: melee_4, - }, - { - name: 'Melee V', - mastery: MeleeMastery.MELEE_5, - masteryStyle: 'melee', - image: melee_5, - }, - { - name: 'Melee VI', - mastery: MeleeMastery.MELEE_6, - masteryStyle: 'melee', - image: melee_6, - }, -]; - -export const RANGED_MASTERIES: MasteryUiData<'ranged'>[] = [ - { - name: 'Ranged I', - mastery: RangedMastery.RANGED_1, - masteryStyle: 'ranged', - image: ranged_1, - }, - { - name: 'Ranged II', - mastery: RangedMastery.RANGED_2, - masteryStyle: 'ranged', - image: ranged_2, - }, - { - name: 'Ranged III', - mastery: RangedMastery.RANGED_3, - masteryStyle: 'ranged', - image: ranged_3, - }, - { - name: 'Ranged IV', - mastery: RangedMastery.RANGED_4, - masteryStyle: 'ranged', - image: ranged_4, - }, - { - name: 'Ranged V', - mastery: RangedMastery.RANGED_5, - masteryStyle: 'ranged', - image: ranged_5, - }, - { - name: 'Ranged VI', - mastery: RangedMastery.RANGED_6, - masteryStyle: 'ranged', - image: ranged_6, - }, -]; - -export const MAGIC_MASTERIES: MasteryUiData<'magic'>[] = [ - { - name: 'Magic I', - mastery: MagicMastery.MAGIC_1, - masteryStyle: 'magic', - image: magic_1, - }, - { - name: 'Magic II', - mastery: MagicMastery.MAGIC_2, - masteryStyle: 'magic', - image: magic_2, - }, - { - name: 'Magic III', - mastery: MagicMastery.MAGIC_3, - masteryStyle: 'magic', - image: magic_3, - }, - { - name: 'Magic IV', - mastery: MagicMastery.MAGIC_4, - masteryStyle: 'magic', - image: magic_4, - }, - { - name: 'Magic V', - mastery: MagicMastery.MAGIC_5, - masteryStyle: 'magic', - image: magic_5, - }, - { - name: 'Magic VI', - mastery: MagicMastery.MAGIC_6, - masteryStyle: 'magic', - image: magic_6, - }, -]; diff --git a/src/lib/NPCVsPlayerCalc.ts b/src/lib/NPCVsPlayerCalc.ts index 922634af..0695e18a 100644 --- a/src/lib/NPCVsPlayerCalc.ts +++ b/src/lib/NPCVsPlayerCalc.ts @@ -144,8 +144,7 @@ export default class NPCVsPlayerCalc extends BaseCalc { if (this.isWearingTorags()) { const currentHealth = skills.hp + boosts.hp; const missingHealth = ((Math.round((skills.hp - currentHealth) / skills.hp * 100) / 100) * 100); - const factor = this.wearing('Gloves of the damned') ? 50 : 100; - effectiveLevel = this.trackFactor(DetailKey.PLAYER_DEFENCE_ROLL_LEVEL_TORAGS, effectiveLevel, [factor + missingHealth, factor]); + effectiveLevel = this.trackFactor(DetailKey.PLAYER_DEFENCE_ROLL_LEVEL_TORAGS, effectiveLevel, [1 + missingHealth, 100]); } if (style === 'magic') { diff --git a/src/lib/PlayerVsNPCCalc.ts b/src/lib/PlayerVsNPCCalc.ts index 00350c30..04b543fb 100644 --- a/src/lib/PlayerVsNPCCalc.ts +++ b/src/lib/PlayerVsNPCCalc.ts @@ -40,7 +40,6 @@ import { TTK_DIST_MAX_ITER_ROUNDS, USES_DEFENCE_LEVEL_FOR_MAGIC_DEFENCE_NPC_IDS, VERZIK_P1_IDS, - ZULRAH_IDS, } from '@/lib/constants'; import { EquipmentCategory } from '@/enums/EquipmentCategory'; import { DetailKey } from '@/lib/CalcDetails'; @@ -64,13 +63,9 @@ import { rubyBolts, } from '@/lib/dists/bolts'; import { burningClawDoT, burningClawSpec, dClawDist } from '@/lib/dists/claws'; -import { - LeaguesState, MagicMastery, MeleeMastery, RangedMastery, -} from '@/lib/LeaguesV'; const PARTIALLY_IMPLEMENTED_SPECS: string[] = [ 'Ancient godsword', - 'The dogsword', ]; // https://oldschool.runescape.wiki/w/Category:Weapons_with_Special_attacks @@ -217,11 +212,6 @@ export default class PlayerVsNPCCalc extends BaseCalc { const mattrs = this.monster.attributes; const { buffs } = this.player; - if (this.wearing('Crystal blessing')) { - const crystalPieces = (this.wearing('Crystal helm') ? 1 : 0) + (this.wearing('Crystal legs') ? 2 : 0) + (this.wearing('Crystal body') ? 3 : 0); - attackRoll = Math.trunc(attackRoll * (20 + crystalPieces) / 20); - } - // These bonuses do not stack with each other if (this.wearing('Amulet of avarice') && this.monster.name.startsWith('Revenant')) { const factor = [buffs.forinthrySurge ? 27 : 24, 20]; @@ -358,11 +348,6 @@ export default class PlayerVsNPCCalc extends BaseCalc { // Specific bonuses that are applied from equipment const mattrs = this.monster.attributes; - if (this.wearing('Crystal blessing')) { - const crystalPieces = (this.wearing('Crystal helm') ? 1 : 0) + (this.wearing('Crystal legs') ? 2 : 0) + (this.wearing('Crystal body') ? 3 : 0); - maxHit = Math.trunc(maxHit * (40 + crystalPieces) / 40); - } - // These bonuses do not stack with each other if (this.wearing('Amulet of avarice') && this.monster.name.startsWith('Revenant')) { const factor = [buffs.forinthrySurge ? 27 : 24, 20]; @@ -451,7 +436,7 @@ export default class PlayerVsNPCCalc extends BaseCalc { if (this.wearing('Bandos godsword')) { maxHit = this.trackFactor(DetailKey.MAX_HIT_SPEC, maxHit, [11, 10]); - } else if (this.wearing(['Armadyl godsword', 'The dogsword'])) { + } else if (this.wearing('Armadyl godsword')) { maxHit = this.trackFactor(DetailKey.MAX_HIT_SPEC, maxHit, [5, 4]); } else if (this.wearing('Dragon warhammer')) { maxHit = this.trackFactor(DetailKey.MAX_HIT_SPEC, maxHit, [3, 2]); @@ -707,11 +692,6 @@ export default class PlayerVsNPCCalc extends BaseCalc { } } - if (this.hasLeaguesMastery('ranged', RangedMastery.RANGED_2)) { - const factor = Math.max(0, Math.min(4, this.player.leagues.five.attackCount)); - maxHit = this.trackFactor(DetailKey.MAX_HIT_REPEAT_SHOOTER, maxHit, [20 + factor, 20]); - } - return [minHit, maxHit]; } @@ -807,10 +787,7 @@ export default class PlayerVsNPCCalc extends BaseCalc { const spellement = this.player.spell?.element; if (this.monster.weakness && spellement) { if (spellement === this.monster.weakness.element) { - let severity = this.monster.weakness.severity; - if (this.wearing("Devil's element")) { - severity *= 2; - } + const severity = this.monster.weakness.severity; const bonus = this.trackFactor(DetailKey.PLAYER_ACCURACY_SPELLEMENT_BONUS, baseRoll, [severity, 100]); attackRoll = this.trackAdd(DetailKey.PLAYER_ACCURACY_SPELLEMENT, attackRoll, bonus); } @@ -885,8 +862,6 @@ export default class PlayerVsNPCCalc extends BaseCalc { maxHit = Math.trunc((magicLevel * (92 + 64) + 320) / 640); } else if (this.wearing('Tecu salamander')) { maxHit = Math.trunc((magicLevel * (104 + 64) + 320) / 640); - } else if (this.wearing("Nature's reprisal")) { - maxHit = Math.max(0, Math.trunc(magicLevel / 3) - 2); } if (maxHit === 0) { @@ -955,11 +930,7 @@ export default class PlayerVsNPCCalc extends BaseCalc { const spellement = this.player.spell?.element; if (this.monster.weakness && spellement) { if (spellement === this.monster.weakness.element) { - let severity = this.monster.weakness.severity; - if (this.wearing("Devil's element")) { - severity *= 2; - } - maxHit += Math.trunc(baseMax * (severity / 100)); + maxHit += Math.trunc(baseMax * (this.monster.weakness.severity / 100)); } } @@ -974,17 +945,6 @@ export default class PlayerVsNPCCalc extends BaseCalc { maxHit = this.trackFactor(DetailKey.MAX_HIT_TOME, maxHit, [11, 10]); } - if (this.hasLeaguesMastery('magic', MagicMastery.MAGIC_2)) { - const delay = this.getAttackSpeed(); - const factor = Math.max(0, Math.min(8, delay)); - maxHit = this.trackFactor(DetailKey.MAX_HIT_FOCUS_BLASTS, maxHit, [20 + factor, 20]); - } - - if (this.hasLeaguesMastery('magic', MagicMastery.MAGIC_6)) { - const factor = Math.min(10, Math.max(0, Math.trunc(this.monster.inputs.monsterCurrentHp / 100))); - maxHit = this.trackFactor(DetailKey.MAX_HIT_ADRENALINE_CHARGES, maxHit, [100 + factor, 100]); - } - return [minHit, maxHit]; } @@ -1045,62 +1005,6 @@ export default class PlayerVsNPCCalc extends BaseCalc { return minMax; } - public getDisplayMax(): number { - if (this.hasLeaguesMastery('melee', MeleeMastery.MELEE_2)) { - return this.noInitSubCalc( - { - ...this.player, - leagues: { - ...this.player.leagues, - five: { - ...this.player.leagues.five, - melee: MeleeMastery.MELEE_1, - }, - }, - }, - this.monster, - ).getDisplayMax(); - } - - if (this.hasLeaguesMastery('ranged', RangedMastery.RANGED_2)) { - return this.noInitSubCalc( - { - ...this.player, - leagues: { - ...this.player.leagues, - five: { - ...this.player.leagues.five, - attackCount: 4, - }, - }, - }, - this.monster, - ).getDistribution().getMax(); - } - - return this.getDistribution().getMax(); - } - - public getHistogram(hideMisses: boolean = false): ReturnType { - if (this.hasLeaguesMastery('melee', MeleeMastery.MELEE_2)) { - return this.noInitSubCalc( - { - ...this.player, - leagues: { - ...this.player.leagues, - five: { - ...this.player.leagues.five, - melee: MeleeMastery.MELEE_1, - }, - }, - }, - this.monster, - ).getHistogram(hideMisses); - } - - return this.getDistribution().asHistogram(hideMisses); - } - /** * Get the max attack roll for this loadout, which is based on the player's current combat style */ @@ -1129,12 +1033,6 @@ export default class PlayerVsNPCCalc extends BaseCalc { atkRoll = this.getPlayerMaxMagicAttackRoll(); } - const leagueData = this.player.leagues.five; - const maxMastery = Math.max(leagueData.melee, leagueData.ranged, leagueData.magic); - if (maxMastery >= 3) { - atkRoll = this.trackFactor(DetailKey.PLAYER_ACCURACY_LEAGUES_5_PASSIVE, atkRoll, [2, 1]); - } - return this.track(DetailKey.PLAYER_ACCURACY_ROLL_FINAL, atkRoll); } @@ -1143,10 +1041,6 @@ export default class PlayerVsNPCCalc extends BaseCalc { return this.track(DetailKey.PLAYER_ACCURACY_FINAL, this.opts.overrides.accuracy); } - if (this.hasLeaguesMastery('ranged', RangedMastery.RANGED_6)) { - return this.track(DetailKey.PLAYER_ACCURACY_FINAL, 1.0); - } - if (VERZIK_P1_IDS.includes(this.monster.id) && this.wearing('Dawnbringer')) { this.track(DetailKey.PLAYER_ACCURACY_DAWNBRINGER, 1.0); return this.track(DetailKey.PLAYER_ACCURACY_FINAL, 1.0); @@ -1182,10 +1076,8 @@ export default class PlayerVsNPCCalc extends BaseCalc { hitChance = this.track(DetailKey.PLAYER_ACCURACY_BRIMSTONE, (0.75 * hitChance) + (0.25 * effectHitChance)); } - const fangAccuracy = this.isWearingFang() && this.player.style.type === 'stab'; - const drygoreAccuracy = this.wearing('Drygore blowpipe') && this.player.style.stance !== 'Manual Cast'; - if (fangAccuracy || drygoreAccuracy) { - if (fangAccuracy && TOMBS_OF_AMASCUT_MONSTER_IDS.includes(this.monster.id)) { + if (this.isWearingFang() && this.player.style.type === 'stab') { + if (TOMBS_OF_AMASCUT_MONSTER_IDS.includes(this.monster.id)) { hitChance = this.track(DetailKey.PLAYER_ACCURACY_FANG_TOA, 1 - (1 - hitChance) ** 2); } else { hitChance = this.track( @@ -1256,78 +1148,8 @@ export default class PlayerVsNPCCalc extends BaseCalc { const style = this.player.style.type; // standard linear - /* eslint-disable @typescript-eslint/no-shadow */ - const generateStandardDist = (acc: number, min: number, max: number): HitDistribution => { - // wrap this so we can nest it in echo dists - const rollDamageTwice = (acc: number, min: number, max: number): HitDistribution => { - // 75% chance to roll damage twice, take larger of two - const d = new HitDistribution([]); - const n = max - min + 1; - const hitProb = acc / n; - for (let i = min; i <= max; i++) { - d.addHit(new WeightedHit(hitProb * 0.75, [new Hitsplat(i)])); - for (let j = min; j <= max; j++) { - d.addHit(new WeightedHit((hitProb * 0.25) / n, [new Hitsplat(Math.max(i, j))])); - } - } - d.addHit(new WeightedHit(1 - acc, [Hitsplat.INACCURATE])); - return d.flatten(); - }; - - // const echo = (chance: number, chainN: number, maxChain: number): HitTransformer => (h) => { - // if (!h.accurate || chainN >= maxChain) { - // return HitDistribution.single(1.0, [h]); - // } - // - // const r = new HitDistribution([ - // new WeightedHit(1 - chance, [h]), - // ...HitDistribution.single(1.0, [h]) - // .scaleProbability(chance) - // .zip(rollDamageTwice(acc, min, max) - // .transform(echo(0.1, chainN + 1, maxChain))) - // .cumulative() - // .hits, - // ]); - // - // console.log(`Completed echo ${chainN} at ${global.performance.now()}`); - // return r; - // }; - if (this.hasLeaguesMastery('melee', MeleeMastery.MELEE_6)) { - const echoMax = Math.trunc(max / 2); - let echoDist = rollDamageTwice(acc * 0.2, min, echoMax); - for (let i = 1; i < 8; i++) { - echoDist = echoDist - .zip(rollDamageTwice(0.2 ** (i + 1), min, echoMax)) - .cumulative(); - } - return rollDamageTwice(acc, min, max) - .zip(echoDist) - .cumulative(); - } if (this.hasLeaguesMastery('melee', MeleeMastery.MELEE_2)) { - const echoDistSingle = rollDamageTwice(acc * 0.1, min, Math.trunc(max / 2)); - return rollDamageTwice(acc, min, max) - .zip(echoDistSingle) - .cumulative(); - } if (this.hasLeaguesMastery('melee', MeleeMastery.MELEE_1)) { - return rollDamageTwice(acc, min, max); - } - - if (this.hasLeaguesMastery('ranged', RangedMastery.RANGED_1)) { - // raise rolls below 30% to 30%, this is NOT the same as a min hit since it applies post-roll - const floor = Math.trunc(max * 3 / 10); - return HitDistribution.linear(acc, min, max).transform((h) => { - if (h.accurate && h.damage < floor) { - return HitDistribution.single(1.0, [new Hitsplat(floor, h.accurate)]); - } - return HitDistribution.single(1.0, [h]); - }); - } - - return HitDistribution.linear(acc, min, max); - }; - /* eslint-enable @typescript-eslint/no-shadow */ - - let dist = new AttackDistribution([generateStandardDist(acc, min, max)]); + const standardHitDist = HitDistribution.linear(acc, min, max); + let dist = new AttackDistribution([standardHitDist]); // Monsters that always die in one hit no matter what if (ONE_HIT_MONSTERS.includes(this.monster.id)) { @@ -1336,31 +1158,17 @@ export default class PlayerVsNPCCalc extends BaseCalc { ]); } - const maxHitMonster = (this.player.style.type === 'magic' && ALWAYS_MAX_HIT_MONSTERS.magic.includes(this.monster.id)) + // monsters that are always max hit no matter what + if ((this.player.style.type === 'magic' && ALWAYS_MAX_HIT_MONSTERS.magic.includes(this.monster.id)) || (this.isUsingMeleeStyle() && ALWAYS_MAX_HIT_MONSTERS.melee.includes(this.monster.id)) - || (this.player.style.type === 'ranged' && ALWAYS_MAX_HIT_MONSTERS.ranged.includes(this.monster.id)); - - if (this.hasLeaguesMastery('magic', MagicMastery.MAGIC_1)) { - const hasMagic6 = this.hasLeaguesMastery('magic', MagicMastery.MAGIC_6); - const critThreshold = Math.trunc(max * 9 / 10); - const critMax = Math.trunc(max * 3 / 2); - dist = dist.transform((h) => { - // magic 1 & 6 have weird complications with forced max hits - const newDmg = h.damage >= critThreshold ? Math.trunc(h.damage * 3 / 2) : h.damage; - const newMax = h.damage >= critThreshold ? critMax : max; - if (maxHitMonster || (hasMagic6 && newMax >= this.monster.inputs.monsterCurrentHp)) { - return HitDistribution.single(1.0, [new Hitsplat(newMax)]); - } - return HitDistribution.single(1.0, [new Hitsplat(newDmg)]); - }, { transformInaccurate: maxHitMonster }); - } else if (maxHitMonster) { + || (this.player.style.type === 'ranged' && ALWAYS_MAX_HIT_MONSTERS.ranged.includes(this.monster.id))) { return new AttackDistribution([HitDistribution.single(1.0, [new Hitsplat(max)])]); } if (style === 'ranged' && this.wearing('Tonalztics of ralos') && this.player.equipment.weapon?.version === 'Charged') { // roll two independent hits if (!this.opts.usingSpecialAttack) { - dist = new AttackDistribution([generateStandardDist(acc, min, max), generateStandardDist(acc, min, max)]); + dist = new AttackDistribution([standardHitDist, standardHitDist]); } else { // the defence reduction from the first hit applies to the second hit, // so we need a full subcalc with the new defence value to determine the dist @@ -1375,10 +1183,10 @@ export default class PlayerVsNPCCalc extends BaseCalc { }, })).getHitChance(); - const loweredDefHitDist = generateStandardDist(loweredDefHitAccuracy, min, max); + const loweredDefHitDist = HitDistribution.linear(loweredDefHitAccuracy, min, max); dist = dist.transform((firstHit) => { const firstHitDist = HitDistribution.single(1.0, [firstHit]); - const secondHitDist = firstHit.accurate ? loweredDefHitDist : generateStandardDist(acc, min, max); + const secondHitDist = firstHit.accurate ? loweredDefHitDist : standardHitDist; return firstHitDist.zip(secondHitDist); }); } @@ -1387,14 +1195,14 @@ export default class PlayerVsNPCCalc extends BaseCalc { if (this.isUsingMeleeStyle() && this.wearing('Gadderhammer') && mattrs.includes(MonsterAttribute.SHADE)) { dist = new AttackDistribution([ new HitDistribution([ - ...generateStandardDist(acc, min, max).scaleProbability(0.95).scaleDamage(5, 4).hits, - ...generateStandardDist(acc, min, max).scaleProbability(0.05).scaleDamage(2).hits, + ...standardHitDist.scaleProbability(0.95).scaleDamage(5, 4).hits, + ...standardHitDist.scaleProbability(0.05).scaleDamage(2).hits, ]), ]); } if (style === 'ranged' && this.wearing('Dark bow')) { - dist = new AttackDistribution([generateStandardDist(acc, min, max), generateStandardDist(acc, min, max)]); + dist = new AttackDistribution([standardHitDist, standardHitDist]); if (this.opts.usingSpecialAttack) { dist = dist.transform(flatLimitTransformer(48, min)); } @@ -1411,32 +1219,6 @@ export default class PlayerVsNPCCalc extends BaseCalc { } } - if (this.wearing('Thunder khopesh')) { - if (this.opts.usingSpecialAttack) { - // two melee hits and iff at least one hits, add a bolt hit - dist = new AttackDistribution([ - generateStandardDist(acc, min, max), - generateStandardDist(acc, min, max), - HitDistribution.linear(1 - ((1 - acc) ** 2), min, max), - ]); - } else { - const boltDist = HitDistribution.linear(1.0, 0, Math.trunc(max / 2)); - dist = dist.transform((khopeshSplat) => new HitDistribution([ - new WeightedHit(0.8, [khopeshSplat]), - ...HitDistribution.single(1.0, [khopeshSplat]) - .scaleProbability(0.2) - .zip(boltDist) - .hits, - ])); - } - } - - if (this.wearing('Sunlight spear') && this.opts.usingSpecialAttack) { - // todo I assumed this was post-roll, is it? - const factor = this.player.bonuses.prayer * 3; - dist = dist.transform(multiplyTransformer(100 + factor, 100)); - } - if (this.opts.usingSpecialAttack && this.wearing(['Dragon halberd', 'Crystal halberd']) && this.monster.size > 1) { const secondHitAttackRoll = Math.trunc(this.getMaxAttackRoll() * 3 / 4); const secondHitAcc = this.noInitSubCalc( @@ -1445,7 +1227,7 @@ export default class PlayerVsNPCCalc extends BaseCalc { { overrides: { attackRoll: secondHitAttackRoll } }, ).getHitChance(); - dist = new AttackDistribution([generateStandardDist(acc, min, max), generateStandardDist(secondHitAcc, min, max)]); + dist = new AttackDistribution([standardHitDist, HitDistribution.linear(secondHitAcc, min, max)]); } // simple multi-hit specs @@ -1458,7 +1240,7 @@ export default class PlayerVsNPCCalc extends BaseCalc { } if (hitCount !== 1) { - dist = new AttackDistribution(Array(hitCount).fill(generateStandardDist(acc, min, max))); + dist = new AttackDistribution(Array(hitCount).fill(standardHitDist)); } } @@ -1467,22 +1249,20 @@ export default class PlayerVsNPCCalc extends BaseCalc { } if (this.isUsingMeleeStyle() && this.isWearingVeracs()) { - const effectChance = this.wearing('Gloves of the damned') ? 0.5 : 0.25; dist = new AttackDistribution([ new HitDistribution([ - ...generateStandardDist(acc, min, max).scaleProbability(1 - effectChance).hits, - ...generateStandardDist(1.0, 1, max + 1).scaleProbability(effectChance).hits, + ...standardHitDist.scaleProbability(0.75).hits, + ...HitDistribution.linear(1.0, 1, max + 1).scaleProbability(0.25).hits, ]), ]); } if (style === 'ranged' && this.isWearingKarils()) { // 25% chance to deal a second hitsplat at half the damage of the first (flat, not rolled) - const effectChance = this.wearing('Gloves of the damned') ? 0.5 : 0.25; dist = dist.transform( (h) => new HitDistribution([ - new WeightedHit(1 - effectChance, [h]), - new WeightedHit(effectChance, [h, new Hitsplat(Math.trunc(h.damage / 2))]), + new WeightedHit(0.75, [h]), + new WeightedHit(0.25, [h, new Hitsplat(Math.trunc(h.damage / 2))]), ]), { transformInaccurate: false }, ); @@ -1492,14 +1272,14 @@ export default class PlayerVsNPCCalc extends BaseCalc { const hits: HitDistribution[] = []; for (let i = 0; i < Math.min(Math.max(this.monster.size, 1), 3); i++) { const splatMax = Math.trunc(max / (2 ** i)); - hits.push(generateStandardDist(acc, Math.min(min, splatMax), splatMax)); + hits.push(HitDistribution.linear(acc, Math.min(min, splatMax), splatMax)); } dist = new AttackDistribution(hits); } if (this.isUsingMeleeStyle() && this.wearing('Dual macuahuitl')) { - const secondHit = generateStandardDist(acc, min, max - Math.trunc(max / 2)); - const firstHit = new AttackDistribution([generateStandardDist(acc, min, Math.trunc(max / 2))]); + const secondHit = HitDistribution.linear(acc, 0, max - Math.trunc(max / 2)); + const firstHit = new AttackDistribution([HitDistribution.linear(acc, 0, Math.trunc(max / 2))]); dist = firstHit.transform( (h) => { if (h.accurate) { @@ -1512,16 +1292,16 @@ export default class PlayerVsNPCCalc extends BaseCalc { if (this.isUsingMeleeStyle() && this.isWearingTwoHitWeapon()) { dist = new AttackDistribution([ - generateStandardDist(acc, min, Math.trunc(max / 2)), - generateStandardDist(acc, min, max - Math.trunc(max / 2)), + HitDistribution.linear(acc, 0, Math.trunc(max / 2)), + HitDistribution.linear(acc, 0, max - Math.trunc(max / 2)), ]); } if (this.isUsingMeleeStyle() && this.isWearingKeris() && mattrs.includes(MonsterAttribute.KALPHITE)) { dist = new AttackDistribution([ new HitDistribution([ - ...generateStandardDist(acc, min, max).scaleProbability(50.0 / 51.0).hits, - ...generateStandardDist(acc, min, max).scaleProbability(1.0 / 51.0).scaleDamage(3).hits, + ...standardHitDist.scaleProbability(50.0 / 51.0).hits, + ...standardHitDist.scaleProbability(1.0 / 51.0).scaleDamage(3).hits, ]), ]); } @@ -1555,11 +1335,10 @@ export default class PlayerVsNPCCalc extends BaseCalc { } if (this.player.style.type === 'magic' && this.isWearingAhrims()) { - const effectChance = this.wearing('Gloves of the damned') ? 0.5 : 0.25; dist = dist.transform( (h) => new HitDistribution([ - new WeightedHit(1 - effectChance, [h]), - new WeightedHit(effectChance, [new Hitsplat(Math.trunc(h.damage * 13 / 10), h.accurate)]), + new WeightedHit(0.75, [h]), + new WeightedHit(0.25, [new Hitsplat(Math.trunc(h.damage * 13 / 10), h.accurate)]), ]), ); } @@ -1573,13 +1352,9 @@ export default class PlayerVsNPCCalc extends BaseCalc { } if (this.isUsingMeleeStyle() && this.isWearingDharok()) { - const baseHp = this.player.skills.hp; - const currHp = this.player.skills.hp + this.player.boosts.hp; - let factor = (baseHp - currHp) * baseHp; - if (this.wearing('Gloves of the damned')) { - factor *= 2; - } - dist = dist.scaleDamage(10000 + factor, 10000); + const newMax = this.player.skills.hp; + const curr = this.player.skills.hp + this.player.boosts.hp; + dist = dist.scaleDamage(10000 + (newMax - curr) * newMax, 10000); } if (this.isUsingMeleeStyle() && this.isWearingBerserkerNecklace() && this.isWearingTzhaarWeapon()) { @@ -1618,14 +1393,12 @@ export default class PlayerVsNPCCalc extends BaseCalc { // bolt effects const boltContext: BoltContext = { - minHit: min, maxHit: max, rangedLvl: this.player.skills.ranged + this.player.boosts.ranged, zcb: this.wearing('Zaryte crossbow'), spec: this.opts.usingSpecialAttack, kandarinDiary: this.player.buffs.kandarinDiary, monster: this.monster, - generateStandardDist, }; if (this.player.style.type === 'ranged' && this.player.equipment.weapon?.name.includes('rossbow')) { if (this.wearing(['Opal bolts (e)', 'Opal dragon bolts (e)'])) { @@ -1696,13 +1469,8 @@ export default class PlayerVsNPCCalc extends BaseCalc { } if (this.monster.name === 'Zulrah') { - // during leagues this cap has been removed. - // we're just using the presence of any league mastery at all to check - const maxMastery = Math.max(this.player.leagues.five.melee, this.player.leagues.five.ranged, this.player.leagues.five.magic); - if (maxMastery === 0) { - // https://twitter.com/JagexAsh/status/1745852774607183888 - dist = dist.transform(cappedRerollTransformer(50, 5, 45)); - } + // https://twitter.com/JagexAsh/status/1745852774607183888 + dist = dist.transform(cappedRerollTransformer(50, 5, 45)); } if (this.monster.name === 'Fragment of Seren') { // https://twitter.com/JagexAsh/status/1375037874559721474 @@ -1808,27 +1576,15 @@ export default class PlayerVsNPCCalc extends BaseCalc { if (IMMUNE_TO_MAGIC_DAMAGE_NPC_IDS.includes(monsterId) && styleType === 'magic') { return true; } - if (IMMUNE_TO_RANGED_DAMAGE_NPC_IDS.includes(monsterId) && styleType === 'ranged' - && this.monster.name !== 'Dusk (Echo)') { + if (IMMUNE_TO_RANGED_DAMAGE_NPC_IDS.includes(monsterId) && styleType === 'ranged') { return true; } if (IMMUNE_TO_MELEE_DAMAGE_NPC_IDS.includes(monsterId) && this.isUsingMeleeStyle()) { - if (ZULRAH_IDS.includes(monsterId)) { - // during leagues this immunity has been removed. - // we're just using the presence of any league mastery at all to check - // but not bothering with a range >= 2 check since we don't track that - const maxMastery = Math.max(this.player.leagues.five.melee, this.player.leagues.five.ranged, this.player.leagues.five.magic); - if (maxMastery === 0) { - return true; - } - } else { - return true; - } + return true; } if (IMMUNE_TO_NON_SALAMANDER_MELEE_DAMAGE_NPC_IDS.includes(monsterId) && this.isUsingMeleeStyle() - && this.player.equipment.weapon?.category !== EquipmentCategory.SALAMANDER - && this.player.equipment.weapon?.category !== EquipmentCategory.MULTISTYLE) { + && this.player.equipment.weapon?.category !== EquipmentCategory.SALAMANDER) { return true; } if (mattrs.includes(MonsterAttribute.VAMPYRE_3) && !this.wearingVampyrebane(MonsterAttribute.VAMPYRE_3)) { @@ -1888,30 +1644,6 @@ export default class PlayerVsNPCCalc extends BaseCalc { * Returns the expected damage per tick, based on the player's attack speed. */ public getDpt() { - if (this.hasLeaguesMastery('ranged', RangedMastery.RANGED_2)) { - const subCalc = (n: number) => this.noInitSubCalc( - { - ...this.player, - leagues: { - ...this.player.leagues, - five: { - ...this.player.leagues.five, - attackCount: n, - }, - }, - }, - this.monster, - ); - - return ( - subCalc(0).getExpectedDamage() - + subCalc(1).getExpectedDamage() - + subCalc(2).getExpectedDamage() - + subCalc(3).getExpectedDamage() - + subCalc(4).getExpectedDamage() - ) / (5 * this.getExpectedAttackSpeed()); - } - return this.getExpectedDamage() / this.getExpectedAttackSpeed(); } @@ -1977,23 +1709,6 @@ export default class PlayerVsNPCCalc extends BaseCalc { * Returns the average time-to-kill (in seconds) calculation. */ public getTtk() { - if (this.hasLeaguesMastery('magic', MagicMastery.MAGIC_6) || this.hasLeaguesMastery('ranged', RangedMastery.RANGED_2)) { - // Use slower TTK calculation - const calc = this.noInitSubCalc(this.player, this.monster); - const ttkDist = calc.getTtkDistribution(); - let accum = 0.0; - let probAccum = 0.0; - for (const [k, v] of ttkDist.entries()) { - probAccum += v; - accum += k * v; - } - - if (probAccum < (1 - TTK_DIST_EPSILON)) { - return undefined; - } - return (accum + this.getExpectedAttackSpeed() - 1) * SECONDS_PER_TICK; - } - return this.getHtk() * this.getExpectedAttackSpeed() * SECONDS_PER_TICK; } @@ -2003,11 +1718,6 @@ export default class PlayerVsNPCCalc extends BaseCalc { const ticksPerSpec = this.getAttackSpeed() * this.player.buffs.soulreaperStacks; return this.getDps() * this.getExpectedAttackSpeed() / ticksPerSpec; } - if (this.wearing('Sunlight spear')) { - // assumes using spec every time you reach 7 stacks - const ticksPerSpec = this.getAttackSpeed() * 7; - return this.getDps() * this.getExpectedAttackSpeed() / ticksPerSpec; - } const specCost = this.getSpecCost(); if (!specCost) { @@ -2065,31 +1775,7 @@ export default class PlayerVsNPCCalc extends BaseCalc { // dist attack-on-specific-tick probabilities // todo thralls, append here - const dists: DelayedHit[][] = [playerDist]; - - const hasRepeatShooter = this.hasLeaguesMastery('ranged', RangedMastery.RANGED_2); - const repeatShooterDists: DelayedHit[][] = [playerDist]; - if (hasRepeatShooter) { - // todo(leagues): this depends on getWeaponDelayProvider offsetting the attack timings - // since the implementation does not support same-tick attacks - for (let i = 1; i < 5; i++) { - repeatShooterDists.push( - this.noInitSubCalc({ - ...this.player, - leagues: { - ...this.player.leagues, - five: { - ...this.player.leagues.five, - attackCount: (this.player.leagues.five.attackCount + i) % 5, - }, - }, - }, this.monster) - .getDistribution() - .zipped - .withProbabilisticDelays(this.getWeaponDelayProvider()), - ); - } - } + const dists = [playerDist]; const attackOnTick = dists.map(() => new Float64Array(iterMax + 1)); attackOnTick.forEach((arr) => { @@ -2136,20 +1822,12 @@ export default class PlayerVsNPCCalc extends BaseCalc { // 3. for each possible hp value, const hps = tickHps[tick]; for (const [hp, hpProb] of hps.entries()) { + // this is a bit of a hack, but idk if there's a better way + const currDist: DelayedHit[] = recalcDistOnHp ? hpHitDists[hp] : dist; if (hpProb === 0) { continue; } - // this is a bit of a hack, but idk if there's a better way - let currDist: DelayedHit[]; // todo(leagues): support dynamic hp AND repeat shooter? - if (recalcDistOnHp) { - currDist = hpHitDists[hp]; - } else if (hasRepeatShooter) { - currDist = repeatShooterDists[Math.trunc(tick / speed) % 5]; - } else { - currDist = dist; - } - // 4. for each damage amount possible, for (const [wh, delay] of currDist) { const dmgProb = wh.probability; @@ -2185,15 +1863,6 @@ export default class PlayerVsNPCCalc extends BaseCalc { return baseDist; } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [baseMin, baseMax] = this.getMinAndMax(); - if (this.hasLeaguesMastery('magic', MagicMastery.MAGIC_6)) { - const critMax = Math.trunc(baseMax * 3 / 2); - if (baseMax && hp > critMax) { - return baseDist; - } - } - // a special case for optimization, ruby bolts only change dps under 500 hp // so for high health targets, avoid recomputing dist until then if (this.player.style.type === 'ranged' @@ -2254,9 +1923,6 @@ export default class PlayerVsNPCCalc extends BaseCalc { if (this.wearing('Keris partisan of the sun') && TOMBS_OF_AMASCUT_MONSTER_IDS.includes(monster.id)) { return true; } - if (this.hasLeaguesMastery('magic', MagicMastery.MAGIC_6)) { - return true; - } return false; } @@ -2295,10 +1961,6 @@ export default class PlayerVsNPCCalc extends BaseCalc { ? FeatureStatus.NOT_APPLICABLE : FeatureStatus.IMPLEMENTED; } - if (this.wearing('Sunlight spear')) { - // has no spec cost since it uses stacks - return FeatureStatus.IMPLEMENTED; - } if (PARTIALLY_IMPLEMENTED_SPECS.includes(weaponName)) { return FeatureStatus.PARTIALLY_IMPLEMENTED; @@ -2328,20 +1990,4 @@ export default class PlayerVsNPCCalc extends BaseCalc { return null; } } - - private hasLeaguesMastery< - U extends keyof Pick, - >(key: U, mastery: LeaguesState[U]) { - if (key === 'melee' && !this.isUsingMeleeStyle()) { - return false; - } - if (key === 'ranged' && this.player.style.type !== 'ranged') { - return false; - } - if (key === 'magic' && this.player.style.type !== 'magic') { - return false; - } - - return this.player.leagues.five[key] >= mastery; - } } diff --git a/src/lib/constants.ts b/src/lib/constants.ts index eaecfefe..a83f5d2b 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -3,7 +3,6 @@ import { CombatStyleStance } from '@/types/PlayerCombatStyle'; export const BLOWPIPE_IDS: number[] = [ 12926, // regular 28688, // blazing - 30374, // drygore ]; // The maximum number of loadouts that users can have. Do not lower it, else it will cause share link issues. @@ -423,19 +422,3 @@ export const HUEYCOATL_IDS = [ export const HUEYCOATL_PHASES = ['Without Pillar', 'With Pillar']; export const HUEYCOATL_PHASE_IDS = [...HUEYCOATL_HEAD_IDS, ...HUEYCOATL_TAIL_IDS]; // body can't receive pillar buff HUEYCOATL_PHASE_IDS.forEach((id) => { MONSTER_PHASES_BY_ID[id] = HUEYCOATL_PHASES; }); - -export const LEAGUES_FIVE_MOCK_ID_MAPPINGS: { [k: number]: number } = { - 1000000: 30367, // the dogsword - 1000001: 30369, // sunlight spear - 1000002: 30371, // devil's element - 1000003: 30373, // drygore blowpipe#empty - 1000004: 30374, // drygore blowpipe#charged - 1000005: 30376, // amulet of the monarchs - 1000006: 30378, // emperor ring - 1000007: 30380, // gloves of the damned - 1000008: 30382, // thousand-dragon ward - 1000009: 30384, // crystal blessing - 1000010: 30386, // sunlit bracers - 1000012: 30390, // nature's reprisal#charged - 1000013: 30392, // nature's reprisal#uncharged -}; diff --git a/src/lib/dists/bolts.ts b/src/lib/dists/bolts.ts index e3b90e3e..263171ec 100644 --- a/src/lib/dists/bolts.ts +++ b/src/lib/dists/bolts.ts @@ -7,13 +7,11 @@ import { Monster } from '@/types/Monster'; export interface BoltContext { rangedLvl: number; - minHit: number; maxHit: number; zcb: boolean; spec: boolean; kandarinDiary: boolean; monster: Monster; - generateStandardDist: (acc: number, min: number, max: number) => HitDistribution; } export type BoltTransformer = (ctx: BoltContext) => HitTransformer; @@ -54,7 +52,7 @@ export const diamondBolts: BoltTransformer = (ctx) => { const chance = 0.1 * kandarinFactor(ctx); const effectMax = Math.trunc(maxHit * (zcb ? 126 : 115) / 100); - const effectDist = ctx.generateStandardDist(1.0, ctx.minHit, effectMax); + const effectDist = HitDistribution.linear(1.0, 0, effectMax); return (h) => { if (h.accurate && zcb && spec) { return effectDist; @@ -93,7 +91,7 @@ export const onyxBolts: BoltTransformer = (ctx) => { const chance = 0.11 * kandarinFactor(ctx); const effectMax = Math.trunc(maxHit * (zcb ? 132 : 120) / 100); - const effectDist = ctx.generateStandardDist(1.0, ctx.minHit, effectMax); + const effectDist = HitDistribution.linear(1.0, 0, effectMax); return (h) => { if (!h.accurate) { return new HitDistribution([new WeightedHit(1.0, [h])]); diff --git a/src/public/img/league/magic_1.png b/src/public/img/league/magic_1.png deleted file mode 100644 index b254fd4690d33b74bd8ade99ee77b6731049fc63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 739 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxKmUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l!yxO332_;@E^#Gci-q@ z-fOO3c;drNRUTKvYhTnQ8)RjoYTP$^=ocDGHn2CHj5P0^X}LI8rzTQ0*&y@r3{X|4;I`nFKVNu_VYZn8D%MjWiG^$=lsU z_BC_iL?DN~#M9T6{ROu$1C!P(qrhsQkU(;xUm1}80K^qQ3<3d}JN(#yT4Oz3978Nl z_g>a4em@;Z?p=%pvR^t#H@&{Ee$yOl0p z{koH9U8Y}Z(eh0@gm)IT8|`DCmAv!G*7ljPg{ILb-yOJX(Y^3$AItUYiyFiDdnf1~ zo|qAGH9w`Wx5?yWg>qivwu!QDv!~8USUcrr?diUz?oO_6mXmFkJ^rc9S-4loYs#ce zXCAEkrD9$6eyhQakQc1eGv|f9cU;u19d&cZDVZ;OcAZDBwQ`Klvx(R95(xiJ$a*FQIFyZ3ifzNNbP+nqWG z&)8Y+;%}CDKIhkdXG`DD)>mv~Eu8EWx_=84=@@=Kqgpv@`cqNyr=snjW-;%4{*OU+ zlSbm5(uU{209P$>jVMV;EJ?LWE=mPb3`Pbkei>9nO2Eg!}&eOGC^_b>FVdQ&MBb@0I%g8ZvX%Q diff --git a/src/public/img/league/magic_2.png b/src/public/img/league/magic_2.png deleted file mode 100644 index d2f9c4b6c84a321a86ee4a59ce6e632b08a22b9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 732 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxKmUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l!yxO332_;@E^#Gci-q@ z-fOO3Xn5_5Dv#@l4>xPvH+tw78cQ}rn)j+pHn2CHl$D8^X}LI8rzTQ0*&y@r3{X|L>XjI1XquV@Z%-FoVOh8)+a;lDE5y z>}%%2i9il}iKnkC`wMPi1}3dnMuF8pA%WyXzcL{G0f;Mr806o88Jt>&fm*{nT^vI! zPWN7F+|;BXz#0%>IJKRTHSOE~`#;MP8;ydeOQt+LoGX17K9@~+RtlRmGqQocBM z;)_?mBG|5*v`@OT@XR}heLG_%_cNc}r&H)uIAd{tO* zXjOj7)wL;Qy*YXT8-8)$_1d;)wd&3|&ZU!nPtj!;7gn9q@6>B3`@Vj%!1a@H-*!$p zC2N!FzUk?8zwd1AJjMDVU$<2A3dP>JcY| zbM*7}`ujHE+~-d`ThCbcZ4^K8Ca&kL_Zgca75>kYY#uMNc^ojWSYi7e`=ZC{X`R>o z7Xw3DwZt`|BqgyV)hf9t6-Y4{85o)98XD>vT7(!GTbUYK85(LE7+4t??0Z;u6-7gC beoAIqC2kGp_Z-UvYGCkm^>bP0l+XkKk>eYw diff --git a/src/public/img/league/magic_3.png b/src/public/img/league/magic_3.png deleted file mode 100644 index 5d70721330bf64d38b9c92cef04e98ac6402a9f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 770 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxKmUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l!yxO332_;@E^#Gci-q@ z-fOO3$li2PmB-ca+Lsd_Zpz9;)wpl;&@YTM?^TyYzT9Qm;rDA?MpS1^DY#L8c9e>&y@r3{X|8pcf-vKn5u_VYZn8D%MjWiG^$=lsU z_BC_iL?DN~#M9T6{ROu$1C!P(qrhsQkU(;xUm1}80K^qQ900gL-M=G!$-Tb{@{9WEF5aC=Ty@TzrN)}5Vl^G{J!=q-u4#{Swu zvC)x5Zjx(zH?bAG^?N&A=bRQRpX{kyO{=DFnwa}^!}uq#5JNMC9x#c zD!C{XNHG{07@6oA8tNKagcuoHnHpLd8fqIDSQ!}XdsufBMMG|WN@iLmZVl)69Lof1 OVDNPHb6Mw<&;$UbTP$e+ diff --git a/src/public/img/league/magic_4.png b/src/public/img/league/magic_4.png deleted file mode 100644 index 05ff8c790567b15c5c9574647cc0183d27faeacb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 776 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxKmUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l!yxO332_;@E^#Gci-q@ z-fOO3c;drN!)ssIn@-BgM5*$)*0^u<&@YTM?^TyYzT9Qm;rDA?MpS1^DY#L8c9e>&y@r3{X|GzyDYY#M~mbqf359>v@f9IY|@2TZGcXR2%pQa}(17jvWS?5ud zTB)~K_)&usf(?47HNRsJjF z|C#qKcVEz>S8rsW?|y744Gnb-EkcZptxOHA3=Opn46FNS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l!yxO332_;@E^#Gci-q@ z-fOO3Xn5_*i4QkrWujDhT-lpWs!KN1xNr2(FEo~Hh&1n=X}LI8rzTQ0*&y@r3{X|GU@pX8?_6ED7=pW^j0RBMrn!@^*KT zea&1r5y)XL@$_|Nf59!xz@+ucD6kqRB#@lwR|cd%0C5EnF96~I$@v#20JY|Nx;Tbd zobJ8Uc&SN&han+AZIaiC`Lk~Rwtv3=V1RM3`|<<(Za<54s%f9{D9EGI*K1~KxXMoL zNip;0pMEgKYU&%2IVWz49yhXF>L_2@f4@;LJWBS=$*&53uH;7+#piITB~5y&-~LkC zF>BsikCM5N2ad$9%?ZnW@@7hCDdU#it(V=VZ94V#5yPb4Q2zD`hNG2}>NVsybygmK zZ+2$Joa_%bD}D-i>fMz&x8t!#)qTB(x0u3?U#l)tvA(~3PmFxpob&r8pFDdhFpb0b znC#tB?qtgyQ&^pw=KlY)+}5+`>^%vgjq9iTZ=4!y|3!rD0FUfjt2z0Pn`C~AuVvbw z#I>Sa!efiG|;Kfy)&SS}GRLxf|V)>}h&l z=Je$%&$zUO_QmvAUQh^kMk%5tkuA!l> zp+$(1v6ZQzm7$@wfq|8Q!M=xeS5Y+N=BH$)RpQoge$TN?paup{S3j3^P6W diff --git a/src/public/img/league/magic_6.png b/src/public/img/league/magic_6.png deleted file mode 100644 index 761eb21dfa2c52c8170cabfadfea4fe720978db0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 772 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxKmUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l!yxO332_;@E^#Gci-q@ z-fOO3Xn5_*i4QkrWujDhT-7BT*qct)xNr2(FN`$rooTr^SEnXYHQ8RIu)$}gosgxm zWP>YzT9Qm;rDA?MpS1^DY#L8c9e>&y@r3{X{}+E{-2yb4u_VYZn8D%MjWiG^$=lsU z_BC_iL?DN~#M9T6{ROu$1C!P(qrhsQkU(;xUm1}80K^qQd;*9AHr#zH1JqjQ>Eak- zak}?XZ`)`@wd@(lc)r-r9|MMMIUCGW^%h!7C_>ybOFAJW0 z*O7AF>Xhr#6oHywUQ@6>tzaa#N4?V9O_wW}wc>zaLkr`Ga28!i6IrCI83`uFMP#yQWQ zPD`F?J}0Pr5`TGkR&>#MTlJ#t@l)MnCc4X#m|`$jwj5OsmAL;ryOs QnLrH;p00i_>zopr03tatRR910 diff --git a/src/public/img/league/melee_1.png b/src/public/img/league/melee_1.png deleted file mode 100644 index fede2cababc171457f8e72d75425083d3000c5a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 741 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxKmUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l!yxO332_;@E^#Gci-q@ z-fOO3sLJDd;=@h$rjzQD4K?l?J@gBWB^wN{eTg*hooTr^SEnXYHCa|B%3h?f!DpqN zkfkesT9Qm;rDA?MpS1^DY#L8c9e>&y@r3{X|7+b71sTm)666=m;PC858iFdh=f?Jq@N$ZtSU^P%kAUV;m3`lYUW9z{3y_(9*R@CVJzwfB(z#4k@lJotnbZnBE}C`IFDfGc+JdT{CxB1iWa&@%yIt7{mHL=$GYJBLu)r*X5P}gxO~#mbJ;dWwmZL^ zqnXIsqMQ7DlCz~{eo$8a#oT1`i*L+U?v?zRvqwPTheiAQWrrrsIatZJmF3>8Xt}w+ z|6G>SNW53n*uEvMK>Azh7sqc!pEiHL)|P(ko`1oNS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l!yxO332_;@E^#Gci-q@ z-fOO3c;drN!)squd0f>c8*1D)dgvD#OEyHB_s+CjoU2n4shVsrQrO_L(oV?Il|L;> zCX&7Bq^wL-rDA?MpS1^DY#L8c9e>&y@r3{X{~!E0&jn~SV@Z%-FoVOh8)+a;lDE5y z>}%%2i9il}iKnkC`wMPi1}3dnMuF8pA%WyXzcL{G0f;Mr8024u&q>whAg!J*jv*GO zdoML^b5Rgrd*GzA>t)``qx=3B-hUNaUzyNv3)M# z)4orN3B7yjMc&g_c~8#8p86o0vF_j0wB#mt;PsNL zp_?-Ji%O@i5jb?UIgS11hiy|XmMR+V@V{A5pcr_l%7-WRoyw7|nNi!aaTe{3*FPn0#N_)=H?J|1{4@EJ*FE3D0x3)Gsw_~gP!-+R*Hv3xH*z~4Z z6?^`=saJN)OxV6%r8cDO=lz9qmZdI>TE>0+hV+wH_j+Qz&+I7NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l!yxO332_;@E^#Gci-q@ z-fOO3c;drNRUTJ$$%Y#DjUM`i#*z(@=Dmj3zR1c%&9q#ct5XxHnrtsp*x<9$PRNqI z>7*-vT9Qm;rDA?MpS1^DY#L8c9e>&y@r3{X{~v#=*9SD3u_VYZn8D%MjWiG^$=lsU z_BC_iL?DN~#M9T6{ROu$1C!P(qrhsQkU(;xUm1}80K^qQ900@&RtGLv0=4FPx;Tbd zobJ8Exv5!!han(fqt=pX@zYlA{cl-+C@DI7YDx-c{v#u!Z?bbXFM6=YrzkE|by7^H zO8E8XMmv^SPCYsI)YZ8s%*!<%S1%BJ6tM4?>ksd3t5*y2Y_ctw&a+y!bKUCSY@6ji zTr0e4zUh@&QE9Ksa*wz}dCJlEt{Ux}mryo;)6*A8?w!vCtdFg^ud?lRivDkzD~9h2 zro~Ru-Cvemzj%rXSBt>+l~ufxV%GEoUFPAa-e~3fiT~+beyd$3i;bB!NwdqapM9@$ z+VJ~<^1!6y6R-T5#ynLh@r3Bw$;z2qcOPXjP-F;ljh=T`%iZcDgBRn4t~(j=I-c^f zamOZj_|^Z*e+S$G7$V8oDjqi8t9i>%%s7iNS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l!yxO332_;@E^#Gci-q@ z-fOO3c;drNRUTKvYhP;IH+tw78cQ}rn)j+pHpt3E&9q#ct5XxHnrtsp*x<9$PRNqI z>7*-vT9Qm;rDA?MpS1^DY#L8c9e>&y@r3{X{|9{V-3c_Bu_VYZn8D%MjWiG^$=lsU z_BC_iL?DN~#M9T6{ROu$1C!P(qrhsQkU(;xUm1}80K^qg%y7DJ%O9ZDB2O2`5R22j zm%1l4EAX%e1PD$%s}+5q>+k>fcUszW*M?0F6zLbAGjZejM{{De&aL-6xB69=O1bN# z)vt>sV=vD+^~Ue%E5DRNr<&<6t+{Nboi7!(ne?`Mvj0uF-Bqnu`&_PHU3GBXZ;qE< zrn_F56_xHVa8$g)e>XSv^zJBib{18>i+fK`TkPE_B@&=ya$(8bJsv%;w_MT+_c~*4 zv3j>o^uOD@Sxc^!_;oIhivI8Bsdtv=)`c0u-Nq{a1MXGiPwHN*(W$im?&F>2e~xwa ztoF`vEeH|%eRiTrnh)!>Q!&?^W*MtlPdfL*&`iOXbwXOt?)FK#IZ0z{o_` z&`{UVBE-np%GA)x&`{gJz{nC}Q!>*kacelg=U66C1B0ilpUXO@geCw4 CF)3UC diff --git a/src/public/img/league/melee_5.png b/src/public/img/league/melee_5.png deleted file mode 100644 index 23147bc8b09a5fc49ffdce11bbfd80f11656a7f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 750 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxKmUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l!yxO332_;@E^#Gci-q@ z-fOO3c;drN!)squd0b^>qSPfDYTP$^=ocDGHbk2D&a_;dt5XxHnrtsp*x<9$PRP=g zKP^cnlD+9&y@r3{X|L?sPWC%2xu_VYZn8D%MjWiG^$=lsU z_BC_iL?DN~#M9T6{ROu$1C!P(qrhsQkU(;xUm1}80K^qQya0$9ioP_Y0JUa#x;Tbd zobJ8UyRAuqhao}W!cw84;#oI;+dr4xKWnB{pW?e>X8)dA_UCVc3hI1|;zYui6a$gd z9Pjr_Y^GkEd+LqflT)&%3ivdqT1?v+w~{YAY^VO3)UUTYqnnQQsf2gCT@uj!%2(cd z#dYt*o1VJTA3i+`myY}x_2S8rMfWb&p6Z&+ExPrz^P9K7*Q-ojUAlO-s@K#olh;2M?GT``9*|uk!t-qdkbIHAphl?Z{{7im*|JCYhImwxI z3TqLEmc{R%{?%5V=N);cEYA*j@H^^XO7zXg9a|iaBp$eU_QvIZX6H_G#@9{RVqbr~ zeltHO!$IF_JNS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l!yxO332_;@E^#Gci-q@ z-fOO3$li2PmB-ca+Lsd_Zpz9;)wpl;&@VKWY=|`PRhMj-X}LI8rzTQ0*&y@r3{X|2Guz?gARkSQ6wH%;50sMjD8dj+moG|qe*b?a|D-#?vo$X5i?cft_AQY^q{1-qMdr1~Kh5+` z8QNq%*fHOE&e!=ly1g@R^j7ChGYC#l|DHI7d9^`pxytdvs$(;bF{>YufAZ*Gi^a4n zpU-Txd24=JTkoWH-xE{zozMRNS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l!yxO332_;@E^#Gci-q@ z-fOO3sLJE2F4<7yzR^R!&{(n|(!BS?hnt4izR1c%u{WKZX}LI8rzTQ0*&y@r3{X|Nl9*DiCNiV@Z%-FoVOh8)+a;lDE5y z>}%%2i9il}iKnkC`wMPi1}3dnMuF8pA%WyXzcL{G0f;Mr7z8xT)3$Q}wWfNyIEGl9 z?!DAKsab)CHQ>X>MK)SFjZwe<-`~l}61{fyy)zBRl2;g~zOkOO+56$2Sx@$?TIW#` zuCi;F-Sm^DXIH9}Yl)XD+22`s=6>U|h<(px=ikeVo%Xizx8{ah6Cdn~G6^WX-w{(_ zA9B}CZ=Gb<DbE&WX^F1{=dwxo#RYA{Q2fcSkZ)`j=GrjN0%RNf#cj&CQ zm~vg_gzb0sTT9f+BscGz0}Oc864!{5l*E!$tK_0oAjM#0U}U0eXsByw5n^O)Wol?; pXsB&qU}a#i?_u3l6b-rgDVb@NxHX*Lb1V}Sx1O$kF6*2UngC*JA-Dhl diff --git a/src/public/img/league/ranged_2.png b/src/public/img/league/ranged_2.png deleted file mode 100644 index e7bd080e132ed6c754fccb079c3b728caf7682c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 752 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxKmUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l!yxO332_;@E^#Gci-q@ z-fOO3sLJE2F4=J6!_6A^jUM`ik>YzT9Qm;rDA?MpS1^DY#L8c9e>&y@r3{X|A$o8T?ZP?SQ6wH%;50sMjD8doEugaZsCO$dm zQ50ILvw8Jv=g2z?=iF`lQ?Pr|{^m3LYQEo|e{YTK>9pg2pDZ$v7K)8_>|Gs{;jpsi zmEK#gbL-w^t!t3lm!f>Tt999efKyj)6lzL$_E|-(_N(c+v{{UO}xI1-F;imw2OSvWxMtM^DVo81)8U|1WzoQDVO3;|-5nDuAKk-BIgl+aEKr{jXZ( zv5hv5J?0f_Y`-J_X0sa4Goz~cz!*?1ag8WRNi0dVN-jzTQVd20Mkcz3hPs9pAx6el uriNCAhS~-ORt5(99@bq&(U6;;l9^VCTf_N1$1;H$7(8A5T-G@yGywpO$}05$ diff --git a/src/public/img/league/ranged_3.png b/src/public/img/league/ranged_3.png deleted file mode 100644 index b126cdb86933b30852ff330917f5844d621830f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 763 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxKmUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l!yxO332_;@E^#Gci-q@ z-fOO3sLJE2F4<7yzR^R!&{(n|(!BS?hnt4izR1c%u{WKZX}LI8rzTQ0*&y@r3{X|Nl9*DiCNiV@Z%-FoVOh8)+a;lDE5y z>}%%2i9il}iKnkC`wMPi1}3dnMuF8pA%WyXzcL{G0f;MrH~@$>_)9Ae1GScVx;Tbd zobJ8UJ*`PWfGxozW@7ZAwzO~m&hPxs5H)l4IT^=~#S(so-(=@(4nFwP^5oB_MQ)R< zl~0CV-RR@j;T(BqVa%Pzn&RC?^PA7?tGRmn{@%FVIr1peCfmr&Ic{M}cK=9nR#l|ced^| zT(p0xchMX9;z!qyNbJA4d`_Et(c@{epE69ZmHBDrQ#9k*s{huL9$VN3`8@7Re#5zQ zhkEkwNm+6`mH1zu{nT^%Q;%}xImf)6_HH}WD%qLx_5&kAwZt`|BqgyV)hf9t6-Y4{ z85o)98XD>vT7(!GTbUYK85(LE7+4t??0Z;u6-7gCeoAIqC2kGp_Z-UvYGCkm^>bP0 Hl+XkKfjuQR diff --git a/src/public/img/league/ranged_4.png b/src/public/img/league/ranged_4.png deleted file mode 100644 index f75a71e1687fd9751889f8ae7b41ac6506547dbd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 748 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxKmUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l!yxO332_;@E^#Gci-q@ z-fOO3sLJDd;=@gK$%Y#DjUM`ik>v-k$1;AxUazRs3U4SBVDOIYO6(s@^QX%wmKl<{1r z)33UFmDQAsvZs#9o-p68{nYxzo73U<{=NHpt8-1gm4-j3n6yxA@GLHNht!k@wb3Tp zJ1x9jwHanz-*nCQX43trblC>g^pmxxq82}PVONh{cw|%h&HuhhF_&ju3ur7n7N9Zb zr`+yb;ncPIzUfs`x%?$tXRu#-_^wse@@2_d^FK0k4}YJsPW@;5r&alJGO>z6#WnOR@L3T?2Z61^i@k-BT7;dOH!?pi&B9UgOP!eiLRlcuAxPUk+GGjp_QSb owt<0_fx*6qbyra|NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l!yxO332_;@E^#Gci-q@ z-fOO3sLJE2F4=J6!_6A^jUM`i#*z(@=Dmj3zOXl)l$D8^X}LI8rzTQ0*&y@r3{X|9gK*aRD05SQ6wH%;50sMjD8d?bKB$h#_d*lEnB`)i{U`zM#YP!OIK}}s{7T! zIP~SB$dgAyXUR6~`f$4e%KXBOcJtFkFBUIeel~IQ=lk`ynwNB6D=};3T)A#K|Hf&Gd>3z>XuQw* zyW#uxBAw$u=1A8SzB#P_d)nm>Ddu@QBl%uT;$M8HO{YEc+^_x0mVK8eCrg#j5zwE~ zUCp^sU-S9I)1M6P>zv=Aa^7OnbD2}R-`VF(RJ~fh)rt=o4XP!s5hW>!C8<`)MX5lF z!N|bKMAy(z*U%!w$k@u%(8|zI+rYrez+m6Qx~nJ}a`RI%(<*UmIKSsuCQt)|r>mdK II;Vst0Px!}iU0rr diff --git a/src/public/img/league/ranged_6.png b/src/public/img/league/ranged_6.png deleted file mode 100644 index 4ebac19a12e76300db5d1bcc593177acf6032a3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 769 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxKmUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5ln4*-332_;@E^#Gci-q@ z-fOO3sLJDd;=|1v_l+LSEnXYHQ8RIu)$}gosgv~ ze_E1EWTj$$I-j)%TWlInP#u5T8u5hx|Nl>(eWDp?E@MfMUoeBivm0q3PLj8~i_9Dr zM|L2Gy~NYkmHh>`Fawj;E2F?_ppZavqF))1{s6=kKzss-HQq6c2m!U$dAc};Se)*? z#67K9frm99!!$@y+2`o0yZ`N;vu=>T9oT%zFiz;j%AolRw_1rr$xn`&@8`Z`hjP%J zpL_HtuU-`!d1qbC4%wfN%u?;zXXO4|v;FSgxZOIhdEG@%w*F9hIBWAtgBO$L?bx`; z)>M1u+}2F(WX3`z%jFPhttvW$ZU&{l5>+(%f!B}(fu2*a>{gVW^dy0 z6bsMOJn1U8&0MejTK=+U`oVcNDwd}|`B~onr=qF1clJ8om(rV4vp2Mi_onT~Vmy{6Y0ZHHW@>RFI;G`X>I1%N<$TCC@4^ z^?C92k=&;4AF{D=jg=F2zMjA4#KLa5^aVmOF%|!p-*s7}d~U+wZ#(lZm~Wj{E2C8G z^Dt(M;kO(2PpMiS%anX0bA09hl=nq)YGUSf#oaOwHobTME30OC#{263xhD$eEEoB_ zR;xk<`i~oi@)D_V2WBFjEl^Z)aZyj1kom*NBpo#FA92 zsBK_iWni%HVck^}4Y~O#nQ4`{HJsmbEEA}K N!PC{xWt~$(69Bh-CPDxJ diff --git a/src/state.tsx b/src/state.tsx index c4b8905a..b86add54 100644 --- a/src/state.tsx +++ b/src/state.tsx @@ -26,7 +26,6 @@ import { fetchPlayerSkills, fetchShortlinkData, getCombatStylesForCategory, - keys, PotionMap, } from '@/utils'; import { ComputeBasicRequest, ComputeReverseRequest, WorkerRequestType } from '@/worker/CalcWorkerTypes'; @@ -36,10 +35,8 @@ import { CalcWorker } from '@/worker/CalcWorker'; import { spellByName } from '@/types/Spell'; import { DEFAULT_ATTACK_SPEED, - LEAGUES_FIVE_MOCK_ID_MAPPINGS, NUMBER_OF_LOADOUTS, } from '@/lib/constants'; -import { defaultLeaguesState } from '@/lib/LeaguesV'; import { EquipmentCategory } from './enums/EquipmentCategory'; import { ARM_PRAYERS, @@ -133,9 +130,6 @@ export const generateEmptyPlayer = (name?: string): Player => ({ usingSunfireRunes: false, }, spell: null, - leagues: { - five: defaultLeaguesState(), - }, }); export const parseLoadoutsFromImportedData = (data: ImportableData) => data.loadouts.map((loadout, i) => { @@ -470,43 +464,6 @@ class GlobalState implements State { case 1: data.monster.inputs.phase = data.monster.inputs.tormentedDemonPhase; - case 2: - data.loadouts.forEach((l) => { - if (l.equipment?.weapon?.id === 1000012) { // old mock version of Nature's reprisal - l.equipment.weapon.category = EquipmentCategory.MULTISTYLE; - } - }); - - case 3: - data.loadouts.forEach((l) => { - if (!l.leagues?.five) { - l.leagues = { five: defaultLeaguesState() }; - } - }); - - case 4: - data.loadouts.forEach((l, ix) => { - if (l.equipment) { - for (const slot of keys(l.equipment)) { - const eq = l.equipment[slot]; - if (!eq?.id) { - continue; - } - - const newId = LEAGUES_FIVE_MOCK_ID_MAPPINGS[eq.id]; - if (newId) { - eq.id = newId; - console.info('mock id migration', { - ix, - slot, - oldId: eq.id, - newId, - }); - } - } - } - }); - default: } /* eslint-enable no-fallthrough */ diff --git a/src/types/Player.ts b/src/types/Player.ts index c29d24e3..fdbf184a 100644 --- a/src/types/Player.ts +++ b/src/types/Player.ts @@ -3,7 +3,6 @@ import { Prayer } from '@/enums/Prayer'; import Potion from '@/enums/Potion'; import { Spell } from '@/types/Spell'; import { PlayerCombatStyle } from '@/types/PlayerCombatStyle'; -import { LeaguesState } from '@/lib/LeaguesV'; export interface PlayerSkills { atk: number; @@ -144,8 +143,4 @@ export interface Player extends EquipmentStats { usingSunfireRunes: boolean; }; spell: Spell | null; - - leagues: { - five: LeaguesState, - }; } diff --git a/src/types/PlayerCombatStyle.ts b/src/types/PlayerCombatStyle.ts index f8b489ca..fcd0897b 100644 --- a/src/types/PlayerCombatStyle.ts +++ b/src/types/PlayerCombatStyle.ts @@ -37,7 +37,6 @@ export function getRangedDamageType(category: EquipmentCategory): RangedDamageTy return 'heavy'; case EquipmentCategory.SALAMANDER: - case EquipmentCategory.MULTISTYLE: return 'mixed'; default: diff --git a/src/utils.ts b/src/utils.ts index b7b7cb01..1f7a7f41 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -552,11 +552,6 @@ export const CombatStyleMap: { [k in EquipmentCategory]: { [k: string]: { image: Flare: { image: '290' }, Blaze: { image: '291' }, }, - [EquipmentCategory.MULTISTYLE]: { - Melee: { image: '289' }, - Ranged: { image: '290' }, - Magic: { image: '291' }, - }, [EquipmentCategory.DAGGER]: {}, [EquipmentCategory.NONE]: { Punch: { image: '247' }, @@ -621,13 +616,6 @@ export const getCombatStylesForCategory = (style: EquipmentCategory): PlayerComb { name: 'Block', type: null, stance: null }, ]; break; - case EquipmentCategory.MULTISTYLE: - ret = [ - { name: 'Melee', type: 'stab', stance: 'Aggressive' }, - { name: 'Ranged', type: 'ranged', stance: 'Rapid' }, - { name: 'Magic', type: 'magic', stance: 'Defensive' }, - ]; - break; case EquipmentCategory.PARTISAN: ret = [ { name: 'Stab', type: 'stab', stance: 'Accurate' }, diff --git a/src/worker/worker.ts b/src/worker/worker.ts index b2b9f723..b29cfbca 100644 --- a/src/worker/worker.ts +++ b/src/worker/worker.ts @@ -36,22 +36,22 @@ const computePvMValues: Handler = async (data) res.push({ npcDefRoll: calc.getNPCDefenceRoll(), - maxHit: calc.getDisplayMax(), + maxHit: calc.getDistribution().getMax(), expectedHit: calc.getDistribution().getExpectedDamage(), maxAttackRoll: calc.getMaxAttackRoll(), accuracy: calc.getHitChance(), dps: calc.getDps(), ttk: calc.getTtk(), - hitDist: calc.getHistogram(calcOpts.hitDistHideMisses), + hitDist: calc.getDistribution().asHistogram(calcOpts.hitDistHideMisses), details: calc.details, userIssues: calc.userIssues, specAccuracy: specCalc?.getHitChance(), - specMaxHit: specCalc?.getDisplayMax(), + specMaxHit: specCalc?.getMax(), specExpected: specCalc?.getExpectedDamage(), specMomentDps: specCalc?.getDps(), specFullDps: specCalc?.getSpecDps(), - specHitDist: specCalc?.getHistogram(calcOpts.hitDistHideMisses), + specHitDist: specCalc?.getDistribution().asHistogram(calcOpts.hitDistHideMisses), specDetails: specCalc?.details, }); From c8f8e56a520409911d73845345db2eb2b7adc808 Mon Sep 17 00:00:00 2001 From: LlemonDuck Date: Sat, 1 Feb 2025 20:21:42 -0500 Subject: [PATCH 2/2] remove leagues 5 shortlink on migration --- src/state.tsx | 17 +++++++++++++++++ src/types/State.ts | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/state.tsx b/src/state.tsx index b86add54..81afe9af 100644 --- a/src/state.tsx +++ b/src/state.tsx @@ -1,3 +1,5 @@ +// noinspection FallThroughInSwitchStatementJS + import { autorun, IReactionDisposer, IReactionPublic, makeAutoObservable, reaction, toJS, } from 'mobx'; @@ -464,9 +466,24 @@ class GlobalState implements State { case 1: data.monster.inputs.phase = data.monster.inputs.tormentedDemonPhase; + case 2: // reserved: used during leagues 5 + case 3: // reserved: used during leagues 5 + case 4: // reserved: used during leagues 5 + case 5: + data.loadouts.forEach((l) => { + /* eslint-disable @typescript-eslint/dot-notation */ + /* eslint-disable @typescript-eslint/no-explicit-any */ + if ((l as any)['leagues']) { + delete (l as any)['leagues']; + } + /* eslint-enable @typescript-eslint/dot-notation */ + /* eslint-enable @typescript-eslint/no-explicit-any */ + }); + default: } /* eslint-enable no-fallthrough */ + console.debug('IMPORT | ', data); if (data.monster) { let newMonster: PartialDeep = {}; diff --git a/src/types/State.ts b/src/types/State.ts index cf90da3c..ae59de81 100644 --- a/src/types/State.ts +++ b/src/types/State.ts @@ -99,7 +99,7 @@ export interface Calculator { * or any of its subproperties, are updated in a non-backwards-compatible manner, * or also in any manner that could affect the migrations required on load. */ -export const IMPORT_VERSION = 5 as const; +export const IMPORT_VERSION = 6 as const; /** * This is the state that can be exported and imported (through shortlinks).