Skip to content

Commit

Permalink
Merge pull request WoWAnalyzer#6593 from Sharrq/frost-event-links
Browse files Browse the repository at this point in the history
[Frost Mage] Event Links + Core Rewrite
  • Loading branch information
Sharrq authored Jan 6, 2024
2 parents 5394bf0 + f151849 commit 3dcd6b0
Show file tree
Hide file tree
Showing 18 changed files with 820 additions and 683 deletions.
7 changes: 7 additions & 0 deletions src/analysis/retail/mage/frost/CHANGELOG.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ 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 <SpellLink spell={TALENTS.RAY_OF_FROST_TALENT} /> and <SpellLink spell={TALENTS.ICY_VEINS_TALENT} />.</>, Sharrq),
change(date(2024, 1, 5), <>Added a statistic for the average delay to use <SpellLink spell={TALENTS.FINGERS_OF_FROST_TALENT} />. This is just informational.</>, Sharrq),
change(date(2024, 1, 5), <>Added tracking for <SpellLink spell={TALENTS.RAY_OF_FROST_TALENT} />.</>, Sharrq),
change(date(2024, 1, 5), <>Adjusted <SpellLink spell={SPELLS.WINTERS_CHILL} /> to change the spells that can be used to spend <SpellLink spell={SPELLS.WINTERS_CHILL} />.</>, Sharrq),
change(date(2024, 1, 5), <>Updated <SpellLink spell={SPELLS.WINTERS_CHILL} /> to ignore pre-casts at 4 <SpellLink spell={SPELLS.ICICLES_BUFF} />.</>, 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 <SpellLink spell={TALENTS.TEMPORAL_WARP_TALENT} /> to list of Bloodlust Buffs.</>, Sharrq),
Expand Down
2 changes: 1 addition & 1 deletion src/analysis/retail/mage/frost/CONFIG.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<Warning>` component.
description: (
<>
Expand Down
8 changes: 6 additions & 2 deletions src/analysis/retail/mage/frost/CombatLogParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -35,13 +35,15 @@ 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';
import ThermalVoid from './talents/ThermalVoid';

//Normalizers
import CometStormLinkNormalizer from './normalizers/CometStormLinkNormalizer';
import CastLinkNormalizer from './normalizers/CastLinkNormalizer';

class CombatLogParser extends CoreCombatLogParser {
static specModules = {
Expand All @@ -50,6 +52,7 @@ class CombatLogParser extends CoreCombatLogParser {

//Normalizers
cometStormLinkNormalizer: CometStormLinkNormalizer,
castLinkNormalizer: CastLinkNormalizer,

//Core
abilities: Abilities,
Expand All @@ -62,7 +65,7 @@ class CombatLogParser extends CoreCombatLogParser {
iceLance: IceLance,
icyVeins: IcyVeins,
arcaneIntellect: ArcaneIntellect,
munchedProcs: MunchedProcs,
fingersOfFrost: FingersOfFrost,

// Talents - Frost
boneChilling: BoneChilling,
Expand All @@ -72,6 +75,7 @@ class CombatLogParser extends CoreCombatLogParser {
thermalVoid: ThermalVoid,
glacialSpike: GlacialSpike,
cometStorm: CometStorm,
rayOfFrost: RayOfFrost,
icyPropulsion: IcyPropulsion,
coldFront: ColdFront,
mirrorImage: MirrorImage,
Expand Down
26 changes: 0 additions & 26 deletions src/analysis/retail/mage/frost/checklist/Component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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."
/>
</Rule>
<Rule
name="Use Glacial Spike properly"
description={
<>
When talented into <SpellLink spell={TALENTS.GLACIAL_SPIKE_TALENT} /> 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{' '}
<SpellLink spell={TALENTS.FLURRY_TALENT} /> alongside it (
<SpellLink spell={TALENTS.GLACIAL_SPIKE_TALENT} /> {'>'}{' '}
<SpellLink spell={TALENTS.FLURRY_TALENT} /> {'>'}
<SpellLink spell={TALENTS.ICE_LANCE_TALENT} />) or if you also have the{' '}
<SpellLink spell={TALENTS.SPLITTING_ICE_TALENT} /> and the Glacial Spike will hit a
second target. If neither of those are true, then you should continue casting{' '}
<SpellLink spell={SPELLS.FROSTBOLT} /> until <SpellLink spell={TALENTS.FLURRY_TALENT} />{' '}
is available or you get a <SpellLink spell={TALENTS.BRAIN_FREEZE_TALENT} /> proc.
</>
}
>
{combatant.hasTalent(TALENTS.GLACIAL_SPIKE_TALENT) && (
<Requirement
name="Glacial Spike utilization"
thresholds={thresholds.glacialSpikeUtilization}
/>
)}
</Rule>
<Rule
name="Use your talents effectively"
description="Regardless of which talents you select, you should ensure that you are utilizing them properly. If you are having trouble effectively using a particular talent, you should consider taking a different talent that you can utilize properly or focus on effectively using the talents that you have selected."
Expand Down
14 changes: 5 additions & 9 deletions src/analysis/retail/mage/frost/checklist/Module.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ 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 GlacialSpike from '../talents/GlacialSpike';
import ThermalVoid from '../talents/ThermalVoid';
import Component from './Component';

Expand All @@ -21,9 +20,8 @@ class Checklist extends BaseChecklist {
combatants: Combatants,
castEfficiency: CastEfficiency,
icyVeins: IcyVeins,
munchedProcs: MunchedProcs,
fingersOfFrost: FingersOfFrost,
brainFreeze: BrainFreeze,
glacialSpike: GlacialSpike,
iceLance: IceLance,
thermalVoid: ThermalVoid,
wintersChill: WintersChill,
Expand All @@ -36,9 +34,8 @@ class Checklist extends BaseChecklist {
protected combatants!: Combatants;
protected castEfficiency!: CastEfficiency;
protected icyVeins!: IcyVeins;
protected munchedProcs!: MunchedProcs;
protected fingersOfFrost!: FingersOfFrost;
protected brainFreeze!: BrainFreeze;
protected glacialSpike!: GlacialSpike;
protected iceLance!: IceLance;
protected thermalVoid!: ThermalVoid;
protected wintersChill!: WintersChill;
Expand All @@ -58,12 +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,
glacialSpikeUtilization: this.glacialSpike.glacialSpikeUtilizationThresholds,
fingersOfFrostUtilization: this.iceLance.fingersProcUtilizationThresholds,
fingersOfFrostUtilization: this.fingersOfFrost.fingersProcUtilizationThresholds,
iceLanceNotShattered: this.iceLance.nonShatteredIceLanceThresholds,
wintersChillShatter: this.wintersChill.wintersChillShatterThresholds,
wintersChillHardCasts: this.wintersChill.wintersChillPreCastThresholds,
Expand Down
4 changes: 2 additions & 2 deletions src/analysis/retail/mage/frost/core/Abilities.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
121 changes: 68 additions & 53 deletions src/analysis/retail/mage/frost/core/BrainFreeze.tsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,88 @@
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';
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,
DamageEvent,
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';
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;

overlappedFlurries = () => {
let casts = this.eventHistory.getEvents(EventType.Cast, {
spell: TALENTS.FLURRY_TALENT,
brainFreezeRefreshes = 0;
flurry: { timestamp: number; damage: DamageEvent | undefined; 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) {
const damage: DamageEvent | undefined = GetRelatedEvent(event, 'SpellDamage');
const enemy = damage && this.enemies.getEntity(damage);
this.flurry.push({
timestamp: event.timestamp,
damage: damage,
overlapped: enemy?.hasBuff(SPELLS.WINTERS_CHILL.id, event.timestamp - 10) || false,
});
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() {
Expand All @@ -79,7 +104,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,
Expand All @@ -104,7 +129,7 @@ class BrainFreeze extends Analyzer {

get overlappedFlurryThresholds() {
return {
actual: this.overlappedFlurries(),
actual: this.overlappedFlurries,
isGreaterThan: {
average: 0,
major: 3,
Expand All @@ -126,11 +151,7 @@ class BrainFreeze extends Analyzer {
</>,
)
.icon(TALENTS.BRAIN_FREEZE_TALENT.icon)
.actual(
<Trans id="mage.frost.suggestions.brainFreeze.overwritten">
{formatPercentage(actual)}% overwritten
</Trans>,
)
.actual(`${formatPercentage(actual)}% overwritten`)
.recommended(`Overwriting none is recommended`),
);
when(this.brainFreezeExpiredThresholds).addSuggestion((suggest, actual, recommended) =>
Expand All @@ -142,29 +163,23 @@ class BrainFreeze extends Analyzer {
</>,
)
.icon(TALENTS.BRAIN_FREEZE_TALENT.icon)
.actual(
<Trans id="mage.frost.suggestions.brainFreeze.expired">
{formatPercentage(actual)}% expired
</Trans>,
)
.actual(`${formatPercentage(actual)}% expired`)
.recommended(`Letting none expire is recommended`),
);
when(this.overlappedFlurryThresholds).addSuggestion((suggest, actual, recommended) =>
suggest(
<>
You cast <SpellLink spell={TALENTS.FLURRY_TALENT} /> and applied{' '}
<SpellLink spell={SPELLS.WINTERS_CHILL} /> while the target still had the{' '}
<SpellLink spell={SPELLS.WINTERS_CHILL} /> debuff on them {this.overlappedFlurries()}{' '}
times. Casting <SpellLink spell={TALENTS.FLURRY_TALENT} /> applies 2 stacks of{' '}
<SpellLink spell={SPELLS.WINTERS_CHILL} /> debuff on them {this.overlappedFlurries} times.
Casting <SpellLink spell={TALENTS.FLURRY_TALENT} /> applies 2 stacks of{' '}
<SpellLink spell={SPELLS.WINTERS_CHILL} /> to the target so you should always ensure you
are spending both stacks before you cast <SpellLink spell={TALENTS.FLURRY_TALENT} /> and
apply <SpellLink spell={SPELLS.WINTERS_CHILL} /> again.
</>,
)
.icon(TALENTS.FLURRY_TALENT.icon)
.actual(
<Trans id="mage.frost.suggestions.brainFreeze.casts">{formatNumber(actual)} casts</Trans>,
)
.actual(`${formatNumber(actual)} casts`)
.recommended(`Casting none is recommended`),
);
}
Expand All @@ -178,8 +193,8 @@ class BrainFreeze extends Analyzer {
<>
You got {this.totalProcs} total procs.
<ul>
<li>{this.totalProcs - this.expiredProcs - this.overwrittenProcs} used</li>
<li>{this.overwrittenProcs} overwritten</li>
<li>{this.totalProcs - this.expiredProcs - this.brainFreezeRefreshes} used</li>
<li>{this.brainFreezeRefreshes} overwritten</li>
<li>{this.expiredProcs} expired</li>
</ul>
</>
Expand Down
Loading

0 comments on commit 3dcd6b0

Please sign in to comment.