diff --git a/src/analysis/classic/warrior/arms/modules/features/AlwaysBeCasting.ts b/src/analysis/classic/warrior/arms/modules/features/AlwaysBeCasting.ts index 0328a274a2b..6e6d0951d96 100644 --- a/src/analysis/classic/warrior/arms/modules/features/AlwaysBeCasting.ts +++ b/src/analysis/classic/warrior/arms/modules/features/AlwaysBeCasting.ts @@ -1,7 +1,29 @@ +import CLASSIC_SPELLS from 'common/SPELLS/classic'; import { ThresholdStyle, type NumberThreshold } from 'parser/core/ParseResults'; +import { AplChecker, build } from 'parser/shared/metrics/apl'; import CoreAlwaysBeCasting from 'parser/shared/modules/AlwaysBeCasting'; +import SpellUsable from 'parser/shared/modules/SpellUsable'; +import * as cnd from 'parser/shared/metrics/apl/conditions'; +import RESOURCE_TYPES from 'game/RESOURCE_TYPES'; +import { Options } from 'parser/core/Analyzer'; +import Events, { AnyEvent, FightEndEvent } from 'parser/core/Events'; + +export default class AlwaysBeCasting extends CoreAlwaysBeCasting.withDependencies({ + spellUsable: SpellUsable, +}) { + private aplChecker; + + private lastUptimeStart?: number; + + constructor(options: Options) { + super(options); + + this.aplChecker = new AplChecker(apl, this.owner.info); + + this.addEventListener(Events.any, this.processAplDowntime); + this.addEventListener(Events.fightend, this.finalize); + } -export default class AlwaysBeCasting extends CoreAlwaysBeCasting { get downtimeSuggestionThresholds(): NumberThreshold { return { actual: this.downtimePercentage, @@ -13,4 +35,60 @@ export default class AlwaysBeCasting extends CoreAlwaysBeCasting { style: ThresholdStyle.PERCENTAGE, }; } + + private processAplDowntime(event: AnyEvent) { + // the idea here is that we add "uptime" from the point where the APL says "don't cast anything" to the next point where the APL says "cast something" + const expectedCast = this.aplChecker.expectedCast(); + + this.aplChecker.processEvent(event); + + if (expectedCast && this.lastUptimeStart && event.timestamp >= this.owner.fight.start_time) { + // apl: "cast something" + if (this.lastUptimeStart !== event.timestamp) { + this.addNewUptime(this.lastUptimeStart, event.timestamp, false, 'Arms APL-based downtime'); + } + this.lastUptimeStart = undefined; + } else if (!expectedCast && !this.lastUptimeStart) { + // apl: "don't cast anything" + this.lastUptimeStart = event.timestamp; + } + } + + private finalize(event: FightEndEvent) { + if (this.lastUptimeStart) { + this.addNewUptime( + this.lastUptimeStart, + event.timestamp, + false, + 'Arms APL-based downtime (finalizer)', + ); + } + } } + +const apl = build([ + { + spell: CLASSIC_SPELLS.REND, + condition: cnd.debuffMissing(CLASSIC_SPELLS.REND_DEBUFF), + }, + { + spell: CLASSIC_SPELLS.COLOSSUS_SMASH, + condition: cnd.debuffMissing(CLASSIC_SPELLS.COLOSSUS_SMASH), + }, + CLASSIC_SPELLS.MORTAL_STRIKE, + { + spell: CLASSIC_SPELLS.EXECUTE, + condition: cnd.inExecute(), + }, + { + spell: CLASSIC_SPELLS.OVERPOWER, + condition: cnd.buffPresent(CLASSIC_SPELLS.TASTE_FOR_BLOOD), + }, + { + spell: CLASSIC_SPELLS.HEROIC_STRIKE, + condition: cnd.or( + cnd.buffPresent(CLASSIC_SPELLS.BATTLE_TRANCE), + cnd.hasResource(RESOURCE_TYPES.RAGE, { atLeast: 800 }, 0), + ), + }, +]); diff --git a/src/common/SPELLS/classic/warrior.ts b/src/common/SPELLS/classic/warrior.ts index 9931985aeab..b74ec2b97ae 100644 --- a/src/common/SPELLS/classic/warrior.ts +++ b/src/common/SPELLS/classic/warrior.ts @@ -115,6 +115,11 @@ const spells = { name: 'Taste for Blood', icon: 'ability_rogue_hungerforblood.jpg.jpg', }, + BATTLE_TRANCE: { + id: 12964, + name: 'Battle Trance', + icon: 'inv_helmet_06', + }, PUMMEL: { id: 6552, name: 'Pummel', @@ -130,6 +135,11 @@ const spells = { name: 'Rend', icon: 'ability_gouge.jpg', }, + REND_DEBUFF: { + id: 94009, + name: 'Rend', + icon: 'ability_gouge.jpg', + }, RETALIATION: { id: 20230, name: 'Retaliation', diff --git a/src/parser/shared/modules/AlwaysBeCasting.tsx b/src/parser/shared/modules/AlwaysBeCasting.tsx index e7a18b73158..dcbae87f734 100644 --- a/src/parser/shared/modules/AlwaysBeCasting.tsx +++ b/src/parser/shared/modules/AlwaysBeCasting.tsx @@ -100,7 +100,7 @@ class AlwaysBeCasting extends Analyzer { } /** Validates and logs inputs, then adds to activeTimeEdges list */ - private addNewUptime(start: number, end: number, isHealingAbility: boolean, reason: string) { + protected addNewUptime(start: number, end: number, isHealingAbility: boolean, reason: string) { DEBUG && console.log( `Active Time: adding from ${reason}: ${this.owner.formatTimestamp(start, 3)} to ${this.owner.formatTimestamp(end, 3)}${isHealingAbility ? ' (heal)' : ''}`,