From 800104edd634d7fb941e81806eaf65a061c89606 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Sat, 30 Dec 2023 19:00:13 -0500 Subject: [PATCH 01/46] wording changes --- src/analysis/retail/mage/fire/SPELLS.ts | 15 +++++++++++++ .../mage/fire/core/CombustionActiveTime.tsx | 6 ++--- .../fire/normalizers/CastLinkNormalizer.ts | 10 +++++++++ .../retail/mage/fire/talents/LivingBomb.tsx | 22 ++++++++++++++----- .../mage/fire/talents/MeteorCombustion.tsx | 4 ++-- 5 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/analysis/retail/mage/fire/SPELLS.ts b/src/analysis/retail/mage/fire/SPELLS.ts index 57e8a0309a8..6e37417477a 100644 --- a/src/analysis/retail/mage/fire/SPELLS.ts +++ b/src/analysis/retail/mage/fire/SPELLS.ts @@ -103,6 +103,21 @@ const spells = { name: 'Improved Scorch', icon: 'ability_mage_fierypayback', }, + LIVING_BOMB_TICK_DAMAGE: { + id: 217694, + name: 'Living Bomb', + icon: 'ability_mage_livingbomb', + }, + LIVING_BOMB_EXPLODE_DAMAGE: { + id: 244813, + name: 'Living Bomb', + icon: 'ability_mage_livingbomb', + }, + LIVING_BOMB_EXPLODE_DEBUFF: { + id: 244813, + name: 'Living Bomb', + icon: 'ability_mage_livingbomb', + }, } satisfies Record; export default spells; diff --git a/src/analysis/retail/mage/fire/core/CombustionActiveTime.tsx b/src/analysis/retail/mage/fire/core/CombustionActiveTime.tsx index cdf86aae571..b7c4e71ae65 100644 --- a/src/analysis/retail/mage/fire/core/CombustionActiveTime.tsx +++ b/src/analysis/retail/mage/fire/core/CombustionActiveTime.tsx @@ -88,7 +88,7 @@ class CombustionActiveTime extends Analyzer { } get downtimeSeconds() { - return this.buffUptime - this.combustionActiveTime(); + return (this.buffUptime - this.combustionActiveTime()) / 1000; } get percentActiveTime() { @@ -111,8 +111,8 @@ class CombustionActiveTime extends Analyzer { when(this.combustionActiveTimeThresholds).addSuggestion((suggest, actual, recommended) => suggest( <> - You spent {formatNumber(this.downtimeSeconds)} ( - {formatNumber(this.downtimeSeconds / this.buffApplies)} average per{' '} + You spent {formatNumber(this.downtimeSeconds)}s ( + {formatNumber(this.downtimeSeconds / this.buffApplies)}s average per{' '} ), not casting anything while was active. Because a large portion of your damage comes from Combustion, you should ensure that you diff --git a/src/analysis/retail/mage/fire/normalizers/CastLinkNormalizer.ts b/src/analysis/retail/mage/fire/normalizers/CastLinkNormalizer.ts index a8b4a210b9f..77ea1788566 100644 --- a/src/analysis/retail/mage/fire/normalizers/CastLinkNormalizer.ts +++ b/src/analysis/retail/mage/fire/normalizers/CastLinkNormalizer.ts @@ -24,6 +24,7 @@ export const CAST_BEGIN = 'CastBegin'; export const SPELL_CAST = 'SpellCast'; export const PRE_CAST = 'PreCast'; export const SPELL_DAMAGE = 'SpellDamage'; +export const EXPLODE_DEBUFF = 'ExplosionDebuff'; const EVENT_LINKS: EventLink[] = [ { @@ -126,6 +127,15 @@ const EVENT_LINKS: EventLink[] = [ forwardBufferMs: 600_000, //If you manage your charges, you can keep the buff up pretty much the whole fight, so 10min just in case. backwardBufferMs: CAST_BUFFER_MS, }, + { + linkingEventId: TALENTS.LIVING_BOMB_TALENT.id, + linkingEventType: EventType.Cast, + linkRelation: EXPLODE_DEBUFF, + referencedEventId: SPELLS.LIVING_BOMB_EXPLODE_DEBUFF.id, + referencedEventType: EventType.ApplyDebuff, + forwardBufferMs: 7_000, + backwardBufferMs: CAST_BUFFER_MS, + }, { reverseLinkRelation: CAST_BEGIN, linkingEventId: TALENTS.PYROBLAST_TALENT.id, diff --git a/src/analysis/retail/mage/fire/talents/LivingBomb.tsx b/src/analysis/retail/mage/fire/talents/LivingBomb.tsx index d7b7821ec94..7686c73c48e 100644 --- a/src/analysis/retail/mage/fire/talents/LivingBomb.tsx +++ b/src/analysis/retail/mage/fire/talents/LivingBomb.tsx @@ -1,7 +1,8 @@ import { Trans } from '@lingui/macro'; import TALENTS from 'common/TALENTS/mage'; import { SpellLink } from 'interface'; -import Analyzer, { Options } from 'parser/core/Analyzer'; +import Analyzer, { Options, SELECTED_PLAYER } from 'parser/core/Analyzer'; +import Events, { CastEvent } from 'parser/core/Events'; import { When, ThresholdStyle } from 'parser/core/ParseResults'; import AbilityTracker from 'parser/shared/modules/AbilityTracker'; @@ -11,11 +12,19 @@ class LivingBomb extends Analyzer { }; protected abilityTracker!: AbilityTracker; + livingBombs: { timestamp: number; targetsHit: number }[] = []; + constructor(options: Options) { super(options); this.active = this.selectedCombatant.hasTalent(TALENTS.LIVING_BOMB_TALENT); + this.addEventListener( + Events.cast.by(SELECTED_PLAYER).spell(TALENTS.LIVING_BOMB_TALENT), + this.onLivingBomb, + ); } + onLivingBomb(event: CastEvent) {} + get livingBombCasts() { return this.abilityTracker.getAbility(TALENTS.LIVING_BOMB_TALENT.id).casts; } @@ -38,11 +47,12 @@ class LivingBomb extends Analyzer { <> You cast {this.livingBombCasts} times. Although it is worth it to take the {' '} - talent, this is only to get to the talent. - On Single Target there is no benefit to casting{' '} - and is overall considered a DPS loss. On - AOE fights, it is not worth taking the {' '} - talent at all. + talent in Single Target, this is only to get to the{' '} + talent. On Single Target there is no benefit + to casting and is overall considered a + DPS loss. On AOE fights, you can get a very minor DPS increase from casting{' '} + as filler if it will hit at least 5 + targets. , ) .icon(TALENTS.LIVING_BOMB_TALENT.icon) diff --git a/src/analysis/retail/mage/fire/talents/MeteorCombustion.tsx b/src/analysis/retail/mage/fire/talents/MeteorCombustion.tsx index 370322253db..9aceb15ff31 100644 --- a/src/analysis/retail/mage/fire/talents/MeteorCombustion.tsx +++ b/src/analysis/retail/mage/fire/talents/MeteorCombustion.tsx @@ -121,8 +121,8 @@ class MeteorCombustion extends Analyzer { You failed to cast during{' '} {this.combustionWithoutMeteor} times. In order to make the most of Combustion and , you should - always cast Meteor during Combustion. If Meteor will not come off cooldown before - Combustion is available, then you should hold Meteor for Combustion. + always ensure Meteor hits the target during Combustion. If Meteor will not come off + cooldown before Combustion is available, then you should hold Meteor for Combustion. , ) .icon(TALENTS.METEOR_TALENT.icon) From 520e77895c2470ae5e135ddcb8c91804b6478ecd Mon Sep 17 00:00:00 2001 From: Sharrq Date: Sun, 31 Dec 2023 02:20:46 -0500 Subject: [PATCH 02/46] Active Time MS --- src/parser/shared/modules/AlwaysBeCasting.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/parser/shared/modules/AlwaysBeCasting.tsx b/src/parser/shared/modules/AlwaysBeCasting.tsx index 6acca2bdf48..526cdf79b95 100644 --- a/src/parser/shared/modules/AlwaysBeCasting.tsx +++ b/src/parser/shared/modules/AlwaysBeCasting.tsx @@ -46,6 +46,13 @@ class AlwaysBeCasting extends Analyzer { /** Gets active time percentage within a specified time segment. * This will not work properly unless the current timestamp advances past the end time. */ getActiveTimePercentageInWindow(start: number, end: number): number { + const windowDuration = end - start; + return this.getActiveTimeMillisecondsInWindow(start, end) / windowDuration; + } + + /** Gets active time milliseconds within a specified time segment. + * This will not work properly unless the current timestamp advances past the end time. */ + getActiveTimeMillisecondsInWindow(start: number, end: number): number { let activeTime = 0; for (let i = 0; i < this.activeTimeSegments.length; i += 1) { const seg = this.activeTimeSegments[i]; @@ -58,8 +65,7 @@ class AlwaysBeCasting extends Analyzer { const overlapEnd = Math.min(end, seg.end); activeTime += Math.max(0, overlapEnd - overlapStart); } - const windowDuration = end - start; - return activeTime / windowDuration; + return activeTime; } activeTime = 0; From 7c5fe2c30ea73e6b846ad4228c82a5fa356f0fd1 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Sun, 31 Dec 2023 02:21:07 -0500 Subject: [PATCH 03/46] Living Bomb & Active Time --- src/analysis/retail/mage/fire/CHANGELOG.tsx | 3 ++ src/analysis/retail/mage/fire/SPELLS.ts | 2 +- .../mage/fire/core/CombustionActiveTime.tsx | 32 ++++++------- .../fire/normalizers/CastLinkNormalizer.ts | 4 +- .../retail/mage/fire/talents/LivingBomb.tsx | 45 ++++++++++++------- 5 files changed, 50 insertions(+), 36 deletions(-) diff --git a/src/analysis/retail/mage/fire/CHANGELOG.tsx b/src/analysis/retail/mage/fire/CHANGELOG.tsx index 42c4c37a1f5..083ab9c42bd 100644 --- a/src/analysis/retail/mage/fire/CHANGELOG.tsx +++ b/src/analysis/retail/mage/fire/CHANGELOG.tsx @@ -6,6 +6,9 @@ import { Sharrq, ToppleTheNun } from 'CONTRIBUTORS'; // prettier-ignore export default [ + change(date(2023, 12, 31), <> Active Time had one job, and it now does it properly. And counts in seconds instead of milliseconds., Sharrq), + change(date(2023, 12, 31), <>Adjusted to be acceptable to cast in AOE at 5+ targets., Sharrq), + change(date(2023, 12, 31), <>Fixed the wording on to specify that it has to land during ., Sharrq), change(date(2023, 12, 2), <>Fixed an issue that was causing to not calculate expirations and wasted properly., Sharrq), change(date(2023, 11, 30), 'Rewrote a number of modules to vastly increase performance in M+ dungeons and prevent crashes from especially long dungeon logs.', Sharrq), change(date(2023, 8, 20), <>Added , , and to the Checklist., Sharrq), diff --git a/src/analysis/retail/mage/fire/SPELLS.ts b/src/analysis/retail/mage/fire/SPELLS.ts index 6e37417477a..30e0325ba6e 100644 --- a/src/analysis/retail/mage/fire/SPELLS.ts +++ b/src/analysis/retail/mage/fire/SPELLS.ts @@ -109,7 +109,7 @@ const spells = { icon: 'ability_mage_livingbomb', }, LIVING_BOMB_EXPLODE_DAMAGE: { - id: 244813, + id: 44461, name: 'Living Bomb', icon: 'ability_mage_livingbomb', }, diff --git a/src/analysis/retail/mage/fire/core/CombustionActiveTime.tsx b/src/analysis/retail/mage/fire/core/CombustionActiveTime.tsx index b7c4e71ae65..59bcee39d3c 100644 --- a/src/analysis/retail/mage/fire/core/CombustionActiveTime.tsx +++ b/src/analysis/retail/mage/fire/core/CombustionActiveTime.tsx @@ -57,10 +57,10 @@ class CombustionActiveTime extends Analyzer { if (!buffApply) { return; } - this.activeTime[buffApply.timestamp] = this.alwaysBeCasting.getActiveTimePercentageInWindow( - buffApply.timestamp, - event.timestamp, - ); + const combustDuration = event.timestamp - buffApply.timestamp; + this.activeTime[buffApply.timestamp] = + combustDuration - + this.alwaysBeCasting.getActiveTimeMillisecondsInWindow(buffApply.timestamp, event.timestamp); } onFightEnd(event: FightEndEvent) { @@ -71,28 +71,24 @@ class CombustionActiveTime extends Analyzer { if (!this.selectedCombatant.hasBuff(TALENTS.COMBUSTION_TALENT.id) || !buffApply) { return; } - this.activeTime[buffApply.timestamp] = this.alwaysBeCasting.getActiveTimePercentageInWindow( - buffApply.timestamp, - event.timestamp, - ); + const combustDuration = event.timestamp - buffApply.timestamp; + this.activeTime[buffApply.timestamp] = + combustDuration - + this.alwaysBeCasting.getActiveTimeMillisecondsInWindow(buffApply.timestamp, event.timestamp); } - combustionActiveTime = () => { + combustionDowntime = () => { let activeTime = 0; this.activeTime.forEach((c) => (activeTime += c)); - return activeTime; + return activeTime / 1000; }; get buffUptime() { - return this.selectedCombatant.getBuffUptime(TALENTS.COMBUSTION_TALENT.id); - } - - get downtimeSeconds() { - return (this.buffUptime - this.combustionActiveTime()) / 1000; + return this.selectedCombatant.getBuffUptime(TALENTS.COMBUSTION_TALENT.id) / 1000; } get percentActiveTime() { - return this.combustionActiveTime() / this.buffApplies; + return 1 - this.combustionDowntime() / this.buffUptime; } get combustionActiveTimeThresholds() { @@ -111,8 +107,8 @@ class CombustionActiveTime extends Analyzer { when(this.combustionActiveTimeThresholds).addSuggestion((suggest, actual, recommended) => suggest( <> - You spent {formatNumber(this.downtimeSeconds)}s ( - {formatNumber(this.downtimeSeconds / this.buffApplies)}s average per{' '} + You spent {formatNumber(this.combustionDowntime())}s ( + {formatNumber(this.combustionDowntime() / this.buffApplies)}s average per{' '} ), not casting anything while was active. Because a large portion of your damage comes from Combustion, you should ensure that you diff --git a/src/analysis/retail/mage/fire/normalizers/CastLinkNormalizer.ts b/src/analysis/retail/mage/fire/normalizers/CastLinkNormalizer.ts index 77ea1788566..9affb8a9ed6 100644 --- a/src/analysis/retail/mage/fire/normalizers/CastLinkNormalizer.ts +++ b/src/analysis/retail/mage/fire/normalizers/CastLinkNormalizer.ts @@ -128,12 +128,14 @@ const EVENT_LINKS: EventLink[] = [ backwardBufferMs: CAST_BUFFER_MS, }, { + reverseLinkRelation: SPELL_CAST, linkingEventId: TALENTS.LIVING_BOMB_TALENT.id, linkingEventType: EventType.Cast, linkRelation: EXPLODE_DEBUFF, referencedEventId: SPELLS.LIVING_BOMB_EXPLODE_DEBUFF.id, referencedEventType: EventType.ApplyDebuff, - forwardBufferMs: 7_000, + anyTarget: true, + forwardBufferMs: 7000, backwardBufferMs: CAST_BUFFER_MS, }, { diff --git a/src/analysis/retail/mage/fire/talents/LivingBomb.tsx b/src/analysis/retail/mage/fire/talents/LivingBomb.tsx index 7686c73c48e..97d2f428c43 100644 --- a/src/analysis/retail/mage/fire/talents/LivingBomb.tsx +++ b/src/analysis/retail/mage/fire/talents/LivingBomb.tsx @@ -1,11 +1,13 @@ -import { Trans } from '@lingui/macro'; import TALENTS from 'common/TALENTS/mage'; +import { formatPercentage } from 'common/format'; import { SpellLink } from 'interface'; import Analyzer, { Options, SELECTED_PLAYER } from 'parser/core/Analyzer'; -import Events, { CastEvent } from 'parser/core/Events'; +import Events, { CastEvent, GetRelatedEvents } from 'parser/core/Events'; import { When, ThresholdStyle } from 'parser/core/ParseResults'; import AbilityTracker from 'parser/shared/modules/AbilityTracker'; +const AOE_THRESHOLD = 5; + class LivingBomb extends Analyzer { static dependencies = { abilityTracker: AbilityTracker, @@ -23,21 +25,33 @@ class LivingBomb extends Analyzer { ); } - onLivingBomb(event: CastEvent) {} + onLivingBomb(event: CastEvent) { + const debuffs = GetRelatedEvents(event, 'ExplosionDebuff'); + this.livingBombs.push({ + timestamp: event.timestamp, + targetsHit: debuffs.length, + }); + } + + get badCasts() { + return this.livingBombs.filter((c) => c.targetsHit < AOE_THRESHOLD).length || 0; + } + + get castUtilization() { + return 1 - this.badCasts / this.totalCasts; + } - get livingBombCasts() { + get totalCasts() { return this.abilityTracker.getAbility(TALENTS.LIVING_BOMB_TALENT.id).casts; } get livingBombCastThresholds() { return { - actual: this.livingBombCasts, - isGreaterThan: { - minor: 0, + actual: this.castUtilization, + isLessThan: { average: 1, - major: 2, }, - style: ThresholdStyle.NUMBER, + style: ThresholdStyle.PERCENTAGE, }; } @@ -45,21 +59,20 @@ class LivingBomb extends Analyzer { when(this.livingBombCastThresholds).addSuggestion((suggest, actual, recommended) => suggest( <> - You cast {this.livingBombCasts} times. + You misused {this.badCasts} times. Although it is worth it to take the {' '} talent in Single Target, this is only to get to the{' '} talent. On Single Target there is no benefit - to casting and is overall considered a + to casting and it is overall considered a DPS loss. On AOE fights, you can get a very minor DPS increase from casting{' '} - as filler if it will hit at least 5 + as filler if it will hit at least{' '} + {AOE_THRESHOLD} targets. , ) .icon(TALENTS.LIVING_BOMB_TALENT.icon) - .actual( - {this.livingBombCasts} casts, - ) - .recommended(`${this.livingBombCasts} is recommended`), + .actual(`${formatPercentage(actual)}% utilization`) + .recommended(`${formatPercentage(recommended)}% is recommended`), ); } } From 3838575697fae096dd147fff2bd612c0a55bc8e8 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Sun, 31 Dec 2023 02:31:07 -0500 Subject: [PATCH 04/46] missing space --- src/analysis/retail/mage/fire/talents/LivingBomb.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/analysis/retail/mage/fire/talents/LivingBomb.tsx b/src/analysis/retail/mage/fire/talents/LivingBomb.tsx index 97d2f428c43..cd8f1c66a3b 100644 --- a/src/analysis/retail/mage/fire/talents/LivingBomb.tsx +++ b/src/analysis/retail/mage/fire/talents/LivingBomb.tsx @@ -66,8 +66,7 @@ class LivingBomb extends Analyzer { to casting and it is overall considered a DPS loss. On AOE fights, you can get a very minor DPS increase from casting{' '} as filler if it will hit at least{' '} - {AOE_THRESHOLD} - targets. + {AOE_THRESHOLD} targets. , ) .icon(TALENTS.LIVING_BOMB_TALENT.icon) From c5061a8bf5e45fdc53d79c5aed86664d47fe6fc7 Mon Sep 17 00:00:00 2001 From: Richard Harrah Date: Tue, 2 Jan 2024 14:44:09 -0500 Subject: [PATCH 05/46] remove Shadowlands food and augment rune support --- src/CHANGELOG.tsx | 1 + src/common/SPELLS/shadowlands/others.ts | 141 ------------------ .../retail/modules/items/FoodChecker.ts | 18 --- src/parser/shared/modules/StatTracker.ts | 3 - 4 files changed, 1 insertion(+), 162 deletions(-) delete mode 100644 src/common/SPELLS/shadowlands/others.ts diff --git a/src/CHANGELOG.tsx b/src/CHANGELOG.tsx index 152ca0363d5..b43a2b5b87c 100644 --- a/src/CHANGELOG.tsx +++ b/src/CHANGELOG.tsx @@ -32,6 +32,7 @@ import SpellLink from 'interface/SpellLink'; // prettier-ignore export default [ + change(date(2024, 1, 2), 'Remove Shadowlands food and augment rune support.', ToppleTheNun), change(date(2023, 12, 30), 'Fix errors caused by ESLint update.', ToppleTheNun), change(date(2023, 12, 28), 'Improve internal handling of phases', emallson), change(date(2023, 12, 21), <>Mark as a max rank enchant., ToppleTheNun), diff --git a/src/common/SPELLS/shadowlands/others.ts b/src/common/SPELLS/shadowlands/others.ts deleted file mode 100644 index 925c82a8763..00000000000 --- a/src/common/SPELLS/shadowlands/others.ts +++ /dev/null @@ -1,141 +0,0 @@ -import Spell from '../Spell'; - -const spells = { - ETERNAL_CAULDRON: { - id: 171281, - name: 'Eternal Cauldron', - icon: 'inv_alchemy_90_cauldron', - }, - ETERNAL_FLASK: { - id: 307166, - name: 'Eternal Flask', - icon: 'inv_potion_132', - }, - SPECTRAL_FLASK_OF_POWER: { - id: 307185, - name: 'Spectral Flask of Power', - icon: 'inv_alchemy_90_flask_green', - }, - SPECTRAL_FLASK_OF_STAMINA: { - id: 307187, - name: 'Spectral Flask of Stamina', - icon: 'inv_alchemy_90_flask_red', - }, - BUTTERSCOTCH_MARINATED_RIBS: { - id: 308430, - name: 'Butterscotch Marinated Ribs', - icon: 'inv_cooking_90_butterscotchmarinatedribs', - }, - CINNAMON_BONEFISH_STEW: { - id: 308474, - name: 'Cinnamon Bonefish Stew', - icon: 'inv_cooking_90_cinnamonbonefishstew', - }, - MEATY_APPLE_DUMPLINGS: { - id: 308504, - name: 'Meaty Apple Dumplings', - icon: 'inv_cooking_90_meatyappledumplings', - }, - SWEET_SILVERGILL_SAUSAGES: { - id: 308509, - name: 'Sweet Silvergill Sausages', - icon: 'inv_cooking_90_silvergillsausage', - }, - PICKLED_MEAT_SMOOTHIE: { - id: 308520, - name: 'Pickled Meat Smoothie', - icon: 'inv_cooking_90_pickledmeatsmoothie', - }, - SPINEFIN_SOUFFLE_AND_FRIES: { - id: 308434, - name: 'Spinefin Souffle and Fries', - icon: 'inv_cooking_90_phantasmalsoufflefries', - }, - TENEBROUS_CROWN_ROAST_ASPIC: { - id: 308488, - name: 'Tenebrous Crown Roast Aspic', - icon: 'inv_cooking_90_tenebrouscrownroastaspic', - }, - IRIDESCENT_RAVIOLI_WITH_APPLE_SAUCE: { - id: 308506, - name: 'Iridescent Ravioli with Apple Sauce', - icon: 'inv_cooking_90_crawlerravioliapplesauce', - }, - STEAK_A_LA_MODE: { - id: 308514, - name: 'Steak a la Mode', - icon: 'inv_cooking_90_steakalamode', - }, - BANANA_BEEF_PUDDING: { - id: 308525, - name: 'Banana Beef Pudding', - icon: 'inv_cooking_90_bananabeefpudding', - }, - FRIED_BONEFISH: { - id: 327860, - name: 'Fried Bonefish', - icon: 'inv_cooking_90_friedbonefish', - }, - QUIETHOUNDS: { - id: 308629, - name: 'Quiethounds', - icon: 'inv_cooking_90_quiethounds', - }, - SMOTHERED_SHANK: { - id: 327877, - name: 'Smothered Shank', - icon: 'inv_cooking_90_smotheredshank', - }, - SURPRISINGLY_PALATABLE_FEAST_INT: { - id: 327704, - name: 'Surprisingly Palatable Feast', - icon: 'inv_tradeskill_cooking_shadowlandsfeast_small01', - }, - SURPRISINGLY_PALATABLE_FEAST_AGI: { - id: 327705, - name: 'Surprisingly Palatable Feast', - icon: 'inv_tradeskill_cooking_shadowlandsfeast_small01', - }, - SURPRISINGLY_PALATABLE_FEAST_STR: { - id: 327701, - name: 'Surprisingly Palatable Feast', - icon: 'inv_tradeskill_cooking_shadowlandsfeast_small01', - }, - FEAST_OF_GLUTTONOUS_HEDONISM_INT: { - id: 327708, - name: 'Feast of Gluttonous Hedonism', - icon: 'inv_tradeskill_cooking_shadowlandsfeast_large01', - }, - FEAST_OF_GLUTTONOUS_HEDONISM_AGI: { - id: 327709, - name: 'Feast of Gluttonous Hedonism', - icon: 'inv_tradeskill_cooking_shadowlandsfeast_large01', - }, - FEAST_OF_GLUTTONOUS_HEDONISM_STR: { - id: 327706, - name: 'Feast of Gluttonous Hedonism', - icon: 'inv_tradeskill_cooking_shadowlandsfeast_large01', - }, - DRUID_BORN_ANEW: { - id: 341449, - name: 'Born Anew', - icon: 'spell_nature_reincarnation', - }, - VEILED_AUGMENT_RUNE: { - id: 347901, - name: 'Veiled Augment Rune', - icon: 'inv_misc_gem_azuredraenite_01', - }, - ADAPTIVE_ARMOR_FRAGMENT_BUFF: { - // buff procced by Adaptive Armor Fragment, a generic tanking conduit - id: 357972, - name: 'Adaptive Armor Fragment', - icon: 'inv_belt_plate_ardenweald_d_01', - }, - ETERNAL_AUGMENT_RUNE: { - id: 367405, - name: 'Eternal Augmentation', - icon: 'inv_jewelcrafting_optionalreagent_02', - }, -} satisfies Record; -export default spells; diff --git a/src/parser/retail/modules/items/FoodChecker.ts b/src/parser/retail/modules/items/FoodChecker.ts index e9a37692c6d..dd86004a113 100644 --- a/src/parser/retail/modules/items/FoodChecker.ts +++ b/src/parser/retail/modules/items/FoodChecker.ts @@ -1,25 +1,7 @@ -import SHADOWLANDS_SPELLS from 'common/SPELLS/shadowlands/others'; import SPELLS from 'common/SPELLS/dragonflight/food'; import BaseFoodChecker from 'parser/shared/modules/items/FoodChecker'; const LOWER_FOOD_IDS: number[] = [ - // Shadowlands Food - SHADOWLANDS_SPELLS.SURPRISINGLY_PALATABLE_FEAST_INT.id, - SHADOWLANDS_SPELLS.SURPRISINGLY_PALATABLE_FEAST_AGI.id, - SHADOWLANDS_SPELLS.SURPRISINGLY_PALATABLE_FEAST_STR.id, - SHADOWLANDS_SPELLS.PICKLED_MEAT_SMOOTHIE.id, - SHADOWLANDS_SPELLS.BUTTERSCOTCH_MARINATED_RIBS.id, - SHADOWLANDS_SPELLS.CINNAMON_BONEFISH_STEW.id, - SHADOWLANDS_SPELLS.MEATY_APPLE_DUMPLINGS.id, - SHADOWLANDS_SPELLS.SWEET_SILVERGILL_SAUSAGES.id, - SHADOWLANDS_SPELLS.FEAST_OF_GLUTTONOUS_HEDONISM_INT.id, - SHADOWLANDS_SPELLS.FEAST_OF_GLUTTONOUS_HEDONISM_AGI.id, - SHADOWLANDS_SPELLS.FEAST_OF_GLUTTONOUS_HEDONISM_STR.id, - SHADOWLANDS_SPELLS.BANANA_BEEF_PUDDING.id, - SHADOWLANDS_SPELLS.SPINEFIN_SOUFFLE_AND_FRIES.id, - SHADOWLANDS_SPELLS.TENEBROUS_CROWN_ROAST_ASPIC.id, - SHADOWLANDS_SPELLS.IRIDESCENT_RAVIOLI_WITH_APPLE_SAUCE.id, - SHADOWLANDS_SPELLS.STEAK_A_LA_MODE.id, // Dragonflight SPELLS.CHURNBELLY_TEA.id, ]; diff --git a/src/parser/shared/modules/StatTracker.ts b/src/parser/shared/modules/StatTracker.ts index 0bd2518d5c0..4004019bebc 100644 --- a/src/parser/shared/modules/StatTracker.ts +++ b/src/parser/shared/modules/StatTracker.ts @@ -1,7 +1,6 @@ import { formatMilliseconds } from 'common/format'; import SPELLS from 'common/SPELLS'; import CLASSIC_SPELLS from 'common/SPELLS/classic'; -import SHADOWLANDS_SPELLS from 'common/SPELLS/shadowlands/others'; import ITEMS from 'common/ITEMS'; import RACES from 'game/RACES'; import SPECS, { isRetailSpec, specMasteryCoefficient } from 'game/SPECS'; @@ -63,8 +62,6 @@ class StatTracker extends Analyzer { // endregion // region Runes - [SHADOWLANDS_SPELLS.VEILED_AUGMENT_RUNE.id]: primaryStat(18), - [SHADOWLANDS_SPELLS.ETERNAL_AUGMENT_RUNE.id]: primaryStat(18), [SPELLS.DRACONIC_AUGMENT_RUNE.id]: primaryStat(87), // endregion From a0ecee1c2fbb058a6a0a66c8a59d026f575068ba Mon Sep 17 00:00:00 2001 From: Kelton Finch Date: Tue, 2 Jan 2024 21:21:23 -0500 Subject: [PATCH 06/46] Fixed a bug where the 2nd clearcasting stack wasn't properly being counted as a gained proc --- src/analysis/retail/druid/restoration/CHANGELOG.tsx | 1 + .../restoration/modules/spells/RegrowthAndClearcasting.tsx | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/analysis/retail/druid/restoration/CHANGELOG.tsx b/src/analysis/retail/druid/restoration/CHANGELOG.tsx index 1b6cff02874..b5392e1192e 100644 --- a/src/analysis/retail/druid/restoration/CHANGELOG.tsx +++ b/src/analysis/retail/druid/restoration/CHANGELOG.tsx @@ -5,6 +5,7 @@ import { TALENTS_DRUID } from 'common/TALENTS'; import SPELLS from 'common/SPELLS'; export default [ + change(date(2024, 1, 2), <>Fixed a bug where the 2nd stack of Clearcasting wasn't being properly counted., Sref), change(date(2023, 11, 7), <>Added statistic for the ToL version of , and split healing from Grove Guardians due to this talent into the next statistic (and out of the existing Grove Guardians statistic)., Sref), change(date(2023, 11, 7), <>Updated all talent and spell values for 10.2, and updated guide text to be more appropriate for 10.2 playstyles., Sref), change(date(2023, 10, 17), <>Enable spec with 10.2 changes, Arlie), diff --git a/src/analysis/retail/druid/restoration/modules/spells/RegrowthAndClearcasting.tsx b/src/analysis/retail/druid/restoration/modules/spells/RegrowthAndClearcasting.tsx index 1833ea8bf89..0360d5eb0f4 100644 --- a/src/analysis/retail/druid/restoration/modules/spells/RegrowthAndClearcasting.tsx +++ b/src/analysis/retail/druid/restoration/modules/spells/RegrowthAndClearcasting.tsx @@ -69,6 +69,10 @@ class RegrowthAndClearcasting extends Analyzer { Events.applybuff.by(SELECTED_PLAYER).spell(SPELLS.CLEARCASTING_BUFF), this.onApplyClearcast, ); + this.addEventListener( + Events.applybuffstack.by(SELECTED_PLAYER).spell(SPELLS.CLEARCASTING_BUFF), + this.onApplyClearcast, + ); this.addEventListener( Events.refreshbuff.by(SELECTED_PLAYER).spell(SPELLS.CLEARCASTING_BUFF), this.onRefreshClearcast, From f59b5f93bb4610b4a334d90c98a9360e9316abce Mon Sep 17 00:00:00 2001 From: Sharrq Date: Wed, 3 Jan 2024 17:52:50 -0500 Subject: [PATCH 07/46] Flame Accelerant + Fireball --- src/analysis/retail/mage/fire/SPELLS.ts | 5 +++ .../retail/mage/fire/core/Combustion.tsx | 36 ++++++++++--------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/analysis/retail/mage/fire/SPELLS.ts b/src/analysis/retail/mage/fire/SPELLS.ts index 57e8a0309a8..3093fb90f18 100644 --- a/src/analysis/retail/mage/fire/SPELLS.ts +++ b/src/analysis/retail/mage/fire/SPELLS.ts @@ -103,6 +103,11 @@ const spells = { name: 'Improved Scorch', icon: 'ability_mage_fierypayback', }, + FLAME_ACCELERANT_BUFF: { + id: 203277, + name: 'Flame Accelerant', + icon: 'inv_ember', + }, } satisfies Record; export default spells; diff --git a/src/analysis/retail/mage/fire/core/Combustion.tsx b/src/analysis/retail/mage/fire/core/Combustion.tsx index e0f33ec0ca7..2b7fc804a39 100644 --- a/src/analysis/retail/mage/fire/core/Combustion.tsx +++ b/src/analysis/retail/mage/fire/core/Combustion.tsx @@ -1,4 +1,3 @@ -import { Trans } from '@lingui/macro'; import { formatNumber, formatPercentage } from 'common/format'; import SPELLS from 'common/SPELLS'; import TALENTS from 'common/TALENTS/mage'; @@ -26,6 +25,7 @@ class CombustionCasts extends Analyzer { protected abilityTracker!: AbilityTracker; hasFlameOn: boolean = this.selectedCombatant.hasTalent(TALENTS.FLAME_ON_TALENT); + hasFlameAccelerant: boolean = this.selectedCombatant.hasTalent(TALENTS.FLAME_ACCELERANT_TALENT); combustionCasts: { cast: CastEvent; precast: CastEvent | undefined; delay: number }[] = []; combustionCastEvents: CastEvent[] = []; @@ -124,7 +124,12 @@ class CombustionCasts extends Analyzer { return activeBuffs < 2 ? true : false; }) - const tooltip = `This Fireball was cast during Combustion. Since Combustion has a short duration, you are better off using your instant abilities to get as many instant/free Pyroblasts as possible. If you run out of instant abilities, cast Scorch instead since it has a shorter cast time.`; + //If the player had a Flame Accelerant proc, disregard it. + if (this.hasFlameAccelerant) { + fireballCasts = fireballCasts.filter(f => this.selectedCombatant.hasBuff(SPELLS.FLAME_ACCELERANT_BUFF.id)); + } + + const tooltip = `This Fireball was cast during Combustion. Since Combustion has a short duration, you are better off using your instant abilities to get as many instant/free Pyroblasts as possible. If you run out of instant abilities, cast Scorch instead unless you have >100% Haste (Double Lust) or you have a Flame Accelerant proc`; fireballCasts.forEach(e => e.cast && highlightInefficientCast(e.cast, tooltip)); return fireballCasts.length; @@ -238,11 +243,7 @@ class CombustionCasts extends Analyzer { , ) .icon(TALENTS.COMBUSTION_TALENT.icon) - .actual( - - {formatNumber(actual)}s Avg. Pre-Cast Delay - , - ) + .actual(`${formatNumber(actual)}s Avg. Pre-Cast Delay`) .recommended(`${recommended} is recommended`), ); when(this.fireballDuringCombustionThresholds).addSuggestion((suggest, actual, recommended) => @@ -250,19 +251,20 @@ class CombustionCasts extends Analyzer { <> On average, you cast {' '} {this.fireballCastsDuringCombustion()} times ({actual.toFixed(2)} per Combustion), during{' '} - . Combustion has a short duration, so you - are better off using instant abilities like or{' '} - . If you run out of instant cast - abilities, use instead of Fireball since it has a - shorter cast time. + . In order to get the most casts (and{' '} + + s) as possible before Combustion ends, you should use your instant abilities like + or{' '} + . If you are running low on charges of + those spells, or need to conserve charges to make it to the end of the Combustion buff, + you should cast instead of Fireball since it has a + shorter cast time. The only exception to this is if you have 100% Haste (such as during + Double Lust), or if you have a proc of{' '} + . , ) .icon(TALENTS.COMBUSTION_TALENT.icon) - .actual( - - {this.fireballDuringCombustionThresholds.actual.toFixed(2)} Casts Per Combustion - , - ) + .actual(`${actual.toFixed(2)} Casts Per Combustion`) .recommended(`${formatNumber(recommended)} is recommended`), ); } From 3b4312a212d859df5a589fb2561b62361890c5cc Mon Sep 17 00:00:00 2001 From: Sharrq Date: Wed, 3 Jan 2024 17:54:51 -0500 Subject: [PATCH 08/46] changelog --- src/analysis/retail/mage/fire/CHANGELOG.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analysis/retail/mage/fire/CHANGELOG.tsx b/src/analysis/retail/mage/fire/CHANGELOG.tsx index 42c4c37a1f5..3f7f387d8b9 100644 --- a/src/analysis/retail/mage/fire/CHANGELOG.tsx +++ b/src/analysis/retail/mage/fire/CHANGELOG.tsx @@ -6,6 +6,7 @@ import { Sharrq, ToppleTheNun } from 'CONTRIBUTORS'; // prettier-ignore export default [ + change(date(2024, 1, 3), <>Updated during to disregard casts where the player had a proc. Reworded the suggestion to include Double Lust and Flame Accelerant., Sharrq), change(date(2023, 12, 2), <>Fixed an issue that was causing to not calculate expirations and wasted properly., Sharrq), change(date(2023, 11, 30), 'Rewrote a number of modules to vastly increase performance in M+ dungeons and prevent crashes from especially long dungeon logs.', Sharrq), change(date(2023, 8, 20), <>Added , , and to the Checklist., Sharrq), From 290fb6302138db2e3318f16299768ee341bec9d6 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Wed, 3 Jan 2024 17:57:48 -0500 Subject: [PATCH 09/46] checklist tooltip --- src/analysis/retail/mage/fire/Checklist/Component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analysis/retail/mage/fire/Checklist/Component.tsx b/src/analysis/retail/mage/fire/Checklist/Component.tsx index 316c414784c..17088796a7e 100644 --- a/src/analysis/retail/mage/fire/Checklist/Component.tsx +++ b/src/analysis/retail/mage/fire/Checklist/Component.tsx @@ -70,7 +70,7 @@ const FireMageChecklist = ({ combatant, castEfficiency, thresholds }: ChecklistP Date: Thu, 4 Jan 2024 17:19:22 +1100 Subject: [PATCH 10/46] Add statistics for Echoing Tyrstone trinket. --- src/CHANGELOG.tsx | 1 + src/common/ITEMS/dragonflight/others.ts | 5 + src/common/SPELLS/dragonflight/trinkets.ts | 15 ++ src/parser/core/CombatLogParser.tsx | 2 + .../items/dragonflight/EchoingTyrstone.tsx | 219 ++++++++++++++++++ 5 files changed, 242 insertions(+) create mode 100644 src/parser/retail/modules/items/dragonflight/EchoingTyrstone.tsx diff --git a/src/CHANGELOG.tsx b/src/CHANGELOG.tsx index b43a2b5b87c..a85fa3eba76 100644 --- a/src/CHANGELOG.tsx +++ b/src/CHANGELOG.tsx @@ -32,6 +32,7 @@ import SpellLink from 'interface/SpellLink'; // prettier-ignore export default [ + change(date(2024, 1, 4), <>Add statistics for ., Arbixal), change(date(2024, 1, 2), 'Remove Shadowlands food and augment rune support.', ToppleTheNun), change(date(2023, 12, 30), 'Fix errors caused by ESLint update.', ToppleTheNun), change(date(2023, 12, 28), 'Improve internal handling of phases', emallson), diff --git a/src/common/ITEMS/dragonflight/others.ts b/src/common/ITEMS/dragonflight/others.ts index 7a8126e19e4..0049c888df9 100644 --- a/src/common/ITEMS/dragonflight/others.ts +++ b/src/common/ITEMS/dragonflight/others.ts @@ -71,6 +71,11 @@ const others = { name: 'Voice of the Silent Star', icon: 'inv_cloth_raidpriestdragon_d_01_cape', }, + ECHOING_TYRSTONE: { + id: 207552, + name: 'Echoing Tyrstone', + icon: 'ability_paladin_lightofthemartyr', + }, } satisfies Record; export default others; diff --git a/src/common/SPELLS/dragonflight/trinkets.ts b/src/common/SPELLS/dragonflight/trinkets.ts index 53ddc83f871..0633eaae868 100644 --- a/src/common/SPELLS/dragonflight/trinkets.ts +++ b/src/common/SPELLS/dragonflight/trinkets.ts @@ -71,6 +71,21 @@ const spells = { name: 'Elemental Lariat - Empowered Frost', icon: 'inv_10_elementalcombinedfoozles_frost', }, + ECHOING_TYRSTONE_HEAL: { + id: 417957, + name: 'Echoing Tyrstone', + icon: 'ability_paladin_lightofthemartyr', + }, + ECHOING_TYRSTONE_BUFF: { + id: 417939, + name: 'Echoing Tyrstone', + icon: 'ability_paladin_lightofthemartyr', + }, + ECHOING_TYRSTONE_HASTE_BUFF: { + id: 418080, + name: 'Echoing Tyrstone', + icon: 'achievement_dungeon_ulduarraid_titan_01', + }, } satisfies Record; export default spells; diff --git a/src/parser/core/CombatLogParser.tsx b/src/parser/core/CombatLogParser.tsx index da2b754da84..45fa29345a7 100644 --- a/src/parser/core/CombatLogParser.tsx +++ b/src/parser/core/CombatLogParser.tsx @@ -106,6 +106,7 @@ import UsurpedFromBeyond from 'parser/retail/modules/items/dragonflight/UsurpedF import VoiceOfTheSilentStar from 'parser/retail/modules/items/dragonflight/VoiceOfTheSilentStar'; import AmalgamsSeventhSpine from 'parser/retail/modules/items/dragonflight/AmalgamsSeventhSpine'; import ElementalLariat from 'parser/retail/modules/items/dragonflight/ElementalLariat'; +import EchoingTyrstone from 'parser/retail/modules/items/dragonflight/EchoingTyrstone'; // This prints to console anything that the DI has to do const debugDependencyInjection = false; @@ -225,6 +226,7 @@ class CombatLogParser { voiceOfTheSilentStar: VoiceOfTheSilentStar, amalgamsSeventhSpine: AmalgamsSeventhSpine, elementalLariat: ElementalLariat, + echoingTyrstone: EchoingTyrstone, // Enchants burningDevotion: BurningDevotion, diff --git a/src/parser/retail/modules/items/dragonflight/EchoingTyrstone.tsx b/src/parser/retail/modules/items/dragonflight/EchoingTyrstone.tsx new file mode 100644 index 00000000000..26df173b56d --- /dev/null +++ b/src/parser/retail/modules/items/dragonflight/EchoingTyrstone.tsx @@ -0,0 +1,219 @@ +import ITEMS from 'common/ITEMS'; +import SPELLS from 'common/SPELLS'; +import { formatNumber, formatPercentage } from 'common/format'; +import ItemLink from 'interface/ItemLink'; +import { CooldownIcon, HasteIcon } from 'interface/icons'; +import Analyzer, { Options, SELECTED_PLAYER } from 'parser/core/Analyzer'; +import Events, { ApplyBuffEvent, HealEvent, Item } from 'parser/core/Events'; +import BoringSpellValueText from 'parser/ui/BoringItemValueText'; +import ItemPercentHealingDone from 'parser/ui/ItemPercentHealingDone'; +import STATISTIC_CATEGORY from 'parser/ui/STATISTIC_CATEGORY'; +import STATISTIC_ORDER from 'parser/ui/STATISTIC_ORDER'; +import Statistic from 'parser/ui/Statistic'; + +const ECHOING_TYRSTONE_COOLDOWN = 120000; // 120sec + +// Need a better way to do this. The calculateSecondaryStat functions don't seem to line up +const HEALS_BY_ILVL: { [key: number]: EchoingTyrstoneStats } = { + 421: { heal: 181431, haste: 179 }, + 424: { heal: 189728, haste: 184 }, + 428: { heal: 201297, haste: 191 }, + 431: { heal: 210370, haste: 197 }, + 434: { heal: 219795, haste: 202 }, + 437: { heal: 229585, haste: 208 }, + 441: { heal: 243230, haste: 216 }, + 444: { heal: 253925, haste: 222 }, + 447: { heal: 265031, haste: 228 }, + 450: { heal: 276562, haste: 235 }, + 454: { heal: 292625, haste: 243 }, + 457: { heal: 305208, haste: 250 }, + 460: { heal: 318273, haste: 257 }, + 463: { heal: 331829, haste: 265 }, + 467: { heal: 350708, haste: 275 }, + 470: { heal: 365491, haste: 283 }, + 473: { heal: 380829, haste: 291 }, + 476: { heal: 396746, haste: 299 }, + 480: { heal: 418896, haste: 310 }, + 483: { heal: 436236, haste: 319 }, + 486: { heal: 454222, haste: 328 }, + 489: { heal: 472877, haste: 337 }, +}; + +class EchoingTyrstone extends Analyzer { + healAmount: number = 0; + overhealAmount: number = 0; + trinket: Item | undefined; + maxStored: number = 0; + hasteValue: number = 0; + useTracker: EchoingTyrstoneUse[] = []; + timeOnCooldown: number = 0; + + constructor(options: Options) { + super(options); + this.trinket = this.selectedCombatant.getTrinket(ITEMS.ECHOING_TYRSTONE.id); + this.active = this.trinket !== undefined; + + if (!this.active) { + return; + } + const itemLevel = this.trinket?.itemLevel || 421; + + this.maxStored = HEALS_BY_ILVL[itemLevel].heal; + this.hasteValue = HEALS_BY_ILVL[itemLevel].haste; + + this.addEventListener(Events.heal.by(SELECTED_PLAYER), this.onHeal); + + this.addEventListener( + Events.heal.by(SELECTED_PLAYER).spell(SPELLS.ECHOING_TYRSTONE_HEAL), + this.onTyrstoneHeal, + ); + + this.addEventListener( + Events.applybuff.by(SELECTED_PLAYER).spell(SPELLS.ECHOING_TYRSTONE_BUFF), + this.onTyrstoneActivate, + ); + } + + onTyrstoneHeal(event: HealEvent) { + this.healAmount += event.amount + (event.absorbed || 0); + this.overhealAmount += event.overheal || 0; + } + + onHeal(event: HealEvent) { + if (!this.selectedCombatant.hasBuff(SPELLS.ECHOING_TYRSTONE_BUFF.id, event.timestamp)) { + return; + } + + const totalHeal = event.amount + (event.overheal || 0); + + this.useTracker[this.useTracker.length - 1].heal += totalHeal * 0.3; + + if (this.useTracker[this.useTracker.length - 1].heal > this.maxStored) { + this.useTracker[this.useTracker.length - 1].heal = this.maxStored; + } + } + + onTyrstoneActivate(event: ApplyBuffEvent) { + this.useTracker.push({ + timestamp: event.timestamp, + heal: 0, + }); + + if (event.timestamp + ECHOING_TYRSTONE_COOLDOWN < this.owner.fight.end_time) { + this.timeOnCooldown += ECHOING_TYRSTONE_COOLDOWN; + } else { + this.timeOnCooldown += this.owner.fight.end_time - event.timestamp; + } + } + + getCastEfficiency() { + return this.timeOnCooldown / this.owner.fightDuration; + } + + getHasteOverFight() { + const uptime = this.selectedCombatant.getBuffUptime(SPELLS.ECHOING_TYRSTONE_HASTE_BUFF.id); + const averageHaste = (uptime / this.owner.fightDuration) * this.hasteValue; + + return Math.round(averageHaste); + } + + tooltip() { + return ( + <> +

+ was used {this.useTracker.length} times + spending {formatPercentage(this.getCastEfficiency())}% of the fight + either active or on cooldown.{' '} + + This trinket will make use of the healing when it's needed or when it is next triggered, + so it should be used off cooldown. + +

+

+ A total of {formatNumber(this.healAmount)} healing was done with{' '} + {formatNumber(this.overhealAmount)} overhealing. +

+

+ Each use of the healing also granted {formatNumber(this.hasteValue)}{' '} + haste for 15 seconds to each member of the raid. This equates to{' '} + {this.getHasteOverFight()} haste each over the length of the fight. +

+ + ); + } + + subStatistic() { + return ( + + +
+ {this.getHasteOverFight()} Haste over time (per raider) +
+ {formatPercentage(this.getCastEfficiency())}% use efficiency +
+ ); + } + + getClass(value: number) { + if (value >= this.maxStored) { + return 'good-mark'; + } else if (value >= this.maxStored / 2) { + return 'ok-mark'; + } + + return 'bad-mark'; + } + + dropdown() { + return ( + <> + + + + + + + + + + {this.useTracker.map((use, index) => ( + + + + + + ))} + +
UseTimestampHealing Stored
{index + 1}{this.owner.formatTimestamp(use.timestamp)}{formatNumber(use.heal)}
+ + ); + } + + statistic() { + return ( + + {this.subStatistic()} + + ); + } +} + +export default EchoingTyrstone; + +interface EchoingTyrstoneUse { + /** Cast's timestamp */ + timestamp: number; + /** Heal stored during use */ + heal: number; +} + +interface EchoingTyrstoneStats { + heal: number; + haste: number; +} From 6e4cab4a0dde8054199a510ed8defa1ef8102299 Mon Sep 17 00:00:00 2001 From: Richard Harrah Date: Thu, 4 Jan 2024 13:30:14 -0500 Subject: [PATCH 11/46] add uptime graph to Guides --- .../retail/demonhunter/havoc/CHANGELOG.tsx | 1 + .../retail/demonhunter/havoc/Guide.tsx | 29 +++++++++++++++-- .../retail/demonhunter/havoc/constants.ts | 2 -- .../AcceleratingBladeExplanation.tsx | 19 ++++++------ .../retail/demonhunter/shared/CHANGELOG.tsx | 1 + .../retail/demonhunter/vengeance/Guide.tsx | 31 +++++++++++++++++-- 6 files changed, 66 insertions(+), 17 deletions(-) diff --git a/src/analysis/retail/demonhunter/havoc/CHANGELOG.tsx b/src/analysis/retail/demonhunter/havoc/CHANGELOG.tsx index 150e58b6eca..bec9063bd8b 100644 --- a/src/analysis/retail/demonhunter/havoc/CHANGELOG.tsx +++ b/src/analysis/retail/demonhunter/havoc/CHANGELOG.tsx @@ -7,6 +7,7 @@ import SHARED_CHANGELOG from 'analysis/retail/demonhunter/shared/CHANGELOG'; // prettier-ignore export default [ + change(date(2024, 1, 4), <>Update explanation for to match 10.2 logic., ToppleTheNun), change(date(2023, 7, 13), 'Disable checklist.', ToppleTheNun), change(date(2023, 7, 11), 'Update for 10.1.5.', ToppleTheNun), change(date(2023, 6, 17), <>Fix damage calculations for ., ToppleTheNun), diff --git a/src/analysis/retail/demonhunter/havoc/Guide.tsx b/src/analysis/retail/demonhunter/havoc/Guide.tsx index af71da56dcc..b673eb07388 100644 --- a/src/analysis/retail/demonhunter/havoc/Guide.tsx +++ b/src/analysis/retail/demonhunter/havoc/Guide.tsx @@ -16,6 +16,9 @@ import { } from './modules/resourcetracker/FuryTracker'; import FuryCapWaste from './guide/FuryCapWaste'; import HideGoodCastsToggle from 'interface/guide/components/HideGoodCastsToggle'; +import { PerformanceStrong } from 'analysis/retail/priest/shadow/modules/guide/ExtraComponents'; +import { formatPercentage } from 'common/format'; +import ActiveTimeGraph from 'parser/ui/ActiveTimeGraph'; export default function Guide({ modules, events, info }: GuideProps) { return ( @@ -28,12 +31,12 @@ export default function Guide({ modules, events, info }: GuideProps) { +function ResourceUsageSection({ info, modules }: GuideProps) { const percentAtFuryCap = modules.furyTracker.percentAtCap; const percentAtFuryCapPerformance = modules.furyTracker.percentAtCapPerformance; const furyWasted = modules.furyTracker.wasted; return ( -
+

Havoc's primary resource is . You should avoid @@ -50,6 +53,28 @@ function ResourceUsageSection({ modules }: GuideProps) { /> {modules.furyGraph.plot} + +

+ + Continuously casting throughout an encounter is the single most important thing for + achieving good DPS. + +
+ Some fights have unavoidable downtime due to phase transitions and the like, so in these + cases 0% downtime will not be possible - do the best you can. +

+

+ Active Time:{' '} + + {formatPercentage(modules.alwaysBeCasting.activeTimePercentage, 1)}% + {' '} +

+ +
); } diff --git a/src/analysis/retail/demonhunter/havoc/constants.ts b/src/analysis/retail/demonhunter/havoc/constants.ts index 4f4bde1de96..aba37c46094 100644 --- a/src/analysis/retail/demonhunter/havoc/constants.ts +++ b/src/analysis/retail/demonhunter/havoc/constants.ts @@ -22,6 +22,4 @@ export const GROWING_INFERNO_SCALING = [0, 0.08, 0.15]; export const BURNING_WOUND_SCALING = [0, 0.4]; -export const ACCELERATING_BLADE_SCALING = [0, 0.2]; - export const MOMENTUM_SCALING = [0, 0.08]; diff --git a/src/analysis/retail/demonhunter/havoc/modules/spells/ThrowGlaive/AcceleratingBladeExplanation.tsx b/src/analysis/retail/demonhunter/havoc/modules/spells/ThrowGlaive/AcceleratingBladeExplanation.tsx index c634fae1dea..9097eb069da 100644 --- a/src/analysis/retail/demonhunter/havoc/modules/spells/ThrowGlaive/AcceleratingBladeExplanation.tsx +++ b/src/analysis/retail/demonhunter/havoc/modules/spells/ThrowGlaive/AcceleratingBladeExplanation.tsx @@ -2,27 +2,26 @@ import { useInfo } from 'interface/guide'; import TALENTS from 'common/TALENTS/demonhunter'; import SpellLink from 'interface/SpellLink'; import { formatPercentage } from 'common/format'; -import { ACCELERATING_BLADE_SCALING } from 'analysis/retail/demonhunter/havoc/constants'; import Tooltip from 'interface/Tooltip'; import InformationIcon from 'interface/icons/Information'; +export const SCALING_PER_TARGET_HIT = 0.3; +export const INITIAL_HIT_SCALING = 0.6; + export const AcceleratingBladeExplanation = () => { const info = useInfo(); if (!info || !info.combatant.hasTalent(TALENTS.ACCELERATED_BLADE_TALENT)) { return null; } - const scaling = - ACCELERATING_BLADE_SCALING[info.combatant.getTalentRank(TALENTS.ACCELERATED_BLADE_TALENT)]; - const perTargetScaling = 1 + scaling; + const primaryTargetScalingValue = 1 + INITIAL_HIT_SCALING; + const secondaryTargetScalingValue = primaryTargetScalingValue * (1 - SCALING_PER_TARGET_HIT); + const tertiaryTargetScalingValue = secondaryTargetScalingValue * (1 - SCALING_PER_TARGET_HIT); // multiplicative scaling - const primaryTargetScaling = formatPercentage(perTargetScaling, 0); - const secondaryTargetScaling = formatPercentage(perTargetScaling * perTargetScaling, 0); - const tertiaryTargetScaling = formatPercentage( - perTargetScaling * perTargetScaling * perTargetScaling, - 0, - ); + const primaryTargetScaling = formatPercentage(primaryTargetScalingValue, 0); + const secondaryTargetScaling = formatPercentage(secondaryTargetScalingValue, 0); + const tertiaryTargetScaling = formatPercentage(tertiaryTargetScalingValue, 0); return (
  • diff --git a/src/analysis/retail/demonhunter/shared/CHANGELOG.tsx b/src/analysis/retail/demonhunter/shared/CHANGELOG.tsx index 9d60043d7dd..e2c4e4a4919 100644 --- a/src/analysis/retail/demonhunter/shared/CHANGELOG.tsx +++ b/src/analysis/retail/demonhunter/shared/CHANGELOG.tsx @@ -8,6 +8,7 @@ import { ResourceLink } from 'interface'; // prettier-ignore export default [ + change(date(2024, 1, 4), 'Add uptime graph.', ToppleTheNun), change(date(2023, 11, 6), 'Mark as updated for 10.2.', ToppleTheNun), change(date(2023, 10, 9), 'Update ability cooldowns and scaling in spellbooks.', ToppleTheNun), change(date(2023, 10, 9), 'Start updating for 10.2.', ToppleTheNun), diff --git a/src/analysis/retail/demonhunter/vengeance/Guide.tsx b/src/analysis/retail/demonhunter/vengeance/Guide.tsx index fa3af576716..ede53729c47 100644 --- a/src/analysis/retail/demonhunter/vengeance/Guide.tsx +++ b/src/analysis/retail/demonhunter/vengeance/Guide.tsx @@ -19,11 +19,14 @@ import { OK_TIME_AT_FURY_CAP, PERFECT_TIME_AT_FURY_CAP, } from './modules/resourcetracker/FuryTracker'; +import { PerformanceStrong } from 'analysis/retail/priest/shadow/modules/guide/ExtraComponents'; +import { formatPercentage } from 'common/format'; +import ActiveTimeGraph from 'parser/ui/ActiveTimeGraph'; export default function Guide({ modules, events, info }: GuideProps) { return ( <> - + @@ -32,13 +35,13 @@ export default function Guide({ modules, events, info }: GuideProps) { +function CoreSection({ modules, info }: GuideProps) { const percentAtFuryCap = modules.furyTracker.percentAtCap; const percentAtFuryCapPerformance = modules.furyTracker.percentAtCapPerformance; const furyWasted = modules.furyTracker.wasted; return ( -
    +

    Vengeance's primary resource is . You should @@ -68,6 +71,28 @@ function ResourceUsageSection({ modules }: GuideProps) {

    {modules.soulFragmentsGraph.plot}
    + +

    + + Continuously casting throughout an encounter is the single most important thing for + achieving good DPS. + +
    + Some fights have unavoidable downtime due to phase transitions and the like, so in these + cases 0% downtime will not be possible - do the best you can. +

    +

    + Active Time:{' '} + + {formatPercentage(modules.alwaysBeCasting.activeTimePercentage, 1)}% + {' '} +

    + +
    ); } From 52c40186af5337dc10de76682ee732ef65653212 Mon Sep 17 00:00:00 2001 From: emallson Date: Thu, 4 Jan 2024 18:24:24 -0500 Subject: [PATCH 12/46] update scaling tables + add effect scaling table --- .../core/effectMultiplierTables.generated.jsx | 401 ++ src/parser/core/stats.jsx | 13 + .../core/statsMultiplierTables.generated.jsx | 3776 ++++++++--------- .../items/dragonflight/EchoingTyrstone.tsx | 36 +- 4 files changed, 2305 insertions(+), 1921 deletions(-) create mode 100644 src/parser/core/effectMultiplierTables.generated.jsx diff --git a/src/parser/core/effectMultiplierTables.generated.jsx b/src/parser/core/effectMultiplierTables.generated.jsx new file mode 100644 index 00000000000..c5be7741913 --- /dev/null +++ b/src/parser/core/effectMultiplierTables.generated.jsx @@ -0,0 +1,401 @@ +// copied from https://github.com/simulationcraft/simc/blob/dragonflight/engine/dbc/generated/rand_prop_points.inc, column 3 +export const damageSecondary = [ + 0.0, 0.0, 0.0, 0.08483823, 0.10496229, 0.12607884, 0.14995892, 0.15589654, 0.18081029, 0.18512012, + 0.21277912, 0.21756545, 0.24557212, 0.25080174, 0.25614274, 0.29836267, 0.30143923, 0.30753383, + 0.31054947, 0.35903385, 0.36594343, 0.37298596, 0.42831287, 0.43617707, 0.44418567, 0.51129156, + 0.52026707, 0.5957585, 0.60577685, 0.61210108, 0.6984362, 0.70869154, 0.71909755, 0.81318909, + 0.82466507, 0.93178129, 0.94443876, 0.95726818, 1.07747269, 1.09216416, 1.22517443, 1.24217176, + 1.2594049, 1.35445094, 1.41621864, 1.56253922, 1.63473821, 1.71027327, 1.8925364, 1.98038065, + 2.07230234, 2.28219652, 2.38860655, 2.62712145, 2.75016546, 2.79275107, 2.97655582, 3.02325273, + 3.07068229, 3.25714612, 3.30890775, 3.36149192, 3.56217527, 3.61950874, 3.67776513, 3.89377522, + 3.95723748, 4.02173376, 4.25427055, 4.3244729, 4.39583349, 4.53699589, 4.61278534, 4.68984079, + 4.83940887, 4.9212327, 5.00444031, 5.16300535, 5.25134897, 5.34120369, 5.50940371, 5.60479259, + 5.70183229, 5.8803544, 5.98335838, 6.08816671, 6.27775145, 6.36052132, 6.47322655, 6.67377472, + 6.76277685, 6.88398027, 6.97682762, 7.19194508, 7.32229805, 7.42216444, 7.64998913, 7.75432491, + 7.89644146, 8.1378088, 8.25002861, 8.4029007, 8.52004623, 8.77945709, 8.94391727, 9.06995678, + 9.34510517, 9.47679806, 9.65623951, 9.94818115, 10.08987617, 10.28296661, 10.43098164, + 10.74535847, 10.90002918, 11.05857182, 11.39089584, 11.55657864, 11.78241062, 11.95556355, + 12.31388569, 12.49485016, 12.68035889, 13.05947018, 13.2533617, 13.45013237, 13.65185165, + 14.05909348, 14.26994514, 14.48610973, 14.70554924, 15.14332962, 15.37272453, 15.6079092, + 16.07169724, 16.31757545, 16.56721687, 16.82316971, 17.42473984, 17.69394302, 17.87660217, + 18.15547562, 18.80024338, 19.09352493, 19.39425659, 19.6997242, 20.39480972, 20.71603584, + 21.04543686, 21.78347397, 22.12984848, 22.48172951, 22.84258652, 23.63903618, 24.01846886, + 24.27599907, 24.66930389, 25.52478027, 25.93831635, 26.21901512, 26.6477375, 27.56710434, + 28.01786995, 28.32386398, 28.79125595, 29.77980614, 30.27122498, 30.60484123, 32.18738174, + 32.751091, 33.35290909, 33.80961227, 34.30161285, 35.55413055, 36.28045273, 37.01726151, + 37.76468277, 38.52285385, 39.29190826, 40.07198715, 40.86322784, 41.66577148, 42.47975922, + 43.30533218, 44.14264297, 44.99183273, 45.85305405, 46.72645569, 47.61219025, 48.51041412, + 49.42128372, 50.34495544, 51.28158951, 52.23134613, 53.19438934, 54.17089081, 55.16100693, + 56.16491699, 57.18278885, 58.21479416, 59.2611084, 60.32191086, 61.39738464, 62.48770142, + 63.59305573, 64.71363068, 65.84960938, 67.00118256, 68.16854858, 69.35189819, 70.55142975, + 71.76733398, 72.99983215, 74.24910736, 75.51538086, 76.79885101, 78.09973145, 79.41824341, + 80.7545929, 82.10900116, 83.48169708, 84.87288666, 86.28281403, 87.71170044, 89.15977478, + 90.62728119, 92.11444092, 93.62150574, 95.14871216, 96.69631195, 98.26454163, 99.85366058, + 101.46392059, 103.09558105, 104.74889374, 106.4241333, 108.12153625, 109.84142303, 111.58400726, + 113.34960938, 115.13848114, 116.95091248, 118.7871933, 120.64759064, 122.5324173, 124.4419632, + 126.37651825, 128.33639526, 130.32188416, 132.33329773, 134.37094116, 136.43515015, 138.52622986, + 140.64450073, 142.7902832, 144.96392822, 147.16575623, 149.39608765, 151.6552887, 153.94369507, + 156.26165771, 158.60952759, 160.98765564, 163.39642334, 165.83616638, 168.30726624, 170.81010437, + 173.34506226, 175.91249084, 178.51281738, 181.14639282, 183.81364441, 186.51495361, 189.25073242, + 192.02137756, 194.82733154, 197.66897583, 200.54675293, 203.46107483, 206.41239929, 209.40115356, + 212.42776489, 215.49269104, 218.59638977, 221.73930359, 224.92192078, 228.14468384, 231.4080658, + 234.71255493, 238.05863953, 241.4467926, 244.87753296, 248.35133362, 251.86871338, 255.43019104, + 259.0362854, 262.6875, 266.3843689, 270.12741089, 273.91720581, 277.75430298, 281.63922119, + 285.57250977, 289.55477905, 293.58657837, 297.66848755, 301.80111694, 305.98501587, 310.22076416, + 314.5090332, 318.85040283, 319.14764404, 319.44512939, 319.74291992, 320.04098511, 320.33932495, + 320.63793945, 320.93682861, 321.23599243, 321.53543091, 321.83517456, 322.13519287, 322.43545532, + 322.73602295, 323.03689575, 323.3380127, 323.63943481, 323.94110107, 324.24307251, 324.54534912, + 324.84786987, 325.1506958, 325.45379639, 325.75717163, 326.06082153, 326.36477661, 326.66900635, + 326.97354126, 327.27832031, 327.58340454, 327.88876343, 328.19442749, 329.11309814, 330.03433228, + 330.95812988, 331.884552, 332.8135376, 333.74514771, 334.67935181, 335.6161499, 336.55560303, + 337.49765015, 338.44238281, 339.38970947, 340.33972168, 341.29238892, 342.24771118, 343.20571899, + 344.16641235, 345.12976074, 346.09585571, 347.06460571, 348.03610229, 349.01031494, 349.98724365, + 350.96691895, 351.9493103, 352.93447876, 353.9223938, 354.91308594, 355.90652466, 356.902771, + 357.90179443, 358.90359497, 359.90823364, 360.91567993, 361.92593384, 362.93899536, 363.95492554, + 364.97369385, 365.99530029, 367.01977539, 368.04711914, 369.07733154, 370.11044312, 371.14645386, + 376.92825317, 383.10852051, 389.37149048, 395.71810913, 402.14944458, 408.66653442, 415.27035522, + 421.96203613, 428.74261475, 435.61312866, 442.57473755, 449.628479, 456.7755127, 464.01696777, + 471.35394287, 478.78762817, 486.31918335, 493.94979858, 501.68066406, 509.51296997, 517.44793701, + 525.48681641, 533.63092041, 541.88140869, 550.23956299, 558.70672607, 567.28424072, 575.97338867, + 584.77545166, 593.6918335, 602.72393799, 611.87310791, 621.14074707, 630.52832031, 640.03717041, + 649.66882324, 659.42468262, 669.30627441, 679.31506348, 689.45263672, 699.72045898, 710.12005615, + 720.65313721, 731.32110596, 742.12567139, 753.06842041, 764.15106201, 775.37518311, 786.74243164, + 798.25457764, 809.91333008, 821.72039795, 833.67755127, 845.78656006, 858.04925537, 870.46734619, + 883.04278564, 895.77740479, 908.67303467, 921.73156738, 934.95501709, 948.34521484, 961.9041748, + 975.63391113, 989.53643799, 1003.6137085, 1017.86779785, 1032.30090332, 1046.91491699, + 1061.7121582, 1076.69458008, 1091.86462402, 1107.22424316, 1122.77575684, 1138.5213623, + 1154.46350098, 1170.60424805, 1186.94604492, 1203.49108887, 1220.24206543, 1237.2010498, + 1254.37072754, 1271.75341797, 1289.3515625, 1307.16772461, 1325.20458984, 1343.46447754, + 1361.95019531, 1380.66418457, 1399.60925293, 1418.78796387, 1438.203125, 1457.85754395, + 1477.75378418, 1497.89477539, 1518.28344727, 1538.92248535, 1559.81494141, 1580.96362305, + 1602.37158203, 1624.04187012, 1645.97741699, 1668.18127441, 1690.65673828, 1713.40673828, + 1736.43444824, 1759.74316406, 1783.33618164, 1807.2166748, 1831.38793945, 1855.85339355, + 1880.61645508, 1905.68041992, 1931.0489502, 1956.7253418, 1982.71325684, 2009.01623535, + 2035.63793945, 2062.58203125, 2089.85205078, 2117.45214844, 2145.38574219, 2173.65673828, + 2202.26879883, 2231.22631836, 2260.53295898, 2290.19287109, 2320.2097168, 2350.58813477, + 2381.33178711, 2412.44506836, 2443.93212891, 2475.79760742, 2508.04516602, 2540.6796875, + 2573.70532227, 2607.12670898, 2640.94824219, 2675.17456055, 2709.81005859, 2744.85986328, + 2780.328125, 2816.22021484, 2852.5402832, 2889.29394531, 2926.48535156, 2964.12011719, + 3002.203125, 3040.73925781, 3079.73364258, 3119.19189453, 3159.11865234, 3199.52001953, + 3240.40063477, 3281.76635742, 3323.62255859, 3365.97485352, 3408.82885742, 3452.19018555, + 3496.06445312, 3540.45776367, 3585.37597656, 3630.82470703, 3676.81030273, 3723.33862305, + 3770.41577148, 3818.0480957, 3866.24194336, 3915.00317383, 3964.33886719, 4014.25488281, + 4064.75830078, 4115.85498047, 4167.55273438, 4219.85742188, 4272.77636719, 4326.31591797, + 4380.48339844, 4435.28613281, 4490.73095703, 4546.82519531, 4603.57617188, 4660.99121094, + 4719.07763672, 1436.6784668, 1450.12719727, 1463.70178223, 1477.40356445, 1491.23352051, + 1505.19299316, 1519.28308105, 1533.50512695, 1547.86022949, 1562.34985352, 1576.97497559, + 1591.73706055, 1606.6373291, 1621.67700195, 1636.85754395, 1652.18017578, 1667.64624023, + 1683.25708008, 1699.01403809, 1714.9185791, 1730.97192383, 1747.17553711, 1763.53088379, + 1780.03930664, 1796.70227051, 1813.52124023, 1830.49755859, 1847.63293457, 1864.92858887, + 1882.38623047, 1900.00720215, 1917.79321289, 1935.74572754, 1953.86621094, 1972.15637207, + 1990.61767578, 2009.25195312, 2028.06054688, 2047.04516602, 2066.20751953, 2085.54931641, + 2105.07226562, 2124.77783203, 2144.66796875, 2164.74414062, 2185.00830078, 2205.4621582, + 2226.10742188, 2246.94604492, 2267.97973633, 2289.21044922, 2310.63964844, 2332.26953125, + 2354.10205078, 2376.13867188, 2398.38183594, 2420.83300781, 2443.49438477, 2466.36816406, + 2489.45581055, 2512.75952148, 2536.28149414, 2560.02368164, 2583.98803711, 2608.17675781, + 2632.59204102, 2657.2355957, 2682.11010742, 2707.21728516, 2732.55957031, 2758.13916016, + 2783.95800781, 2810.01855469, 2836.32324219, 2862.87402344, 2889.67358398, 2916.72363281, + 2944.02709961, 2971.58618164, 2999.40332031, 3027.48071289, 3055.82104492, 3084.42651367, + 3113.29980469, 3142.44360352, 3171.85986328, 3201.55175781, 3231.52148438, 3261.77172852, + 3292.30517578, 3323.12451172, 3354.23242188, 3385.63134766, 3417.32421875, 3449.3137207, + 3481.6027832, 3514.1940918, 3547.09057617, 3580.29492188, 3613.81005859, 3647.63891602, + 3681.78442383, 3716.24975586, 3751.03759766, 3786.15112305, 3821.59326172, 3857.3671875, + 3893.47607422, 3929.92285156, 3966.7109375, 4003.84326172, 4041.32324219, 4079.15405273, + 4117.33886719, 4155.88134766, 4194.78466797, 4234.05224609, 4273.68701172, 4313.69335938, + 4354.07373047, 4394.83203125, 4435.97216797, 4477.49755859, 4519.41113281, 4561.71728516, + 4604.41992188, 4647.52197266, 4691.02734375, 4734.93994141, 4779.26367188, 4824.00244141, + 4869.16015625, 4914.74023438, 4960.74707031, 5007.18457031, 5054.05712891, 5101.36816406, + 5149.12207031, 5197.32275391, 5245.97509766, 5295.08251953, 5344.64990234, 5394.68115234, + 5445.18115234, 5496.15332031, 5547.60302734, 5599.53417969, 5651.95117188, 5704.859375, + 5758.26220703, 5812.16552734, 5866.57324219, 5921.49023438, 5976.92138672, 6032.87158203, + 6089.34521484, 6146.34765625, 6203.88330078, 6261.95800781, 6320.57617188, 6379.74316406, + 6439.46386719, 6499.74414062, 6560.58789062, 6622.00195312, 6683.99023438, 6746.55908203, + 6809.71386719, 6873.45947266, 6937.80224609, 7002.74707031, 7068.29980469, 7134.46582031, + 7201.25195312, 7268.66259766, 7336.70458984, 7405.38378906, 7474.70556641, 7544.67626953, + 7615.30224609, 7686.58886719, 7758.54296875, 7831.17089844, 7904.47851562, 7978.47216797, + 8053.15869141, 8128.54443359, 8204.63574219, 8281.43945312, 8358.96191406, 8437.2109375, + 8516.19140625, 8595.91113281, 8676.37792969, 8757.59765625, 8839.57714844, 8922.32421875, + 9005.84667969, 9090.15039062, 9175.24316406, 9261.1328125, 9347.82617188, 9435.33105469, + 9523.65527344, 9612.80664062, 9702.79199219, 9793.62011719, 9885.29785156, 9977.83398438, + 10071.23730469, 10165.51367188, 10260.67285156, 10356.72363281, 10453.67285156, 10551.52929688, + 10650.30273438, 10750.0, 10850.63085938, 10952.20410156, 11054.72753906, 11158.2109375, + 11262.66308594, 11368.09277344, 11474.50976562, 11581.92285156, 11690.34082031, 11799.77441406, + 11910.23242188, 12021.72363281, 12134.25878906, 12247.84765625, 12362.5, 12478.22558594, + 12595.03417969, 12712.93652344, 12831.94238281, 12952.06347656, 13073.30664062, 13195.68554688, + 13319.2109375, 13443.89257812, 13569.74023438, 13696.76660156, 13824.98242188, 13954.3984375, + 14085.02539062, 14216.875, 14349.95996094, 14484.29003906, 14619.87695312, 14756.73339844, + 14894.87207031, 15034.30273438, 15175.0390625, 15317.09277344, 15460.47558594, 15605.20117188, + 15751.28222656, 15898.72851562, 16047.55761719, 16197.77929688, 16349.40625, 16502.453125, + 16656.93359375, 16812.859375, 16970.24414062, 17129.1015625, 17289.44726562, 17451.29492188, + 17614.65625, 17779.546875, 17945.98242188, 18113.97460938, 18283.5390625, 18454.69140625, + 18627.4453125, 18801.81835938, 18977.82226562, 19155.47265625, 19334.78710938, 19515.78125, + 19698.46875, 19882.86523438, 20068.98828125, 20256.85546875, 20446.47851562, 20637.87890625, + 20831.0703125, 21026.0703125, 21222.89453125, 21421.5625, 21622.08984375, 21824.49414062, + 22028.79492188, 22235.00585938, 22443.1484375, 22653.23828125, 22865.29492188, 23079.33789062, + 23295.3828125, 23513.45117188, 23733.56054688, 23955.73046875, 24179.98046875, 24406.33007812, + 24634.796875, 24865.40429688, 25098.16796875, 25333.11328125, 25570.25585938, 25809.61914062, + 26051.22460938, 26295.08984375, 26541.23828125, 26789.69140625, 27040.46875, 27293.59570312, + 27549.08984375, 27806.97851562, 28067.27929688, 28330.01757812, 28595.21484375, 28862.89453125, + 29133.08007812, 29405.79492188, 29681.0625, 29958.90820312, 30239.35351562, 30522.42382812, + 30808.14453125, 31096.5390625, 31387.63476562, 31681.453125, 31978.0234375, 32277.37109375, + 32579.51953125, 32884.49609375, 33192.328125, 33503.04296875, 33816.6640625, 34133.22265625, + 34452.7421875, 34775.25390625, 35100.78515625, 35429.3671875, 35761.01953125, 36095.78125, + 36433.671875, 36774.7265625, 37118.9765625, 37466.44921875, 37817.171875, 38171.17578125, 38528.5, + 38889.1640625, 39253.20703125, 39620.65625, 39991.54296875, 40365.90625, 40743.76953125, + 41125.171875, 41510.14453125, 41898.72265625, 42290.9375, 42686.82421875, 43086.4140625, + 43489.74609375, 43896.85546875, 44307.7734375, 44722.5390625, 45141.1875, 45563.75390625, + 45990.27734375, 46420.7890625, 46855.3359375, 47293.94921875, 47736.66796875, 48183.53125, + 48634.578125, 49089.84765625, 49549.375, 50013.20703125, 50481.3828125, 50953.9375, + 51430.91796875, 51912.36328125, 52398.31640625, 52888.81640625, 53383.91015625, 53883.63671875, + 54388.04296875, 54897.16796875, 55411.0625, 55929.765625, 56453.32421875, 56981.78125, + 57515.19140625, 58053.58984375, 58597.03125, 59145.55859375, 59699.21875, 60258.0625, + 60822.140625, 61391.49609375, 61966.18359375, 62546.24609375, 63131.7421875, 63722.71875, + 64319.23046875, 64921.3203125, 65529.05078125, 66142.46875, 66761.625, 67386.5859375, + 68017.390625, 68654.1015625, 69296.7734375, 69945.4609375, 70600.21875, 71261.109375, 71928.1875, + 72601.5078125, 73281.125, 73967.1171875, 74659.5234375, 75358.40625, 76063.8359375, 76775.875, + 77494.5703125, 78220.0, 78952.21875, 79691.2890625, 80437.28125, 81190.25, 81950.2734375, + 82717.4140625, 83491.734375, 84273.296875, 85062.1796875, 85858.4453125, 86662.171875, + 87473.4140625, 88292.25, 89118.7578125, 89953.0, 90795.046875, 91644.984375, 92502.875, + 93368.7890625, 94242.8203125, 95125.0234375, 96015.4921875, 96914.2890625, 97821.5078125, + 98737.21875, 99661.4921875, 100594.4296875, 101536.09375, 102486.5703125, 103445.953125, + 104414.296875, 105391.7265625, 106378.296875, 107374.109375, 108379.2421875, 109393.78125, + 110417.8125, 111451.4375, 112494.7265625, 113547.796875, 114610.71875, 115683.59375, + 116766.5078125, 117859.546875, 118962.84375, 120076.453125, 121200.4921875, 122335.046875, + 123480.2265625, 124636.1328125, 125802.84375, 126980.4765625, 128169.1484375, 129368.953125, + 130579.96875, 131802.328125, 133036.125, 134281.484375, 135538.484375, 136807.265625, + 138087.921875, 139380.5625, 140685.3125, 142002.265625, 143331.546875, 144673.265625, 146027.5625, + 147394.53125, 148774.28125, 150166.96875, 151572.671875, 152991.546875, 154423.703125, + 155869.265625, 157328.359375, 158801.109375, 160287.640625, 161788.09375, 163302.59375, + 164831.28125, 166374.265625, 167931.6875, 169503.703125, 171090.421875, 172692.0, 174308.578125, + 175940.28125, 177587.265625, 179249.65625, 180927.609375, 182621.28125, 184330.796875, + 186056.3125, 187797.984375, 189555.96875, 191330.40625, 193121.453125, 194929.265625, + 196753.984375, 198595.8125, 200454.859375, 202331.328125, 204225.34375, 206137.109375, 208066.75, + 210014.46875, 211980.40625, 213964.765625, 215967.6875, 217989.359375, 220029.96875, + 222089.671875, 224168.640625, 226267.09375, 228385.171875, 230523.09375, 232681.015625, + 234859.15625, 237057.671875, 239276.765625, 241516.640625, 243777.46875, 246059.484375, + 248362.84375, 250687.765625, 253034.453125, 255403.109375, 257793.9375, 260207.15625, + 262642.96875, 265101.5625, 267583.1875, 270088.03125, 272616.3125, 275168.28125, 277744.125, + 280344.09375, 282968.40625, 285617.28125, 288290.9375, 290989.625, 293713.59375, 296463.03125, + 299238.21875, 302039.40625, 304866.78125, 307720.65625, 310601.21875, 313508.75, 316443.53125, + 319405.75, 322395.71875, 325413.65625, 328459.84375, 331534.5625, 334638.0625, 337770.625, + 340932.5, 344123.96875, 347345.3125, 350596.8125, 353878.75, 357191.40625, 360535.09375, + 363910.0625, 367316.625, 370755.0625, 374225.71875, 377728.84375, 381264.75, 384833.78125, + 388436.21875, 392072.375, 395742.5625, 399447.09375, 403186.34375, 406960.5625, 410770.125, + 414615.34375, 418496.5625, 422414.09375, 426368.3125, 430359.5625, 434388.15625, 438454.46875, + 442558.84375, 446701.65625, 450883.21875, 455103.9375, 459364.15625, 463664.28125, 468004.65625, + 472385.625, 476807.65625, 481271.03125, 485776.21875, 490323.5625, 494913.5, 499546.375, + 504222.65625, 508942.6875, 513706.875, 518515.6875, 523369.53125, 528268.8125, 533213.9375, + 538205.3125, 543243.5, 548328.8125, 553461.6875, 558642.6875, 563872.125, 569150.5, 574478.375, + 579856.0625, 585284.0625, 590762.9375, 596293.0625, 601874.9375, 607509.125, 613196.0, 618936.125, + 624730.0, 630578.125, 636480.9375, 642439.0625, 648452.9375, 654523.125, 660650.125, 666834.4375, + 673076.6875, 679377.375, 685737.0, 692156.1875, 698635.5, 705175.4375, 711776.5625, 718439.5, + 725164.8125, 731953.125, 738804.9375, 745720.875, 752701.5625, 759747.625, 766859.625, + 774038.1875, 781283.9375, 788597.5625, 795979.625, 803430.8125, 810951.75, 818543.0625, + 826205.4375, 833939.5625, 841746.0625, 849625.625, 857579.0, 865606.8125, 873709.75, 881888.5625, + 890143.9375, 898476.5625, 906887.1875, 915376.5625, 923945.4375, 932594.5, 941324.5, 950136.25, + 959030.5, 968008.0, 977069.5, 986215.875, 995447.8125, 1004766.1875, 1014171.8125, 1023665.5, + 1033248.125, 1042920.3125, 1052683.125, 1062537.25, 1072483.625, 1082523.125, 1092656.625, + 1102885.0, 1113209.125, 1123629.875, 1134148.25, 1144765.0, 1155481.125, 1166297.625, 1177215.375, + 1188235.25, 1199358.375, 1210585.5, 1221917.875, 1233356.25, 1244901.625, 1256555.25, 1268317.75, + 1280190.5, +]; + +// copied from https://github.com/simulationcraft/simc/blob/dragonflight/engine/dbc/generated/rand_prop_points.inc, column 2 +export const damageReplaceStat = [ + 0.0, 0.0, 0.0, 0.08483823, 0.10496229, 0.12607884, 0.14995892, 0.15589654, 0.18081029, 0.18512012, + 0.21277912, 0.21756545, 0.24557212, 0.25080174, 0.25614274, 0.29836267, 0.30143923, 0.30753383, + 0.31054947, 0.35903385, 0.36594343, 0.37298596, 0.42831287, 0.43617707, 0.44418567, 0.51129156, + 0.52026707, 0.5957585, 0.60577685, 0.61210108, 0.6984362, 0.70869154, 0.71909755, 0.81318909, + 0.82466507, 0.93178129, 0.94443876, 0.95726818, 1.07747269, 1.09216416, 1.22517443, 1.24217176, + 1.2594049, 1.35445094, 1.41621864, 1.56253922, 1.63473821, 1.71027327, 1.8925364, 1.98038065, + 2.07230234, 2.28219652, 2.38860655, 2.62712145, 2.75016546, 2.79275107, 2.97655582, 3.02325273, + 3.07068229, 3.25714612, 3.30890775, 3.36149192, 3.56217527, 3.61950874, 3.67776513, 3.89377522, + 3.95723748, 4.02173376, 4.25427055, 4.3244729, 4.39583349, 4.53699589, 4.61278534, 4.68984079, + 4.83940887, 4.9212327, 5.00444031, 5.16300535, 5.25134897, 5.34120369, 5.50940371, 5.60479259, + 5.70183229, 5.8803544, 5.98335838, 6.08816671, 6.27775145, 6.36052132, 6.47322655, 6.67377472, + 6.76277685, 6.88398027, 6.97682762, 7.19194508, 7.32229805, 7.42216444, 7.64998913, 7.75432491, + 7.89644146, 8.1378088, 8.25002861, 8.4029007, 8.52004623, 8.77945709, 8.94391727, 9.06995678, + 9.34510517, 9.47679806, 9.65623951, 9.94818115, 10.08987617, 10.28296661, 10.43098164, + 10.74535847, 10.90002918, 11.05857182, 11.39089584, 11.55657864, 11.78241062, 11.95556355, + 12.31388569, 12.49485016, 12.68035889, 13.05947018, 13.2533617, 13.45013237, 13.65185165, + 14.05909348, 14.26994514, 14.48610973, 14.70554924, 15.14332962, 15.37272453, 15.6079092, + 16.07169724, 16.31757545, 16.56721687, 16.82316971, 17.42473984, 17.69394302, 17.87660217, + 18.15547562, 18.80024338, 19.09352493, 19.39425659, 19.6997242, 20.39480972, 20.71603584, + 21.04543686, 21.78347397, 22.12984848, 22.48172951, 22.84258652, 23.63903618, 24.01846886, + 24.27599907, 24.66930389, 25.52478027, 25.93831635, 26.21901512, 26.6477375, 27.56710434, + 28.01786995, 28.32386398, 28.79125595, 29.77980614, 30.27122498, 30.60484123, 32.18738174, + 32.751091, 33.35290909, 33.80961227, 34.30161285, 35.55413055, 35.92337418, 36.29641724, + 36.67329407, 37.05405045, 37.43871689, 37.82733917, 38.21995926, 38.61661148, 39.01734161, + 39.42219162, 39.83120346, 40.24441528, 40.66187668, 41.08362198, 41.50970459, 41.94016266, + 42.37504196, 42.81438828, 43.25824738, 43.70666122, 44.15968323, 44.61735535, 45.07972717, + 45.5468483, 46.01876068, 46.49551773, 46.97716904, 47.46376038, 47.95534897, 48.45198059, + 48.95370865, 49.46058273, 49.97266006, 50.48998642, 51.01262283, 51.54061508, 52.0740242, + 52.61290359, 53.15731049, 53.70729828, 54.26292419, 54.82424545, 55.39132309, 55.96421432, + 56.54297256, 57.12766647, 57.71834946, 58.31508636, 58.91793823, 59.52696609, 60.1422348, + 60.76380539, 61.39174271, 62.0261116, 62.66697693, 63.31440353, 63.9684639, 64.62921906, + 65.29673767, 65.97109222, 66.65235138, 67.3405838, 68.03585815, 68.73825073, 69.4478302, + 70.16467285, 70.88885498, 71.62043762, 72.35951233, 73.10614014, 73.8604126, 74.62239838, + 75.39217377, 76.16983032, 76.95543671, 77.74906921, 78.55082703, 79.36077881, 80.17900848, + 81.00559998, 81.84064484, 82.68422699, 83.53643036, 84.39733887, 85.2670517, 86.14565277, + 87.03322601, 87.92986298, 88.83567047, 89.75072479, 90.67513275, 91.60897827, 92.55236053, + 93.50537872, 94.46812439, 95.44070435, 96.42321777, 97.41575623, 98.41843414, 99.43134308, + 100.45459747, 101.48828888, 102.53253937, 103.58744049, 104.65310669, 105.7296524, 106.81717682, + 107.91578674, 109.02562714, 110.14678192, 111.27937317, 112.42350769, 113.57932281, 114.74691772, + 115.92642212, 117.11795044, 118.32163239, 119.53759003, 120.76593781, 122.00681305, 123.26033783, + 124.52662659, 125.80583954, 127.0980835, 128.40350342, 129.72221375, 131.05436707, 132.40008545, + 133.75952148, 135.1328125, 136.52008057, 137.92149353, 139.33717346, 140.76727295, 142.21192932, + 143.67131042, 145.14555359, 145.89028931, 146.63883972, 147.39123535, 148.14749146, 148.90763855, + 149.67167664, 150.43963623, 151.21153259, 151.98739624, 152.76722717, 153.55107117, 154.33894348, + 155.13084412, 155.92681885, 156.72686768, 157.53103638, 158.33932495, 159.15174866, 159.96835327, + 160.78913879, 161.614151, 162.44337463, 163.27687073, 164.11463928, 164.95671082, 165.80308533, + 166.65382385, 167.50891113, 168.36839294, 169.23228455, 170.10061646, 171.85430908, 173.62594604, + 175.41567993, 177.22370911, 179.05021667, 180.895401, 182.75942993, 184.64250183, 186.54483032, + 188.46658325, 190.40795898, 192.3691864, 194.35043335, 196.35191345, 198.37384033, 200.41642761, + 202.4798584, 204.56436157, 206.67015076, 208.79742432, 210.94642639, 213.11737061, 215.31047058, + 217.5259552, 219.76405334, 222.02497864, 224.30899048, 226.61630249, 228.94714355, 231.30178833, + 233.68043518, 236.08335876, 238.51078796, 240.96296692, 243.44017029, 245.94262695, 248.47061157, + 251.02436829, 253.60415649, 256.21026611, 258.84292603, 261.50244141, 264.18902588, 266.90304565, + 269.64468384, 272.41427612, 275.21209717, 278.03845215, 280.89358521, 283.77780151, 286.69140625, + 289.63467407, 292.60794067, 295.61151123, 298.64562988, 301.71069336, 304.8069458, 307.9347229, + 311.09436035, 314.28619385, 317.51049805, 320.76760864, 324.05789185, 327.38168335, 330.73928833, + 334.131073, 337.55737305, 341.01855469, 344.51495361, 348.04690552, 351.61480713, 355.21902466, + 358.8598938, 362.53778076, 366.25311279, 370.00622559, 373.79751587, 377.62734985, 381.49612427, + 385.40423584, 389.35211182, 393.34011841, 397.36865234, 401.43817139, 405.54904175, 409.70169067, + 413.89654541, 418.13406372, 422.41461182, 426.73867798, 431.10668945, 435.51907349, 439.97628784, + 444.4788208, 449.02703857, 453.62149048, 458.26263428, 462.95089722, 467.68676758, 472.47073364, + 477.30328369, 482.18493652, 487.11611938, 492.09738159, 497.12924194, 502.2121582, 507.34667969, + 512.5333252, 517.77264404, 523.06512451, 528.41131592, 533.81170654, 539.26696777, 544.77758789, + 550.34411621, 555.96710205, 561.64715576, 567.38482666, 573.18066406, 579.03533936, 584.94940186, + 590.92340088, 596.95800781, 603.05383301, 609.21148682, 615.43151855, 621.71466064, 628.06152344, + 634.47271729, 640.94885254, 647.49072266, 654.09881592, 660.77398682, 667.51672363, 674.32781982, + 681.20800781, 688.15783691, 695.17810059, 702.26953125, 709.43273926, 716.6685791, 723.97772217, + 731.36090088, 738.81884766, 746.35235596, 753.9621582, 761.64898682, 769.41369629, 777.25708008, + 785.17980957, 793.18280029, 801.26678467, 809.43267822, 817.68115234, 826.01318359, 834.42956543, + 842.93109131, 851.51867676, 860.1932373, 868.95550537, 877.80645752, 886.74700928, 895.77801514, + 904.90032959, 914.11499023, 923.4229126, 932.82495117, 942.32208252, 951.9152832, 961.60552979, + 971.39379883, 981.28100586, 991.26824951, 1001.35638428, 1011.54669189, 1021.84002686, + 1032.23730469, 1042.73974609, 1053.34838867, 1064.06420898, 1074.88842773, 1085.82202148, + 1096.86608887, 1108.02185059, 1119.2902832, 1130.67260742, 1142.16992188, 1153.78344727, + 1165.51428223, 1177.36376953, 1189.33276367, 1201.42285156, 1213.63500977, 1225.97045898, + 1238.43054199, 1251.01647949, 1263.72961426, 1276.57104492, 1289.54211426, 1302.64428711, + 1315.87866211, 1329.2467041, 1342.74975586, 1356.38903809, 1370.16601562, 1384.08215332, + 1398.13867188, 1412.3371582, 1426.67895508, 1441.16540527, 1455.7980957, 1470.57849121, + 1485.50805664, 1500.58813477, 1515.82043457, 1531.2064209, 1546.74768066, 1562.44567871, + 1578.30212402, 1594.31848145, 1610.49633789, 1626.83740234, 1643.34326172, 1660.01550293, + 1676.8560791, 1693.86633301, 1711.0480957, 1728.40319824, 1736.47412109, 1744.58276367, + 1752.72924805, 1760.91381836, 1769.13659668, 1777.39770508, 1785.69750977, 1794.03601074, + 1802.41345215, 1810.82995605, 1819.28588867, 1827.78112793, 1836.31616211, 1844.89099121, + 1853.50598145, 1862.16101074, 1870.85656738, 1879.59277344, 1888.36975098, 1897.18762207, + 1906.04675293, 1914.94726562, 1923.88928223, 1932.87304688, 1941.89880371, 1950.9666748, + 1960.0769043, 1969.22973633, 1978.4251709, 1987.66369629, 1996.9453125, 2006.27026367, + 2015.63867188, 2025.05090332, 2034.50708008, 2044.00744629, 2053.55224609, 2063.14135742, + 2072.77539062, 2082.45458984, 2092.17871094, 2105.07226562, 2124.77783203, 2144.66796875, + 2164.74414062, 2185.00830078, 2205.4621582, 2226.10742188, 2246.94604492, 2267.97973633, + 2289.21044922, 2310.63964844, 2332.26953125, 2354.10205078, 2376.13867188, 2398.38183594, + 2420.83300781, 2443.49438477, 2466.36816406, 2489.45581055, 2512.75952148, 2536.28149414, + 2560.02368164, 2583.98803711, 2608.17675781, 2632.59204102, 2657.2355957, 2682.11010742, + 2707.21728516, 2732.55957031, 2758.13916016, 2783.95800781, 2810.01855469, 2836.32324219, + 2862.87402344, 2889.67358398, 2916.72363281, 2944.02709961, 2971.58618164, 2999.40332031, + 3027.48071289, 3055.82104492, 3084.42651367, 3113.29980469, 3142.44360352, 3171.85986328, + 3201.55175781, 3231.52148438, 3261.77172852, 3292.30517578, 3323.12451172, 3354.23242188, + 3385.63134766, 3417.32421875, 3449.3137207, 3481.6027832, 3514.1940918, 3547.09057617, + 3580.29492188, 3613.81005859, 3647.63891602, 3681.78442383, 3716.24975586, 3751.03759766, + 3786.15112305, 3821.59326172, 3857.3671875, 3893.47607422, 3929.92285156, 3966.7109375, + 4003.84326172, 4041.32324219, 4079.15405273, 4117.33886719, 4155.88134766, 4194.78466797, + 4234.05224609, 4273.68701172, 4313.69335938, 4354.07373047, 4394.83203125, 4435.97216797, + 4477.49755859, 4519.41113281, 4561.71728516, 4604.41992188, 4647.52197266, 4691.02734375, + 4734.93994141, 4779.26367188, 4824.00244141, 4869.16015625, 4914.74023438, 4960.74707031, + 5007.18457031, 5054.05712891, 5101.36816406, 5149.12207031, 5197.32275391, 5245.97509766, + 5295.08251953, 5344.64990234, 5394.68115234, 5445.18115234, 5496.15332031, 5547.60302734, + 5599.53417969, 5651.95117188, 5704.859375, 5758.26220703, 5812.16552734, 5866.57324219, + 5921.49023438, 5976.92138672, 6032.87158203, 6089.34521484, 6146.34765625, 6203.88330078, + 6261.95800781, 6320.57617188, 6379.74316406, 6439.46386719, 6499.74414062, 6560.58789062, + 6622.00195312, 6683.99023438, 6746.55908203, 6809.71386719, 6873.45947266, 6937.80224609, + 7002.74707031, 7068.29980469, 7134.46582031, 7201.25195312, 7268.66259766, 7336.70458984, + 7405.38378906, 7474.70556641, 7544.67626953, 7615.30224609, 7686.58886719, 7758.54296875, + 7831.17089844, 7904.47851562, 7978.47216797, 8053.15869141, 8128.54443359, 8204.63574219, + 8281.43945312, 8358.96191406, 8437.2109375, 8516.19140625, 8595.91113281, 8676.37792969, + 8757.59765625, 8839.57714844, 8922.32421875, 9005.84667969, 9090.15039062, 9175.24316406, + 9261.1328125, 9347.82617188, 9435.33105469, 9523.65527344, 9612.80664062, 9702.79199219, + 9793.62011719, 9885.29785156, 9977.83398438, 10071.23730469, 10165.51367188, 10260.67285156, + 10356.72363281, 10453.67285156, 10551.52929688, 10650.30273438, 10750.0, 10850.63085938, + 10952.20410156, 11054.72753906, 11158.2109375, 11262.66308594, 11368.09277344, 11474.50976562, + 11581.92285156, 11690.34082031, 11799.77441406, 11910.23242188, 12021.72363281, 12134.25878906, + 12247.84765625, 12362.5, 12478.22558594, 12595.03417969, 12712.93652344, 12831.94238281, + 12952.06347656, 13073.30664062, 13195.68554688, 13319.2109375, 13443.89257812, 13569.74023438, + 13696.76660156, 13824.98242188, 13954.3984375, 14085.02539062, 14216.875, 14349.95996094, + 14484.29003906, 14619.87695312, 14756.73339844, 14894.87207031, 15034.30273438, 15175.0390625, + 15317.09277344, 15460.47558594, 15605.20117188, 15751.28222656, 15898.72851562, 16047.55761719, + 16197.77929688, 16349.40625, 16502.453125, 16656.93359375, 16812.859375, 16970.24414062, + 17129.1015625, 17289.44726562, 17451.29492188, 17614.65625, 17779.546875, 17945.98242188, + 18113.97460938, 18283.5390625, 18454.69140625, 18627.4453125, 18801.81835938, 18977.82226562, + 19155.47265625, 19334.78710938, 19515.78125, 19698.46875, 19882.86523438, 20068.98828125, + 20256.85546875, 20446.47851562, 20637.87890625, 20831.0703125, 21026.0703125, 21222.89453125, + 21421.5625, 21622.08984375, 21824.49414062, 22028.79492188, 22235.00585938, 22443.1484375, + 22653.23828125, 22865.29492188, 23079.33789062, 23295.3828125, 23513.45117188, 23733.56054688, + 23955.73046875, 24179.98046875, 24406.33007812, 24634.796875, 24865.40429688, 25098.16796875, + 25333.11328125, 25570.25585938, 25809.61914062, 26051.22460938, 26295.08984375, 26541.23828125, + 26789.69140625, 27040.46875, 27293.59570312, 27549.08984375, 27806.97851562, 28067.27929688, + 28330.01757812, 28595.21484375, 28862.89453125, 29133.08007812, 29405.79492188, 29681.0625, + 29958.90820312, 30239.35351562, 30522.42382812, 30808.14453125, 31096.5390625, 31387.63476562, + 31681.453125, 31978.0234375, 32277.37109375, 32579.51953125, 32884.49609375, 33192.328125, + 33503.04296875, 33816.6640625, 34133.22265625, 34452.7421875, 34775.25390625, 35100.78515625, + 35429.3671875, 35761.01953125, 36095.78125, 36433.671875, 36774.7265625, 37118.9765625, + 37466.44921875, 37817.171875, 38171.17578125, 38528.5, 38889.1640625, 39253.20703125, 39620.65625, + 39991.54296875, 40365.90625, 40743.76953125, 41125.171875, 41510.14453125, 41898.72265625, + 42290.9375, 42686.82421875, 43086.4140625, 43489.74609375, 43896.85546875, 44307.7734375, + 44722.5390625, 45141.1875, 45563.75390625, 45990.27734375, 46420.7890625, 46855.3359375, + 47293.94921875, 47736.66796875, 48183.53125, 48634.578125, 49089.84765625, 49549.375, + 50013.20703125, 50481.3828125, 50953.9375, 51430.91796875, 51912.36328125, 52398.31640625, + 52888.81640625, 53383.91015625, 53883.63671875, 54388.04296875, 54897.16796875, 55411.0625, + 55929.765625, 56453.32421875, 56981.78125, 57515.19140625, 58053.58984375, 58597.03125, + 59145.55859375, 59699.21875, 60258.0625, 60822.140625, 61391.49609375, 61966.18359375, + 62546.24609375, 63131.7421875, 63722.71875, 64319.23046875, 64921.3203125, 65529.05078125, + 66142.46875, 66761.625, 67386.5859375, 68017.390625, 68654.1015625, 69296.7734375, 69945.4609375, + 70600.21875, 71261.109375, 71928.1875, 72601.5078125, 73281.125, 73967.1171875, 74659.5234375, + 75358.40625, 76063.8359375, 76775.875, 77494.5703125, 78220.0, 78952.21875, 79691.2890625, + 80437.28125, 81190.25, 81950.2734375, 82717.4140625, 83491.734375, 84273.296875, 85062.1796875, + 85858.4453125, 86662.171875, 87473.4140625, 88292.25, 89118.7578125, 89953.0, 90795.046875, + 91644.984375, 92502.875, 93368.7890625, 94242.8203125, 95125.0234375, 96015.4921875, + 96914.2890625, 97821.5078125, 98737.21875, 99661.4921875, 100594.4296875, 101536.09375, + 102486.5703125, 103445.953125, 104414.296875, 105391.7265625, 106378.296875, 107374.109375, + 108379.2421875, 109393.78125, 110417.8125, 111451.4375, 112494.7265625, 113547.796875, + 114610.71875, 115683.59375, 116766.5078125, 117859.546875, 118962.84375, 120076.453125, + 121200.4921875, 122335.046875, 123480.2265625, 124636.1328125, 125802.84375, 126980.4765625, + 128169.1484375, 129368.953125, 130579.96875, 131802.328125, 133036.125, 134281.484375, + 135538.484375, 136807.265625, 138087.921875, 139380.5625, 140685.3125, 142002.265625, + 143331.546875, 144673.265625, 146027.5625, 147394.53125, 148774.28125, 150166.96875, + 151572.671875, 152991.546875, 154423.703125, 155869.265625, 157328.359375, 158801.109375, + 160287.640625, 161788.09375, 163302.59375, 164831.28125, 166374.265625, 167931.6875, + 169503.703125, 171090.421875, 172692.0, 174308.578125, 175940.28125, 177587.265625, 179249.65625, + 180927.609375, 182621.28125, 184330.796875, 186056.3125, 187797.984375, 189555.96875, + 191330.40625, 193121.453125, 194929.265625, 196753.984375, 198595.8125, 200454.859375, + 202331.328125, 204225.34375, 206137.109375, 208066.75, 210014.46875, 211980.40625, 213964.765625, + 215967.6875, 217989.359375, 220029.96875, 222089.671875, 224168.640625, 226267.09375, + 228385.171875, 230523.09375, 232681.015625, 234859.15625, 237057.671875, 239276.765625, + 241516.640625, 243777.46875, 246059.484375, 248362.84375, 250687.765625, 253034.453125, + 255403.109375, 257793.9375, 260207.15625, 262642.96875, 265101.5625, 267583.1875, 270088.03125, + 272616.3125, 275168.28125, 277744.125, 280344.09375, 282968.40625, 285617.28125, 288290.9375, + 290989.625, 293713.59375, 296463.03125, 299238.21875, 302039.40625, 304866.78125, 307720.65625, + 310601.21875, 313508.75, 316443.53125, 319405.75, 322395.71875, 325413.65625, 328459.84375, + 331534.5625, 334638.0625, 337770.625, 340932.5, 344123.96875, 347345.3125, 350596.8125, 353878.75, + 357191.40625, 360535.09375, 363910.0625, 367316.625, 370755.0625, 374225.71875, 377728.84375, + 381264.75, 384833.78125, 388436.21875, 392072.375, 395742.5625, 399447.09375, 403186.34375, + 406960.5625, 410770.125, 414615.34375, 418496.5625, 422414.09375, 426368.3125, 430359.5625, + 434388.15625, 438454.46875, 442558.84375, 446701.65625, 450883.21875, 455103.9375, 459364.15625, + 463664.28125, 468004.65625, 472385.625, 476807.65625, 481271.03125, 485776.21875, 490323.5625, + 494913.5, 499546.375, 504222.65625, 508942.6875, 513706.875, 518515.6875, 523369.53125, + 528268.8125, 533213.9375, 538205.3125, 543243.5, 548328.8125, 553461.6875, 558642.6875, + 563872.125, 569150.5, 574478.375, 579856.0625, 585284.0625, 590762.9375, 596293.0625, 601874.9375, + 607509.125, 613196.0, 618936.125, 624730.0, 630578.125, 636480.9375, 642439.0625, 648452.9375, + 654523.125, 660650.125, 666834.4375, 673076.6875, 679377.375, 685737.0, 692156.1875, 698635.5, + 705175.4375, 711776.5625, 718439.5, 725164.8125, 731953.125, 738804.9375, 745720.875, 752701.5625, + 759747.625, 766859.625, 774038.1875, 781283.9375, 788597.5625, 795979.625, 803430.8125, 810951.75, + 818543.0625, 826205.4375, 833939.5625, 841746.0625, 849625.625, 857579.0, 865606.8125, 873709.75, + 881888.5625, 890143.9375, 898476.5625, 906887.1875, 915376.5625, 923945.4375, 932594.5, 941324.5, + 950136.25, 959030.5, 968008.0, 977069.5, 986215.875, 995447.8125, 1004766.1875, 1014171.8125, + 1023665.5, 1033248.125, 1042920.3125, 1052683.125, 1062537.25, 1072483.625, 1082523.125, + 1092656.625, 1102885.0, 1113209.125, 1123629.875, 1134148.25, 1144765.0, 1155481.125, 1166297.625, + 1177215.375, 1188235.25, 1199358.375, 1210585.5, 1221917.875, 1233356.25, 1244901.625, 1256555.25, + 1268317.75, 1280190.5, +]; diff --git a/src/parser/core/stats.jsx b/src/parser/core/stats.jsx index f388c53ece5..92018151dd2 100644 --- a/src/parser/core/stats.jsx +++ b/src/parser/core/stats.jsx @@ -1,4 +1,5 @@ import multiplierTables from './statsMultiplierTables.generated'; +import * as effectTables from './effectMultiplierTables.generated'; function scaleStat(baseItemLevel, baseStat, itemLevel) { return Math.round(baseStat * 1.15 ** ((itemLevel - baseItemLevel) / 15)); @@ -30,3 +31,15 @@ export function calculateSecondaryStatDefault(baseItemLevel, baseStat, itemLevel export function calculateSecondaryStatJewelry(baseItemLevel, baseStat, itemLevel) { return scaleStatViaMultiplierTable(baseItemLevel, baseStat, itemLevel, multiplierTables.jewelry); } + +/** + * Calculate the value of a secondary damage or healing effect. For example: the healing cap on Echoing Tyrstone. + * + * This is *almost never* needed since we can just read the logged values. This should only be used when the value is not logged (like Tyrstone) and you should take care to double-check the value produced against **in-game** tooltips! + */ +export function calculateEffectScaling(baseItemLevel, baseValue, itemLevel) { + return ( + baseValue * + (effectTables.damageSecondary[itemLevel - 1] / effectTables.damageSecondary[baseItemLevel - 1]) + ); +} diff --git a/src/parser/core/statsMultiplierTables.generated.jsx b/src/parser/core/statsMultiplierTables.generated.jsx index 238530f3214..e7b20114e15 100644 --- a/src/parser/core/statsMultiplierTables.generated.jsx +++ b/src/parser/core/statsMultiplierTables.generated.jsx @@ -363,946 +363,946 @@ export default { 1.449819046, 1.446620228, 1.443356043, // 360 - 1.440027978, - 1.4366375, - 1.433186052, - 1.429675057, - 1.426105916, // 365 - 1.42248001, - 1.418798698, - 1.41506332, + 1.440027979, + 1.436637501, + 1.433186053, + 1.429675058, + 1.426105917, // 365 + 1.422480011, + 1.418798699, + 1.415063321, 1.411275196, - 1.407435624, // 370 - 1.403545886, - 1.399607242, + 1.407435625, // 370 + 1.403545887, + 1.399607243, 1.395620934, 1.391588184, - 1.387510196, // 375 - 1.383388158, - 1.379223236, - 1.375016581, - 1.370769324, - 1.366482581, // 380 - 1.36215745, - 1.357795011, - 1.353396328, - 1.348962448, - 1.344494404, // 385 - 1.339993209, - 1.335459864, - 1.330895351, - 1.326300639, - 1.32167668, // 390 - 1.317024412, - 1.312344758, - 1.307638626, - 1.30290691, - 1.298150488, // 395 - 1.293370227, - 1.288566977, - 1.283741575, - 1.278894847, - 1.274027603, // 400 - 1.269140639, - 1.264234741, - 1.25931068, - 1.254369214, - 1.249411091, // 405 - 1.244437044, - 1.239447794, - 1.234444053, - 1.229426517, - 1.224395872, // 410 - 1.219352795, - 1.214297947, - 1.209231982, - 1.204155539, - 1.199069249, // 415 - 1.193973732, - 1.188869596, - 1.183757438, - 1.178637848, - 1.173511402, // 420 - 1.168378668, - 1.163240203, - 1.158096556, - 1.152948263, - 1.147795855, // 425 - 1.14263985, - 1.137480758, - 1.132319079, - 1.127155306, - 1.121989921, // 430 - 1.116823399, - 1.111656203, - 1.106488793, - 1.101321615, - 1.09615511, // 435 - 1.09098971, - 1.085825839, - 1.080663913, - 1.07550434, - 1.07034752, // 440 - 1.065193845, - 1.060043702, - 1.054897468, - 1.049755513, - 1.0446182, // 445 - 1.039485886, - 1.03435892, - 1.029237645, - 1.024122394, - 1.019013498, // 450 - 1.013911279, - 1.008816051, - 1.003728125, - 0.998647802, - 0.99357538, // 455 - 0.988511149, - 0.983455393, - 0.97840839, - 0.973370414, - 0.968341731, // 460 - 0.963322601, - 0.958313281, - 0.953314019, - 0.94832506, - 0.943346644, // 465 - 0.938379003, - 0.933422365, - 0.928476955, - 0.92354299, - 0.918620684, // 470 - 0.913710243, - 0.908811872, - 0.903925769, - 0.899052128, - 0.894191138, // 475 - 0.889342984, - 0.884507845, - 0.879685897, - 0.874877311, - 0.870082255, // 480 - 0.86530089, - 0.860533376, - 0.855779866, - 0.851040511, - 0.846315457, // 485 - 0.841604847, - 0.836908819, - 0.832227508, - 0.827561044, - 0.822909556, // 490 - 0.818273166, - 0.813651994, - 0.809046158, - 0.80445577, - 0.799880939, // 495 - 0.795321773, - 0.790778373, - 0.78625084, - 0.78173927, - 0.777243757, // 500 - 0.77276439, - 0.768301257, - 0.763854442, - 0.759424026, - 0.755010087, // 505 - 0.750612701, - 0.746231941, - 0.741867877, - 0.737520574, - 0.733190099, // 510 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 515 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 520 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 525 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 530 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 535 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 540 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 545 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 550 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 555 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 560 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 565 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 570 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 575 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 580 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 585 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 590 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 595 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 600 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 605 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 610 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 615 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 620 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 625 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 630 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 635 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 640 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 645 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 650 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 655 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 660 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 665 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 670 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 675 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 680 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 685 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 690 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 695 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 700 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 705 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 710 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 715 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 720 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 725 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 730 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 735 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 740 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 745 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 750 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 755 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 760 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 765 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 770 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 775 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 780 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 785 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 790 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 795 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 800 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 805 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 810 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 815 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 820 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 825 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 830 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 835 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 840 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 845 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 850 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 855 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 860 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 865 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 870 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 875 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 880 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 885 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 890 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 895 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 900 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 905 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 910 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 915 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 920 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 925 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 930 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 935 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 940 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 945 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 950 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 955 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 960 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 965 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 970 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 975 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 980 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 985 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 990 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 995 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1000 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1005 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1010 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1015 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1020 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1025 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1030 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1035 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1040 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1045 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1050 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1055 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1060 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1065 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1070 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1075 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1080 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1085 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1090 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1095 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1100 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1105 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1110 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1115 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1120 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1125 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1130 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1135 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1140 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1145 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1150 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1155 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1160 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1165 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1170 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1175 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1180 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1185 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1190 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1195 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1200 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1205 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1210 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1215 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1220 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1225 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1230 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1235 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1240 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1245 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1250 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1255 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1260 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1265 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1270 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1275 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1280 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1285 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1290 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1295 - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, - 0.733190099, // 1300 + 1.387510197, // 375 + 1.383388159, + 1.379223237, + 1.375016582, + 1.370769325, + 1.366482582, // 380 + 1.362157451, + 1.357795012, + 1.353396329, + 1.348962449, + 1.344494405, // 385 + 1.33999321, + 1.335459865, + 1.330895352, + 1.32630064, + 1.321676681, // 390 + 1.317024413, + 1.312344759, + 1.307638627, + 1.302906911, + 1.298150489, // 395 + 1.293370228, + 1.288566978, + 1.283741577, + 1.278894848, + 1.274027604, // 400 + 1.26914064, + 1.264234742, + 1.259310681, + 1.254369215, + 1.249411092, // 405 + 1.244437045, + 1.239447795, + 1.234444054, + 1.229426518, + 1.224395874, // 410 + 1.219352796, + 1.214297949, + 1.209231983, + 1.20415554, + 1.199069251, // 415 + 1.193973733, + 1.188869597, + 1.18375744, + 1.178637849, + 1.173511403, // 420 + 1.168378669, + 1.163240204, + 1.158096557, + 1.152948265, + 1.147795857, // 425 + 1.142639851, + 1.137480759, + 1.132319081, + 1.127155308, + 1.121989923, // 430 + 1.1168234, + 1.111656205, + 1.106488794, + 1.101321616, + 1.096155111, // 435 + 1.090989712, + 1.085825841, + 1.080663914, + 1.075504341, + 1.070347521, // 440 + 1.065193847, + 1.060043703, + 1.054897469, + 1.049755514, + 1.044618201, // 445 + 1.039485888, + 1.034358922, + 1.029237646, + 1.024122396, + 1.0190135, // 450 + 1.01391128, + 1.008816052, + 1.003728126, + 0.998647803, + 0.993575381, // 455 + 0.98851115, + 0.983455394, + 0.978408392, + 0.973370415, + 0.968341732, // 460 + 0.963322602, + 0.958313282, + 0.95331402, + 0.948325062, + 0.943346645, // 465 + 0.938379004, + 0.933422367, + 0.928476957, + 0.923542992, + 0.918620685, // 470 + 0.913710244, + 0.908811873, + 0.903925771, + 0.89905213, + 0.89419114, // 475 + 0.889342985, + 0.884507846, + 0.879685898, + 0.874877313, + 0.870082256, // 480 + 0.865300891, + 0.860533377, + 0.855779867, + 0.851040512, + 0.846315459, // 485 + 0.841604848, + 0.83690882, + 0.832227509, + 0.827561046, + 0.822909557, // 490 + 0.818273167, + 0.813651996, + 0.809046159, + 0.804455771, + 0.799880941, // 495 + 0.795321774, + 0.790778374, + 0.786250841, + 0.781739271, + 0.777243758, // 500 + 0.772764391, + 0.768301258, + 0.763854443, + 0.759424027, + 0.755010088, // 505 + 0.750612703, + 0.746231943, + 0.741867878, + 0.737520575, + 0.7331901, // 510 + 0.728876513, + 0.724579875, + 0.720300241, + 0.716037665, + 0.7117922, // 515 + 0.707563895, + 0.703352796, + 0.699158948, + 0.694982394, + 0.690823173, // 520 + 0.686681322, + 0.682556879, + 0.678449875, + 0.674360342, + 0.67028831, // 525 + 0.666233805, + 0.662196852, + 0.658177476, + 0.654175696, + 0.650191533, // 530 + 0.646225003, + 0.642276123, + 0.638344906, + 0.634431364, + 0.630535508, // 535 + 0.626657345, + 0.622796884, + 0.618954128, + 0.615129082, + 0.611321748, // 540 + 0.607532127, + 0.603760216, + 0.600006014, + 0.596269517, + 0.592550719, // 545 + 0.588849613, + 0.585166191, + 0.581500443, + 0.577852358, + 0.574221924, // 550 + 0.570609128, + 0.567013953, + 0.563436385, + 0.559876406, + 0.556333997, // 555 + 0.552809139, + 0.549301811, + 0.54581199, + 0.542339654, + 0.538884779, // 560 + 0.535447339, + 0.532027309, + 0.52862466, + 0.525239365, + 0.521871394, // 565 + 0.518520717, + 0.515187304, + 0.511871122, + 0.508572138, + 0.505290318, // 570 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 575 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 580 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 585 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 590 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 595 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 600 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 605 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 610 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 615 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 620 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 625 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 630 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 635 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 640 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 645 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 650 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 655 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 660 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 665 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 670 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 675 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 680 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 685 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 690 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 695 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 700 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 705 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 710 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 715 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 720 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 725 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 730 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 735 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 740 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 745 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 750 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 755 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 760 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 765 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 770 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 775 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 780 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 785 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 790 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 795 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 800 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 805 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 810 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 815 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 820 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 825 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 830 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 835 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 840 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 845 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 850 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 855 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 860 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 865 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 870 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 875 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 880 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 885 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 890 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 895 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 900 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 905 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 910 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 915 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 920 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 925 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 930 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 935 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 940 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 945 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 950 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 955 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 960 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 965 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 970 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 975 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 980 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 985 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 990 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 995 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1000 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1005 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1010 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1015 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1020 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1025 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1030 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1035 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1040 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1045 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1050 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1055 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1060 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1065 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1070 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1075 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1080 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1085 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1090 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1095 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1100 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1105 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1110 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1115 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1120 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1125 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1130 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1135 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1140 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1145 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1150 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1155 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1160 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1165 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1170 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1175 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1180 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1185 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1190 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1195 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1200 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1205 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1210 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1215 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1220 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1225 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1230 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1235 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1240 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1245 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1250 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1255 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1260 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1265 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1270 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1275 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1280 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1285 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1290 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1295 + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, + 0.505290318, // 1300 ], // https://github.com/simulationcraft/simc/blob/dragonflight/engine/dbc/generated/sc_scale_data.inc#L1825-2085 jewelry: [ @@ -1654,957 +1654,957 @@ export default { 1.223039857, 1.230032045, 1.236789344, - 1.24331551, - 1.24961425, // 350 - 1.25568922, - 1.261544029, - 1.267182238, - 1.272607362, - 1.277822867, // 355 - 1.282832175, + 1.243315511, + 1.249614251, // 350 + 1.255689221, + 1.26154403, + 1.267182239, + 1.272607363, + 1.277822868, // 355 + 1.282832176, 1.287638663, - 1.29224566, - 1.296656455, - 1.30087429, // 360 - 1.304902365, - 1.308743836, - 1.312401819, - 1.315879387, - 1.31917957, // 365 - 1.322305361, - 1.325259711, - 1.328045529, - 1.330665689, - 1.333123023, // 370 - 1.335420327, - 1.337560356, - 1.339545831, - 1.341379434, - 1.343063812, // 375 - 1.344601574, - 1.345995296, - 1.347247516, - 1.34836074, - 1.349337438, // 380 - 1.350180046, - 1.350890968, - 1.351472574, - 1.351927201, - 1.352257154, // 385 - 1.352464708, - 1.352552103, - 1.35252155, - 1.352375231, - 1.352115294, // 390 - 1.351743861, - 1.35126302, - 1.350674834, - 1.349981335, - 1.349184527, // 395 - 1.348286386, - 1.347288859, - 1.346193868, - 1.345003307, - 1.343719041, // 400 - 1.342342912, - 1.340876734, - 1.339322295, - 1.337681359, - 1.335955664, // 405 - 1.334146923, - 1.332256825, - 1.330287034, - 1.328239191, - 1.326114914, // 410 - 1.323915796, - 1.321643409, - 1.319299299, - 1.316884994, - 1.314401996, // 415 - 1.311851788, - 1.30923583, - 1.30655556, - 1.303812397, - 1.301007737, // 420 - 1.298142958, - 1.295219415, - 1.292238445, - 1.289201365, - 1.286109471, // 425 - 1.282964042, - 1.279766337, - 1.276517595, - 1.273219039, - 1.269871872, // 430 - 1.266477278, - 1.263036427, - 1.259550467, - 1.256020532, - 1.252447737, // 435 - 1.24883318, - 1.245177943, - 1.241483093, - 1.237749677, - 1.233978729, // 440 - 1.230171267, - 1.226328292, - 1.22245079, - 1.218539732, - 1.214596075, // 445 - 1.210620758, - 1.206614709, - 1.202578839, - 1.198514047, - 1.194421215, // 450 - 1.190301214, - 1.1861549, - 1.181983114, - 1.177786686, - 1.173566432, // 455 - 1.169323155, - 1.165057645, - 1.160770678, - 1.156463021, - 1.152135425, // 460 - 1.147788631, - 1.143423367, - 1.13904035, - 1.134640284, - 1.130223863, // 465 - 1.125791768, - 1.12134467, - 1.116883228, - 1.112408091, - 1.107919897, // 470 - 1.103419273, - 1.098906836, - 1.094383191, - 1.089848935, - 1.085304653, // 475 - 1.080750922, - 1.076188307, - 1.071617365, - 1.067038644, - 1.06245268, // 480 - 1.057860001, - 1.053261127, - 1.048656568, - 1.044046824, - 1.039432387, // 485 - 1.034813742, - 1.030191363, - 1.025565717, - 1.020937262, - 1.016306447, // 490 - 1.011673715, - 1.0070395, - 1.002404227, - 0.997768315, - 0.993132174, // 495 - 0.988496208, - 0.983860811, - 0.979226372, - 0.974593272, - 0.969961885, // 500 - 0.965332577, - 0.960705709, - 0.956081632, - 0.951460694, - 0.946843233, // 505 - 0.942229583, - 0.93762007, - 0.933015015, - 0.92841473, - 0.923819525, // 510 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 515 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 520 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 525 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 530 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 535 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 540 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 545 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 550 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 555 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 560 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 565 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 570 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 575 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 580 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 585 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 590 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 595 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 600 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 605 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 610 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 615 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 620 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 625 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 630 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 635 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 640 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 645 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 650 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 655 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 660 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 665 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 670 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 675 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 680 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 685 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 690 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 695 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 700 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 705 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 710 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 715 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 720 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 725 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 730 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 735 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 740 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 745 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 750 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 755 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 760 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 765 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 770 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 775 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 780 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 785 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 790 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 795 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 800 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 805 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 810 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 815 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 820 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 825 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 830 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 835 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 840 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 845 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 850 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 855 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 860 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 865 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 870 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 875 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 880 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 885 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 890 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 895 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 900 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 905 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 910 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 915 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 920 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 925 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 930 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 935 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 940 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 945 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 950 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 955 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 960 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 965 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 970 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 975 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 980 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 985 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 990 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 995 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1000 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1005 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1010 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1015 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1020 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1025 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1030 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1035 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1040 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1045 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1050 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1055 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1060 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1065 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1070 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1075 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1080 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1085 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1090 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1095 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1100 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1105 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1110 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1115 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1120 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1125 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1130 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1135 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1140 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1145 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1150 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1155 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1160 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1165 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1170 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1175 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1180 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1185 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1190 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1195 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1200 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1205 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1210 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1215 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1220 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1225 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1230 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1235 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1240 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1245 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1250 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1255 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1260 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1265 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1270 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1275 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1280 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1285 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1290 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1295 - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, - 0.923819525, // 1300 + 1.292245661, + 1.296656456, + 1.300874291, // 360 + 1.304902366, + 1.308743837, + 1.31240182, + 1.315879388, + 1.319179571, // 365 + 1.322305362, + 1.325259712, + 1.32804553, + 1.33066569, + 1.333123025, // 370 + 1.335420328, + 1.337560357, + 1.339545832, + 1.341379435, + 1.343063813, // 375 + 1.344601575, + 1.345995297, + 1.347247518, + 1.348360741, + 1.349337439, // 380 + 1.350180048, + 1.35089097, + 1.351472576, + 1.351927203, + 1.352257156, // 385 + 1.352464709, + 1.352552104, + 1.352521552, + 1.352375233, + 1.352115296, // 390 + 1.351743862, + 1.351263022, + 1.350674836, + 1.349981337, + 1.349184529, // 395 + 1.348286387, + 1.347288861, + 1.34619387, + 1.345003308, + 1.343719043, // 400 + 1.342342914, + 1.340876736, + 1.339322297, + 1.337681361, + 1.335955666, // 405 + 1.334146925, + 1.332256826, + 1.330287036, + 1.328239193, + 1.326114916, // 410 + 1.323915798, + 1.32164341, + 1.319299301, + 1.316884996, + 1.314401998, // 415 + 1.31185179, + 1.309235832, + 1.306555562, + 1.303812399, + 1.301007739, // 420 + 1.29814296, + 1.295219417, + 1.292238447, + 1.289201367, + 1.286109473, // 425 + 1.282964044, + 1.279766339, + 1.276517597, + 1.273219041, + 1.269871874, // 430 + 1.266477281, + 1.263036429, + 1.259550469, + 1.256020534, + 1.252447739, // 435 + 1.248833182, + 1.245177945, + 1.241483095, + 1.237749679, + 1.233978732, // 440 + 1.230171269, + 1.226328294, + 1.222450792, + 1.218539734, + 1.214596077, // 445 + 1.21062076, + 1.206614711, + 1.202578841, + 1.198514049, + 1.194421217, // 450 + 1.190301216, + 1.186154902, + 1.181983116, + 1.177786688, + 1.173566434, // 455 + 1.169323157, + 1.165057647, + 1.16077068, + 1.156463023, + 1.152135427, // 460 + 1.147788633, + 1.143423369, + 1.139040352, + 1.134640286, + 1.130223865, // 465 + 1.12579177, + 1.121344672, + 1.11688323, + 1.112408093, + 1.1079199, // 470 + 1.103419275, + 1.098906838, + 1.094383193, + 1.089848937, + 1.085304655, // 475 + 1.080750924, + 1.076188309, + 1.071617367, + 1.067038646, + 1.062452682, // 480 + 1.057860003, + 1.053261129, + 1.04865657, + 1.044046826, + 1.039432389, // 485 + 1.034813744, + 1.030191365, + 1.025565719, + 1.020937264, + 1.016306449, // 490 + 1.011673717, + 1.007039502, + 1.002404229, + 0.997768317, + 0.993132176, // 495 + 0.98849621, + 0.983860813, + 0.979226374, + 0.974593274, + 0.969961887, // 500 + 0.965332579, + 0.96070571, + 0.956081634, + 0.951460695, + 0.946843235, // 505 + 0.942229585, + 0.937620072, + 0.933015017, + 0.928414732, + 0.923819527, // 510 + 0.919229701, + 0.914645551, + 0.910067366, + 0.905495429, + 0.90093002, // 515 + 0.896371409, + 0.891819863, + 0.887275644, + 0.882739006, + 0.878210201, // 520 + 0.873689473, + 0.869177062, + 0.864673202, + 0.860178123, + 0.855692049, // 525 + 0.8512152, + 0.846747791, + 0.842290032, + 0.837842127, + 0.833404278, // 530 + 0.828976681, + 0.824559527, + 0.820153003, + 0.815757292, + 0.811372573, // 535 + 0.806999019, + 0.802636801, + 0.798286085, + 0.793947032, + 0.7896198, // 540 + 0.785304544, + 0.781001413, + 0.776710554, + 0.772432108, + 0.768166216, // 545 + 0.763913012, + 0.759672627, + 0.75544519, + 0.751230825, + 0.747029653, // 550 + 0.742841793, + 0.738667358, + 0.73450646, + 0.730359207, + 0.726225703, // 555 + 0.72210605, + 0.718000347, + 0.713908689, + 0.70983117, + 0.705767878, // 560 + 0.7017189, + 0.697684321, + 0.693664222, + 0.689658681, + 0.685667773, // 565 + 0.681691573, + 0.677730149, + 0.673783572, + 0.669851904, + 0.665935211, // 570 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 575 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 580 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 585 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 590 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 595 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 600 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 605 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 610 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 615 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 620 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 625 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 630 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 635 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 640 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 645 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 650 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 655 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 660 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 665 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 670 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 675 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 680 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 685 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 690 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 695 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 700 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 705 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 710 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 715 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 720 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 725 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 730 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 735 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 740 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 745 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 750 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 755 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 760 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 765 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 770 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 775 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 780 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 785 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 790 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 795 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 800 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 805 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 810 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 815 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 820 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 825 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 830 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 835 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 840 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 845 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 850 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 855 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 860 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 865 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 870 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 875 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 880 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 885 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 890 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 895 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 900 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 905 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 910 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 915 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 920 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 925 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 930 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 935 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 940 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 945 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 950 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 955 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 960 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 965 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 970 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 975 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 980 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 985 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 990 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 995 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1000 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1005 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1010 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1015 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1020 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1025 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1030 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1035 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1040 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1045 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1050 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1055 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1060 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1065 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1070 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1075 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1080 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1085 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1090 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1095 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1100 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1105 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1110 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1115 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1120 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1125 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1130 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1135 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1140 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1145 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1150 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1155 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1160 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1165 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1170 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1175 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1180 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1185 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1190 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1195 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1200 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1205 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1210 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1215 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1220 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1225 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1230 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1235 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1240 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1245 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1250 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1255 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1260 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1265 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1270 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1275 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1280 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1285 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1290 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1295 + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, + 0.665935211, // 1300 ], }; diff --git a/src/parser/retail/modules/items/dragonflight/EchoingTyrstone.tsx b/src/parser/retail/modules/items/dragonflight/EchoingTyrstone.tsx index 26df173b56d..a22767e5dd3 100644 --- a/src/parser/retail/modules/items/dragonflight/EchoingTyrstone.tsx +++ b/src/parser/retail/modules/items/dragonflight/EchoingTyrstone.tsx @@ -10,35 +10,10 @@ import ItemPercentHealingDone from 'parser/ui/ItemPercentHealingDone'; import STATISTIC_CATEGORY from 'parser/ui/STATISTIC_CATEGORY'; import STATISTIC_ORDER from 'parser/ui/STATISTIC_ORDER'; import Statistic from 'parser/ui/Statistic'; +import { calculateEffectScaling, calculateSecondaryStatDefault } from 'parser/core/stats'; const ECHOING_TYRSTONE_COOLDOWN = 120000; // 120sec -// Need a better way to do this. The calculateSecondaryStat functions don't seem to line up -const HEALS_BY_ILVL: { [key: number]: EchoingTyrstoneStats } = { - 421: { heal: 181431, haste: 179 }, - 424: { heal: 189728, haste: 184 }, - 428: { heal: 201297, haste: 191 }, - 431: { heal: 210370, haste: 197 }, - 434: { heal: 219795, haste: 202 }, - 437: { heal: 229585, haste: 208 }, - 441: { heal: 243230, haste: 216 }, - 444: { heal: 253925, haste: 222 }, - 447: { heal: 265031, haste: 228 }, - 450: { heal: 276562, haste: 235 }, - 454: { heal: 292625, haste: 243 }, - 457: { heal: 305208, haste: 250 }, - 460: { heal: 318273, haste: 257 }, - 463: { heal: 331829, haste: 265 }, - 467: { heal: 350708, haste: 275 }, - 470: { heal: 365491, haste: 283 }, - 473: { heal: 380829, haste: 291 }, - 476: { heal: 396746, haste: 299 }, - 480: { heal: 418896, haste: 310 }, - 483: { heal: 436236, haste: 319 }, - 486: { heal: 454222, haste: 328 }, - 489: { heal: 472877, haste: 337 }, -}; - class EchoingTyrstone extends Analyzer { healAmount: number = 0; overhealAmount: number = 0; @@ -58,8 +33,8 @@ class EchoingTyrstone extends Analyzer { } const itemLevel = this.trinket?.itemLevel || 421; - this.maxStored = HEALS_BY_ILVL[itemLevel].heal; - this.hasteValue = HEALS_BY_ILVL[itemLevel].haste; + this.maxStored = calculateEffectScaling(437, 229585, itemLevel); + this.hasteValue = calculateSecondaryStatDefault(437, 225, itemLevel); this.addEventListener(Events.heal.by(SELECTED_PLAYER), this.onHeal); @@ -212,8 +187,3 @@ interface EchoingTyrstoneUse { /** Heal stored during use */ heal: number; } - -interface EchoingTyrstoneStats { - heal: number; - haste: number; -} From a5fccc5cb7e3af109c0cd5e5b65fa27f01c52246 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Thu, 4 Jan 2024 18:57:08 -0500 Subject: [PATCH 13/46] Cast Links + GS --- .../retail/mage/frost/CombatLogParser.ts | 2 + .../frost/normalizers/CastLinkNormalizer.ts | 125 +++++++++++++++ .../mage/frost/talents/GlacialSpike.tsx | 151 +++--------------- 3 files changed, 153 insertions(+), 125 deletions(-) create mode 100644 src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts diff --git a/src/analysis/retail/mage/frost/CombatLogParser.ts b/src/analysis/retail/mage/frost/CombatLogParser.ts index 75d267bf7f1..c5df5470261 100644 --- a/src/analysis/retail/mage/frost/CombatLogParser.ts +++ b/src/analysis/retail/mage/frost/CombatLogParser.ts @@ -42,6 +42,7 @@ import ThermalVoid from './talents/ThermalVoid'; //Normalizers import CometStormLinkNormalizer from './normalizers/CometStormLinkNormalizer'; +import CastLinkNormalizer from './normalizers/CastLinkNormalizer'; class CombatLogParser extends CoreCombatLogParser { static specModules = { @@ -50,6 +51,7 @@ class CombatLogParser extends CoreCombatLogParser { //Normalizers cometStormLinkNormalizer: CometStormLinkNormalizer, + castLinkNormalizer: CastLinkNormalizer, //Core abilities: Abilities, diff --git a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts new file mode 100644 index 00000000000..8bb8ce709b4 --- /dev/null +++ b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts @@ -0,0 +1,125 @@ +import SPELLS from 'common/SPELLS'; +import TALENTS from 'common/TALENTS/mage'; +import EventLinkNormalizer, { EventLink } from 'parser/core/EventLinkNormalizer'; +import { + AbilityEvent, + BeginCastEvent, + RemoveBuffEvent, + CastEvent, + EventType, + GetRelatedEvents, + HasRelatedEvent, + HasTarget, +} from 'parser/core/Events'; +import { Options } from 'parser/core/Module'; +import { encodeTargetString } from 'parser/shared/modules/Enemies'; + +const CAST_BUFFER_MS = 75; + +export const BUFF_APPLY = 'BuffApply'; +export const BUFF_REMOVE = 'BuffRemove'; +export const BUFF_REFRESH = 'BuffRefresh'; +export const CAST_BEGIN = 'CastBegin'; +export const SPELL_CAST = 'SpellCast'; +export const PRE_CAST = 'PreCast'; +export const SPELL_DAMAGE = 'SpellDamage'; +export const CLEAVE_DAMAGE = 'CleaveDamage'; +export const EXPLODE_DEBUFF = 'ExplosionDebuff'; + +const EVENT_LINKS: EventLink[] = [ + { + reverseLinkRelation: SPELL_CAST, + linkingEventId: TALENTS.GLACIAL_SPIKE_TALENT.id, + linkingEventType: EventType.Cast, + linkRelation: SPELL_DAMAGE, + referencedEventId: SPELLS.GLACIAL_SPIKE_DAMAGE.id, + referencedEventType: EventType.Damage, + anyTarget: true, + additionalCondition(linkingEvent, referencedEvent): boolean { + if (!linkingEvent || !referencedEvent) { + return false; + } + const castTarget = + HasTarget(linkingEvent) && + encodeTargetString(linkingEvent.targetID, linkingEvent.targetInstance); + const damageTarget = + HasTarget(referencedEvent) && + encodeTargetString(referencedEvent.targetID, referencedEvent.targetInstance); + return castTarget === damageTarget; + }, + maximumLinks: 1, + forwardBufferMs: 3000, + backwardBufferMs: CAST_BUFFER_MS, + }, + { + reverseLinkRelation: SPELL_CAST, + linkingEventId: TALENTS.GLACIAL_SPIKE_TALENT.id, + linkingEventType: EventType.Cast, + linkRelation: CLEAVE_DAMAGE, + referencedEventId: SPELLS.GLACIAL_SPIKE_DAMAGE.id, + referencedEventType: EventType.Damage, + anyTarget: true, + additionalCondition(linkingEvent, referencedEvent): boolean { + if (!linkingEvent || !referencedEvent) { + return false; + } + const castTarget = + HasTarget(linkingEvent) && + encodeTargetString(linkingEvent.targetID, linkingEvent.targetInstance); + const damageTarget = + HasTarget(referencedEvent) && + encodeTargetString(referencedEvent.targetID, referencedEvent.targetInstance); + return castTarget !== damageTarget; + }, + maximumLinks: 1, + forwardBufferMs: 3000, + backwardBufferMs: CAST_BUFFER_MS, + }, +]; + +/** + * When a spell is cast on a target, the ordering of the Cast and ApplyBuff/RefreshBuff/(direct)Heal + * can be semi-arbitrary, making analysis difficult. + * + * This normalizer adds a _linkedEvent to the ApplyBuff/RefreshBuff/Heal linking back to the Cast event + * that caused it (if one can be found). + * + * This normalizer adds links for the buffs Rejuvenation, Regrowth, Wild Growth, Lifebloom, + * and for the direct heals of Swiftmend and Regrowth, and the self buff from Flourish. + * A special link key is used when the HoTs were applied by an Overgrowth cast instead of a normal hardcast. + */ +class CastLinkNormalizer extends EventLinkNormalizer { + constructor(options: Options) { + super(options, EVENT_LINKS); + } +} + +/** Returns true iff the given buff application or heal can be matched back to a hardcast */ +export function isFromHardcast(event: AbilityEvent): boolean { + return HasRelatedEvent(event, SPELL_CAST); +} + +export function isInstantCast(event: CastEvent): boolean { + const beginCast = GetRelatedEvents(event, CAST_BEGIN)[0]; + return !beginCast || event.timestamp - beginCast.timestamp <= CAST_BUFFER_MS; +} + +export function hasPreCast(event: AbilityEvent): boolean { + return HasRelatedEvent(event, PRE_CAST); +} + +/** Returns the hardcast event that caused this buff or heal, if there is one */ +export function getHardcast(event: AbilityEvent): CastEvent | undefined { + return GetRelatedEvents( + event, + SPELL_CAST, + (e): e is CastEvent => e.type === EventType.Cast, + ).pop(); +} + +export function isProcExpired(event: RemoveBuffEvent, spenderId: number): boolean { + const cast = GetRelatedEvents(event, SPELL_CAST)[0]; + return !cast || cast.ability.guid !== spenderId; +} + +export default CastLinkNormalizer; diff --git a/src/analysis/retail/mage/frost/talents/GlacialSpike.tsx b/src/analysis/retail/mage/frost/talents/GlacialSpike.tsx index 7a9c561f366..13fedc5256f 100644 --- a/src/analysis/retail/mage/frost/talents/GlacialSpike.tsx +++ b/src/analysis/retail/mage/frost/talents/GlacialSpike.tsx @@ -1,15 +1,8 @@ -import { Trans } from '@lingui/macro'; import { SHATTER_DEBUFFS } from 'analysis/retail/mage/shared'; -import { formatPercentage } from 'common/format'; -import SPELLS from 'common/SPELLS'; import TALENTS from 'common/TALENTS/mage'; -import { SpellLink } from 'interface'; -import { TooltipElement } from 'interface'; import Analyzer, { SELECTED_PLAYER, Options } from 'parser/core/Analyzer'; -import Events, { CastEvent, DamageEvent, FightEndEvent, HasTarget } from 'parser/core/Events'; -import { When, ThresholdStyle } from 'parser/core/ParseResults'; -import AbilityTracker from 'parser/shared/modules/AbilityTracker'; -import Enemies, { encodeTargetString } from 'parser/shared/modules/Enemies'; +import Events, { CastEvent, DamageEvent, GetRelatedEvent } from 'parser/core/Events'; +import Enemies from 'parser/shared/modules/Enemies'; import BoringSpellValueText from 'parser/ui/BoringSpellValueText'; import Statistic from 'parser/ui/Statistic'; import STATISTIC_CATEGORY from 'parser/ui/STATISTIC_CATEGORY'; @@ -17,15 +10,15 @@ import STATISTIC_CATEGORY from 'parser/ui/STATISTIC_CATEGORY'; class GlacialSpike extends Analyzer { static dependencies = { enemies: Enemies, - abilityTracker: AbilityTracker, }; protected enemies!: Enemies; - protected abilityTracker!: AbilityTracker; - lastCastEvent?: CastEvent; - lastCastDidDamage = false; - spikeShattered = 0; - spikeNotShattered = 0; + glacialSpike: { + timestamp: number; + shattered: boolean; + damage: DamageEvent | undefined; + cleave: DamageEvent | undefined; + }[] = []; constructor(options: Options) { super(options); @@ -35,122 +28,28 @@ class GlacialSpike extends Analyzer { Events.cast.by(SELECTED_PLAYER).spell(TALENTS.GLACIAL_SPIKE_TALENT), this.onGlacialSpikeCast, ); - this.addEventListener( - Events.damage.by(SELECTED_PLAYER).spell(SPELLS.GLACIAL_SPIKE_DAMAGE), - this.onGlacialSpikeDamage, - ); - this.addEventListener(Events.fightend, this.onFightEnd); } onGlacialSpikeCast(event: CastEvent) { - if (this.lastCastEvent) { - this.flagTimeline(this.lastCastEvent); - } - - this.lastCastEvent = event; - this.lastCastDidDamage = false; - } - - onGlacialSpikeDamage(event: DamageEvent) { - if (!this.lastCastEvent) { - return; - } - if (!HasTarget(this.lastCastEvent)) { - return; - } - - const castTarget = encodeTargetString( - this.lastCastEvent.targetID, - this.lastCastEvent.targetInstance, - ); - const damageTarget = encodeTargetString(event.targetID, event.targetInstance); - - //We dont care about the Glacial Spikes that split to something else via Splitting Ice. - if (castTarget !== damageTarget) { - return; - } - - this.lastCastDidDamage = true; - const enemy: any = this.enemies.getEntity(event); - if (enemy && SHATTER_DEBUFFS.some((effect) => enemy.hasBuff(effect.id, event.timestamp))) { - this.spikeShattered += 1; - } else { - this.spikeNotShattered += 1; - this.flagTimeline(this.lastCastEvent); - } - this.lastCastEvent = undefined; + const damage: DamageEvent | undefined = GetRelatedEvent(event, 'SpellDamage'); + const enemy = damage && this.enemies.getEntity(damage); + const cleave: DamageEvent | undefined = GetRelatedEvent(event, 'CleaveDamage'); + this.glacialSpike.push({ + timestamp: event.timestamp, + shattered: + (enemy && SHATTER_DEBUFFS.some((effect) => enemy.hasBuff(effect.id, event.timestamp))) || + false, + damage: damage, + cleave: cleave, + }); } - onFightEnd(event: FightEndEvent) { - if (this.lastCastEvent) { - this.flagTimeline(this.lastCastEvent); - } - } - - flagTimeline(event: CastEvent) { - if (!this.lastCastEvent) { - return; - } - - event.meta = event.meta || {}; - event.meta.isInefficientCast = true; - if (this.lastCastDidDamage) { - event.meta.inefficientCastReason = `You cast Glacial Spike without shattering it. You should wait until it is frozen or you are able to use a Brain Freeze proc to maximize its damage.`; - } else { - event.meta.inefficientCastReason = - 'The target died before Glacial Spike hit it. You should avoid this by casting faster spells on very low-health targets, it is important to not waste potential Glacial Spike damage.'; - } - } - - get utilPercentage() { - return this.spikeShattered / this.totalCasts || 0; + get shatteredCasts() { + return this.glacialSpike.filter((gs) => gs.shattered).length; } get totalCasts() { - return this.abilityTracker.getAbility(TALENTS.GLACIAL_SPIKE_TALENT.id).casts; - } - - get glacialSpikeUtilizationThresholds() { - return { - actual: this.utilPercentage, - isLessThan: { - minor: 1.0, - average: 0.85, - major: 0.7, - }, - style: ThresholdStyle.PERCENTAGE, - }; - } - - suggestions(when: When) { - when(this.glacialSpikeUtilizationThresholds).addSuggestion((suggest, actual, recommended) => - suggest( - <> - You cast without{' '} - - ing it {this.spikeNotShattered} times. Because it is such a potent ability, it is - important to maximize it's damage by only casting it if the target is - - Winter's Chill, Frost Nova, Ice Nova, Ring of Frost, and your pet's Freeze will all - cause the target to be frozen or act as frozen. - - } - > - Frozen or acting as Frozen - - . - , - ) - .icon(TALENTS.GLACIAL_SPIKE_TALENT.icon) - .actual( - - {formatPercentage(actual, 1)}% utilization - , - ) - .recommended(`${formatPercentage(recommended, 1)}% is recommended`), - ); + return this.glacialSpike.length; } statistic() { @@ -160,13 +59,15 @@ class GlacialSpike extends Analyzer { size="flexible" tooltip={ <> - You cast Glacial Spike {this.totalCasts} times, {this.spikeShattered} casts of which + You cast Glacial Spike {this.totalCasts} times, {this.shatteredCasts} casts of which were Shattered } > - {`${formatPercentage(this.utilPercentage, 0)}%`} Cast utilization + {this.shatteredCasts} Shattered Casts +
    + {this.totalCasts - this.shatteredCasts} Non-Shattered Casts
    ); From 8a4b310c305e4f538f4b6f4b956d69a96ffb9b9f Mon Sep 17 00:00:00 2001 From: Sharrq Date: Thu, 4 Jan 2024 19:00:24 -0500 Subject: [PATCH 14/46] Remove GS from Checklist --- .../retail/mage/frost/checklist/Component.tsx | 26 ------------------- .../retail/mage/frost/checklist/Module.tsx | 4 --- 2 files changed, 30 deletions(-) diff --git a/src/analysis/retail/mage/frost/checklist/Component.tsx b/src/analysis/retail/mage/frost/checklist/Component.tsx index 95fbf565cef..51978a31aca 100644 --- a/src/analysis/retail/mage/frost/checklist/Component.tsx +++ b/src/analysis/retail/mage/frost/checklist/Component.tsx @@ -109,32 +109,6 @@ const FrostMageChecklist = ({ combatant, castEfficiency, thresholds }: Checklist tooltip="Munching a proc refers to a situation where you have a Fingers of Frost proc at the same time that Winters Chill is on the target. This essentially leads to a wasted Fingers of Frost proc since Fingers of Frost and Winter's Chill both do the same thing, and casting Ice Lance will remove both a Fingers of Frost proc and a stack of Winter's Chill. This is sometimes unavoidable, but if you have both a Fingers of Frost proc and a Brain Freeze proc, you can minimize this by ensuring that you use the Fingers of Frost procs first before you start casting Frostbolt and Flurry to use the Brain Freeze proc." /> - - When talented into you should always - ensure that you are getting the most out of it, because a large part of your damage will - come from making sure that you are handling Glacial Spike properly. As a rule, once you - have Glacial Spike available, you should not cast it unless you can cast{' '} - alongside it ( - {'>'}{' '} - {'>'} - ) or if you also have the{' '} - and the Glacial Spike will hit a - second target. If neither of those are true, then you should continue casting{' '} - until {' '} - is available or you get a proc. - - } - > - {combatant.hasTalent(TALENTS.GLACIAL_SPIKE_TALENT) && ( - - )} - Date: Thu, 4 Jan 2024 19:34:02 -0500 Subject: [PATCH 15/46] Brain Freeze --- .../retail/mage/frost/core/BrainFreeze.tsx | 91 ++++++++++++------- .../frost/normalizers/CastLinkNormalizer.ts | 22 +++++ 2 files changed, 81 insertions(+), 32 deletions(-) diff --git a/src/analysis/retail/mage/frost/core/BrainFreeze.tsx b/src/analysis/retail/mage/frost/core/BrainFreeze.tsx index 639c7376a72..ab830975bb4 100644 --- a/src/analysis/retail/mage/frost/core/BrainFreeze.tsx +++ b/src/analysis/retail/mage/frost/core/BrainFreeze.tsx @@ -4,8 +4,14 @@ import { formatNumber, formatPercentage } from 'common/format'; import SPELLS from 'common/SPELLS'; import TALENTS from 'common/TALENTS/mage'; import { SpellLink } from 'interface'; -import Analyzer from 'parser/core/Analyzer'; -import { EventType } from 'parser/core/Events'; +import Analyzer, { Options, SELECTED_PLAYER } from 'parser/core/Analyzer'; +import Events, { + CastEvent, + ApplyBuffEvent, + RemoveBuffEvent, + RefreshBuffEvent, + GetRelatedEvent, +} from 'parser/core/Events'; import { ThresholdStyle, When } from 'parser/core/ParseResults'; import Enemies from 'parser/shared/modules/Enemies'; import EventHistory from 'parser/shared/modules/EventHistory'; @@ -23,41 +29,62 @@ class BrainFreeze extends Analyzer { protected eventHistory!: EventHistory; protected sharedCode!: SharedCode; - overlappedFlurries = () => { - let casts = this.eventHistory.getEvents(EventType.Cast, { - spell: TALENTS.FLURRY_TALENT, + brainFreezeRefreshes = 0; + flurry: { timestamp: number; overlapped: boolean }[] = []; + brainFreeze: { apply: ApplyBuffEvent; remove: RemoveBuffEvent | undefined; expired: boolean }[] = + []; + + constructor(options: Options) { + super(options); + this.addEventListener( + Events.cast.by(SELECTED_PLAYER).spell(TALENTS.FLURRY_TALENT), + this.onFlurryCast, + ); + this.addEventListener( + Events.applybuff.by(SELECTED_PLAYER).spell(SPELLS.BRAIN_FREEZE_BUFF), + this.onBrainFreeze, + ); + this.addEventListener( + Events.refreshbuff.by(SELECTED_PLAYER).spell(SPELLS.BRAIN_FREEZE_BUFF), + this.onBrainFreezeRefresh, + ); + } + + onFlurryCast(event: CastEvent) { + this.flurry.push({ + timestamp: event.timestamp, + overlapped: this.selectedCombatant.hasBuff(SPELLS.BRAIN_FREEZE_BUFF.id, event.timestamp - 10), }); - casts = casts.filter((c) => { - const enemy = this.enemies.getEntity(c); - return enemy && enemy.hasBuff(SPELLS.WINTERS_CHILL.id); + } + + onBrainFreeze(event: ApplyBuffEvent) { + const remove: RemoveBuffEvent | undefined = GetRelatedEvent(event, 'BuffRemove'); + const spender: CastEvent | undefined = remove && GetRelatedEvent(remove, 'SpellCast'); + this.brainFreeze.push({ + apply: event, + remove: remove || undefined, + expired: !spender, }); - return casts.length || 0; - }; + } - get expiredProcs() { - return ( - this.sharedCode.getExpiredProcs(SPELLS.BRAIN_FREEZE_BUFF, TALENTS.FLURRY_TALENT).length || 0 - ); + onBrainFreezeRefresh(event: RefreshBuffEvent) { + this.brainFreezeRefreshes += 1; } - get totalProcs() { - return ( - this.eventHistory.getEvents(EventType.ApplyBuff, { - spell: SPELLS.BRAIN_FREEZE_BUFF, - }).length || 0 - ); + get overlappedFlurries() { + return this.flurry.filter((f) => f.overlapped).length; } - get overwrittenProcs() { - return ( - this.eventHistory.getEvents(EventType.RefreshBuff, { - spell: SPELLS.BRAIN_FREEZE_BUFF, - }).length || 0 - ); + get expiredProcs() { + return this.brainFreeze.filter((bf) => bf.expired).length; + } + + get totalProcs() { + return this.brainFreeze.length; } get wastedPercent() { - return (this.overwrittenProcs + this.expiredProcs) / this.totalProcs || 0; + return (this.brainFreezeRefreshes + this.expiredProcs) / this.totalProcs || 0; } get utilPercent() { @@ -79,7 +106,7 @@ class BrainFreeze extends Analyzer { // Percentages lowered from .00, .08, .16; with the addition of the forgiveness window it is almost as bad as letting BF expire when you waste a proc get brainFreezeOverwritenThresholds() { return { - actual: this.overwrittenProcs / this.totalProcs || 0, + actual: this.brainFreezeRefreshes / this.totalProcs || 0, isGreaterThan: { minor: 0.0, average: 0.05, @@ -104,7 +131,7 @@ class BrainFreeze extends Analyzer { get overlappedFlurryThresholds() { return { - actual: this.overlappedFlurries(), + actual: this.brainFreezeRefreshes, isGreaterThan: { average: 0, major: 3, @@ -154,7 +181,7 @@ class BrainFreeze extends Analyzer { <> You cast and applied{' '} while the target still had the{' '} - debuff on them {this.overlappedFlurries()}{' '} + debuff on them {this.brainFreezeRefreshes}{' '} times. Casting applies 2 stacks of{' '} to the target so you should always ensure you are spending both stacks before you cast and @@ -178,8 +205,8 @@ class BrainFreeze extends Analyzer { <> You got {this.totalProcs} total procs.
      -
    • {this.totalProcs - this.expiredProcs - this.overwrittenProcs} used
    • -
    • {this.overwrittenProcs} overwritten
    • +
    • {this.totalProcs - this.expiredProcs - this.brainFreezeRefreshes} used
    • +
    • {this.brainFreezeRefreshes} overwritten
    • {this.expiredProcs} expired
    diff --git a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts index 8bb8ce709b4..78f36063c48 100644 --- a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts +++ b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts @@ -75,6 +75,28 @@ const EVENT_LINKS: EventLink[] = [ forwardBufferMs: 3000, backwardBufferMs: CAST_BUFFER_MS, }, + { + reverseLinkRelation: BUFF_APPLY, + linkingEventId: SPELLS.BRAIN_FREEZE_BUFF.id, + linkingEventType: EventType.ApplyBuff, + linkRelation: BUFF_REMOVE, + referencedEventId: SPELLS.BRAIN_FREEZE_BUFF.id, + referencedEventType: EventType.RemoveBuff, + maximumLinks: 1, + forwardBufferMs: 17_000, + backwardBufferMs: CAST_BUFFER_MS, + }, + { + reverseLinkRelation: BUFF_REMOVE, + linkingEventId: SPELLS.BRAIN_FREEZE_BUFF.id, + linkingEventType: EventType.RemoveBuff, + linkRelation: SPELL_CAST, + referencedEventId: TALENTS.FLURRY_TALENT.id, + referencedEventType: EventType.Cast, + maximumLinks: 1, + forwardBufferMs: CAST_BUFFER_MS, + backwardBufferMs: CAST_BUFFER_MS, + }, ]; /** From b4d812d47c1089d4e33be13605524c68a0b38823 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Thu, 4 Jan 2024 19:35:41 -0500 Subject: [PATCH 16/46] remove brain freeze dependencies --- src/analysis/retail/mage/frost/core/BrainFreeze.tsx | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/analysis/retail/mage/frost/core/BrainFreeze.tsx b/src/analysis/retail/mage/frost/core/BrainFreeze.tsx index ab830975bb4..35fd9c21690 100644 --- a/src/analysis/retail/mage/frost/core/BrainFreeze.tsx +++ b/src/analysis/retail/mage/frost/core/BrainFreeze.tsx @@ -1,5 +1,4 @@ import { Trans } from '@lingui/macro'; -import { SharedCode } from 'analysis/retail/mage/shared'; import { formatNumber, formatPercentage } from 'common/format'; import SPELLS from 'common/SPELLS'; import TALENTS from 'common/TALENTS/mage'; @@ -13,22 +12,11 @@ import Events, { GetRelatedEvent, } from 'parser/core/Events'; import { ThresholdStyle, When } from 'parser/core/ParseResults'; -import Enemies from 'parser/shared/modules/Enemies'; -import EventHistory from 'parser/shared/modules/EventHistory'; import BoringSpellValueText from 'parser/ui/BoringSpellValueText'; import Statistic from 'parser/ui/Statistic'; import STATISTIC_ORDER from 'parser/ui/STATISTIC_ORDER'; class BrainFreeze extends Analyzer { - static dependencies = { - enemies: Enemies, - eventHistory: EventHistory, - sharedCode: SharedCode, - }; - protected enemies!: Enemies; - protected eventHistory!: EventHistory; - protected sharedCode!: SharedCode; - brainFreezeRefreshes = 0; flurry: { timestamp: number; overlapped: boolean }[] = []; brainFreeze: { apply: ApplyBuffEvent; remove: RemoveBuffEvent | undefined; expired: boolean }[] = From 465fde81fe77640a82041e9880d928a12fad4b1c Mon Sep 17 00:00:00 2001 From: Sharrq Date: Thu, 4 Jan 2024 21:57:27 -0500 Subject: [PATCH 17/46] Winter's Chill --- .../retail/mage/frost/core/WintersChill.tsx | 216 +++++++----------- .../frost/normalizers/CastLinkNormalizer.ts | 36 ++- 2 files changed, 115 insertions(+), 137 deletions(-) diff --git a/src/analysis/retail/mage/frost/core/WintersChill.tsx b/src/analysis/retail/mage/frost/core/WintersChill.tsx index 6cfcc1df547..13a0609ce6b 100644 --- a/src/analysis/retail/mage/frost/core/WintersChill.tsx +++ b/src/analysis/retail/mage/frost/core/WintersChill.tsx @@ -1,11 +1,16 @@ -import { Trans } from '@lingui/macro'; -import { formatPercentage, formatDuration } from 'common/format'; +import { formatPercentage } from 'common/format'; import SPELLS from 'common/SPELLS'; import TALENTS from 'common/TALENTS/mage'; import { SpellIcon } from 'interface'; import { SpellLink } from 'interface'; -import Analyzer from 'parser/core/Analyzer'; -import { EventType } from 'parser/core/Events'; +import Analyzer, { Options, SELECTED_PLAYER } from 'parser/core/Analyzer'; +import Events, { + ApplyDebuffEvent, + RemoveDebuffEvent, + CastEvent, + DamageEvent, + GetRelatedEvent, +} from 'parser/core/Events'; import { When, ThresholdStyle } from 'parser/core/ParseResults'; import Enemies from 'parser/shared/modules/Enemies'; import EventHistory from 'parser/shared/modules/EventHistory'; @@ -13,18 +18,7 @@ import BoringSpellValueText from 'parser/ui/BoringSpellValueText'; import Statistic from 'parser/ui/Statistic'; import STATISTIC_ORDER from 'parser/ui/STATISTIC_ORDER'; -const WINTERS_CHILL_SPENDERS = [ - SPELLS.ICE_LANCE_DAMAGE, - SPELLS.GLACIAL_SPIKE_DAMAGE, - TALENTS.ICE_NOVA_TALENT, - TALENTS.RAY_OF_FROST_TALENT, -]; - -const WINTERS_CHILL_PRECAST_CASTS = [SPELLS.FROSTBOLT, TALENTS.GLACIAL_SPIKE_TALENT]; - -const WINTERS_CHILL_PRECAST_DAMAGE = [SPELLS.FROSTBOLT_DAMAGE, SPELLS.GLACIAL_SPIKE_DAMAGE]; - -const debug = false; +const WINTERS_CHILL_SPENDERS = [SPELLS.ICE_LANCE_DAMAGE.id, SPELLS.GLACIAL_SPIKE_DAMAGE.id]; class WintersChill extends Analyzer { static dependencies = { @@ -35,124 +29,83 @@ class WintersChill extends Analyzer { protected eventHistory!: EventHistory; hasGlacialSpike: boolean = this.selectedCombatant.hasTalent(TALENTS.GLACIAL_SPIKE_TALENT); + wintersChill: { + apply: ApplyDebuffEvent; + remove: RemoveDebuffEvent | undefined; + precast: CastEvent | undefined; + precastIcicles: number; + damageEvents: DamageEvent[]; + }[] = []; + + constructor(options: Options) { + super(options); + this.addEventListener( + Events.applydebuff.by(SELECTED_PLAYER).spell(SPELLS.WINTERS_CHILL), + this.onWintersChill, + ); + this.addEventListener( + Events.damage + .by(SELECTED_PLAYER) + .spell([SPELLS.FROSTBOLT_DAMAGE, SPELLS.GLACIAL_SPIKE_DAMAGE, SPELLS.ICE_LANCE_DAMAGE]), + this.onDamage, + ); + } - wintersChillHardCasts = () => { - let debuffApplies = this.eventHistory.getEvents(EventType.ApplyDebuff, { - spell: SPELLS.WINTERS_CHILL, + onWintersChill(event: ApplyDebuffEvent) { + const remove: RemoveDebuffEvent | undefined = GetRelatedEvent(event, 'DebuffRemove'); + const flurry: CastEvent | undefined = GetRelatedEvent(event, 'SpellCast'); + const precast: CastEvent | undefined = GetRelatedEvent(event, 'PreCast'); + this.wintersChill.push({ + apply: event, + remove: remove, + precast: precast, + precastIcicles: + (flurry && + this.selectedCombatant.getBuff(SPELLS.ICICLES_BUFF.id, flurry.timestamp)?.stacks) || + 0, + damageEvents: [], }); + } - //Filter out buffs where there was not a valid precast before Winter's Chill was applied or the precast didnt land in Winter's Chill - debuffApplies = debuffApplies.filter((e) => { - const debuffRemoved = this.eventHistory.getEvents(EventType.RemoveDebuff, { - searchBackwards: false, - spell: SPELLS.WINTERS_CHILL, - count: 1, - startTimestamp: e.timestamp, - })[0]; - const preCast = this.eventHistory.getEvents(EventType.Cast, { - spell: WINTERS_CHILL_PRECAST_CASTS, - count: 1, - startTimestamp: e.timestamp, - duration: 1000, - })[0]; - if (!preCast) { - debug && - this.log( - 'PRECAST NOT FOUND @' + formatDuration(e.timestamp - this.owner.fight.start_time), - ); - return false; - } + onDamage(event: DamageEvent) { + const enemy = this.enemies.getEntity(event); + if (!enemy || !enemy.hasBuff(SPELLS.WINTERS_CHILL.id)) { + return; + } + const wintersChillDebuff: number | undefined = this.wintersChill.findIndex( + (d) => + d.apply.timestamp <= event.timestamp && d.remove && d.remove.timestamp >= event.timestamp, + ); + this.wintersChill[wintersChillDebuff].damageEvents?.push(event); + } - //Check to see if the precast landed in Winter's Chill - const duration = debuffRemoved - ? debuffRemoved.timestamp - e.timestamp - : this.owner.fight.end_time - e.timestamp; - const damageEvents = this.eventHistory.getEvents(EventType.Damage, { - searchBackwards: false, - spell: WINTERS_CHILL_PRECAST_DAMAGE, - startTimestamp: preCast.timestamp, - duration: duration, - }); - if (!damageEvents || damageEvents.length === 0) { - debug && - this.log( - 'PRECAST DAMAGE NOT FOUND @' + - formatDuration(e.timestamp - this.owner.fight.start_time), - ); - return false; - } + missedPreCasts = () => { + //If there is no Pre Cast, or if there is a Precast but it didnt land in Winter's Chlll + let missingPreCast = this.wintersChill.filter( + (w) => + !w.precast || + w.damageEvents.filter((d) => w.precast?.ability.guid === d.ability.guid).length > 0, + ); - //Check if the target had Winter's Chill - let preCastHits = 0; - damageEvents.forEach((d) => { - const enemy = this.enemies.getEntity(d); - if (enemy && enemy.hasBuff(SPELLS.WINTERS_CHILL.id, d.timestamp)) { - preCastHits += 1; - } - }); - if (preCastHits < 1) { - debug && - this.log( - 'PRECAST DAMAGE NOT SHATTERED @ ' + - formatDuration(e.timestamp - this.owner.fight.start_time), - ); - return false; - } - return true; - }); - return debuffApplies.length; + //If the player had exactly 4 Icicles, disregard it + missingPreCast = missingPreCast.filter((w) => w.precastIcicles !== 4); + + return missingPreCast.length; }; wintersChillShatters = () => { - let debuffApplies = this.eventHistory.getEvents(EventType.ApplyDebuff, { - spell: SPELLS.WINTERS_CHILL, - }); - - //Filter out buffs where both stacks of Winter's Chill were used before Winter's Chill expired - debuffApplies = debuffApplies.filter((e) => { - const debuffRemoved = this.eventHistory.getEvents(EventType.RemoveDebuff, { - searchBackwards: false, - spell: SPELLS.WINTERS_CHILL, - count: 1, - startTimestamp: e.timestamp, - })[0]; - const duration = debuffRemoved - ? debuffRemoved.timestamp - e.timestamp - : this.owner.fight.end_time - e.timestamp; - const damageEvents = this.eventHistory.getEvents(EventType.Damage, { - searchBackwards: false, - spell: WINTERS_CHILL_SPENDERS, - startTimestamp: e.timestamp, - duration: duration, - }); - if (!damageEvents) { - return false; - } + //Winter's Chill Debuffs where there are at least 2 damage hits of Glacial Spike and/or Ice Lance + const badDebuffs = this.wintersChill.filter( + (w) => + w.damageEvents.filter((d) => WINTERS_CHILL_SPENDERS.includes(d.ability.guid)).length >= 2, + ); - //Check if the target had Winter's Chill - let shatteredCasts = 0; - damageEvents.forEach((d) => { - const enemy = this.enemies.getEntity(d); - if (enemy && enemy.hasBuff(SPELLS.WINTERS_CHILL.id, d.timestamp)) { - shatteredCasts += 1; - } - }); - debug && - this.log( - 'Shattered Casts: ' + - shatteredCasts + - ' @ ' + - formatDuration(e.timestamp - this.owner.fight.start_time), - ); - return shatteredCasts >= 2; - }); - return debuffApplies.length; + return badDebuffs.length; }; get totalProcs() { - return this.eventHistory.getEvents(EventType.ApplyDebuff, { - spell: SPELLS.WINTERS_CHILL, - }).length; + this.log(this.wintersChill); + return this.wintersChill.length; } get missedShatters() { @@ -163,12 +116,8 @@ class WintersChill extends Analyzer { return this.wintersChillShatters() / this.totalProcs || 0; } - get missedPreCasts() { - return this.totalProcs - this.wintersChillHardCasts(); - } - get preCastPercent() { - return this.wintersChillHardCasts() / this.totalProcs || 0; + return 1 - this.missedPreCasts() / this.totalProcs; } // less strict than the ice lance suggestion both because it's less important, @@ -220,11 +169,7 @@ class WintersChill extends Analyzer { , ) .icon(TALENTS.ICE_LANCE_TALENT.icon) - .actual( - - {formatPercentage(1 - actual)}% Winter's Chill not shattered with Ice Lance - , - ) + .actual(`${formatPercentage(1 - actual)}% Winter's Chill not shattered with Ice Lance`) .recommended(`${formatPercentage(1 - recommended)}% is recommended`), ); when(this.wintersChillPreCastThresholds).addSuggestion((suggest, actual, recommended) => @@ -242,10 +187,9 @@ class WintersChill extends Analyzer { ) .icon(SPELLS.FROSTBOLT.icon) .actual( - - {formatPercentage(1 - actual)}% Winter's Chill not shattered with Frostbolt, Glacial - Spike, or Ebonbolt - , + `${formatPercentage( + 1 - actual, + )}% Winter's Chill not shattered with Frostbolt or Glacial Spike`, ) .recommended(`${formatPercentage(1 - recommended)}% is recommended`), ); diff --git a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts index 78f36063c48..b368b809a79 100644 --- a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts +++ b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts @@ -19,12 +19,13 @@ const CAST_BUFFER_MS = 75; export const BUFF_APPLY = 'BuffApply'; export const BUFF_REMOVE = 'BuffRemove'; export const BUFF_REFRESH = 'BuffRefresh'; +export const DEBUFF_APPLY = 'DebuffApply'; +export const DEBUFF_REMOVE = 'DebuffRemove'; export const CAST_BEGIN = 'CastBegin'; export const SPELL_CAST = 'SpellCast'; export const PRE_CAST = 'PreCast'; export const SPELL_DAMAGE = 'SpellDamage'; export const CLEAVE_DAMAGE = 'CleaveDamage'; -export const EXPLODE_DEBUFF = 'ExplosionDebuff'; const EVENT_LINKS: EventLink[] = [ { @@ -97,6 +98,39 @@ const EVENT_LINKS: EventLink[] = [ forwardBufferMs: CAST_BUFFER_MS, backwardBufferMs: CAST_BUFFER_MS, }, + { + reverseLinkRelation: DEBUFF_APPLY, + linkingEventId: SPELLS.WINTERS_CHILL.id, + linkingEventType: EventType.ApplyDebuff, + linkRelation: DEBUFF_REMOVE, + referencedEventId: SPELLS.WINTERS_CHILL.id, + referencedEventType: EventType.RemoveDebuff, + maximumLinks: 1, + forwardBufferMs: 7000, + backwardBufferMs: CAST_BUFFER_MS, + }, + { + reverseLinkRelation: DEBUFF_APPLY, + linkingEventId: SPELLS.WINTERS_CHILL.id, + linkingEventType: EventType.ApplyDebuff, + linkRelation: SPELL_CAST, + referencedEventId: TALENTS.FLURRY_TALENT.id, + referencedEventType: EventType.Cast, + maximumLinks: 1, + forwardBufferMs: CAST_BUFFER_MS, + backwardBufferMs: 1000, + }, + { + reverseLinkRelation: DEBUFF_APPLY, + linkingEventId: SPELLS.WINTERS_CHILL.id, + linkingEventType: EventType.ApplyDebuff, + linkRelation: PRE_CAST, + referencedEventId: [SPELLS.FROSTBOLT.id, TALENTS.GLACIAL_SPIKE_TALENT.id], + referencedEventType: EventType.Cast, + maximumLinks: 1, + forwardBufferMs: CAST_BUFFER_MS, + backwardBufferMs: 1000, + }, ]; /** From 9031282490b2fd7fa504f3b70da97b0f54e5d80f Mon Sep 17 00:00:00 2001 From: Sharrq Date: Thu, 4 Jan 2024 22:06:07 -0500 Subject: [PATCH 18/46] remove logging --- src/analysis/retail/mage/frost/core/WintersChill.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/analysis/retail/mage/frost/core/WintersChill.tsx b/src/analysis/retail/mage/frost/core/WintersChill.tsx index 13a0609ce6b..1626471bcbd 100644 --- a/src/analysis/retail/mage/frost/core/WintersChill.tsx +++ b/src/analysis/retail/mage/frost/core/WintersChill.tsx @@ -104,7 +104,6 @@ class WintersChill extends Analyzer { }; get totalProcs() { - this.log(this.wintersChill); return this.wintersChill.length; } From f56f8bf2ef6c90c1f94ef23c57d5164728a2d632 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Thu, 4 Jan 2024 22:06:54 -0500 Subject: [PATCH 19/46] fix flurry cast link --- src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts index b368b809a79..cff31ad282d 100644 --- a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts +++ b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts @@ -95,6 +95,7 @@ const EVENT_LINKS: EventLink[] = [ referencedEventId: TALENTS.FLURRY_TALENT.id, referencedEventType: EventType.Cast, maximumLinks: 1, + anyTarget: true, forwardBufferMs: CAST_BUFFER_MS, backwardBufferMs: CAST_BUFFER_MS, }, From 3edd5a4c9526edf30802a9b650ca348086224cce Mon Sep 17 00:00:00 2001 From: Sharrq Date: Thu, 4 Jan 2024 22:25:39 -0500 Subject: [PATCH 20/46] Fix Flurry Overlaps --- .../retail/mage/frost/core/BrainFreeze.tsx | 21 ++++++++++++++----- .../frost/normalizers/CastLinkNormalizer.ts | 11 ++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/analysis/retail/mage/frost/core/BrainFreeze.tsx b/src/analysis/retail/mage/frost/core/BrainFreeze.tsx index 35fd9c21690..1b96c31921a 100644 --- a/src/analysis/retail/mage/frost/core/BrainFreeze.tsx +++ b/src/analysis/retail/mage/frost/core/BrainFreeze.tsx @@ -6,19 +6,27 @@ import { SpellLink } from 'interface'; import Analyzer, { Options, SELECTED_PLAYER } from 'parser/core/Analyzer'; import Events, { CastEvent, + DamageEvent, ApplyBuffEvent, RemoveBuffEvent, RefreshBuffEvent, GetRelatedEvent, } from 'parser/core/Events'; import { ThresholdStyle, When } from 'parser/core/ParseResults'; +import Enemies from 'parser/shared/modules/Enemies'; import BoringSpellValueText from 'parser/ui/BoringSpellValueText'; import Statistic from 'parser/ui/Statistic'; import STATISTIC_ORDER from 'parser/ui/STATISTIC_ORDER'; class BrainFreeze extends Analyzer { + static dependencies = { + enemies: Enemies, + }; + + protected enemies!: Enemies; + brainFreezeRefreshes = 0; - flurry: { timestamp: number; overlapped: boolean }[] = []; + flurry: { timestamp: number; damage: DamageEvent | undefined; overlapped: boolean }[] = []; brainFreeze: { apply: ApplyBuffEvent; remove: RemoveBuffEvent | undefined; expired: boolean }[] = []; @@ -39,9 +47,12 @@ class BrainFreeze extends Analyzer { } onFlurryCast(event: CastEvent) { + const damage: DamageEvent | undefined = GetRelatedEvent(event, 'SpellDamage'); + const enemy = damage && this.enemies.getEntity(damage); this.flurry.push({ timestamp: event.timestamp, - overlapped: this.selectedCombatant.hasBuff(SPELLS.BRAIN_FREEZE_BUFF.id, event.timestamp - 10), + damage: damage, + overlapped: enemy?.hasBuff(SPELLS.WINTERS_CHILL.id, event.timestamp - 10) || false, }); } @@ -119,7 +130,7 @@ class BrainFreeze extends Analyzer { get overlappedFlurryThresholds() { return { - actual: this.brainFreezeRefreshes, + actual: this.overlappedFlurries, isGreaterThan: { average: 0, major: 3, @@ -169,8 +180,8 @@ class BrainFreeze extends Analyzer { <> You cast and applied{' '} while the target still had the{' '} - debuff on them {this.brainFreezeRefreshes}{' '} - times. Casting applies 2 stacks of{' '} + debuff on them {this.overlappedFlurries} times. + Casting applies 2 stacks of{' '} to the target so you should always ensure you are spending both stacks before you cast and apply again. diff --git a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts index cff31ad282d..cf5aa66a061 100644 --- a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts +++ b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts @@ -76,6 +76,17 @@ const EVENT_LINKS: EventLink[] = [ forwardBufferMs: 3000, backwardBufferMs: CAST_BUFFER_MS, }, + { + reverseLinkRelation: SPELL_CAST, + linkingEventId: TALENTS.FLURRY_TALENT.id, + linkingEventType: EventType.Cast, + linkRelation: SPELL_DAMAGE, + referencedEventId: SPELLS.FLURRY_DAMAGE.id, + referencedEventType: EventType.Damage, + anyTarget: true, + forwardBufferMs: 1500, + backwardBufferMs: CAST_BUFFER_MS, + }, { reverseLinkRelation: BUFF_APPLY, linkingEventId: SPELLS.BRAIN_FREEZE_BUFF.id, From 4f8d02e191056a83b377a6ac86841a522a2bbe01 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Thu, 4 Jan 2024 22:30:22 -0500 Subject: [PATCH 21/46] remove apl --- src/analysis/retail/mage/frost/core/apl.tsx | 79 --------------------- 1 file changed, 79 deletions(-) delete mode 100644 src/analysis/retail/mage/frost/core/apl.tsx diff --git a/src/analysis/retail/mage/frost/core/apl.tsx b/src/analysis/retail/mage/frost/core/apl.tsx deleted file mode 100644 index be6cfe84d88..00000000000 --- a/src/analysis/retail/mage/frost/core/apl.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import SPELLS from 'common/SPELLS'; -import TALENTS from 'common/TALENTS/mage'; -import { SpellLink } from 'interface'; -import { suggestion as buildSuggestion } from 'parser/core/Analyzer'; -import { EventType } from 'parser/core/Events'; -import aplCheck, { build, Condition } from 'parser/shared/metrics/apl'; -import annotateTimeline from 'parser/shared/metrics/apl/annotate'; -import * as cnd from 'parser/shared/metrics/apl/conditions'; - -const precastFrostbolt: Condition<{ brainFreeze?: number; frostbolt?: number }> = { - key: 'precast-frostbolt', - init: () => ({}), - update: (state, event) => { - if ( - event.type === EventType.ApplyBuff && - event.ability.guid === TALENTS.BRAIN_FREEZE_TALENT.id - ) { - state.brainFreeze = event.timestamp; - } - - if ( - event.type === EventType.RemoveBuff && - event.ability.guid === TALENTS.BRAIN_FREEZE_TALENT.id - ) { - state.brainFreeze = undefined; - } - - if (event.type === EventType.Cast && event.ability.guid === SPELLS.FROSTBOLT.id) { - state.frostbolt = event.timestamp; - } - - return state; - }, - validate: (state, _event) => { - if (!state.brainFreeze) { - return false; - } - - // if brain freeze is up, did the previous cast overlap sufficiently? - if ((state.frostbolt || 0) > state.brainFreeze + 500) { - return false; - } - - // otherwise, brain freeze is up and the previous frostbolt doesn't count - return true; - }, - describe: () => ( - <> - was just applied - - ), -}; - -export const apl = build([ - { - spell: TALENTS.ICE_LANCE_TALENT, - condition: cnd.debuffPresent(SPELLS.WINTERS_CHILL), - }, - { - spell: SPELLS.FROSTBOLT, - condition: cnd.and(precastFrostbolt), - }, - { - spell: TALENTS.FLURRY_TALENT, - condition: cnd.buffPresent(TALENTS.BRAIN_FREEZE_TALENT), - }, - { spell: TALENTS.ICE_LANCE_TALENT, condition: cnd.buffPresent(TALENTS.FINGERS_OF_FROST_TALENT) }, - TALENTS.FROZEN_ORB_TALENT, - SPELLS.FROSTBOLT, -]); - -export const check = aplCheck(apl); - -export default buildSuggestion((events, info) => { - const { violations } = check(events, info); - annotateTimeline(violations); - - return undefined; -}); From 33ba8905b37bfe7311a434429c80576cbc1dea44 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Thu, 4 Jan 2024 22:36:00 -0500 Subject: [PATCH 22/46] fix Ray of Frost cooldown --- src/analysis/retail/mage/frost/core/Abilities.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analysis/retail/mage/frost/core/Abilities.tsx b/src/analysis/retail/mage/frost/core/Abilities.tsx index 27a61c9822d..4be7037c8e9 100644 --- a/src/analysis/retail/mage/frost/core/Abilities.tsx +++ b/src/analysis/retail/mage/frost/core/Abilities.tsx @@ -56,7 +56,7 @@ class Abilities extends CoreAbilities { gcd: { base: 1500, }, - cooldown: 80, + cooldown: 60, enabled: combatant.hasTalent(TALENTS.RAY_OF_FROST_TALENT), castEfficiency: { suggestion: true, From 9f04a5475d2948144e3accb37b1d94cf42b6c349 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Thu, 4 Jan 2024 22:37:03 -0500 Subject: [PATCH 23/46] fix icy veins cooldown --- src/analysis/retail/mage/frost/core/Abilities.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analysis/retail/mage/frost/core/Abilities.tsx b/src/analysis/retail/mage/frost/core/Abilities.tsx index 4be7037c8e9..d89f55f3130 100644 --- a/src/analysis/retail/mage/frost/core/Abilities.tsx +++ b/src/analysis/retail/mage/frost/core/Abilities.tsx @@ -112,7 +112,7 @@ class Abilities extends CoreAbilities { category: SPELL_CATEGORY.COOLDOWNS, enabled: combatant.hasTalent(TALENTS.ICY_VEINS_TALENT), gcd: null, - cooldown: 180, + cooldown: 120, castEfficiency: { suggestion: true, recommendedEfficiency: 0.9, From d356825bb97680b0272eda77649bd23440fa5ac0 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Thu, 4 Jan 2024 22:52:06 -0500 Subject: [PATCH 24/46] add function for cleave damage --- .../frost/normalizers/CastLinkNormalizer.ts | 61 +++++++++++++------ 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts index cf5aa66a061..440572bdcbb 100644 --- a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts +++ b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts @@ -6,6 +6,7 @@ import { BeginCastEvent, RemoveBuffEvent, CastEvent, + DamageEvent, EventType, GetRelatedEvents, HasRelatedEvent, @@ -37,16 +38,7 @@ const EVENT_LINKS: EventLink[] = [ referencedEventType: EventType.Damage, anyTarget: true, additionalCondition(linkingEvent, referencedEvent): boolean { - if (!linkingEvent || !referencedEvent) { - return false; - } - const castTarget = - HasTarget(linkingEvent) && - encodeTargetString(linkingEvent.targetID, linkingEvent.targetInstance); - const damageTarget = - HasTarget(referencedEvent) && - encodeTargetString(referencedEvent.targetID, referencedEvent.targetInstance); - return castTarget === damageTarget; + return isCleaveDamage(linkingEvent as CastEvent, referencedEvent as DamageEvent) === false; }, maximumLinks: 1, forwardBufferMs: 3000, @@ -61,16 +53,7 @@ const EVENT_LINKS: EventLink[] = [ referencedEventType: EventType.Damage, anyTarget: true, additionalCondition(linkingEvent, referencedEvent): boolean { - if (!linkingEvent || !referencedEvent) { - return false; - } - const castTarget = - HasTarget(linkingEvent) && - encodeTargetString(linkingEvent.targetID, linkingEvent.targetInstance); - const damageTarget = - HasTarget(referencedEvent) && - encodeTargetString(referencedEvent.targetID, referencedEvent.targetInstance); - return castTarget !== damageTarget; + return isCleaveDamage(linkingEvent as CastEvent, referencedEvent as DamageEvent) === true; }, maximumLinks: 1, forwardBufferMs: 3000, @@ -87,6 +70,36 @@ const EVENT_LINKS: EventLink[] = [ forwardBufferMs: 1500, backwardBufferMs: CAST_BUFFER_MS, }, + { + reverseLinkRelation: SPELL_CAST, + linkingEventId: TALENTS.ICE_LANCE_TALENT.id, + linkingEventType: EventType.Cast, + linkRelation: SPELL_DAMAGE, + referencedEventId: SPELLS.ICE_LANCE_DAMAGE.id, + referencedEventType: EventType.Cast, + anyTarget: true, + additionalCondition(linkingEvent, referencedEvent): boolean { + return isCleaveDamage(linkingEvent as CastEvent, referencedEvent as DamageEvent) === false; + }, + maximumLinks: 1, + forwardBufferMs: CAST_BUFFER_MS, + backwardBufferMs: 1000, + }, + { + reverseLinkRelation: SPELL_CAST, + linkingEventId: TALENTS.ICE_LANCE_TALENT.id, + linkingEventType: EventType.Cast, + linkRelation: CLEAVE_DAMAGE, + referencedEventId: SPELLS.ICE_LANCE_DAMAGE.id, + referencedEventType: EventType.Cast, + anyTarget: true, + additionalCondition(linkingEvent, referencedEvent): boolean { + return isCleaveDamage(linkingEvent as CastEvent, referencedEvent as DamageEvent) === true; + }, + maximumLinks: 1, + forwardBufferMs: CAST_BUFFER_MS, + backwardBufferMs: 1000, + }, { reverseLinkRelation: BUFF_APPLY, linkingEventId: SPELLS.BRAIN_FREEZE_BUFF.id, @@ -190,4 +203,12 @@ export function isProcExpired(event: RemoveBuffEvent, spenderId: number): boolea return !cast || cast.ability.guid !== spenderId; } +export function isCleaveDamage(castEvent: CastEvent, damageEvent: DamageEvent): boolean { + const castTarget = + HasTarget(castEvent) && encodeTargetString(castEvent.targetID, castEvent.targetInstance); + const damageTarget = + HasTarget(damageEvent) && encodeTargetString(damageEvent.targetID, damageEvent.targetInstance); + return castTarget !== damageTarget; +} + export default CastLinkNormalizer; From 9505b6b2f455e6f46b3855b78626c49914223b02 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 20:29:08 -0500 Subject: [PATCH 25/46] Fingers of Frost --- .../retail/mage/frost/CombatLogParser.ts | 4 +- .../retail/mage/frost/checklist/Module.tsx | 10 +- .../{MunchedProcs.tsx => FingersOfFrost.tsx} | 112 +++++++++++------ .../retail/mage/frost/core/IceLance.tsx | 119 ++++-------------- .../frost/normalizers/CastLinkNormalizer.ts | 55 +++++++- 5 files changed, 150 insertions(+), 150 deletions(-) rename src/analysis/retail/mage/frost/core/{MunchedProcs.tsx => FingersOfFrost.tsx} (58%) diff --git a/src/analysis/retail/mage/frost/CombatLogParser.ts b/src/analysis/retail/mage/frost/CombatLogParser.ts index c5df5470261..988e5836d54 100644 --- a/src/analysis/retail/mage/frost/CombatLogParser.ts +++ b/src/analysis/retail/mage/frost/CombatLogParser.ts @@ -23,7 +23,7 @@ import Buffs from './core/Buffs'; import CooldownThroughputTracker from './core/CooldownThroughputTracker'; import IceLance from './core/IceLance'; import IcyVeins from './core/IcyVeins'; -import MunchedProcs from './core/MunchedProcs'; +import FingersOfFrost from './core/FingersOfFrost'; import WintersChill from './core/WintersChill'; //Talents @@ -64,7 +64,7 @@ class CombatLogParser extends CoreCombatLogParser { iceLance: IceLance, icyVeins: IcyVeins, arcaneIntellect: ArcaneIntellect, - munchedProcs: MunchedProcs, + fingersOfFrost: FingersOfFrost, // Talents - Frost boneChilling: BoneChilling, diff --git a/src/analysis/retail/mage/frost/checklist/Module.tsx b/src/analysis/retail/mage/frost/checklist/Module.tsx index 56e132f0d7f..8ed13f7da7a 100644 --- a/src/analysis/retail/mage/frost/checklist/Module.tsx +++ b/src/analysis/retail/mage/frost/checklist/Module.tsx @@ -8,7 +8,7 @@ import AlwaysBeCasting from '../core/AlwaysBeCasting'; import BrainFreeze from '../core/BrainFreeze'; import IceLance from '../core/IceLance'; import IcyVeins from '../core/IcyVeins'; -import MunchedProcs from '../core/MunchedProcs'; +import FingersOfFrost from '../core/FingersOfFrost'; import WaterElemental from '../talents/WaterElemental'; import WintersChill from '../core/WintersChill'; import ThermalVoid from '../talents/ThermalVoid'; @@ -20,7 +20,7 @@ class Checklist extends BaseChecklist { combatants: Combatants, castEfficiency: CastEfficiency, icyVeins: IcyVeins, - munchedProcs: MunchedProcs, + fingersOfFrost: FingersOfFrost, brainFreeze: BrainFreeze, iceLance: IceLance, thermalVoid: ThermalVoid, @@ -34,7 +34,7 @@ class Checklist extends BaseChecklist { protected combatants!: Combatants; protected castEfficiency!: CastEfficiency; protected icyVeins!: IcyVeins; - protected munchedProcs!: MunchedProcs; + protected fingersOfFrost!: FingersOfFrost; protected brainFreeze!: BrainFreeze; protected iceLance!: IceLance; protected thermalVoid!: ThermalVoid; @@ -55,11 +55,11 @@ class Checklist extends BaseChecklist { downtimeSuggestionThresholds: this.alwaysBeCasting.overrideDowntimeSuggestionThresholds, icyVeinsActiveTime: this.icyVeins.icyVeinsActiveTimeThresholds, - munchedProcs: this.munchedProcs.munchedProcsThresholds, + munchedProcs: this.fingersOfFrost.munchedProcsThresholds, brainFreezeUtilization: this.brainFreeze.brainFreezeUtilizationThresholds, brainFreezeOverwrites: this.brainFreeze.brainFreezeOverwritenThresholds, brainFreezeExpired: this.brainFreeze.brainFreezeExpiredThresholds, - fingersOfFrostUtilization: this.iceLance.fingersProcUtilizationThresholds, + fingersOfFrostUtilization: this.fingersOfFrost.fingersProcUtilizationThresholds, iceLanceNotShattered: this.iceLance.nonShatteredIceLanceThresholds, wintersChillShatter: this.wintersChill.wintersChillShatterThresholds, wintersChillHardCasts: this.wintersChill.wintersChillPreCastThresholds, diff --git a/src/analysis/retail/mage/frost/core/MunchedProcs.tsx b/src/analysis/retail/mage/frost/core/FingersOfFrost.tsx similarity index 58% rename from src/analysis/retail/mage/frost/core/MunchedProcs.tsx rename to src/analysis/retail/mage/frost/core/FingersOfFrost.tsx index 0dc3e0c6650..1b3ad8e6dc2 100644 --- a/src/analysis/retail/mage/frost/core/MunchedProcs.tsx +++ b/src/analysis/retail/mage/frost/core/FingersOfFrost.tsx @@ -1,37 +1,40 @@ -import { Trans } from '@lingui/macro'; -import { formatPercentage } from 'common/format'; +import { formatPercentage, formatNumber } from 'common/format'; import SPELLS from 'common/SPELLS'; import TALENTS from 'common/TALENTS/mage'; import { SpellLink } from 'interface'; import Analyzer, { SELECTED_PLAYER, Options } from 'parser/core/Analyzer'; -import Events, { DamageEvent, ApplyBuffEvent, ApplyBuffStackEvent } from 'parser/core/Events'; +import Events, { + DamageEvent, + CastEvent, + ApplyBuffEvent, + RemoveBuffEvent, + ApplyBuffStackEvent, + GetRelatedEvent, +} from 'parser/core/Events'; import { ThresholdStyle, When } from 'parser/core/ParseResults'; -import AbilityTracker from 'parser/shared/modules/AbilityTracker'; import Enemies from 'parser/shared/modules/Enemies'; -import EventHistory from 'parser/shared/modules/EventHistory'; import BoringSpellValueText from 'parser/ui/BoringSpellValueText'; import Statistic from 'parser/ui/Statistic'; import STATISTIC_ORDER from 'parser/ui/STATISTIC_ORDER'; +import { SHATTER_DEBUFFS } from '../../shared'; -class MunchedProcs extends Analyzer { +class FingersOfFrost extends Analyzer { static dependencies = { - abilityTracker: AbilityTracker, enemies: Enemies, - eventHistory: EventHistory, }; - protected abilityTracker!: AbilityTracker; - protected eventHistory!: EventHistory; protected enemies!: Enemies; - munchedProcs = 0; - totalFingersProcs = 0; + fingers: { + apply: ApplyBuffEvent | ApplyBuffStackEvent; + remove: RemoveBuffEvent | undefined; + spender: CastEvent | undefined; + expired: boolean; + munched: boolean; + spendDelay: number | undefined; + }[] = []; constructor(options: Options) { super(options); - this.addEventListener( - Events.damage.by(SELECTED_PLAYER).spell(SPELLS.ICE_LANCE_DAMAGE), - this.onIceLanceDamage, - ); this.addEventListener( Events.applybuff.by(SELECTED_PLAYER).spell(SPELLS.FINGERS_OF_FROST_BUFF), this.onFingersProc, @@ -42,28 +45,49 @@ class MunchedProcs extends Analyzer { ); } - onIceLanceDamage(event: DamageEvent) { - const enemy = this.enemies.getEntity(event); - if (!enemy || !enemy.hasBuff(SPELLS.WINTERS_CHILL.id)) { - return; - } + onFingersProc(event: ApplyBuffEvent | ApplyBuffStackEvent) { + const remove: RemoveBuffEvent | undefined = GetRelatedEvent(event, 'BuffRemove'); + const spender: CastEvent | undefined = remove && GetRelatedEvent(remove, 'SpellCast'); + const damage: DamageEvent | undefined = spender && GetRelatedEvent(spender, 'SpellDamage'); + const enemy = damage && this.enemies.getEntity(damage); + this.log(spender?.timestamp); + this.log(damage?.timestamp); + this.fingers.push({ + apply: event, + remove: remove, + spender: spender, + expired: !spender, + munched: + SHATTER_DEBUFFS.some((effect) => enemy?.hasBuff(effect.id, damage?.timestamp)) || false, + spendDelay: spender && spender.timestamp - event.timestamp, + }); + } - const iceLanceCast = this.eventHistory.last( - 1, - undefined, - Events.cast.by(SELECTED_PLAYER).spell(TALENTS.ICE_LANCE_TALENT), - )[0]; - if (this.selectedCombatant.hasBuff(SPELLS.FINGERS_OF_FROST_BUFF.id, iceLanceCast.timestamp)) { - this.munchedProcs += 1; - } + get expiredProcs() { + return this.fingers.filter((f) => f.expired).length; } - onFingersProc(event: ApplyBuffEvent | ApplyBuffStackEvent) { - this.totalFingersProcs += 1; + get averageSpendDelaySeconds() { + let spendDelay = 0; + this.fingers.forEach((f) => f.spendDelay && (spendDelay += f.spendDelay)); + this.log(spendDelay / this.fingers.filter((f) => f.spendDelay).length / 1000); + return spendDelay / this.fingers.filter((f) => f.spendDelay).length / 1000; + } + + get usedFingersProcs() { + return this.totalProcs - this.expiredProcs; + } + + get munchedProcs() { + return this.fingers.filter((f) => f.munched).length; + } + + get totalProcs() { + return this.fingers.length; } get munchedPercent() { - return this.munchedProcs / this.totalFingersProcs; + return this.munchedProcs / this.totalProcs; } get munchedProcsThresholds() { @@ -78,13 +102,25 @@ class MunchedProcs extends Analyzer { }; } + get fingersProcUtilizationThresholds() { + return { + actual: 1 - this.expiredProcs / this.totalProcs || 0, + isLessThan: { + minor: 0.95, + average: 0.85, + major: 0.7, + }, + style: ThresholdStyle.PERCENTAGE, + }; + } + suggestions(when: When) { when(this.munchedProcsThresholds).addSuggestion((suggest, actual, recommended) => suggest( <> You wasted (munched) {this.munchedProcs}{' '} procs ( - {formatPercentage(this.munchedPercent)} of total procs). Because of the way{' '} + {formatPercentage(this.munchedPercent)}% of total procs). Because of the way{' '} works, this is sometimes unavoidable (i.e. you get a proc while you are using a{' '} proc), but if you have both a{' '} @@ -97,11 +133,7 @@ class MunchedProcs extends Analyzer { , ) .icon(TALENTS.FINGERS_OF_FROST_TALENT.icon) - .actual( - - {formatPercentage(actual)}% procs wasted - , - ) + .actual(`${formatPercentage(actual)}% procs wasted`) .recommended(formatPercentage(recommended)), ); } @@ -125,10 +157,12 @@ class MunchedProcs extends Analyzer { > {formatPercentage(this.munchedPercent, 0)}% Munched Fingers of Frost procs +
    + {formatNumber(this.averageSpendDelaySeconds)}s Avg. delay to spend procs
    ); } } -export default MunchedProcs; +export default FingersOfFrost; diff --git a/src/analysis/retail/mage/frost/core/IceLance.tsx b/src/analysis/retail/mage/frost/core/IceLance.tsx index e79807220a0..9b96f65b67e 100644 --- a/src/analysis/retail/mage/frost/core/IceLance.tsx +++ b/src/analysis/retail/mage/frost/core/IceLance.tsx @@ -1,16 +1,12 @@ -import { Trans } from '@lingui/macro'; -import { MS_BUFFER_100, SHATTER_DEBUFFS } from 'analysis/retail/mage/shared'; +import { SHATTER_DEBUFFS } from 'analysis/retail/mage/shared'; import { formatPercentage } from 'common/format'; import SPELLS from 'common/SPELLS'; import TALENTS from 'common/TALENTS/mage'; import { SpellLink } from 'interface'; import Analyzer, { SELECTED_PLAYER, Options } from 'parser/core/Analyzer'; -import Events, { CastEvent, DamageEvent, ChangeBuffStackEvent } from 'parser/core/Events'; +import Events, { CastEvent, DamageEvent, GetRelatedEvent } from 'parser/core/Events'; import { When, ThresholdStyle } from 'parser/core/ParseResults'; -import AbilityTracker from 'parser/shared/modules/AbilityTracker'; -import Enemies, { encodeTargetString } from 'parser/shared/modules/Enemies'; -import EventHistory from 'parser/shared/modules/EventHistory'; -import SpellUsable from 'parser/shared/modules/SpellUsable'; +import Enemies from 'parser/shared/modules/Enemies'; import BoringSpellValueText from 'parser/ui/BoringSpellValueText'; import Statistic from 'parser/ui/Statistic'; import STATISTIC_ORDER from 'parser/ui/STATISTIC_ORDER'; @@ -18,23 +14,10 @@ import STATISTIC_ORDER from 'parser/ui/STATISTIC_ORDER'; class IceLance extends Analyzer { static dependencies = { enemies: Enemies, - abilityTracker: AbilityTracker, - eventHistory: EventHistory, - spellUsable: SpellUsable, }; protected enemies!: Enemies; - protected abilityTracker!: AbilityTracker; - protected eventHistory!: EventHistory; - protected spellUsable!: SpellUsable; - hadFingersProc = false; - iceLanceTargetId = ''; - nonShatteredCasts = 0; - - iceLanceCastTimestamp = 0; - totalFingersProcs = 0; - overwrittenFingersProcs = 0; - expiredFingersProcs = 0; + icelance: { shattered: boolean; hadFingers: boolean; cleaved: boolean }[] = []; constructor(options: Options) { super(options); @@ -42,90 +25,34 @@ class IceLance extends Analyzer { Events.cast.by(SELECTED_PLAYER).spell(TALENTS.ICE_LANCE_TALENT), this.onCast, ); - this.addEventListener( - Events.damage.by(SELECTED_PLAYER).spell(SPELLS.ICE_LANCE_DAMAGE), - this.onDamage, - ); - this.addEventListener( - Events.changebuffstack.by(SELECTED_PLAYER).spell(SPELLS.FINGERS_OF_FROST_BUFF), - this.onFingersStackChange, - ); } onCast(event: CastEvent) { - this.iceLanceCastTimestamp = event.timestamp; - if (event.targetID) { - this.iceLanceTargetId = encodeTargetString(event.targetID, event.targetInstance); - } - this.hadFingersProc = false; - if (this.selectedCombatant.hasBuff(SPELLS.FINGERS_OF_FROST_BUFF.id)) { - this.hadFingersProc = true; - } - } - - onDamage(event: DamageEvent) { - const damageTarget = encodeTargetString(event.targetID, event.targetInstance); - if (this.iceLanceTargetId !== damageTarget) { - return; - } - - const enemy = this.enemies.getEntity(event); - if ( - enemy && - !SHATTER_DEBUFFS.some((effect) => enemy.hasBuff(effect.id, event.timestamp)) && - !this.hadFingersProc - ) { - this.nonShatteredCasts += 1; - } - } - - onFingersStackChange(event: ChangeBuffStackEvent) { - // FoF overcaps don't show as a refreshbuff, instead they are a stack lost followed immediately by a gain - const stackChange = event.stacksGained; - if (stackChange > 0) { - this.totalFingersProcs += stackChange; - } else if ( - this.iceLanceCastTimestamp && - this.iceLanceCastTimestamp + MS_BUFFER_100 > event.timestamp - ) { - // just cast ice lance, so this stack removal probably a proc used - } else if (event.newStacks === 0) { - this.expiredFingersProcs += -stackChange; // stacks zero out, must be expiration - } else { - this.overwrittenFingersProcs += -stackChange; // stacks don't zero, this is an overwrite - } + const damage: DamageEvent | undefined = GetRelatedEvent(event, 'SpellDamage'); + const enemy = damage && this.enemies.getEntity(damage); + const cleave: DamageEvent | undefined = GetRelatedEvent(event, 'CleaveDamage'); + this.icelance.push({ + shattered: + SHATTER_DEBUFFS.some((effect) => enemy?.hasBuff(effect.id, damage?.timestamp)) || false, + hadFingers: this.selectedCombatant.hasBuff( + SPELLS.FINGERS_OF_FROST_BUFF.id, + event.timestamp - 10, + ), + cleaved: cleave ? true : false, + }); } - get wastedFingersProcs() { - return this.expiredFingersProcs + this.overwrittenFingersProcs; - } - - get usedFingersProcs() { - return this.totalFingersProcs - this.wastedFingersProcs; + get nonShatteredCasts() { + return this.icelance.filter((il) => !il.shattered).length; } get shatteredPercent() { - return ( - 1 - this.nonShatteredCasts / this.abilityTracker.getAbility(TALENTS.ICE_LANCE_TALENT.id).casts - ); - } - - get fingersProcUtilizationThresholds() { - return { - actual: 1 - this.wastedFingersProcs / this.totalFingersProcs || 0, - isLessThan: { - minor: 0.95, - average: 0.85, - major: 0.7, - }, - style: ThresholdStyle.PERCENTAGE, - }; + return 1 - this.nonShatteredCasts / this.icelance.length; } get nonShatteredIceLanceThresholds() { return { - actual: - this.nonShatteredCasts / this.abilityTracker.getAbility(TALENTS.ICE_LANCE_TALENT.id).casts, + actual: this.nonShatteredCasts / this.icelance.length, isGreaterThan: { minor: 0.05, average: 0.15, @@ -148,11 +75,7 @@ class IceLance extends Analyzer { , ) .icon(TALENTS.ICE_LANCE_TALENT.icon) - .actual( - - {formatPercentage(actual)}% missed - , - ) + .actual(`${formatPercentage(actual)}% missed`) .recommended(`<${formatPercentage(recommended)}% is recommended`), ); } diff --git a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts index 440572bdcbb..cb8b42c1b37 100644 --- a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts +++ b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts @@ -76,14 +76,14 @@ const EVENT_LINKS: EventLink[] = [ linkingEventType: EventType.Cast, linkRelation: SPELL_DAMAGE, referencedEventId: SPELLS.ICE_LANCE_DAMAGE.id, - referencedEventType: EventType.Cast, + referencedEventType: EventType.Damage, anyTarget: true, additionalCondition(linkingEvent, referencedEvent): boolean { return isCleaveDamage(linkingEvent as CastEvent, referencedEvent as DamageEvent) === false; }, maximumLinks: 1, - forwardBufferMs: CAST_BUFFER_MS, - backwardBufferMs: 1000, + forwardBufferMs: 1000, + backwardBufferMs: CAST_BUFFER_MS, }, { reverseLinkRelation: SPELL_CAST, @@ -91,14 +91,14 @@ const EVENT_LINKS: EventLink[] = [ linkingEventType: EventType.Cast, linkRelation: CLEAVE_DAMAGE, referencedEventId: SPELLS.ICE_LANCE_DAMAGE.id, - referencedEventType: EventType.Cast, + referencedEventType: EventType.Damage, anyTarget: true, additionalCondition(linkingEvent, referencedEvent): boolean { return isCleaveDamage(linkingEvent as CastEvent, referencedEvent as DamageEvent) === true; }, maximumLinks: 1, - forwardBufferMs: CAST_BUFFER_MS, - backwardBufferMs: 1000, + forwardBufferMs: 1000, + backwardBufferMs: CAST_BUFFER_MS, }, { reverseLinkRelation: BUFF_APPLY, @@ -152,10 +152,53 @@ const EVENT_LINKS: EventLink[] = [ linkRelation: PRE_CAST, referencedEventId: [SPELLS.FROSTBOLT.id, TALENTS.GLACIAL_SPIKE_TALENT.id], referencedEventType: EventType.Cast, + anyTarget: true, maximumLinks: 1, forwardBufferMs: CAST_BUFFER_MS, backwardBufferMs: 1000, }, + { + reverseLinkRelation: BUFF_APPLY, + linkingEventId: SPELLS.FINGERS_OF_FROST_BUFF.id, + linkingEventType: [EventType.ApplyBuff, EventType.ApplyBuffStack], + linkRelation: BUFF_REMOVE, + referencedEventId: SPELLS.FINGERS_OF_FROST_BUFF.id, + referencedEventType: [EventType.RemoveBuff, EventType.RemoveBuffStack], + anyTarget: true, + additionalCondition(linkingEvent, referencedEvent): boolean { + return !HasRelatedEvent(referencedEvent, BUFF_APPLY); + }, + maximumLinks: 1, + forwardBufferMs: 18_000, + backwardBufferMs: CAST_BUFFER_MS, + }, + { + reverseLinkRelation: BUFF_REMOVE, + linkingEventId: SPELLS.FINGERS_OF_FROST_BUFF.id, + linkingEventType: [EventType.RemoveBuff, EventType.RemoveBuffStack], + linkRelation: SPELL_CAST, + referencedEventId: TALENTS.ICE_LANCE_TALENT.id, + referencedEventType: EventType.Cast, + anyTarget: true, + additionalCondition(linkingEvent, referencedEvent): boolean { + return !HasRelatedEvent(referencedEvent, BUFF_REMOVE); + }, + maximumLinks: 1, + forwardBufferMs: CAST_BUFFER_MS, + backwardBufferMs: CAST_BUFFER_MS, + }, + { + reverseLinkRelation: BUFF_REMOVE, + linkingEventId: SPELLS.FINGERS_OF_FROST_BUFF.id, + linkingEventType: EventType.RemoveBuff, + linkRelation: SPELL_CAST, + referencedEventId: TALENTS.ICE_LANCE_TALENT.id, + referencedEventType: EventType.Cast, + anyTarget: true, + maximumLinks: 1, + forwardBufferMs: CAST_BUFFER_MS, + backwardBufferMs: CAST_BUFFER_MS, + }, ]; /** From 2ebe9da57d0d5227975bb7902f9f8859d2944d30 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 20:44:32 -0500 Subject: [PATCH 26/46] Icy Veins --- .../retail/mage/frost/core/IcyVeins.tsx | 82 +++++++++++++------ .../frost/normalizers/CastLinkNormalizer.ts | 12 +++ 2 files changed, 69 insertions(+), 25 deletions(-) diff --git a/src/analysis/retail/mage/frost/core/IcyVeins.tsx b/src/analysis/retail/mage/frost/core/IcyVeins.tsx index 4161152b4b6..9207e3ddd24 100644 --- a/src/analysis/retail/mage/frost/core/IcyVeins.tsx +++ b/src/analysis/retail/mage/frost/core/IcyVeins.tsx @@ -4,7 +4,13 @@ import TALENTS from 'common/TALENTS/mage'; import { SpellLink } from 'interface'; import Analyzer, { Options } from 'parser/core/Analyzer'; import { SELECTED_PLAYER } from 'parser/core/EventFilter'; -import Events, { RemoveBuffEvent } from 'parser/core/Events'; +import Events, { + EventType, + GetRelatedEvent, + ApplyBuffEvent, + RemoveBuffEvent, + FightEndEvent, +} from 'parser/core/Events'; import { When, ThresholdStyle } from 'parser/core/ParseResults'; import AbilityTracker from 'parser/shared/modules/AbilityTracker'; import EventHistory from 'parser/shared/modules/EventHistory'; @@ -12,51 +18,77 @@ import FilteredActiveTime from 'parser/shared/modules/FilteredActiveTime'; import BoringSpellValueText from 'parser/ui/BoringSpellValueText'; import Statistic from 'parser/ui/Statistic'; import STATISTIC_ORDER from 'parser/ui/STATISTIC_ORDER'; +import AlwaysBeCasting from './AlwaysBeCasting'; class IcyVeins extends Analyzer { static dependencies = { eventHistory: EventHistory, filteredActiveTime: FilteredActiveTime, abilityTracker: AbilityTracker, + alwaysBeCasting: AlwaysBeCasting, }; protected eventHistory!: EventHistory; protected filteredActiveTime!: FilteredActiveTime; protected abilityTracker!: AbilityTracker; + protected alwaysBeCasting!: AlwaysBeCasting; - activeTime = 0; + activeTime: number[] = []; + buffApplies: number = 0; constructor(options: Options) { super(options); + this.addEventListener( + Events.applybuff.by(SELECTED_PLAYER).spell(TALENTS.ICY_VEINS_TALENT), + this.onIcyVeinsStart, + ); this.addEventListener( Events.removebuff.by(SELECTED_PLAYER).spell(TALENTS.ICY_VEINS_TALENT), - this.onIcyVeinsRemoved, + this.onIcyVeinsEnd, ); + this.addEventListener(Events.fightend, this.onFightEnd); } - onIcyVeinsRemoved(event: RemoveBuffEvent) { - const buffApplied = this.eventHistory.last( - 1, - undefined, - Events.applybuff.by(SELECTED_PLAYER).spell(TALENTS.ICY_VEINS_TALENT), - )[0].timestamp; - const uptime = this.filteredActiveTime.getActiveTime(buffApplied, event.timestamp); - this.activeTime += uptime; + onIcyVeinsStart(event: ApplyBuffEvent) { + this.buffApplies += 1; } - get buffUptime() { - return this.selectedCombatant.getBuffUptime(TALENTS.ICY_VEINS_TALENT.id); + onIcyVeinsEnd(event: RemoveBuffEvent) { + const buffApply: ApplyBuffEvent | undefined = GetRelatedEvent(event, 'BuffApply'); + if (!buffApply) { + return; + } + const icyVeinsDuration = event.timestamp - buffApply.timestamp; + this.activeTime[buffApply.timestamp] = + icyVeinsDuration - + this.alwaysBeCasting.getActiveTimeMillisecondsInWindow(buffApply.timestamp, event.timestamp); } - get percentActiveTime() { - return this.activeTime / this.buffUptime || 0; + onFightEnd(event: FightEndEvent) { + const buffApply = this.eventHistory.getEvents(EventType.ApplyBuff, { + spell: TALENTS.ICY_VEINS_TALENT, + count: 1, + })[0]; + if (!this.selectedCombatant.hasBuff(TALENTS.ICY_VEINS_TALENT.id) || !buffApply) { + return; + } + const icyVeinsDuration = event.timestamp - buffApply.timestamp; + this.activeTime[buffApply.timestamp] = + icyVeinsDuration - + this.alwaysBeCasting.getActiveTimeMillisecondsInWindow(buffApply.timestamp, event.timestamp); } - get downtimeSeconds() { - return (this.buffUptime - this.activeTime) / 1000; + icyVeinsDowntime = () => { + let activeTime = 0; + this.activeTime.forEach((c) => (activeTime += c)); + return activeTime / 1000; + }; + + get buffUptime() { + return this.selectedCombatant.getBuffUptime(TALENTS.ICY_VEINS_TALENT.id) / 1000; } - get averageDowntime() { - return this.downtimeSeconds / this.abilityTracker.getAbility(TALENTS.ICY_VEINS_TALENT.id).casts; + get percentActiveTime() { + return 1 - this.icyVeinsDowntime() / this.buffUptime; } get icyVeinsActiveTimeThresholds() { @@ -75,12 +107,12 @@ class IcyVeins extends Analyzer { when(this.icyVeinsActiveTimeThresholds).addSuggestion((suggest, actual, recommended) => suggest( <> - You spent {formatNumber(this.downtimeSeconds)} seconds ( - {formatNumber(this.averageDowntime)}s per cast) not casting anything while{' '} - was active. Because a large portion of your - damage comes from Icy Veins, you should ensure that you are getting the most out of it - every time it is cast. While sometimes this is out of your control (you got targeted by a - mechanic at the worst possible time), you should try to minimize that risk by casting{' '} + You spent {formatNumber(this.icyVeinsDowntime())} seconds ( + {formatNumber(this.icyVeinsDowntime() / this.buffApplies)}s per cast) not casting anything + while was active. Because a large portion + of your damage comes from Icy Veins, you should ensure that you are getting the most out + of it every time it is cast. While sometimes this is out of your control (you got targeted + by a mechanic at the worst possible time), you should try to minimize that risk by casting{' '} when you are at a low risk of being interrupted or when the target is vulnerable. , diff --git a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts index cb8b42c1b37..ce6686eea47 100644 --- a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts +++ b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts @@ -199,6 +199,18 @@ const EVENT_LINKS: EventLink[] = [ forwardBufferMs: CAST_BUFFER_MS, backwardBufferMs: CAST_BUFFER_MS, }, + { + reverseLinkRelation: BUFF_APPLY, + linkingEventId: TALENTS.ICY_VEINS_TALENT.id, + linkingEventType: EventType.ApplyBuff, + linkRelation: BUFF_REMOVE, + referencedEventId: TALENTS.ICY_VEINS_TALENT.id, + referencedEventType: EventType.RemoveBuff, + anyTarget: true, + maximumLinks: 1, + forwardBufferMs: 60_000, + backwardBufferMs: CAST_BUFFER_MS, + }, ]; /** From b4f9ac023eb81258812c8564c6c4f795fc3defc2 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 20:46:20 -0500 Subject: [PATCH 27/46] IV dependencies --- src/analysis/retail/mage/frost/core/IcyVeins.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/analysis/retail/mage/frost/core/IcyVeins.tsx b/src/analysis/retail/mage/frost/core/IcyVeins.tsx index 9207e3ddd24..9283c58d68f 100644 --- a/src/analysis/retail/mage/frost/core/IcyVeins.tsx +++ b/src/analysis/retail/mage/frost/core/IcyVeins.tsx @@ -12,9 +12,7 @@ import Events, { FightEndEvent, } from 'parser/core/Events'; import { When, ThresholdStyle } from 'parser/core/ParseResults'; -import AbilityTracker from 'parser/shared/modules/AbilityTracker'; import EventHistory from 'parser/shared/modules/EventHistory'; -import FilteredActiveTime from 'parser/shared/modules/FilteredActiveTime'; import BoringSpellValueText from 'parser/ui/BoringSpellValueText'; import Statistic from 'parser/ui/Statistic'; import STATISTIC_ORDER from 'parser/ui/STATISTIC_ORDER'; @@ -23,13 +21,9 @@ import AlwaysBeCasting from './AlwaysBeCasting'; class IcyVeins extends Analyzer { static dependencies = { eventHistory: EventHistory, - filteredActiveTime: FilteredActiveTime, - abilityTracker: AbilityTracker, alwaysBeCasting: AlwaysBeCasting, }; protected eventHistory!: EventHistory; - protected filteredActiveTime!: FilteredActiveTime; - protected abilityTracker!: AbilityTracker; protected alwaysBeCasting!: AlwaysBeCasting; activeTime: number[] = []; From f88d65eba0a43aeb99b2cf89ef543fcf656a7384 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 20:47:07 -0500 Subject: [PATCH 28/46] Winters Chill dependencies --- src/analysis/retail/mage/frost/core/WintersChill.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/analysis/retail/mage/frost/core/WintersChill.tsx b/src/analysis/retail/mage/frost/core/WintersChill.tsx index 1626471bcbd..14f6f58dee4 100644 --- a/src/analysis/retail/mage/frost/core/WintersChill.tsx +++ b/src/analysis/retail/mage/frost/core/WintersChill.tsx @@ -13,7 +13,6 @@ import Events, { } from 'parser/core/Events'; import { When, ThresholdStyle } from 'parser/core/ParseResults'; import Enemies from 'parser/shared/modules/Enemies'; -import EventHistory from 'parser/shared/modules/EventHistory'; import BoringSpellValueText from 'parser/ui/BoringSpellValueText'; import Statistic from 'parser/ui/Statistic'; import STATISTIC_ORDER from 'parser/ui/STATISTIC_ORDER'; @@ -23,10 +22,8 @@ const WINTERS_CHILL_SPENDERS = [SPELLS.ICE_LANCE_DAMAGE.id, SPELLS.GLACIAL_SPIKE class WintersChill extends Analyzer { static dependencies = { enemies: Enemies, - eventHistory: EventHistory, }; protected enemies!: Enemies; - protected eventHistory!: EventHistory; hasGlacialSpike: boolean = this.selectedCombatant.hasTalent(TALENTS.GLACIAL_SPIKE_TALENT); wintersChill: { From f08d9f2a1172fca18735e3bd8f1206be0adec3ee Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 20:53:16 -0500 Subject: [PATCH 29/46] Cold Front --- .../frost/normalizers/CastLinkNormalizer.ts | 12 ++++++++++++ .../retail/mage/frost/talents/ColdFront.tsx | 18 ++++-------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts index ce6686eea47..b9aeb060a6b 100644 --- a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts +++ b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts @@ -211,6 +211,18 @@ const EVENT_LINKS: EventLink[] = [ forwardBufferMs: 60_000, backwardBufferMs: CAST_BUFFER_MS, }, + { + reverseLinkRelation: BUFF_APPLY, + linkingEventId: TALENTS.COLD_FRONT_TALENT.id, + linkingEventType: EventType.ApplyBuff, + linkRelation: BUFF_REMOVE, + referencedEventId: TALENTS.COLD_FRONT_TALENT.id, + referencedEventType: EventType.RemoveBuff, + anyTarget: true, + maximumLinks: 1, + forwardBufferMs: 500, + backwardBufferMs: CAST_BUFFER_MS, + }, ]; /** diff --git a/src/analysis/retail/mage/frost/talents/ColdFront.tsx b/src/analysis/retail/mage/frost/talents/ColdFront.tsx index d33bfda0a79..7118b7467cf 100644 --- a/src/analysis/retail/mage/frost/talents/ColdFront.tsx +++ b/src/analysis/retail/mage/frost/talents/ColdFront.tsx @@ -4,18 +4,12 @@ import TALENTS from 'common/TALENTS/mage'; import { SpellIcon } from 'interface'; import Analyzer, { Options } from 'parser/core/Analyzer'; import { SELECTED_PLAYER } from 'parser/core/EventFilter'; -import Events from 'parser/core/Events'; -import EventHistory from 'parser/shared/modules/EventHistory'; +import Events, { GetRelatedEvent, ApplyBuffEvent, RemoveBuffEvent } from 'parser/core/Events'; import BoringSpellValueText from 'parser/ui/BoringSpellValueText'; import Statistic from 'parser/ui/Statistic'; import STATISTIC_CATEGORY from 'parser/ui/STATISTIC_CATEGORY'; class ColdFront extends Analyzer { - static dependencies = { - eventHistory: EventHistory, - }; - protected eventHistory!: EventHistory; - bonusFrozenOrbs = 0; constructor(options: Options) { @@ -27,13 +21,9 @@ class ColdFront extends Analyzer { ); } - onBuffApplied() { - const buffRemovedEvent = this.eventHistory.last( - 1, - 500, - Events.removebuff.to(SELECTED_PLAYER).spell(SPELLS.COLD_FRONT_BUFF), - ); - if (buffRemovedEvent) { + onBuffApplied(event: ApplyBuffEvent) { + const remove: RemoveBuffEvent | undefined = GetRelatedEvent(event, 'BuffRemove'); + if (remove) { this.bonusFrozenOrbs += 1; } } From 26cbbe7ee367717550156f05f430f9f438ee0a4a Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 21:22:10 -0500 Subject: [PATCH 30/46] Comet Storm --- .../frost/normalizers/CastLinkNormalizer.ts | 11 +++ .../retail/mage/frost/talents/CometStorm.tsx | 70 ++++++++----------- 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts index b9aeb060a6b..ee08f222e50 100644 --- a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts +++ b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts @@ -223,6 +223,17 @@ const EVENT_LINKS: EventLink[] = [ forwardBufferMs: 500, backwardBufferMs: CAST_BUFFER_MS, }, + { + reverseLinkRelation: SPELL_CAST, + linkingEventId: TALENTS.COMET_STORM_TALENT.id, + linkingEventType: EventType.Cast, + linkRelation: SPELL_DAMAGE, + referencedEventId: SPELLS.COMET_STORM_DAMAGE.id, + referencedEventType: EventType.Damage, + anyTarget: true, + forwardBufferMs: 3000, + backwardBufferMs: CAST_BUFFER_MS, + }, ]; /** diff --git a/src/analysis/retail/mage/frost/talents/CometStorm.tsx b/src/analysis/retail/mage/frost/talents/CometStorm.tsx index 9faf90e1eea..e3c3764f714 100644 --- a/src/analysis/retail/mage/frost/talents/CometStorm.tsx +++ b/src/analysis/retail/mage/frost/talents/CometStorm.tsx @@ -1,74 +1,68 @@ -import { Trans } from '@lingui/macro'; -import { COMET_STORM_AOE_MIN_TARGETS } from 'analysis/retail/mage/shared'; +import { COMET_STORM_AOE_MIN_TARGETS, SHATTER_DEBUFFS } from 'analysis/retail/mage/shared'; import { formatPercentage } from 'common/format'; import SPELLS from 'common/SPELLS'; import TALENTS from 'common/TALENTS/mage'; import { SpellLink } from 'interface'; import Analyzer, { Options } from 'parser/core/Analyzer'; import { SELECTED_PLAYER } from 'parser/core/EventFilter'; -import Events, { AnyEvent, CastEvent } from 'parser/core/Events'; +import Events, { CastEvent, DamageEvent, GetRelatedEvents } from 'parser/core/Events'; import { When, ThresholdStyle } from 'parser/core/ParseResults'; -import AbilityTracker from 'parser/shared/modules/AbilityTracker'; import Enemies from 'parser/shared/modules/Enemies'; -import { cometStormHits } from '../normalizers/CometStormLinkNormalizer'; - const MIN_SHATTERED_PROJECTILES_PER_CAST = 4; class CometStorm extends Analyzer { static dependencies = { - abilityTracker: AbilityTracker, enemies: Enemies, }; - protected abilityTracker!: AbilityTracker; protected enemies!: Enemies; - badCometStormCast = 0; + cometStorm: { enemiesHit: number[]; shatteredHits: number }[] = []; constructor(options: Options) { super(options); this.active = this.selectedCombatant.hasTalent(TALENTS.COMET_STORM_TALENT); this.addEventListener( Events.cast.by(SELECTED_PLAYER).spell(TALENTS.COMET_STORM_TALENT), - this.onCometStormCast, + this.onCometCast, ); } - onCometStormCast(event: CastEvent) { - const damageEvents: AnyEvent[] = cometStormHits(event); - const enemiesHit: number[] = []; - let projectilesShattered = 0; - - damageEvents.forEach((hit) => { - const enemy = this.enemies.getEntity(hit); - if (!enemy) { - return; - } - //Tracks each unique enemy that was hit by the cast - if (!enemiesHit.includes(enemy.guid)) { - enemiesHit.push(enemy.guid); + onCometCast(event: CastEvent) { + const damage: DamageEvent[] | undefined = GetRelatedEvents(event, 'SpellDamage'); + damage.forEach((d) => { + const enemy = this.enemies.getEntity(d); + const enemies: number[] = []; + if (enemy && enemies.includes(enemy.guid)) { + enemies.push(enemy.guid); } - //Tracks how many projectiles were shattered - if (enemy.hasBuff(SPELLS.WINTERS_CHILL.id)) { - projectilesShattered += 1; + let shattered = 0; + if (enemy && SHATTER_DEBUFFS.some((effect) => enemy.hasBuff(effect.id, d.timestamp))) { + shattered += 1; } + + this.cometStorm.push({ + enemiesHit: enemies, + shatteredHits: shattered, + }); }); + } - if ( - enemiesHit.length < COMET_STORM_AOE_MIN_TARGETS && - projectilesShattered < MIN_SHATTERED_PROJECTILES_PER_CAST - ) { - this.badCometStormCast += 1; - } + get badCasts() { + return this.cometStorm.filter( + (cs) => + cs.enemiesHit.length < COMET_STORM_AOE_MIN_TARGETS && + cs.shatteredHits < MIN_SHATTERED_PROJECTILES_PER_CAST, + ).length; } get totalCasts() { - return this.abilityTracker.getAbility(TALENTS.COMET_STORM_TALENT.id).casts; + return this.cometStorm.length; } get castUtilization() { - return 1 - this.badCometStormCast / this.totalCasts; + return 1 - this.badCasts / this.totalCasts; } get cometStormUtilizationThresholds() { @@ -88,7 +82,7 @@ class CometStorm extends Analyzer { suggest( <> You failed to get the most out of your {' '} - casts {this.badCometStormCast} times. Because the projectiles from{' '} + casts {this.badCasts} times. Because the projectiles from{' '} no longer remove your stacks of{' '} , you should always cast{' '} immediately after casting{' '} @@ -101,11 +95,7 @@ class CometStorm extends Analyzer { , ) .icon(TALENTS.COMET_STORM_TALENT.icon) - .actual( - - {formatPercentage(actual)}% Utilization - , - ) + .actual(`${formatPercentage(actual)}% Utilization`) .recommended(`${formatPercentage(recommended)}% is recommended`), ); } From 70b5f5f3ebf6ec3c1875ed895fab0ebbd01c150f Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 21:24:40 -0500 Subject: [PATCH 31/46] Winter Chill Suggestion --- src/analysis/retail/mage/frost/core/WintersChill.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analysis/retail/mage/frost/core/WintersChill.tsx b/src/analysis/retail/mage/frost/core/WintersChill.tsx index 14f6f58dee4..30a38b94f52 100644 --- a/src/analysis/retail/mage/frost/core/WintersChill.tsx +++ b/src/analysis/retail/mage/frost/core/WintersChill.tsx @@ -178,7 +178,8 @@ class WintersChill extends Analyzer { . Doing this will allow your pre-cast ability to hit the target after (unless you are standing too close to the target) allowing it to benefit from{' '} - . + . If you have 4 Icicles, it can be acceptable + to use without a pre-cast. , ) .icon(SPELLS.FROSTBOLT.icon) From 940aba235ec3b343fe2f04825af07afbcaabaf41 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 21:29:29 -0500 Subject: [PATCH 32/46] Remove Translations --- .../retail/mage/frost/core/BrainFreeze.tsx | 17 +++-------------- .../retail/mage/frost/core/IcyVeins.tsx | 7 +------ .../mage/frost/talents/WaterElemental.tsx | 15 +++++---------- 3 files changed, 9 insertions(+), 30 deletions(-) diff --git a/src/analysis/retail/mage/frost/core/BrainFreeze.tsx b/src/analysis/retail/mage/frost/core/BrainFreeze.tsx index 1b96c31921a..0f39b64da5f 100644 --- a/src/analysis/retail/mage/frost/core/BrainFreeze.tsx +++ b/src/analysis/retail/mage/frost/core/BrainFreeze.tsx @@ -1,4 +1,3 @@ -import { Trans } from '@lingui/macro'; import { formatNumber, formatPercentage } from 'common/format'; import SPELLS from 'common/SPELLS'; import TALENTS from 'common/TALENTS/mage'; @@ -152,11 +151,7 @@ class BrainFreeze extends Analyzer { , ) .icon(TALENTS.BRAIN_FREEZE_TALENT.icon) - .actual( - - {formatPercentage(actual)}% overwritten - , - ) + .actual(`${formatPercentage(actual)}% overwritten`) .recommended(`Overwriting none is recommended`), ); when(this.brainFreezeExpiredThresholds).addSuggestion((suggest, actual, recommended) => @@ -168,11 +163,7 @@ class BrainFreeze extends Analyzer { , ) .icon(TALENTS.BRAIN_FREEZE_TALENT.icon) - .actual( - - {formatPercentage(actual)}% expired - , - ) + .actual(`${formatPercentage(actual)}% expired`) .recommended(`Letting none expire is recommended`), ); when(this.overlappedFlurryThresholds).addSuggestion((suggest, actual, recommended) => @@ -188,9 +179,7 @@ class BrainFreeze extends Analyzer { , ) .icon(TALENTS.FLURRY_TALENT.icon) - .actual( - {formatNumber(actual)} casts, - ) + .actual(`${formatNumber(actual)} casts`) .recommended(`Casting none is recommended`), ); } diff --git a/src/analysis/retail/mage/frost/core/IcyVeins.tsx b/src/analysis/retail/mage/frost/core/IcyVeins.tsx index 9283c58d68f..6acc2002981 100644 --- a/src/analysis/retail/mage/frost/core/IcyVeins.tsx +++ b/src/analysis/retail/mage/frost/core/IcyVeins.tsx @@ -1,4 +1,3 @@ -import { Trans } from '@lingui/macro'; import { formatNumber, formatPercentage } from 'common/format'; import TALENTS from 'common/TALENTS/mage'; import { SpellLink } from 'interface'; @@ -112,11 +111,7 @@ class IcyVeins extends Analyzer { , ) .icon(TALENTS.ICY_VEINS_TALENT.icon) - .actual( - - {formatPercentage(this.percentActiveTime)}% Active Time during Icy Veins - , - ) + .actual(`${formatPercentage(this.percentActiveTime)}% Active Time during Icy Veins`) .recommended(`${formatPercentage(recommended)}% is recommended`), ); } diff --git a/src/analysis/retail/mage/frost/talents/WaterElemental.tsx b/src/analysis/retail/mage/frost/talents/WaterElemental.tsx index e63ae9e78b7..2324956ba30 100644 --- a/src/analysis/retail/mage/frost/talents/WaterElemental.tsx +++ b/src/analysis/retail/mage/frost/talents/WaterElemental.tsx @@ -1,4 +1,3 @@ -import { Trans } from '@lingui/macro'; import { formatPercentage, formatNumber, formatThousands, formatDuration } from 'common/format'; import SPELLS from 'common/SPELLS'; import { PLACEHOLDER_TALENT } from 'common/TALENTS/types'; @@ -142,11 +141,7 @@ class WaterElemental extends Analyzer { , ) .icon(PLACEHOLDER_TALENT.icon) - .actual( - - {formatPercentage(actual)}% uptime - , - ) + .actual(`${formatPercentage(actual)}% uptime`) .recommended( `mirroring your own uptime (${formatPercentage( this.abc.activeTimePercentage, @@ -162,13 +157,13 @@ class WaterElemental extends Analyzer { ) .icon(SPELLS.WATERBOLT.icon) .actual( - - {this._timestampFirstCast === 0 + `${ + this._timestampFirstCast === 0 ? 'Never attacked or not summoned' : 'First attack: ' + formatDuration(this._timestampFirstCast - this.owner.fight.start_time) + - ' into the fight'} - , + ' into the fight' + }`, ) .recommended(`summoning pre-fight is recommended`), ); From 24f1a15cac7b35cfe9fb992b194c16f64646ebe9 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 21:38:38 -0500 Subject: [PATCH 33/46] changelog --- src/analysis/retail/mage/frost/CHANGELOG.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/analysis/retail/mage/frost/CHANGELOG.tsx b/src/analysis/retail/mage/frost/CHANGELOG.tsx index d6e0b393ca2..5e024257f7b 100644 --- a/src/analysis/retail/mage/frost/CHANGELOG.tsx +++ b/src/analysis/retail/mage/frost/CHANGELOG.tsx @@ -6,6 +6,11 @@ import { Sharrq, ToppleTheNun } from 'CONTRIBUTORS'; // prettier-ignore export default [ + change(date(2024, 1, 5), <>Added a statistic for the average delay to use . This is just informational., Sharrq), + change(date(2024, 1, 5), <>Added tracking for ., Sharrq), + change(date(2024, 1, 5), <>Adjusted to change the spells that can be used to spend ., Sharrq), + change(date(2024, 1, 5), <>Updated to ignore pre-casts at 4 ., Sharrq), + change(date(2024, 1, 5), 'Rewrote most core frost functionality to use event links instead.', Sharrq), change(date(2023, 7, 10), 'Remove references to 10.1.5 removed talents.', Sharrq), change(date(2023, 7, 3), 'Update SpellLink usage.', ToppleTheNun), change(date(2023, 6, 27), <>Added to list of Bloodlust Buffs., Sharrq), From 795222568f0dd2d3ddb3e06ed8de8a023f8633bc Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 22:38:36 -0500 Subject: [PATCH 34/46] Ray of Frost --- .../retail/mage/frost/CombatLogParser.ts | 2 + .../retail/mage/frost/core/WintersChill.tsx | 28 +++++- .../frost/normalizers/CastLinkNormalizer.ts | 11 +++ .../retail/mage/frost/talents/RayOfFrost.tsx | 92 +++++++++++++++++++ 4 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 src/analysis/retail/mage/frost/talents/RayOfFrost.tsx diff --git a/src/analysis/retail/mage/frost/CombatLogParser.ts b/src/analysis/retail/mage/frost/CombatLogParser.ts index 988e5836d54..8d21902bba5 100644 --- a/src/analysis/retail/mage/frost/CombatLogParser.ts +++ b/src/analysis/retail/mage/frost/CombatLogParser.ts @@ -35,6 +35,7 @@ import ColdFront from './talents/ColdFront'; import IcyPropulsion from './talents/IcyPropulsion'; import BoneChilling from './talents/BoneChilling'; import CometStorm from './talents/CometStorm'; +import RayOfFrost from './talents/RayOfFrost'; import GlacialSpike from './talents/GlacialSpike'; import LonelyWinter from './talents/LonelyWinter'; import SplittingIce from './talents/SplittingIce'; @@ -74,6 +75,7 @@ class CombatLogParser extends CoreCombatLogParser { thermalVoid: ThermalVoid, glacialSpike: GlacialSpike, cometStorm: CometStorm, + rayOfFrost: RayOfFrost, icyPropulsion: IcyPropulsion, coldFront: ColdFront, mirrorImage: MirrorImage, diff --git a/src/analysis/retail/mage/frost/core/WintersChill.tsx b/src/analysis/retail/mage/frost/core/WintersChill.tsx index 30a38b94f52..6fed3cf7bec 100644 --- a/src/analysis/retail/mage/frost/core/WintersChill.tsx +++ b/src/analysis/retail/mage/frost/core/WintersChill.tsx @@ -43,7 +43,12 @@ class WintersChill extends Analyzer { this.addEventListener( Events.damage .by(SELECTED_PLAYER) - .spell([SPELLS.FROSTBOLT_DAMAGE, SPELLS.GLACIAL_SPIKE_DAMAGE, SPELLS.ICE_LANCE_DAMAGE]), + .spell([ + SPELLS.FROSTBOLT_DAMAGE, + SPELLS.GLACIAL_SPIKE_DAMAGE, + SPELLS.ICE_LANCE_DAMAGE, + TALENTS.RAY_OF_FROST_TALENT, + ]), this.onDamage, ); } @@ -92,10 +97,23 @@ class WintersChill extends Analyzer { wintersChillShatters = () => { //Winter's Chill Debuffs where there are at least 2 damage hits of Glacial Spike and/or Ice Lance - const badDebuffs = this.wintersChill.filter( - (w) => - w.damageEvents.filter((d) => WINTERS_CHILL_SPENDERS.includes(d.ability.guid)).length >= 2, - ); + let badDebuffs = this.wintersChill.filter((w) => { + const shatteredSpenders = w.damageEvents.filter((d) => + WINTERS_CHILL_SPENDERS.includes(d.ability.guid), + ); + return shatteredSpenders.length >= 2; + }); + + //If they shattered one spell but also they used Ray Of Frost, then disregard it. + badDebuffs = badDebuffs.filter((w) => { + const shatteredSpenders = w.damageEvents.filter((d) => + WINTERS_CHILL_SPENDERS.includes(d.ability.guid), + ); + const rayHits = w.damageEvents.filter( + (d) => d.ability.guid === TALENTS.RAY_OF_FROST_TALENT.id, + ); + return shatteredSpenders.length >= 1 && rayHits.length >= 2; + }); return badDebuffs.length; }; diff --git a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts index ee08f222e50..eb4e6141058 100644 --- a/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts +++ b/src/analysis/retail/mage/frost/normalizers/CastLinkNormalizer.ts @@ -234,6 +234,17 @@ const EVENT_LINKS: EventLink[] = [ forwardBufferMs: 3000, backwardBufferMs: CAST_BUFFER_MS, }, + { + reverseLinkRelation: SPELL_CAST, + linkingEventId: TALENTS.RAY_OF_FROST_TALENT.id, + linkingEventType: EventType.Cast, + linkRelation: SPELL_DAMAGE, + referencedEventId: TALENTS.RAY_OF_FROST_TALENT.id, + referencedEventType: EventType.Damage, + anyTarget: true, + forwardBufferMs: 7000, + backwardBufferMs: CAST_BUFFER_MS, + }, ]; /** diff --git a/src/analysis/retail/mage/frost/talents/RayOfFrost.tsx b/src/analysis/retail/mage/frost/talents/RayOfFrost.tsx new file mode 100644 index 00000000000..926082bb564 --- /dev/null +++ b/src/analysis/retail/mage/frost/talents/RayOfFrost.tsx @@ -0,0 +1,92 @@ +import { SHATTER_DEBUFFS } from 'analysis/retail/mage/shared'; +import { formatPercentage } from 'common/format'; +import SPELLS from 'common/SPELLS'; +import TALENTS from 'common/TALENTS/mage'; +import { SpellLink } from 'interface'; +import Analyzer, { Options } from 'parser/core/Analyzer'; +import { SELECTED_PLAYER } from 'parser/core/EventFilter'; +import Events, { CastEvent, DamageEvent, GetRelatedEvents } from 'parser/core/Events'; +import { When, ThresholdStyle } from 'parser/core/ParseResults'; +import Enemies from 'parser/shared/modules/Enemies'; + +class RayOfFrost extends Analyzer { + static dependencies = { + enemies: Enemies, + }; + protected enemies!: Enemies; + + rayOfFrost: { hits: number; shatteredHits: number }[] = []; + + constructor(options: Options) { + super(options); + this.active = this.selectedCombatant.hasTalent(TALENTS.RAY_OF_FROST_TALENT); + this.addEventListener( + Events.cast.by(SELECTED_PLAYER).spell(TALENTS.RAY_OF_FROST_TALENT), + this.onRayCast, + ); + } + + onRayCast(event: CastEvent) { + const damage: DamageEvent[] | undefined = GetRelatedEvents(event, 'SpellDamage'); + let shattered = 0; + damage.forEach((d) => { + const enemy = this.enemies.getEntity(d); + if (SHATTER_DEBUFFS.some((effect) => enemy?.hasBuff(effect.id, d.timestamp))) { + shattered += 1; + } + }); + + this.rayOfFrost.push({ + hits: damage.length, + shatteredHits: shattered, + }); + } + + get badCasts() { + return this.rayOfFrost.filter((r) => r.shatteredHits < 2 || r.hits < 4).length; + } + + get totalCasts() { + return this.rayOfFrost.length; + } + + get castUtilization() { + return 1 - this.badCasts / this.totalCasts; + } + + get rayOfFrostUtilizationThresholds() { + return { + actual: this.castUtilization, + isLessThan: { + minor: 0.9, + average: 0.8, + major: 0.7, + }, + style: ThresholdStyle.PERCENTAGE, + }; + } + + suggestions(when: When) { + when(this.rayOfFrostUtilizationThresholds).addSuggestion((suggest, actual, recommended) => + suggest( + <> + You failed to get the most out of your {' '} + casts {this.badCasts} times. Because the ticks from{' '} + do not remove your stacks of{' '} + , you should always cast{' '} + during{' '} + . However, because{' '} + has such a short duration and therefore will + likely naturally end before finishes, + you should spend your first stack of and then + cast instead of spending the 2nd stack. + , + ) + .icon(TALENTS.RAY_OF_FROST_TALENT.icon) + .actual(`${formatPercentage(actual)}% Utilization`) + .recommended(`${formatPercentage(recommended)}% is recommended`), + ); + } +} + +export default RayOfFrost; From e9c37e5346a2af83ba165c66933878d5650b220e Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 22:38:50 -0500 Subject: [PATCH 35/46] Remove Fingers logging --- src/analysis/retail/mage/frost/core/FingersOfFrost.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/analysis/retail/mage/frost/core/FingersOfFrost.tsx b/src/analysis/retail/mage/frost/core/FingersOfFrost.tsx index 1b3ad8e6dc2..5e0cbd019d9 100644 --- a/src/analysis/retail/mage/frost/core/FingersOfFrost.tsx +++ b/src/analysis/retail/mage/frost/core/FingersOfFrost.tsx @@ -50,8 +50,6 @@ class FingersOfFrost extends Analyzer { const spender: CastEvent | undefined = remove && GetRelatedEvent(remove, 'SpellCast'); const damage: DamageEvent | undefined = spender && GetRelatedEvent(spender, 'SpellDamage'); const enemy = damage && this.enemies.getEntity(damage); - this.log(spender?.timestamp); - this.log(damage?.timestamp); this.fingers.push({ apply: event, remove: remove, @@ -70,7 +68,6 @@ class FingersOfFrost extends Analyzer { get averageSpendDelaySeconds() { let spendDelay = 0; this.fingers.forEach((f) => f.spendDelay && (spendDelay += f.spendDelay)); - this.log(spendDelay / this.fingers.filter((f) => f.spendDelay).length / 1000); return spendDelay / this.fingers.filter((f) => f.spendDelay).length / 1000; } From a1407b46750d53c2080c71f6f9ee2053101bc076 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 22:47:21 -0500 Subject: [PATCH 36/46] spec compat --- src/analysis/retail/mage/frost/CONFIG.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analysis/retail/mage/frost/CONFIG.tsx b/src/analysis/retail/mage/frost/CONFIG.tsx index 200b6266521..4574441d39a 100644 --- a/src/analysis/retail/mage/frost/CONFIG.tsx +++ b/src/analysis/retail/mage/frost/CONFIG.tsx @@ -11,7 +11,7 @@ const config: Config = { expansion: Expansion.Dragonflight, // The WoW client patch this spec was last updated. patchCompatibility: '10.2.0', - isPartial: true, + isPartial: false, // Explain the status of this spec's analysis here. Try to mention how complete it is, and perhaps show links to places users can learn more. If this spec's analysis does not show a complete picture please mention this in the `` component. description: ( <> From 7f6609452c083c6e00a70491dc38c4cd3c6e85cc Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 22:48:10 -0500 Subject: [PATCH 37/46] changelog --- src/analysis/retail/mage/frost/CHANGELOG.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analysis/retail/mage/frost/CHANGELOG.tsx b/src/analysis/retail/mage/frost/CHANGELOG.tsx index 5e024257f7b..4fad1e322f0 100644 --- a/src/analysis/retail/mage/frost/CHANGELOG.tsx +++ b/src/analysis/retail/mage/frost/CHANGELOG.tsx @@ -6,6 +6,7 @@ import { Sharrq, ToppleTheNun } from 'CONTRIBUTORS'; // prettier-ignore export default [ + change(date(2024, 1, 5), 'Updated spec support to full 10.2 support.', Sharrq), change(date(2024, 1, 5), <>Added a statistic for the average delay to use . This is just informational., Sharrq), change(date(2024, 1, 5), <>Added tracking for ., Sharrq), change(date(2024, 1, 5), <>Adjusted to change the spells that can be used to spend ., Sharrq), From a3d12d8ac5588cf265b618af309a2f9b6e2433a6 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 22:53:39 -0500 Subject: [PATCH 38/46] more changelog --- src/analysis/retail/mage/frost/CHANGELOG.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analysis/retail/mage/frost/CHANGELOG.tsx b/src/analysis/retail/mage/frost/CHANGELOG.tsx index 4fad1e322f0..9f903593651 100644 --- a/src/analysis/retail/mage/frost/CHANGELOG.tsx +++ b/src/analysis/retail/mage/frost/CHANGELOG.tsx @@ -7,6 +7,7 @@ import { Sharrq, ToppleTheNun } from 'CONTRIBUTORS'; // prettier-ignore export default [ change(date(2024, 1, 5), 'Updated spec support to full 10.2 support.', Sharrq), + change(date(2024, 1, 5), <>Fixed the cooldowns for and ., Sharrq), change(date(2024, 1, 5), <>Added a statistic for the average delay to use . This is just informational., Sharrq), change(date(2024, 1, 5), <>Added tracking for ., Sharrq), change(date(2024, 1, 5), <>Adjusted to change the spells that can be used to spend ., Sharrq), From 2f3f1422d30d88d54feea6a4f6d836c9c4ec7c18 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 23:11:30 -0500 Subject: [PATCH 39/46] fix Ice Lance --- .../retail/mage/frost/core/IceLance.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/analysis/retail/mage/frost/core/IceLance.tsx b/src/analysis/retail/mage/frost/core/IceLance.tsx index 9b96f65b67e..c63385dc532 100644 --- a/src/analysis/retail/mage/frost/core/IceLance.tsx +++ b/src/analysis/retail/mage/frost/core/IceLance.tsx @@ -42,17 +42,23 @@ class IceLance extends Analyzer { }); } - get nonShatteredCasts() { - return this.icelance.filter((il) => !il.shattered).length; - } + nonShatteredCasts = () => { + //Get casts that were not shattered + let badCasts = this.icelance.filter((il) => !il.shattered); + + //If they had Fingers of Frost, disregard it + badCasts = badCasts.filter((il) => !il.hadFingers); + + return badCasts.length; + }; get shatteredPercent() { - return 1 - this.nonShatteredCasts / this.icelance.length; + return 1 - this.nonShatteredCasts() / this.icelance.length; } get nonShatteredIceLanceThresholds() { return { - actual: this.nonShatteredCasts / this.icelance.length, + actual: this.nonShatteredCasts() / this.icelance.length, isGreaterThan: { minor: 0.05, average: 0.15, @@ -66,7 +72,7 @@ class IceLance extends Analyzer { when(this.nonShatteredIceLanceThresholds).addSuggestion((suggest, actual, recommended) => suggest( <> - You cast {this.nonShatteredCasts} times ( + You cast {this.nonShatteredCasts()} times ( {formatPercentage(actual)}%) without . Make sure that you are only casting Ice Lance when the target has{' '} (or other Shatter effects), if you have a{' '} From d0056b0a6e2a84237e1d73be11942497729be623 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 23:30:17 -0500 Subject: [PATCH 40/46] fixed winters chill --- src/analysis/retail/mage/frost/core/WintersChill.tsx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/analysis/retail/mage/frost/core/WintersChill.tsx b/src/analysis/retail/mage/frost/core/WintersChill.tsx index 6fed3cf7bec..2a20611e969 100644 --- a/src/analysis/retail/mage/frost/core/WintersChill.tsx +++ b/src/analysis/retail/mage/frost/core/WintersChill.tsx @@ -95,13 +95,13 @@ class WintersChill extends Analyzer { return missingPreCast.length; }; - wintersChillShatters = () => { + missedShatters = () => { //Winter's Chill Debuffs where there are at least 2 damage hits of Glacial Spike and/or Ice Lance let badDebuffs = this.wintersChill.filter((w) => { const shatteredSpenders = w.damageEvents.filter((d) => WINTERS_CHILL_SPENDERS.includes(d.ability.guid), ); - return shatteredSpenders.length >= 2; + return shatteredSpenders.length < 2; }); //If they shattered one spell but also they used Ray Of Frost, then disregard it. @@ -112,7 +112,7 @@ class WintersChill extends Analyzer { const rayHits = w.damageEvents.filter( (d) => d.ability.guid === TALENTS.RAY_OF_FROST_TALENT.id, ); - return shatteredSpenders.length >= 1 && rayHits.length >= 2; + return shatteredSpenders.length !== 1 || rayHits.length < 2; }); return badDebuffs.length; @@ -122,12 +122,8 @@ class WintersChill extends Analyzer { return this.wintersChill.length; } - get missedShatters() { - return this.totalProcs - this.wintersChillShatters(); - } - get shatterPercent() { - return this.wintersChillShatters() / this.totalProcs || 0; + return 1 - this.missedShatters() / this.totalProcs; } get preCastPercent() { From a6213fdc394fed941765ecbbad97d20ba081e781 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 23:46:09 -0500 Subject: [PATCH 41/46] Add timeline highlights --- .../retail/mage/frost/core/IceLance.tsx | 7 ++++- .../retail/mage/frost/talents/CometStorm.tsx | 30 ++++++++++++------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/analysis/retail/mage/frost/core/IceLance.tsx b/src/analysis/retail/mage/frost/core/IceLance.tsx index c63385dc532..f01e5b1cc49 100644 --- a/src/analysis/retail/mage/frost/core/IceLance.tsx +++ b/src/analysis/retail/mage/frost/core/IceLance.tsx @@ -3,6 +3,7 @@ import { formatPercentage } from 'common/format'; import SPELLS from 'common/SPELLS'; import TALENTS from 'common/TALENTS/mage'; import { SpellLink } from 'interface'; +import { highlightInefficientCast } from 'interface/report/Results/Timeline/Casts'; import Analyzer, { SELECTED_PLAYER, Options } from 'parser/core/Analyzer'; import Events, { CastEvent, DamageEvent, GetRelatedEvent } from 'parser/core/Events'; import { When, ThresholdStyle } from 'parser/core/ParseResults'; @@ -17,7 +18,7 @@ class IceLance extends Analyzer { }; protected enemies!: Enemies; - icelance: { shattered: boolean; hadFingers: boolean; cleaved: boolean }[] = []; + icelance: { cast: CastEvent; shattered: boolean; hadFingers: boolean; cleaved: boolean }[] = []; constructor(options: Options) { super(options); @@ -32,6 +33,7 @@ class IceLance extends Analyzer { const enemy = damage && this.enemies.getEntity(damage); const cleave: DamageEvent | undefined = GetRelatedEvent(event, 'CleaveDamage'); this.icelance.push({ + cast: event, shattered: SHATTER_DEBUFFS.some((effect) => enemy?.hasBuff(effect.id, damage?.timestamp)) || false, hadFingers: this.selectedCombatant.hasBuff( @@ -49,6 +51,9 @@ class IceLance extends Analyzer { //If they had Fingers of Frost, disregard it badCasts = badCasts.filter((il) => !il.hadFingers); + const tooltip = `This Ice Lance was not shattered.`; + badCasts.forEach((e) => e.cast && highlightInefficientCast(e.cast, tooltip)); + return badCasts.length; }; diff --git a/src/analysis/retail/mage/frost/talents/CometStorm.tsx b/src/analysis/retail/mage/frost/talents/CometStorm.tsx index e3c3764f714..8a4d585a139 100644 --- a/src/analysis/retail/mage/frost/talents/CometStorm.tsx +++ b/src/analysis/retail/mage/frost/talents/CometStorm.tsx @@ -3,6 +3,7 @@ import { formatPercentage } from 'common/format'; import SPELLS from 'common/SPELLS'; import TALENTS from 'common/TALENTS/mage'; import { SpellLink } from 'interface'; +import { highlightInefficientCast } from 'interface/report/Results/Timeline/Casts'; import Analyzer, { Options } from 'parser/core/Analyzer'; import { SELECTED_PLAYER } from 'parser/core/EventFilter'; import Events, { CastEvent, DamageEvent, GetRelatedEvents } from 'parser/core/Events'; @@ -17,7 +18,7 @@ class CometStorm extends Analyzer { }; protected enemies!: Enemies; - cometStorm: { enemiesHit: number[]; shatteredHits: number }[] = []; + cometStorm: { cast: CastEvent; enemiesHit: number[]; shatteredHits: number }[] = []; constructor(options: Options) { super(options); @@ -30,6 +31,8 @@ class CometStorm extends Analyzer { onCometCast(event: CastEvent) { const damage: DamageEvent[] | undefined = GetRelatedEvents(event, 'SpellDamage'); + let shattered = 0; + const enemies: number[] = []; damage.forEach((d) => { const enemy = this.enemies.getEntity(d); const enemies: number[] = []; @@ -37,32 +40,37 @@ class CometStorm extends Analyzer { enemies.push(enemy.guid); } - let shattered = 0; if (enemy && SHATTER_DEBUFFS.some((effect) => enemy.hasBuff(effect.id, d.timestamp))) { shattered += 1; } + }); - this.cometStorm.push({ - enemiesHit: enemies, - shatteredHits: shattered, - }); + this.cometStorm.push({ + cast: event, + enemiesHit: enemies, + shatteredHits: shattered, }); } - get badCasts() { - return this.cometStorm.filter( + badCasts = () => { + const badCasts = this.cometStorm.filter( (cs) => cs.enemiesHit.length < COMET_STORM_AOE_MIN_TARGETS && cs.shatteredHits < MIN_SHATTERED_PROJECTILES_PER_CAST, - ).length; - } + ); + + const tooltip = `This Comet Storm was not shattered and did not hit multiple enemies.`; + badCasts.forEach((e) => e.cast && highlightInefficientCast(e.cast, tooltip)); + + return badCasts.length; + }; get totalCasts() { return this.cometStorm.length; } get castUtilization() { - return 1 - this.badCasts / this.totalCasts; + return 1 - this.badCasts() / this.totalCasts; } get cometStormUtilizationThresholds() { From f1518497049adc1d443e1c4a9a9ebedaf200f662 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Fri, 5 Jan 2024 23:52:05 -0500 Subject: [PATCH 42/46] fix winters chill suggestions --- src/analysis/retail/mage/frost/core/WintersChill.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/analysis/retail/mage/frost/core/WintersChill.tsx b/src/analysis/retail/mage/frost/core/WintersChill.tsx index 2a20611e969..c80bc10336e 100644 --- a/src/analysis/retail/mage/frost/core/WintersChill.tsx +++ b/src/analysis/retail/mage/frost/core/WintersChill.tsx @@ -161,8 +161,8 @@ class WintersChill extends Analyzer { suggest( <> You failed to properly take advantage of on - your target {this.missedShatters} times ({formatPercentage(1 - actual)}%). After debuffing - the target via and{' '} + your target {this.missedShatters()} times ({formatPercentage(1 - actual)}%). After + debuffing the target via and{' '} , you should ensure that you hit the target with{' '} {this.hasGlacialSpike ? ( @@ -186,9 +186,9 @@ class WintersChill extends Analyzer { suggest( <> You failed to use a pre-cast ability before {' '} - {this.missedPreCasts} times ({formatPercentage(1 - actual)}%). Because of the travel time - of , you should cast a damaging ability such as{' '} - immediately before using{' '} + {this.missedPreCasts()} times ({formatPercentage(1 - actual)}%). Because of the travel + time of , you should cast a damaging ability + such as immediately before using{' '} . Doing this will allow your pre-cast ability to hit the target after (unless you are standing too close to the target) allowing it to benefit from{' '} From 6d94d166a30519ad99479baadd37ea557e268edf Mon Sep 17 00:00:00 2001 From: Putro Date: Sat, 6 Jan 2024 14:35:49 +0100 Subject: [PATCH 43/46] [Warlock] Fix an issue where it was trying to access a talent spell inside regular spells object --- .../retail/warlock/demonology/CHANGELOG.tsx | 3 +- .../warlock/demonology/modules/pets/PETS.ts | 36 ++++++++++--------- .../pets/normalizers/PrepullPetNormalizer.ts | 2 +- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/analysis/retail/warlock/demonology/CHANGELOG.tsx b/src/analysis/retail/warlock/demonology/CHANGELOG.tsx index ea456b853a0..9ad0841643d 100644 --- a/src/analysis/retail/warlock/demonology/CHANGELOG.tsx +++ b/src/analysis/retail/warlock/demonology/CHANGELOG.tsx @@ -1,10 +1,11 @@ import { change, date } from 'common/changelog'; import SPELLS from 'common/SPELLS'; import { TALENTS_WARLOCK } from 'common/TALENTS'; -import { Sharrq, Zeboot, Meldris, ToppleTheNun, Jonfanz, Mae, dodse, Arlie } from 'CONTRIBUTORS'; +import { Sharrq, Zeboot, Meldris, ToppleTheNun, Jonfanz, Mae, dodse, Arlie, Putro } from 'CONTRIBUTORS'; import { SpellLink } from 'interface'; export default [ + change(date(2024, 1, 6), <>Fix a crash related to ., Putro), change(date(2023, 7, 31), <>Add support for Aberrus 2set CDR on , Arlie), change(date(2023, 7, 31), 'Update CDR on Dark Pact and Unending Resolve', Arlie), change(date(2023, 7, 8), 'Update SpellLink usage.', ToppleTheNun), diff --git a/src/analysis/retail/warlock/demonology/modules/pets/PETS.ts b/src/analysis/retail/warlock/demonology/modules/pets/PETS.ts index 2ad353b0d9d..0063066fa53 100644 --- a/src/analysis/retail/warlock/demonology/modules/pets/PETS.ts +++ b/src/analysis/retail/warlock/demonology/modules/pets/PETS.ts @@ -1,4 +1,6 @@ import SPELLS from 'common/SPELLS'; +import Spell from 'common/SPELLS/Spell'; +import { Talent } from 'common/TALENTS/types'; import TALENTS from 'common/TALENTS/warlock'; const INNER_DEMON_NETHER_PORTAL_DURATION = 15000; @@ -6,7 +8,7 @@ const INNER_DEMON_NETHER_PORTAL_DURATION = 15000; type PetRecord = { guid: number; duration: number; - summonAbility: number; + summonAbility: Spell | Talent; isRandom?: boolean; }; @@ -22,92 +24,92 @@ const PETS = { WILD_IMP_HOG: { guid: 55659, duration: 15000, // maximum duration, realistically is handled differently - summonAbility: SPELLS.WILD_IMP_HOG_SUMMON.id, + summonAbility: SPELLS.WILD_IMP_HOG_SUMMON, }, DREADSTALKER: { guid: 98035, duration: 12000, - summonAbility: SPELLS.DREADSTALKER_SUMMON_1.id, + summonAbility: SPELLS.DREADSTALKER_SUMMON_1, }, VILEFIEND: { guid: 135816, duration: 15000, - summonAbility: TALENTS.SUMMON_VILEFIEND_TALENT.id, + summonAbility: TALENTS.SUMMON_VILEFIEND_TALENT, }, GRIMOIRE_FELGUARD: { guid: 17252, duration: 15000, - summonAbility: TALENTS.GRIMOIRE_FELGUARD_TALENT.id, + summonAbility: TALENTS.GRIMOIRE_FELGUARD_TALENT, }, DEMONIC_TYRANT: { guid: 135002, duration: 15000, - summonAbility: SPELLS.SUMMON_DEMONIC_TYRANT.id, + summonAbility: SPELLS.SUMMON_DEMONIC_TYRANT, }, // Inner Demons and Nether Portal demons WILD_IMP_INNER_DEMONS: { guid: 143622, duration: INNER_DEMON_NETHER_PORTAL_DURATION, - summonAbility: SPELLS.WILD_IMP_ID_SUMMON.id, + summonAbility: SPELLS.WILD_IMP_ID_SUMMON, }, BILESCOURGE: { guid: 136404, duration: INNER_DEMON_NETHER_PORTAL_DURATION, - summonAbility: SPELLS.BILESCOURGE_SUMMON.id, + summonAbility: SPELLS.BILESCOURGE_SUMMON, isRandom: true, }, VICIOUS_HELLHOUND: { guid: 136399, duration: INNER_DEMON_NETHER_PORTAL_DURATION, - summonAbility: SPELLS.VICIOUS_HELLHOUND_SUMMON.id, + summonAbility: SPELLS.VICIOUS_HELLHOUND_SUMMON, isRandom: true, }, SHIVARRA: { guid: 136406, duration: INNER_DEMON_NETHER_PORTAL_DURATION, - summonAbility: SPELLS.SHIVARRA_SUMMON.id, + summonAbility: SPELLS.SHIVARRA_SUMMON, isRandom: true, }, DARKHOUND: { guid: 136408, duration: INNER_DEMON_NETHER_PORTAL_DURATION, - summonAbility: SPELLS.DARKHOUND_SUMMON.id, + summonAbility: SPELLS.DARKHOUND_SUMMON, isRandom: true, }, ILLIDARI_SATYR: { guid: 136398, duration: INNER_DEMON_NETHER_PORTAL_DURATION, - summonAbility: SPELLS.ILLIDARI_SATYR_SUMMON.id, + summonAbility: SPELLS.ILLIDARI_SATYR_SUMMON, isRandom: true, }, VOID_TERROR: { guid: 136403, duration: INNER_DEMON_NETHER_PORTAL_DURATION, - summonAbility: SPELLS.VOID_TERROR_SUMMON.id, + summonAbility: SPELLS.VOID_TERROR_SUMMON, isRandom: true, }, URZUL: { guid: 136402, duration: INNER_DEMON_NETHER_PORTAL_DURATION, - summonAbility: SPELLS.URZUL_SUMMON.id, + summonAbility: SPELLS.URZUL_SUMMON, isRandom: true, }, WRATHGUARD: { guid: 136407, duration: INNER_DEMON_NETHER_PORTAL_DURATION, - summonAbility: SPELLS.WRATHGUARD_SUMMON.id, + summonAbility: SPELLS.WRATHGUARD_SUMMON, isRandom: true, }, EYE_OF_GULDAN: { guid: 136401, duration: INNER_DEMON_NETHER_PORTAL_DURATION, - summonAbility: SPELLS.EYE_OF_GULDAN_SUMMON.id, + summonAbility: SPELLS.EYE_OF_GULDAN_SUMMON, isRandom: true, }, PRINCE_MALCHEZAAR: { guid: 136397, duration: INNER_DEMON_NETHER_PORTAL_DURATION, - summonAbility: SPELLS.PRINCE_MALCHEZAAR_SUMMON.id, + summonAbility: SPELLS.PRINCE_MALCHEZAAR_SUMMON, isRandom: true, }, }; diff --git a/src/analysis/retail/warlock/demonology/modules/pets/normalizers/PrepullPetNormalizer.ts b/src/analysis/retail/warlock/demonology/modules/pets/normalizers/PrepullPetNormalizer.ts index 676ec140e09..0c9dac78f4b 100644 --- a/src/analysis/retail/warlock/demonology/modules/pets/normalizers/PrepullPetNormalizer.ts +++ b/src/analysis/retail/warlock/demonology/modules/pets/normalizers/PrepullPetNormalizer.ts @@ -94,7 +94,7 @@ class PrepullPetNormalizer extends EventsNormalizer { ); continue; } - spell = SPELLS[PETS[petGUID].summonAbility]; + spell = PETS[petGUID].summonAbility; } const fabricatedEvent: SummonEvent = { target: { From 5b7dd28d5ea58d9f4f830b7f5897c3239362b574 Mon Sep 17 00:00:00 2001 From: Sharrq Date: Sat, 6 Jan 2024 10:34:42 -0500 Subject: [PATCH 44/46] Winters Chill Crash --- src/analysis/retail/mage/frost/CHANGELOG.tsx | 1 + src/analysis/retail/mage/frost/core/WintersChill.tsx | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/analysis/retail/mage/frost/CHANGELOG.tsx b/src/analysis/retail/mage/frost/CHANGELOG.tsx index 9f903593651..822422d04fb 100644 --- a/src/analysis/retail/mage/frost/CHANGELOG.tsx +++ b/src/analysis/retail/mage/frost/CHANGELOG.tsx @@ -6,6 +6,7 @@ import { Sharrq, ToppleTheNun } from 'CONTRIBUTORS'; // prettier-ignore export default [ + change(date(2024, 1, 6), <>Fixed a crash in ., Sharrq), change(date(2024, 1, 5), 'Updated spec support to full 10.2 support.', Sharrq), change(date(2024, 1, 5), <>Fixed the cooldowns for and ., Sharrq), change(date(2024, 1, 5), <>Added a statistic for the average delay to use . This is just informational., Sharrq), diff --git a/src/analysis/retail/mage/frost/core/WintersChill.tsx b/src/analysis/retail/mage/frost/core/WintersChill.tsx index c80bc10336e..202d52203f1 100644 --- a/src/analysis/retail/mage/frost/core/WintersChill.tsx +++ b/src/analysis/retail/mage/frost/core/WintersChill.tsx @@ -78,6 +78,9 @@ class WintersChill extends Analyzer { (d) => d.apply.timestamp <= event.timestamp && d.remove && d.remove.timestamp >= event.timestamp, ); + if (wintersChillDebuff === -1) { + return; + } this.wintersChill[wintersChillDebuff].damageEvents?.push(event); } From 345538a753dd78eda6a14a39469f3085b199b329 Mon Sep 17 00:00:00 2001 From: Richard Harrah Date: Sun, 7 Jan 2024 23:20:54 -0500 Subject: [PATCH 45/46] add checklist support for Fyr'alath --- .../inv_axe_2h_fyrakk_d_01_shadowflame.jpg | Bin 0 -> 2416 bytes src/CHANGELOG.tsx | 1 + src/common/ITEMS/dragonflight/others.ts | 5 ++++ src/common/SPELLS/dragonflight/others.ts | 20 +++++++++++++ src/interface/BAD_ICONS.ts | 1 + src/parser/core/CASTS_THAT_ARENT_CASTS.ts | 4 +++ src/parser/core/CombatLogParser.tsx | 2 ++ .../modules/items/dragonflight/Fyralath.tsx | 27 ++++++++++++++++++ 8 files changed, 60 insertions(+) create mode 100644 public/img/Icons/inv_axe_2h_fyrakk_d_01_shadowflame.jpg create mode 100644 src/parser/retail/modules/items/dragonflight/Fyralath.tsx diff --git a/public/img/Icons/inv_axe_2h_fyrakk_d_01_shadowflame.jpg b/public/img/Icons/inv_axe_2h_fyrakk_d_01_shadowflame.jpg new file mode 100644 index 0000000000000000000000000000000000000000..92248e265303e7b011b7d530d4c69955dc79f129 GIT binary patch literal 2416 zcmb7;c{J1u8^?dM8T%N6VHk!Pp&JsFogrc` zNkXP}nVC zhYwf+FbEV1g}`sn-B~Yg#6xWO8{UH1PZ{w0{`BCzz`@52?$6k!%;d!M=2Hj0gDU%snQ%+ZI;gV`_U? z0JY!s?0MVE6Ec|^o|TM=K#nD}qy9j2zg9dhH(OFZ-$8VN)36z zt(4yQ&Ri{~0H;b2v^Ae-mWM#pK0CfC^R$z$Hu$2YcO6$@9dvPPxzC5*+aOb94r)lKk!v=7h zjZ+Qur5aj7#bf0*Sa8TqvAeKgG24swExz=$(`V~bC93#}p=zU!3b*ld?|E*d4+mr3 zNH-hq^{|7r+3I*mpbfMP{J*B#UbZXbEo-uN^q@rDe3eyGks=R+}J$N)tWQr zQuN1Srm7w5wznL*H~M1F>gugd7Ya6XOrH?3bh~a@!kmKa;yY^q(sEbIRT3!#&mOk#n%9la8!;O> z|0UTcoGN{+)pu+osIp<8C5sd{?&`vm(ig6{bGJo~kaQ8EX8%jOh_ULrk(i6J{Alrx z?~$Nn6bDQ1DvX0C%3!bOr?J#rC40iYr^F8K01=UWaRF7q<$ALKY%+(L(+ju<^c+vp!%t&#Q z$c39+Pl@#BxP^|JP96Ny_iFWOL`C5v7yI;Om5{`CKZ<%ruknL z8J9YkUd1J!DHa}N3*KCd#r;YB)rF`JGj>}yOkL0NV@_G5=R$@JL|Pa{{Y2dp1spx@ z2NIhJGSQAFjDn)}8%~@St}HBR2sh7g&5S>hB$})NecXl3pZL+2tXAC_P`2sD8Djel z$0LN4cYxs|bN*XOwHp(C;>n&vYe{7TozVAza-Wb_O@%av<&9(|Zi83SV3u2W4 zV?)VelKu(%F%2La!hKGgF4AZIhHBM@h=*I2arTH;@T&TtD;U(9UoE54(j*iat>9!t|RmGnav5tBLuV;ngnJ zwhG(p`PeHT;!eKpY(=E>9z{-6MojcorU*VV%INnk;f$S#%fmeIQ9qK1SJAtqe;FQk z^2p7jU1lA%blAIs{xj4w(1na`jOzk}`N+9xPzcuM+-@f=x3FNfIF z&Y8=Q<5Yk_^dR*BN#wTWcBgjP>O&MoxVy;^Y5`K($qhE;Z$t@9@6&{Le6Kx`%o}C^<{T(*xHALf8acRfU zN{Y(_*|pWT_X@}IivOCNgjz8g%emf-i`Z_l`!PA}){00?Wiy)mn8GILec)VjP{mLc zkAD39{!W~`xaVK{Q&pWVO|HlhM49EfOH21KdM}@99zUg$n@{iD+VqO(7^u%sd>HR~ zMU7pu+UDH4y@#gHxivx*OUTMEf4csQ>JXJlkTgBI9KN>d@l_rhRjg+b-WI?j;$N%p zQO5mEx))^~*!7XIrI@YftyM$6Q9I=d5`qZT^0T^sT4x=Tgv&2U*5O?Jzk%DnZCXt^ z#67!s<;`0B{^w(OI>MIp%6r<~rpAdd checklist support for ., ToppleTheNun), change(date(2024, 1, 4), <>Add statistics for ., Arbixal), change(date(2024, 1, 2), 'Remove Shadowlands food and augment rune support.', ToppleTheNun), change(date(2023, 12, 30), 'Fix errors caused by ESLint update.', ToppleTheNun), diff --git a/src/common/ITEMS/dragonflight/others.ts b/src/common/ITEMS/dragonflight/others.ts index 0049c888df9..e6d55f41c49 100644 --- a/src/common/ITEMS/dragonflight/others.ts +++ b/src/common/ITEMS/dragonflight/others.ts @@ -76,6 +76,11 @@ const others = { name: 'Echoing Tyrstone', icon: 'ability_paladin_lightofthemartyr', }, + FYRALATH: { + id: 206448, + name: "Fyr'alath the Dreamrender", + icon: 'inv_axe_2h_fyrakk_d_01_shadowflame', + }, } satisfies Record; export default others; diff --git a/src/common/SPELLS/dragonflight/others.ts b/src/common/SPELLS/dragonflight/others.ts index 8b18bb3d982..de0abcc62c6 100644 --- a/src/common/SPELLS/dragonflight/others.ts +++ b/src/common/SPELLS/dragonflight/others.ts @@ -22,6 +22,26 @@ const others = { name: 'Disintegrate', icon: 'ability_evoker_disintegrate', }, + RAGE_OF_FYRALATH_1: { + id: 417131, + name: "Rage of Fyr'alath", + icon: 'inv_axe_2h_fyrakk_d_01_shadowflame', + }, + RAGE_OF_FYRALATH_2: { + id: 417132, + name: "Rage of Fyr'alath", + icon: 'inv_axe_2h_fyrakk_d_01_shadowflame', + }, + RAGE_OF_FYRALATH_DAMAGE_1: { + id: 417134, + name: "Rage of Fyr'alath", + icon: 'inv_axe_2h_fyrakk_d_01_shadowflame', + }, + RAGE_OF_FYRALATH_DAMAGE_2: { + id: 424094, + name: "Rage of Fyr'alath", + icon: 'inv_axe_2h_fyrakk_d_01_shadowflame', + }, } satisfies Record; export default others; diff --git a/src/interface/BAD_ICONS.ts b/src/interface/BAD_ICONS.ts index 0f13d023729..78b17446a0a 100644 --- a/src/interface/BAD_ICONS.ts +++ b/src/interface/BAD_ICONS.ts @@ -24,5 +24,6 @@ const BAD_ICONS = [ 'spell_priest_void_flay', 'spell_priest_shadow_mend', 'spell_priest_void_blast', + 'inv_axe_2h_fyrakk_d_01_shadowflame', ]; export default BAD_ICONS; diff --git a/src/parser/core/CASTS_THAT_ARENT_CASTS.ts b/src/parser/core/CASTS_THAT_ARENT_CASTS.ts index 3c6ec6bffb9..a7c2da413d2 100644 --- a/src/parser/core/CASTS_THAT_ARENT_CASTS.ts +++ b/src/parser/core/CASTS_THAT_ARENT_CASTS.ts @@ -27,6 +27,10 @@ const spells: number[] = [ SPELLS.RIONTHUS_DISINTEGRATE.id, // targeted player is shown as 'casting' this spell //endregion + //region Items + SPELLS.RAGE_OF_FYRALATH_2.id, // second cast logged by Fyr'alath + //endregion + //region Consumables //endregion diff --git a/src/parser/core/CombatLogParser.tsx b/src/parser/core/CombatLogParser.tsx index 45fa29345a7..90300b12343 100644 --- a/src/parser/core/CombatLogParser.tsx +++ b/src/parser/core/CombatLogParser.tsx @@ -107,6 +107,7 @@ import VoiceOfTheSilentStar from 'parser/retail/modules/items/dragonflight/Voice import AmalgamsSeventhSpine from 'parser/retail/modules/items/dragonflight/AmalgamsSeventhSpine'; import ElementalLariat from 'parser/retail/modules/items/dragonflight/ElementalLariat'; import EchoingTyrstone from 'parser/retail/modules/items/dragonflight/EchoingTyrstone'; +import Fyralath from 'parser/retail/modules/items/dragonflight/Fyralath'; // This prints to console anything that the DI has to do const debugDependencyInjection = false; @@ -227,6 +228,7 @@ class CombatLogParser { amalgamsSeventhSpine: AmalgamsSeventhSpine, elementalLariat: ElementalLariat, echoingTyrstone: EchoingTyrstone, + fyralath: Fyralath, // Enchants burningDevotion: BurningDevotion, diff --git a/src/parser/retail/modules/items/dragonflight/Fyralath.tsx b/src/parser/retail/modules/items/dragonflight/Fyralath.tsx new file mode 100644 index 00000000000..aa51474360a --- /dev/null +++ b/src/parser/retail/modules/items/dragonflight/Fyralath.tsx @@ -0,0 +1,27 @@ +import Analyzer, { Options } from 'parser/core/Analyzer'; +import ITEMS from 'common/ITEMS/dragonflight/others'; +import SPELLS from 'common/SPELLS/dragonflight/others'; +import Abilities from 'parser/core/modules/Abilities'; +import SPELL_CATEGORY from 'parser/core/SPELL_CATEGORY'; + +const deps = { + abilities: Abilities, +}; +export default class Fyralath extends Analyzer.withDependencies(deps) { + constructor(options: Options) { + super(options); + this.active = this.selectedCombatant.hasMainHand(ITEMS.FYRALATH.id); + if (this.active) { + this.deps.abilities.add({ + spell: SPELLS.RAGE_OF_FYRALATH_1.id, + category: SPELL_CATEGORY.COOLDOWNS, + cooldown: 120, + castEfficiency: { + suggestion: true, + recommendedEfficiency: 0.9, + }, + damageSpellIds: [SPELLS.RAGE_OF_FYRALATH_DAMAGE_1.id, SPELLS.RAGE_OF_FYRALATH_DAMAGE_2.id], + }); + } + } +} From 74c1b111dc9b00cae5f9e837b37ea83a6c8faa8c Mon Sep 17 00:00:00 2001 From: Richard Harrah Date: Sun, 7 Jan 2024 23:24:07 -0500 Subject: [PATCH 46/46] add Fyr'lath to Ret Paladin guide --- src/analysis/retail/paladin/retribution/CHANGELOG.tsx | 3 +++ src/analysis/retail/paladin/retribution/Guide.tsx | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/src/analysis/retail/paladin/retribution/CHANGELOG.tsx b/src/analysis/retail/paladin/retribution/CHANGELOG.tsx index b605eed25f8..94f24527d21 100644 --- a/src/analysis/retail/paladin/retribution/CHANGELOG.tsx +++ b/src/analysis/retail/paladin/retribution/CHANGELOG.tsx @@ -1,7 +1,10 @@ import { change, date } from 'common/changelog'; import { emallson, Klamuz, ToppleTheNun, xizbow } from 'CONTRIBUTORS'; +import { ItemLink } from 'interface'; +import ITEMS from 'common/ITEMS'; export default [ + change(date(2024, 1, 7), <>Add to cooldown graph., ToppleTheNun), change(date(2023, 7, 19), 'Make CooldownGraphSubsection generic enough that it can be used by other specs.', ToppleTheNun), change(date(2023, 7, 18), 'Show percentage wasted Holy Power instead of percentage at Holy Power cap.', ToppleTheNun), change(date(2023, 7, 16), 'First pass at a Guide for Ret Paladin.', ToppleTheNun), diff --git a/src/analysis/retail/paladin/retribution/Guide.tsx b/src/analysis/retail/paladin/retribution/Guide.tsx index a19e2c50e21..818fdbaf0db 100644 --- a/src/analysis/retail/paladin/retribution/Guide.tsx +++ b/src/analysis/retail/paladin/retribution/Guide.tsx @@ -8,6 +8,8 @@ import HideExplanationsToggle from 'interface/guide/components/HideExplanationsT import HideGoodCastsToggle from 'interface/guide/components/HideGoodCastsToggle'; import { QualitativePerformance } from 'parser/ui/QualitativePerformance'; import PerformancePercentage from 'analysis/retail/demonhunter/shared/guide/PerformancePercentage'; +import DRAGONFLIGHT_OTHERS_ITEMS from 'common/ITEMS/dragonflight/others'; +import DRAGONFLIGHT_OTHERS_SPELLS from 'common/SPELLS/dragonflight/others'; import TALENTS from 'common/TALENTS/paladin'; import SpellLink from 'interface/SpellLink'; import CooldownGraphSubsection, { @@ -117,6 +119,10 @@ const cooldowns: Cooldown[] = [ spell: TALENTS.DIVINE_TOLL_TALENT, isActive: (c) => c.hasTalent(TALENTS.DIVINE_TOLL_TALENT), }, + { + spell: DRAGONFLIGHT_OTHERS_SPELLS.RAGE_OF_FYRALATH_1, + isActive: (c) => c.hasMainHand(DRAGONFLIGHT_OTHERS_ITEMS.FYRALATH.id), + }, ]; function CooldownSection() { return (