diff --git a/src/CHANGELOG.tsx b/src/CHANGELOG.tsx index fbf72f0320e..72bf9124ca1 100644 --- a/src/CHANGELOG.tsx +++ b/src/CHANGELOG.tsx @@ -26,14 +26,16 @@ import { Seriousnes, Arlie, LucasLevyOB, - dub + dub, + Zyer, } from 'CONTRIBUTORS'; import { ItemLink } from 'interface'; import SpellLink from 'interface/SpellLink'; // prettier-ignore export default [ - change(date(2024, 2, 29), 'Correct incorrect tertiary stat scaling above 25% raw and 19% character sheet rating', Putro), + change(date(2024, 3, 2), 'Correct incorrect tertiary stat scaling above 25% raw and 19% character sheet rating', Putro), + change(date(2024, 2, 26), <>Added checklist support for , , , , Zyer), change(date(2024, 2, 26), 'Switch icon source to the WCL CDN.', emallson), change(date(2024, 2, 18), <>Fix crash in QualitativePerformance, Trevor), change(date(2024, 2, 17), 'Add a missed realm for Classic.', Putro), diff --git a/src/common/ITEMS/dragonflight/others.ts b/src/common/ITEMS/dragonflight/others.ts index e6d55f41c49..f1e117cb111 100644 --- a/src/common/ITEMS/dragonflight/others.ts +++ b/src/common/ITEMS/dragonflight/others.ts @@ -81,6 +81,16 @@ const others = { name: "Fyr'alath the Dreamrender", icon: 'inv_axe_2h_fyrakk_d_01_shadowflame', }, + IRIDAL_THE_EARTHS_MASTER: { + id: 208321, + name: "Iridal, the Earth's Master", + icon: 'inv_staff_2h_dracthyr_c_01', + }, + DREAMBINDER_LOOM_OF_THE_GREAT_CYCLE: { + id: 208616, + name: 'Dreambinder, Loom of the Great Cycle', + icon: 'inv_staff_2h_dreamweaver_d_01', + }, } satisfies Record; export default others; diff --git a/src/common/ITEMS/dragonflight/trinkets.ts b/src/common/ITEMS/dragonflight/trinkets.ts index cbd27b8528b..d7a0b696af6 100644 --- a/src/common/ITEMS/dragonflight/trinkets.ts +++ b/src/common/ITEMS/dragonflight/trinkets.ts @@ -31,6 +31,16 @@ const trinkets = { name: 'Accelerating Sandglass', icon: 'ability_bossmagistrix_timewarp2', }, + NYMUES_UNRAVELING_SPINDLE: { + id: 208615, + name: "Nymue's Unraveling Spindle", + icon: 'inv_cloth_outdooremeralddream_d_01_buckle', + }, + BELORRELOS_THE_SUNCALLER: { + id: 207172, + name: "Belor'relos, the Suncaller", + icon: 'inv_wand_1h_firelandsraid_d_01', + }, } satisfies Record; export default trinkets; diff --git a/src/common/SPELLS/dragonflight/others.ts b/src/common/SPELLS/dragonflight/others.ts index de0abcc62c6..2ef5b88c9a9 100644 --- a/src/common/SPELLS/dragonflight/others.ts +++ b/src/common/SPELLS/dragonflight/others.ts @@ -42,6 +42,16 @@ const others = { name: "Rage of Fyr'alath", icon: 'inv_axe_2h_fyrakk_d_01_shadowflame', }, + IRIDAL_EXTINCTION_BLAST_DAMAGE: { + id: 419278, + name: 'Extinction Blast', + icon: 'inv_staff_2h_blackdragonoutdoor_d_01', + }, + DREAMBINDER_WEB_OF_DREAMS_DAMAGE: { + id: 427113, + name: 'Web of Dreams', + icon: 'inv_staff_2h_dreamweaver_d_01', + }, } satisfies Record; export default others; diff --git a/src/common/SPELLS/dragonflight/trinkets.ts b/src/common/SPELLS/dragonflight/trinkets.ts index 0633eaae868..4685d190f5a 100644 --- a/src/common/SPELLS/dragonflight/trinkets.ts +++ b/src/common/SPELLS/dragonflight/trinkets.ts @@ -86,6 +86,16 @@ const spells = { name: 'Echoing Tyrstone', icon: 'achievement_dungeon_ulduarraid_titan_01', }, + NYMUES_UNRAVELING_SPINDLE: { + id: 422956, + name: "Nymue's Unraveling Spindle", + icon: 'inv_cloth_outdooremeralddream_d_01_buckle', + }, + BELORRELOS_SOLAR_MAELSTROM: { + id: 422146, + name: 'Solar Maelstrom', + icon: 'inv_10_jewelcrafting_gem3primal_titan_cut_bronze', + }, } satisfies Record; export default spells; diff --git a/src/parser/core/CombatLogParser.tsx b/src/parser/core/CombatLogParser.tsx index 90300b12343..f30293c9113 100644 --- a/src/parser/core/CombatLogParser.tsx +++ b/src/parser/core/CombatLogParser.tsx @@ -108,6 +108,10 @@ import AmalgamsSeventhSpine from 'parser/retail/modules/items/dragonflight/Amalg 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'; +import Dreambinder from 'parser/retail/modules/items/dragonflight/Dreambinder'; +import Iridal from 'parser/retail/modules/items/dragonflight/Iridal'; +import BelorrelosTheSuncaller from 'parser/retail/modules/items/dragonflight/BelorrelosTheSuncaller'; +import NymuesUnravelingSpindle from 'parser/retail/modules/items/dragonflight/NymuesUnravelingSpindle'; // This prints to console anything that the DI has to do const debugDependencyInjection = false; @@ -229,6 +233,10 @@ class CombatLogParser { elementalLariat: ElementalLariat, echoingTyrstone: EchoingTyrstone, fyralath: Fyralath, + dreambinder: Dreambinder, + iridal: Iridal, + belorrelosTheSuncaller: BelorrelosTheSuncaller, + nymuesUnravelingSpindle: NymuesUnravelingSpindle, // Enchants burningDevotion: BurningDevotion, diff --git a/src/parser/retail/modules/items/dragonflight/BelorrelosTheSuncaller.tsx b/src/parser/retail/modules/items/dragonflight/BelorrelosTheSuncaller.tsx new file mode 100644 index 00000000000..540dad558d5 --- /dev/null +++ b/src/parser/retail/modules/items/dragonflight/BelorrelosTheSuncaller.tsx @@ -0,0 +1,31 @@ +import Analyzer, { Options } from 'parser/core/Analyzer'; +import SPELLS from 'common/SPELLS/dragonflight/trinkets'; +import Abilities from 'parser/core/modules/Abilities'; +import SPELL_CATEGORY from 'parser/core/SPELL_CATEGORY'; +import TRINKETS from 'common/ITEMS/dragonflight/trinkets'; + +const deps = { + abilities: Abilities, +}; + +export default class BelorrelosTheSuncaller extends Analyzer.withDependencies(deps) { + constructor(options: Options) { + super(options); + + this.active = this.selectedCombatant.hasTrinket(TRINKETS.BELORRELOS_THE_SUNCALLER.id); + if (!this.active) { + return; + } + + this.deps.abilities.add({ + spell: SPELLS.BELORRELOS_SOLAR_MAELSTROM.id, + category: SPELL_CATEGORY.COOLDOWNS, + cooldown: 120, + castEfficiency: { + suggestion: true, + recommendedEfficiency: 0.9, + }, + damageSpellIds: [SPELLS.BELORRELOS_SOLAR_MAELSTROM.id], + }); + } +} diff --git a/src/parser/retail/modules/items/dragonflight/Dreambinder.tsx b/src/parser/retail/modules/items/dragonflight/Dreambinder.tsx new file mode 100644 index 00000000000..f16589af40e --- /dev/null +++ b/src/parser/retail/modules/items/dragonflight/Dreambinder.tsx @@ -0,0 +1,31 @@ +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 Dreambinder extends Analyzer.withDependencies(deps) { + constructor(options: Options) { + super(options); + + this.active = this.selectedCombatant.hasMainHand(ITEMS.DREAMBINDER_LOOM_OF_THE_GREAT_CYCLE.id); + if (!this.active) { + return; + } + + this.deps.abilities.add({ + spell: SPELLS.DREAMBINDER_WEB_OF_DREAMS_DAMAGE.id, + category: SPELL_CATEGORY.COOLDOWNS, + cooldown: 120, + castEfficiency: { + suggestion: true, + recommendedEfficiency: 0.9, + }, + damageSpellIds: [SPELLS.DREAMBINDER_WEB_OF_DREAMS_DAMAGE.id], + }); + } +} diff --git a/src/parser/retail/modules/items/dragonflight/Iridal.tsx b/src/parser/retail/modules/items/dragonflight/Iridal.tsx new file mode 100644 index 00000000000..3faa22598fa --- /dev/null +++ b/src/parser/retail/modules/items/dragonflight/Iridal.tsx @@ -0,0 +1,121 @@ +import { Options, SELECTED_PLAYER } from 'parser/core/Analyzer'; +import ITEMS from 'common/ITEMS/dragonflight/others'; +// import SPELLS from 'common/SPELLS/dragonflight/others'; +import SPELLS from 'common/SPELLS'; +import Abilities from 'parser/core/modules/Abilities'; +import SPELL_CATEGORY from 'parser/core/SPELL_CATEGORY'; +import ExecuteHelper from 'parser/shared/modules/helpers/ExecuteHelper'; +import Events, { CastEvent, DamageEvent, FightEndEvent } from 'parser/core/Events'; +import SpellUsable from 'parser/shared/modules/SpellUsable'; + +const debug = false; + +const IRIDAL_EXECUTE_RANGE = 0.35; +const IRIDAL_BASE_CD = 180000; +const IRIDAL_INTERNAL_CD = 1000; +const IRIDAL_CD_REDUCTION = 1000; + +const FILTERED_SPELL_IDS = [ + SPELLS.IRIDAL_EXTINCTION_BLAST_DAMAGE.id, + SPELLS.NYMUES_UNRAVELING_SPINDLE.id, + // warlock spells + 388070, // inquisitor's gaze / fel barrage damage + SPELLS.DOOM_BRAND_DAMAGE.id, +]; + +// todo add cdr +class Iridal extends ExecuteHelper { + static executeSources = SELECTED_PLAYER; + static lowerThreshold = IRIDAL_EXECUTE_RANGE; + static countCooldownAsExecuteTime = true; + + static dependencies = { + ...ExecuteHelper.dependencies, + abilities: Abilities, + spellUsable: SpellUsable, + }; + protected abilities!: Abilities; + + protected maxCasts = 0; + protected cdrApplied = 0; + protected lastCDRApplied = -1000; + protected iridalCasts = 0; + + constructor(options: Options) { + super(options); + + this.active = this.selectedCombatant.hasMainHand(ITEMS.IRIDAL_THE_EARTHS_MASTER.id); + if (!this.active) { + return; + } + + this.addEventListener(Events.fightend, this.adjustMaxCasts); + + const ctor = this.constructor as typeof ExecuteHelper; + ctor.executeSpells.push(SPELLS.IRIDAL_EXTINCTION_BLAST_DAMAGE); + + (options.abilities as Abilities).add({ + spell: SPELLS.IRIDAL_EXTINCTION_BLAST_DAMAGE.id, + category: SPELL_CATEGORY.COOLDOWNS, + cooldown: 180, + castEfficiency: { + suggestion: true, + recommendedEfficiency: 0.9, + maxCasts: () => this.maxCasts, + }, + damageSpellIds: [SPELLS.IRIDAL_EXTINCTION_BLAST_DAMAGE.id], + }); + + this.addEventListener(Events.damage.by(SELECTED_PLAYER), this.onDamage); + this.addEventListener( + Events.cast.by(SELECTED_PLAYER).spell(SPELLS.IRIDAL_EXTINCTION_BLAST_DAMAGE), + this.onIridalCast, + ); + } + + adjustMaxCasts(event: FightEndEvent) { + super.onFightEnd(event); + this.maxCasts += Math.ceil( + this.totalExecuteDuration / (IRIDAL_BASE_CD - this.cdrApplied / this.iridalCasts), + ); + // this will underestimate the casts, fix statistic if too underestimated + this.maxCasts = Math.max(this.maxCasts, this.iridalCasts); + debug && this.debug('iridal cdr applied:', this.cdrApplied, 'max casts:', this.maxCasts); + } + + onIridalCast(event: CastEvent) { + this.iridalCasts += 1; + } + + onDamage(event: DamageEvent) { + if (this.spellUsable.isAvailable(SPELLS.IRIDAL_EXTINCTION_BLAST_DAMAGE.id)) { + return; + } + if (event.timestamp < this.lastCDRApplied + IRIDAL_INTERNAL_CD) { + return; + } + if (event.targetIsFriendly) { + return; + } + if (FILTERED_SPELL_IDS.includes(event.ability?.guid)) { + return; + } + if (IRIDAL_EXECUTE_RANGE < (event.hitPoints || 0) / (event.maxHitPoints || 1)) { + return; + } + + debug && + this.debug( + 'iridal cd reduced by', + event.ability.name, + event.ability.guid, + 'hp%', + (event.hitPoints || 0) / (event.maxHitPoints || 1), + ); + this.spellUsable.reduceCooldown(SPELLS.IRIDAL_EXTINCTION_BLAST_DAMAGE.id, IRIDAL_CD_REDUCTION); + this.cdrApplied += IRIDAL_CD_REDUCTION; + this.lastCDRApplied = event.timestamp; + } +} + +export default Iridal; diff --git a/src/parser/retail/modules/items/dragonflight/NymuesUnravelingSpindle.tsx b/src/parser/retail/modules/items/dragonflight/NymuesUnravelingSpindle.tsx new file mode 100644 index 00000000000..84d9dcc780d --- /dev/null +++ b/src/parser/retail/modules/items/dragonflight/NymuesUnravelingSpindle.tsx @@ -0,0 +1,31 @@ +import Analyzer, { Options } from 'parser/core/Analyzer'; +import SPELLS from 'common/SPELLS/dragonflight/trinkets'; +import Abilities from 'parser/core/modules/Abilities'; +import SPELL_CATEGORY from 'parser/core/SPELL_CATEGORY'; +import TRINKETS from 'common/ITEMS/dragonflight/trinkets'; + +const deps = { + abilities: Abilities, +}; + +export default class NymuesUnravelingSpindle extends Analyzer.withDependencies(deps) { + constructor(options: Options) { + super(options); + + this.active = this.selectedCombatant.hasTrinket(TRINKETS.NYMUES_UNRAVELING_SPINDLE.id); + if (!this.active) { + return; + } + + this.deps.abilities.add({ + spell: SPELLS.NYMUES_UNRAVELING_SPINDLE.id, + category: SPELL_CATEGORY.COOLDOWNS, + cooldown: 120, + castEfficiency: { + suggestion: true, + recommendedEfficiency: 0.9, + }, + damageSpellIds: [SPELLS.NYMUES_UNRAVELING_SPINDLE.id], + }); + } +}