diff --git a/ui/balance_druid/sim.ts b/ui/balance_druid/sim.ts index 075217e95f..069cced2c8 100644 --- a/ui/balance_druid/sim.ts +++ b/ui/balance_druid/sim.ts @@ -1,13 +1,11 @@ import { Spec } from '../core/proto/common.js'; import { Stat } from '../core/proto/common.js'; import { - APLAction, - APLListItem, APLRotation, } from '../core/proto/apl.js'; import { Stats } from '../core/proto_utils/stats.js'; import { Player } from '../core/player.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; import * as IconInputs from '../core/components/icon_inputs.js'; import * as OtherInputs from '../core/components/other_inputs.js'; @@ -15,130 +13,131 @@ import * as OtherInputs from '../core/components/other_inputs.js'; import * as DruidInputs from './inputs.js'; import * as Presets from './presets.js'; -// noinspection TypeScriptValidateTypes -export class BalanceDruidSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'balance-druid-sim-ui', - cssScheme: 'druid', - // List any known bugs / issues here, and they'll be shown on the site. - knownIssues: [ - ], +const SPEC_CONFIG = registerSpecConfig(Spec.SpecBalanceDruid, { + cssClass: 'balance-druid-sim-ui', + cssScheme: 'druid', + // List any known bugs / issues here, and they'll be shown on the site. + knownIssues: [ + ], + + // All stats for which EP should be calculated. + epStats: [ + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatSpellPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatStamina, + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], - // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatSpellPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatStamina, - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], + defaults: { + // Default equipped gear. + gear: Presets.P3_PRESET_HORDE.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatIntellect]: 0.43, + [Stat.StatSpirit]: 0.34, + [Stat.StatSpellPower]: 1, + [Stat.StatSpellCrit]: 0.82, + [Stat.StatSpellHaste]: 0.80, + [Stat.StatMP5]: 0.00, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultRotation, + // Default talents. + talents: Presets.Phase3Talents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + // Default raid/party buffs settings. + raidBuffs: Presets.DefaultRaidBuffs, + partyBuffs: Presets.DefaultPartyBuffs, + individualBuffs: Presets.DefaultIndividualBuffs, + debuffs: Presets.DefaultDebuffs, + other: Presets.OtherDefaults, + }, - defaults: { - // Default equipped gear. - gear: Presets.P3_PRESET_HORDE.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatIntellect]: 0.43, - [Stat.StatSpirit]: 0.34, - [Stat.StatSpellPower]: 1, - [Stat.StatSpellCrit]: 0.82, - [Stat.StatSpellHaste]: 0.80, - [Stat.StatMP5]: 0.00, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultRotation, - // Default talents. - talents: Presets.Phase3Talents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - // Default raid/party buffs settings. - raidBuffs: Presets.DefaultRaidBuffs, - partyBuffs: Presets.DefaultPartyBuffs, - individualBuffs: Presets.DefaultIndividualBuffs, - debuffs: Presets.DefaultDebuffs, - other: Presets.OtherDefaults, - }, + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + DruidInputs.SelfInnervate, + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: DruidInputs.BalanceDruidRotationConfig, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + IconInputs.MeleeHasteBuff, + IconInputs.MeleeCritBuff, + IconInputs.AttackPowerPercentBuff, + IconInputs.AttackPowerBuff, + IconInputs.MajorArmorDebuff, + IconInputs.MinorArmorDebuff, + IconInputs.PhysicalDamageDebuff, + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + DruidInputs.OkfUptime, + OtherInputs.TankAssignment, + OtherInputs.ReactionTime, + OtherInputs.DistanceFromTarget, + OtherInputs.nibelungAverageCasts, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - DruidInputs.SelfInnervate, - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: DruidInputs.BalanceDruidRotationConfig, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - IconInputs.MeleeHasteBuff, - IconInputs.MeleeCritBuff, - IconInputs.AttackPowerPercentBuff, - IconInputs.AttackPowerBuff, - IconInputs.MajorArmorDebuff, - IconInputs.MinorArmorDebuff, - IconInputs.PhysicalDamageDebuff, - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - DruidInputs.OkfUptime, - OtherInputs.TankAssignment, - OtherInputs.ReactionTime, - OtherInputs.DistanceFromTarget, - OtherInputs.nibelungAverageCasts, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.Phase1Talents, + Presets.Phase2Talents, + Presets.Phase3Talents, + Presets.Phase4Talents, + ], + rotations: [ + Presets.ROTATION_PRESET_P3_APL, + Presets.ROTATION_PRESET_P4_FOCUS_APL, + Presets.ROTATION_PRESET_P4_STARFIRE_APL, + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.PRERAID_PRESET, + Presets.P1_PRESET, + Presets.P2_PRESET, + Presets.P3_PRESET_HORDE, + Presets.P3_PRESET_ALLI, + Presets.P4_PRESET_HORDE, + Presets.P4_PRESET_ALLI, + ], + }, - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.Phase1Talents, - Presets.Phase2Talents, - Presets.Phase3Talents, - Presets.Phase4Talents, - ], - rotations: [ - Presets.ROTATION_PRESET_P3_APL, - Presets.ROTATION_PRESET_P4_FOCUS_APL, - Presets.ROTATION_PRESET_P4_STARFIRE_APL, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.PRERAID_PRESET, - Presets.P1_PRESET, - Presets.P2_PRESET, - Presets.P3_PRESET_HORDE, - Presets.P3_PRESET_ALLI, - Presets.P4_PRESET_HORDE, - Presets.P4_PRESET_ALLI, - ], - }, + autoRotation: (_player: Player): APLRotation => { + return Presets.ROTATION_PRESET_P3_APL.rotation.rotation!; + }, +}); - autoRotation: (player: Player): APLRotation => { - return Presets.ROTATION_PRESET_P3_APL.rotation.rotation!; - }, - }); +export class BalanceDruidSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/core/individual_sim_ui.ts b/ui/core/individual_sim_ui.ts index 648d4c06fd..059b8b4e1f 100644 --- a/ui/core/individual_sim_ui.ts +++ b/ui/core/individual_sim_ui.ts @@ -1,5 +1,5 @@ import { aplLaunchStatuses, LaunchStatus, simLaunchStatuses } from './launched_sims'; -import { Player, AutoRotationGenerator, SimpleRotationGenerator } from './player'; +import { Player, PlayerConfig, registerSpecConfig as registerPlayerConfig } from './player'; import { SimUI, SimWarning } from './sim_ui'; import { EventID, TypedEvent } from './typed_event'; @@ -88,7 +88,7 @@ export interface OtherDefaults { nibelungAverageCasts?: number, } -export interface IndividualSimUIConfig { +export interface IndividualSimUIConfig extends PlayerConfig { // Additional css class to add to the root element. cssClass: string, // Used to generate schemed components. E.g. 'shaman', 'druid', 'raid' @@ -137,11 +137,13 @@ export interface IndividualSimUIConfig { presets: { gear: Array, talents: Array, SavedTalents>>, - rotations?: Array, + rotations: Array, }, +} - autoRotation: AutoRotationGenerator, - simpleRotation?: SimpleRotationGenerator, +export function registerSpecConfig(spec: SpecType, config: IndividualSimUIConfig): IndividualSimUIConfig { + registerPlayerConfig(spec, config); + return config; } export interface Settings { @@ -184,11 +186,6 @@ export abstract class IndividualSimUI extends SimUI { this.prevEpIterations = 0; this.prevEpSimResult = null; - player.setAutoRotationGenerator(config.autoRotation); - if (aplLaunchStatuses[player.spec] == LaunchStatus.Launched && config.simpleRotation) { - player.setSimpleRotationGenerator(config.simpleRotation); - } - this.addWarning({ updateOn: this.player.gearChangeEmitter, getContent: () => { diff --git a/ui/core/player.ts b/ui/core/player.ts index 9e5a870a81..92adc23b0e 100644 --- a/ui/core/player.ts +++ b/ui/core/player.ts @@ -209,6 +209,17 @@ export interface MeleeCritCapInfo { export type AutoRotationGenerator = (player: Player) => APLRotation; export type SimpleRotationGenerator = (player: Player, simpleRotation: SpecRotation, cooldowns: Cooldowns) => APLRotation; +export interface PlayerConfig { + autoRotation: AutoRotationGenerator, + simpleRotation?: SimpleRotationGenerator, +} + +const SPEC_CONFIGS: Partial>> = {}; + +export function registerSpecConfig(spec: Spec, config: PlayerConfig) { + SPEC_CONFIGS[spec] = config; +} + // Manages all the gear / consumes / other settings for a single Player. export class Player { readonly sim: Sim; @@ -241,8 +252,8 @@ export class Player { private healingModel: HealingModel = HealingModel.create(); private healingEnabled: boolean = false; - private autoRotationGenerator: AutoRotationGenerator | null = null; - private simpleRotationGenerator: SimpleRotationGenerator | null = null; + private readonly autoRotationGenerator: AutoRotationGenerator | null = null; + private readonly simpleRotationGenerator: SimpleRotationGenerator | null = null; private itemEPCache = new Array>(); private gemEPCache = new Map(); @@ -294,6 +305,17 @@ export class Player { this.rotation = this.specTypeFunctions.rotationCreate(); this.specOptions = this.specTypeFunctions.optionsCreate(); + const specConfig = SPEC_CONFIGS[this.spec] as PlayerConfig; + if (!specConfig) { + throw new Error('Could not find spec config for spec: ' + this.spec); + } + this.autoRotationGenerator = specConfig.autoRotation; + if (aplLaunchStatuses[this.spec] == LaunchStatus.Launched && specConfig.simpleRotation) { + this.simpleRotationGenerator = specConfig.simpleRotation; + } else { + this.simpleRotationGenerator = null; + } + for(let i = 0; i < ItemSlot.ItemSlotRanged+1; ++i) { this.itemEPCache[i] = new Map(); } @@ -789,14 +811,6 @@ export class Player { } } - setAutoRotationGenerator(generator: AutoRotationGenerator) { - this.autoRotationGenerator = generator; - } - - setSimpleRotationGenerator(generator: SimpleRotationGenerator) { - this.simpleRotationGenerator = generator; - } - hasSimpleRotationGenerator(): boolean { return this.simpleRotationGenerator != null; } diff --git a/ui/deathknight/sim.ts b/ui/deathknight/sim.ts index 2a4e28efad..0a46dbd2ac 100644 --- a/ui/deathknight/sim.ts +++ b/ui/deathknight/sim.ts @@ -9,9 +9,7 @@ import { Stat, PseudoStat } from '../core/proto/common.js'; import { TristateEffect } from '../core/proto/common.js' import { Player } from '../core/player.js'; import { Stats } from '../core/proto_utils/stats.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; - -import { Deathknight, Deathknight_Rotation as DeathKnightRotation, DeathknightTalents as DeathKnightTalents, Deathknight_Options as DeathKnightOptions } from '../core/proto/deathknight.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; import * as IconInputs from '../core/components/icon_inputs.js'; import * as OtherInputs from '../core/components/other_inputs.js'; @@ -19,235 +17,237 @@ import * as OtherInputs from '../core/components/other_inputs.js'; import * as DeathKnightInputs from './inputs.js'; import * as Presets from './presets.js'; -export class DeathknightSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'deathknight-sim-ui', - cssScheme: 'death-knight', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], +const SPEC_CONFIG = registerSpecConfig(Spec.SpecDeathknight, { + cssClass: 'deathknight-sim-ui', + cssScheme: 'death-knight', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + ], - // All stats for which EP should be calculated. - epStats: [ - Stat.StatStrength, - Stat.StatArmor, - Stat.StatAgility, - Stat.StatAttackPower, - Stat.StatExpertise, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatArmorPenetration, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - PseudoStat.PseudoStatOffHandDps, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatAttackPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatArmor, - Stat.StatStrength, - Stat.StatAgility, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatAttackPower, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatArmorPenetration, - Stat.StatExpertise, - ], - defaults: { - // Default equipped gear. - gear: Presets.P2_UNHOLY_DW_PRESET.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatStrength]: 3.22, - [Stat.StatAgility]: 0.62, - [Stat.StatArmor]: 0.01, - [Stat.StatAttackPower]: 1, - [Stat.StatExpertise]: 1.13, - [Stat.StatMeleeHaste]: 1.85, - [Stat.StatMeleeHit]: 1.92, - [Stat.StatMeleeCrit]: 0.76, - [Stat.StatArmorPenetration]: 0.77, - [Stat.StatSpellHit]: 0.80, - [Stat.StatSpellCrit]: 0.34, - }, { - [PseudoStat.PseudoStatMainHandDps]: 3.10, - [PseudoStat.PseudoStatOffHandDps]: 1.79, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultUnholyRotation, - // Default talents. - talents: Presets.UnholyDualWieldTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultUnholyOptions, - // Default raid/party buffs settings. - raidBuffs: RaidBuffs.create({ - giftOfTheWild: TristateEffect.TristateEffectImproved, - swiftRetribution: true, - strengthOfEarthTotem: TristateEffect.TristateEffectImproved, - icyTalons: true, - abominationsMight: true, - leaderOfThePack: TristateEffect.TristateEffectRegular, - sanctifiedRetribution: true, - bloodlust: true, - devotionAura: TristateEffect.TristateEffectImproved, - stoneskinTotem: TristateEffect.TristateEffectImproved, - moonkinAura: TristateEffect.TristateEffectRegular, - wrathOfAirTotem: true, - powerWordFortitude: TristateEffect.TristateEffectImproved, - }), - partyBuffs: PartyBuffs.create({ - heroicPresence: false, - }), - individualBuffs: IndividualBuffs.create({ - blessingOfKings: true, - blessingOfMight: TristateEffect.TristateEffectImproved, - }), - debuffs: Debuffs.create({ - bloodFrenzy: true, - faerieFire: TristateEffect.TristateEffectImproved, - sunderArmor: true, - ebonPlaguebringer: true, - mangle: true, - heartOfTheCrusader: true, - shadowMastery: true, - }), - }, + // All stats for which EP should be calculated. + epStats: [ + Stat.StatStrength, + Stat.StatArmor, + Stat.StatAgility, + Stat.StatAttackPower, + Stat.StatExpertise, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatArmorPenetration, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + ], + epPseudoStats: [ + PseudoStat.PseudoStatMainHandDps, + PseudoStat.PseudoStatOffHandDps, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatAttackPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatArmor, + Stat.StatStrength, + Stat.StatAgility, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatAttackPower, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatArmorPenetration, + Stat.StatExpertise, + ], + defaults: { + // Default equipped gear. + gear: Presets.P2_UNHOLY_DW_PRESET.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatStrength]: 3.22, + [Stat.StatAgility]: 0.62, + [Stat.StatArmor]: 0.01, + [Stat.StatAttackPower]: 1, + [Stat.StatExpertise]: 1.13, + [Stat.StatMeleeHaste]: 1.85, + [Stat.StatMeleeHit]: 1.92, + [Stat.StatMeleeCrit]: 0.76, + [Stat.StatArmorPenetration]: 0.77, + [Stat.StatSpellHit]: 0.80, + [Stat.StatSpellCrit]: 0.34, + }, { + [PseudoStat.PseudoStatMainHandDps]: 3.10, + [PseudoStat.PseudoStatOffHandDps]: 1.79, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultUnholyRotation, + // Default talents. + talents: Presets.UnholyDualWieldTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultUnholyOptions, + // Default raid/party buffs settings. + raidBuffs: RaidBuffs.create({ + giftOfTheWild: TristateEffect.TristateEffectImproved, + swiftRetribution: true, + strengthOfEarthTotem: TristateEffect.TristateEffectImproved, + icyTalons: true, + abominationsMight: true, + leaderOfThePack: TristateEffect.TristateEffectRegular, + sanctifiedRetribution: true, + bloodlust: true, + devotionAura: TristateEffect.TristateEffectImproved, + stoneskinTotem: TristateEffect.TristateEffectImproved, + moonkinAura: TristateEffect.TristateEffectRegular, + wrathOfAirTotem: true, + powerWordFortitude: TristateEffect.TristateEffectImproved, + }), + partyBuffs: PartyBuffs.create({ + heroicPresence: false, + }), + individualBuffs: IndividualBuffs.create({ + blessingOfKings: true, + blessingOfMight: TristateEffect.TristateEffectImproved, + }), + debuffs: Debuffs.create({ + bloodFrenzy: true, + faerieFire: TristateEffect.TristateEffectImproved, + sunderArmor: true, + ebonPlaguebringer: true, + mangle: true, + heartOfTheCrusader: true, + shadowMastery: true, + }), + }, - autoRotation: (player: Player): APLRotation => { - const talentTree = player.getTalentTree(); - const numTargets = player.sim.encounter.targets.length; - switch (talentTree) { - case 0: - if (player.getSpecOptions().drwPestiApply || numTargets > 1) { - if (numTargets > 5) { - return Presets.BLOOD_PESTI_AOE_ROTATION_PRESET_DEFAULT.rotation.rotation!; - } else { - return Presets.BLOOD_DPS_ROTATION_PRESET_DEFAULT.rotation.rotation!; - } - } else { - return Presets.BLOOD_DPS_ROTATION_PRESET_DEFAULT.rotation.rotation!; - } - case 1: - const talentPoints = player.getTalentTreePoints() - // TODO: Add Frost AOE rotation - if (talentPoints[0] > talentPoints[2]) { - return Presets.FROST_BL_PESTI_ROTATION_PRESET_DEFAULT.rotation.rotation!; - } else { - return Presets.FROST_UH_PESTI_ROTATION_PRESET_DEFAULT.rotation.rotation!; - } - default: - if (numTargets > 1) { - return Presets.UNHOLY_DND_AOE_ROTATION_PRESET_DEFAULT.rotation.rotation!; - } else { - if (player.getEquippedItem(ItemSlot.ItemSlotMainHand)!.item.handType == HandType.HandTypeTwoHand) { - return Presets.UNHOLY_2H_ROTATION_PRESET_DEFAULT.rotation.rotation!; - } else { - return Presets.UNHOLY_DW_ROTATION_PRESET_DEFAULT.rotation.rotation!; - } - } + autoRotation: (player: Player): APLRotation => { + const talentTree = player.getTalentTree(); + const numTargets = player.sim.encounter.targets.length; + switch (talentTree) { + case 0: + if (player.getSpecOptions().drwPestiApply || numTargets > 1) { + if (numTargets > 5) { + return Presets.BLOOD_PESTI_AOE_ROTATION_PRESET_DEFAULT.rotation.rotation!; + } else { + return Presets.BLOOD_DPS_ROTATION_PRESET_DEFAULT.rotation.rotation!; + } + } else { + return Presets.BLOOD_DPS_ROTATION_PRESET_DEFAULT.rotation.rotation!; + } + case 1: + const talentPoints = player.getTalentTreePoints() + // TODO: Add Frost AOE rotation + if (talentPoints[0] > talentPoints[2]) { + return Presets.FROST_BL_PESTI_ROTATION_PRESET_DEFAULT.rotation.rotation!; + } else { + return Presets.FROST_UH_PESTI_ROTATION_PRESET_DEFAULT.rotation.rotation!; } - }, + default: + if (numTargets > 1) { + return Presets.UNHOLY_DND_AOE_ROTATION_PRESET_DEFAULT.rotation.rotation!; + } else { + if (player.getEquippedItem(ItemSlot.ItemSlotMainHand)!.item.handType == HandType.HandTypeTwoHand) { + return Presets.UNHOLY_2H_ROTATION_PRESET_DEFAULT.rotation.rotation!; + } else { + return Presets.UNHOLY_DW_ROTATION_PRESET_DEFAULT.rotation.rotation!; + } + } + } + }, + + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: DeathKnightInputs.DeathKnightRotationConfig, + petConsumeInputs: [ + IconInputs.SpicedMammothTreats, + ], + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + IconInputs.SpellDamageDebuff, + IconInputs.StaminaBuff, + ], + excludeBuffDebuffInputs: [ + IconInputs.AttackPowerDebuff, + IconInputs.DamageReductionPercentBuff, + IconInputs.MeleeAttackSpeedDebuff, + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + DeathKnightInputs.DiseaseDowntime, + DeathKnightInputs.DrwPestiApply, + DeathKnightInputs.SelfUnholyFrenzy, + DeathKnightInputs.StartingRunicPower, + DeathKnightInputs.PetUptime, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: DeathKnightInputs.DeathKnightRotationConfig, - petConsumeInputs: [ - IconInputs.SpicedMammothTreats, - ], - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - IconInputs.SpellDamageDebuff, - IconInputs.StaminaBuff, - ], - excludeBuffDebuffInputs: [ - IconInputs.AttackPowerDebuff, - IconInputs.DamageReductionPercentBuff, - IconInputs.MeleeAttackSpeedDebuff, - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - DeathKnightInputs.DiseaseDowntime, - DeathKnightInputs.DrwPestiApply, - DeathKnightInputs.SelfUnholyFrenzy, - DeathKnightInputs.StartingRunicPower, - DeathKnightInputs.PetUptime, + DeathKnightInputs.PrecastGhoulFrenzy, + DeathKnightInputs.PrecastHornOfWinter, - DeathKnightInputs.PrecastGhoulFrenzy, - DeathKnightInputs.PrecastHornOfWinter, + OtherInputs.TankAssignment, + OtherInputs.InFrontOfTarget, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, - OtherInputs.TankAssignment, - OtherInputs.InFrontOfTarget, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.BloodTalents, + Presets.FrostTalents, + Presets.FrostUnholyTalents, + Presets.UnholyDualWieldTalents, + Presets.UnholyDualWieldSSTalents, + Presets.Unholy2HTalents, + Presets.UnholyAoeTalents, + ], + // Preset rotations that the user can quickly select. + rotations: [ + Presets.BLOOD_ROTATION_PRESET_LEGACY_DEFAULT, + Presets.FROST_ROTATION_PRESET_LEGACY_DEFAULT, + Presets.UNHOLY_DW_ROTATION_PRESET_LEGACY_DEFAULT, + Presets.BLOOD_DPS_ROTATION_PRESET_DEFAULT, + Presets.BLOOD_PESTI_AOE_ROTATION_PRESET_DEFAULT, + Presets.FROST_BL_PESTI_ROTATION_PRESET_DEFAULT, + Presets.FROST_UH_PESTI_ROTATION_PRESET_DEFAULT, + Presets.UNHOLY_DW_ROTATION_PRESET_DEFAULT, + Presets.UNHOLY_2H_ROTATION_PRESET_DEFAULT, + Presets.UNHOLY_DND_AOE_ROTATION_PRESET_DEFAULT, + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.P1_BLOOD_PRESET, + Presets.P2_BLOOD_PRESET, + Presets.P3_BLOOD_PRESET, + Presets.P4_BLOOD_PRESET, + Presets.P1_FROST_PRESET, + Presets.P2_FROST_PRESET, + Presets.P3_FROST_PRESET, + Presets.P4_FROST_PRESET, + Presets.P1_UNHOLY_DW_PRESET, + Presets.P2_UNHOLY_DW_PRESET, + Presets.P3_UNHOLY_DW_PRESET, + Presets.P4_UNHOLY_DW_PRESET, + Presets.P4_UNHOLY_2H_PRESET, + // Not needed anymore just filling ui Space + // Disabled on purpose + //Presets.P1_FROSTSUBUNH_PRESET, + //Presets.P1_FROST_PRE_BIS_PRESET, + //Presets.PRERAID_UNHOLY_DW_PRESET, + //Presets.PRERAID_UNHOLY_2H_PRESET, + //Presets.P1_UNHOLY_2H_PRESET, + ], + }, +}); - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.BloodTalents, - Presets.FrostTalents, - Presets.FrostUnholyTalents, - Presets.UnholyDualWieldTalents, - Presets.UnholyDualWieldSSTalents, - Presets.Unholy2HTalents, - Presets.UnholyAoeTalents, - ], - // Preset rotations that the user can quickly select. - rotations: [ - Presets.BLOOD_ROTATION_PRESET_LEGACY_DEFAULT, - Presets.FROST_ROTATION_PRESET_LEGACY_DEFAULT, - Presets.UNHOLY_DW_ROTATION_PRESET_LEGACY_DEFAULT, - Presets.BLOOD_DPS_ROTATION_PRESET_DEFAULT, - Presets.BLOOD_PESTI_AOE_ROTATION_PRESET_DEFAULT, - Presets.FROST_BL_PESTI_ROTATION_PRESET_DEFAULT, - Presets.FROST_UH_PESTI_ROTATION_PRESET_DEFAULT, - Presets.UNHOLY_DW_ROTATION_PRESET_DEFAULT, - Presets.UNHOLY_2H_ROTATION_PRESET_DEFAULT, - Presets.UNHOLY_DND_AOE_ROTATION_PRESET_DEFAULT, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.P1_BLOOD_PRESET, - Presets.P2_BLOOD_PRESET, - Presets.P3_BLOOD_PRESET, - Presets.P4_BLOOD_PRESET, - Presets.P1_FROST_PRESET, - Presets.P2_FROST_PRESET, - Presets.P3_FROST_PRESET, - Presets.P4_FROST_PRESET, - Presets.P1_UNHOLY_DW_PRESET, - Presets.P2_UNHOLY_DW_PRESET, - Presets.P3_UNHOLY_DW_PRESET, - Presets.P4_UNHOLY_DW_PRESET, - Presets.P4_UNHOLY_2H_PRESET, - // Not needed anymore just filling ui Space - // Disabled on purpose - //Presets.P1_FROSTSUBUNH_PRESET, - //Presets.P1_FROST_PRE_BIS_PRESET, - //Presets.PRERAID_UNHOLY_DW_PRESET, - //Presets.PRERAID_UNHOLY_2H_PRESET, - //Presets.P1_UNHOLY_2H_PRESET, - ], - }, - }); +export class DeathknightSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/elemental_shaman/sim.ts b/ui/elemental_shaman/sim.ts index 8218352fd4..31c765daec 100644 --- a/ui/elemental_shaman/sim.ts +++ b/ui/elemental_shaman/sim.ts @@ -6,179 +6,178 @@ import { Spec } from '../core/proto/common.js'; import { Stat } from '../core/proto/common.js'; import { TristateEffect } from '../core/proto/common.js' import { - APLAction, - APLListItem, APLRotation, } from '../core/proto/apl.js'; import { Player } from '../core/player.js'; import { Stats } from '../core/proto_utils/stats.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; -import { EventID, TypedEvent } from '../core/typed_event.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; +import { TypedEvent } from '../core/typed_event.js'; import { TotemsSection } from '../core/components/totem_inputs.js'; import * as OtherInputs from '../core/components/other_inputs.js'; import * as Mechanics from '../core/constants/mechanics.js'; import * as ShamanInputs from './inputs.js'; import * as Presets from './presets.js'; -import { ElementalShaman_Options_ThunderstormRange, ElementalShaman_Rotation_BloodlustUse} from '../core/proto/shaman.js'; -export class ElementalShamanSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'elemental-shaman-sim-ui', - cssScheme: 'shaman', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], - warnings: [ - // Warning to use all 4 totems if T6 2pc bonus is active. - (simUI: IndividualSimUI) => { - return { - updateOn: TypedEvent.onAny([simUI.player.rotationChangeEmitter, simUI.player.currentStatsEmitter]), - getContent: () => { - const hasT62P = simUI.player.getCurrentStats().sets.includes('Skyshatter Regalia (2pc)'); - const totems = simUI.player.getRotation().totems!; - const hasAll4Totems = totems && totems.earth && totems.air && totems.fire && totems.water; - if (hasT62P && !hasAll4Totems) { - return 'T6 2pc bonus is equipped, but inactive because not all 4 totem types are being used.'; - } else { - return ''; - } - }, - }; +const SPEC_CONFIG = registerSpecConfig(Spec.SpecElementalShaman, { + cssClass: 'elemental-shaman-sim-ui', + cssScheme: 'shaman', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + ], + warnings: [ + // Warning to use all 4 totems if T6 2pc bonus is active. + (simUI: IndividualSimUI) => { + return { + updateOn: TypedEvent.onAny([simUI.player.rotationChangeEmitter, simUI.player.currentStatsEmitter]), + getContent: () => { + const hasT62P = simUI.player.getCurrentStats().sets.includes('Skyshatter Regalia (2pc)'); + const totems = simUI.player.getRotation().totems!; + const hasAll4Totems = totems && totems.earth && totems.air && totems.fire && totems.water; + if (hasT62P && !hasAll4Totems) { + return 'T6 2pc bonus is equipped, but inactive because not all 4 totem types are being used.'; + } else { + return ''; + } }, - ], + }; + }, + ], + + // All stats for which EP should be calculated. + epStats: [ + Stat.StatIntellect, + Stat.StatSpellPower, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatSpellPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatMana, + Stat.StatStamina, + Stat.StatIntellect, + Stat.StatSpellPower, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], + modifyDisplayStats: (player: Player) => { + let stats = new Stats(); + stats = stats.addStat(Stat.StatSpellHit, player.getTalents().elementalPrecision * Mechanics.SPELL_HIT_RATING_PER_HIT_CHANCE); + stats = stats.addStat(Stat.StatSpellCrit, + player.getTalents().tidalMastery * 1 * Mechanics.SPELL_CRIT_RATING_PER_CRIT_CHANCE); + return { + talents: stats, + }; + }, - // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatSpellPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatMana, - Stat.StatStamina, - Stat.StatIntellect, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], - modifyDisplayStats: (player: Player) => { - let stats = new Stats(); - stats = stats.addStat(Stat.StatSpellHit, player.getTalents().elementalPrecision * Mechanics.SPELL_HIT_RATING_PER_HIT_CHANCE); - stats = stats.addStat(Stat.StatSpellCrit, - player.getTalents().tidalMastery * 1 * Mechanics.SPELL_CRIT_RATING_PER_CRIT_CHANCE); - return { - talents: stats, - }; - }, + defaults: { + // Default equipped gear. + gear: Presets.P3_PRESET_HORDE.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatIntellect]: 0.22, + [Stat.StatSpellPower]: 1, + [Stat.StatSpellCrit]: 0.67, + [Stat.StatSpellHaste]: 1.29, + [Stat.StatMP5]: 0.08, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultRotation, + // Default talents. + talents: Presets.StandardTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + other: Presets.OtherDefaults, + // Default raid/party buffs settings. + raidBuffs: RaidBuffs.create({ + arcaneBrilliance: true, + divineSpirit: true, + giftOfTheWild: TristateEffect.TristateEffectImproved, + moonkinAura: TristateEffect.TristateEffectImproved, + sanctifiedRetribution: true, + demonicPact: 500, + wrathOfAirTotem: true, + }), + partyBuffs: PartyBuffs.create({ + }), + individualBuffs: IndividualBuffs.create({ + blessingOfKings: true, + blessingOfWisdom: 2, + vampiricTouch: true, + }), + debuffs: Debuffs.create({ + faerieFire: TristateEffect.TristateEffectImproved, + judgementOfWisdom: true, + misery: true, + curseOfElements: true, + shadowMastery: true, + heartOfTheCrusader: true, + }), + }, + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + ShamanInputs.ShamanShieldInput, + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: ShamanInputs.ElementalShamanRotationConfig, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + ShamanInputs.InThunderstormRange, + OtherInputs.TankAssignment, + OtherInputs.nibelungAverageCasts, + ], + }, + customSections: [ + TotemsSection, + ], + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, - defaults: { - // Default equipped gear. - gear: Presets.P3_PRESET_HORDE.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatIntellect]: 0.22, - [Stat.StatSpellPower]: 1, - [Stat.StatSpellCrit]: 0.67, - [Stat.StatSpellHaste]: 1.29, - [Stat.StatMP5]: 0.08, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultRotation, - // Default talents. - talents: Presets.StandardTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - other: Presets.OtherDefaults, - // Default raid/party buffs settings. - raidBuffs: RaidBuffs.create({ - arcaneBrilliance: true, - divineSpirit: true, - giftOfTheWild: TristateEffect.TristateEffectImproved, - moonkinAura: TristateEffect.TristateEffectImproved, - sanctifiedRetribution: true, - demonicPact: 500, - wrathOfAirTotem: true, - }), - partyBuffs: PartyBuffs.create({ - }), - individualBuffs: IndividualBuffs.create({ - blessingOfKings: true, - blessingOfWisdom: 2, - vampiricTouch: true, - }), - debuffs: Debuffs.create({ - faerieFire: TristateEffect.TristateEffectImproved, - judgementOfWisdom: true, - misery: true, - curseOfElements: true, - shadowMastery: true, - heartOfTheCrusader: true, - }), - }, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ShamanInputs.ShamanShieldInput, - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: ShamanInputs.ElementalShamanRotationConfig, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - ShamanInputs.InThunderstormRange, - OtherInputs.TankAssignment, - OtherInputs.nibelungAverageCasts, - ], - }, - customSections: [ - TotemsSection, - ], - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.StandardTalents, + ], + // Preset rotations that the user can quickly select. + rotations: [ + Presets.ROTATION_PRESET_LEGACY, + Presets.ROTATION_PRESET_DEFAULT, + Presets.ROTATION_PRESET_ADVANCED, + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.PRERAID_PRESET, + Presets.P1_PRESET, + Presets.P2_PRESET, + Presets.P3_PRESET_ALLI, + Presets.P3_PRESET_HORDE, + Presets.P4_PRESET, + ], + }, - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.StandardTalents, - ], - // Preset rotations that the user can quickly select. - rotations: [ - Presets.ROTATION_PRESET_LEGACY, - Presets.ROTATION_PRESET_DEFAULT, - Presets.ROTATION_PRESET_ADVANCED, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.PRERAID_PRESET, - Presets.P1_PRESET, - Presets.P2_PRESET, - Presets.P3_PRESET_ALLI, - Presets.P3_PRESET_HORDE, - Presets.P4_PRESET, - ], - }, + autoRotation: (_player: Player): APLRotation => { + return Presets.ROTATION_PRESET_DEFAULT.rotation.rotation!; + }, +}); - autoRotation: (player: Player): APLRotation => { - return Presets.ROTATION_PRESET_DEFAULT.rotation.rotation!; - }, - }); +export class ElementalShamanSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/enhancement_shaman/sim.ts b/ui/enhancement_shaman/sim.ts index a7ab7f5cb5..9145a39340 100644 --- a/ui/enhancement_shaman/sim.ts +++ b/ui/enhancement_shaman/sim.ts @@ -4,14 +4,12 @@ import { Spec } from '../core/proto/common.js'; import { Stat, PseudoStat } from '../core/proto/common.js'; import { TristateEffect } from '../core/proto/common.js' import { - APLAction, - APLListItem, APLRotation, } from '../core/proto/apl.js'; import { ShamanImbue } from '../core/proto/shaman.js'; import { Player } from '../core/player.js'; import { Stats } from '../core/proto_utils/stats.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; import { TotemsSection } from '../core/components/totem_inputs.js'; import * as IconInputs from '../core/components/icon_inputs.js'; import * as OtherInputs from '../core/components/other_inputs.js'; @@ -20,175 +18,177 @@ import * as ShamanInputs from './inputs.js'; import * as Presets from './presets.js'; import { FireElementalSection } from '../core/components/fire_elemental_inputs.js'; -export class EnhancementShamanSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'enhancement-shaman-sim-ui', - cssScheme: 'shaman', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], +const SPEC_CONFIG = registerSpecConfig(Spec.SpecEnhancementShaman, { + cssClass: 'enhancement-shaman-sim-ui', + cssScheme: 'shaman', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + ], + + // All stats for which EP should be calculated. + epStats: [ + Stat.StatIntellect, + Stat.StatAgility, + Stat.StatStrength, + Stat.StatAttackPower, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatArmorPenetration, + Stat.StatExpertise, + Stat.StatSpellPower, + Stat.StatSpellCrit, + Stat.StatSpellHit, + Stat.StatSpellHaste, + ], + epPseudoStats: [ + PseudoStat.PseudoStatMainHandDps, + PseudoStat.PseudoStatOffHandDps, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatAttackPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatStamina, + Stat.StatStrength, + Stat.StatAgility, + Stat.StatIntellect, + Stat.StatAttackPower, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatExpertise, + Stat.StatArmorPenetration, + Stat.StatSpellPower, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + ], - // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatAgility, - Stat.StatStrength, - Stat.StatAttackPower, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatArmorPenetration, - Stat.StatExpertise, - Stat.StatSpellPower, - Stat.StatSpellCrit, - Stat.StatSpellHit, - Stat.StatSpellHaste, - ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - PseudoStat.PseudoStatOffHandDps, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatAttackPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatStamina, - Stat.StatStrength, - Stat.StatAgility, - Stat.StatIntellect, - Stat.StatAttackPower, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatExpertise, - Stat.StatArmorPenetration, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - ], + defaults: { + // Default equipped gear. + gear: Presets.P4_PRESET_WF.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatIntellect]: 1.48, + [Stat.StatAgility]: 1.59, + [Stat.StatStrength]: 1.1, + [Stat.StatSpellPower]: 1.13, + [Stat.StatSpellHit]: 0, //default EP assumes cap + [Stat.StatSpellCrit]: 0.91, + [Stat.StatSpellHaste]: 0.37, + [Stat.StatAttackPower]: 1.0, + [Stat.StatMeleeHit]: 1.38, + [Stat.StatMeleeCrit]: 0.81, + [Stat.StatMeleeHaste]: 1.61, //haste is complicated + [Stat.StatArmorPenetration]: 0.48, + [Stat.StatExpertise]: 0, //default EP assumes cap + }, { + [PseudoStat.PseudoStatMainHandDps]: 5.21, + [PseudoStat.PseudoStatOffHandDps]: 2.21, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultRotation, + // Default talents. + talents: Presets.StandardTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + // Default raid/party buffs settings. + raidBuffs: Presets.DefaultRaidBuffs, + partyBuffs: PartyBuffs.create({ + }), + individualBuffs: IndividualBuffs.create({ + blessingOfKings: true, + blessingOfWisdom: TristateEffect.TristateEffectImproved, + blessingOfMight: TristateEffect.TristateEffectImproved, + judgementsOfTheWise: true, + }), + debuffs: Presets.DefaultDebuffs, + }, - defaults: { - // Default equipped gear. - gear: Presets.P4_PRESET_WF.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatIntellect]: 1.48, - [Stat.StatAgility]: 1.59, - [Stat.StatStrength]: 1.1, - [Stat.StatSpellPower]: 1.13, - [Stat.StatSpellHit]: 0, //default EP assumes cap - [Stat.StatSpellCrit]: 0.91, - [Stat.StatSpellHaste]: 0.37, - [Stat.StatAttackPower]: 1.0, - [Stat.StatMeleeHit]: 1.38, - [Stat.StatMeleeCrit]: 0.81, - [Stat.StatMeleeHaste]: 1.61, //haste is complicated - [Stat.StatArmorPenetration]: 0.48, - [Stat.StatExpertise]: 0, //default EP assumes cap - }, { - [PseudoStat.PseudoStatMainHandDps]: 5.21, - [PseudoStat.PseudoStatOffHandDps]: 2.21, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultRotation, - // Default talents. - talents: Presets.StandardTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - // Default raid/party buffs settings. - raidBuffs: Presets.DefaultRaidBuffs, - partyBuffs: PartyBuffs.create({ - }), - individualBuffs: IndividualBuffs.create({ - blessingOfKings: true, - blessingOfWisdom: TristateEffect.TristateEffectImproved, - blessingOfMight: TristateEffect.TristateEffectImproved, - judgementsOfTheWise: true, - }), - debuffs: Presets.DefaultDebuffs, - }, + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + ShamanInputs.ShamanShieldInput, + ShamanInputs.ShamanImbueMH, + ShamanInputs.ShamanImbueOH, + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: ShamanInputs.EnhancementShamanRotationConfig, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + IconInputs.ReplenishmentBuff, + IconInputs.MP5Buff, + IconInputs.SpellHasteBuff, + IconInputs.SpiritBuff, + ], + excludeBuffDebuffInputs: [ + IconInputs.BleedDebuff, + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + ShamanInputs.SyncTypeInput, + OtherInputs.TankAssignment, + OtherInputs.InFrontOfTarget, + ], + }, + customSections: [ + TotemsSection, + FireElementalSection + ], + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ShamanInputs.ShamanShieldInput, - ShamanInputs.ShamanImbueMH, - ShamanInputs.ShamanImbueOH, - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: ShamanInputs.EnhancementShamanRotationConfig, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - IconInputs.ReplenishmentBuff, - IconInputs.MP5Buff, - IconInputs.SpellHasteBuff, - IconInputs.SpiritBuff, - ], - excludeBuffDebuffInputs: [ - IconInputs.BleedDebuff, - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - ShamanInputs.SyncTypeInput, - OtherInputs.TankAssignment, - OtherInputs.InFrontOfTarget, - ], - }, - customSections: [ - TotemsSection, - FireElementalSection - ], - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.StandardTalents, + Presets.Phase3Talents, + ], + // Preset rotations that the user can quickly select. + rotations: [ + Presets.ROTATION_FT_DEFAULT, + Presets.ROTATION_WF_DEFAULT, + Presets.ROTATION_PHASE_3, + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.PRERAID_PRESET, + Presets.P1_PRESET, + Presets.P2_PRESET_FT, + Presets.P2_PRESET_WF, + Presets.P3_PRESET_ALLIANCE, + Presets.P3_PRESET_HORDE, + Presets.P4_PRESET_FT, + Presets.P4_PRESET_WF, + ], + }, - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.StandardTalents, - Presets.Phase3Talents, - ], - // Preset rotations that the user can quickly select. - rotations: [ - Presets.ROTATION_FT_DEFAULT, - Presets.ROTATION_WF_DEFAULT, - Presets.ROTATION_PHASE_3, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.PRERAID_PRESET, - Presets.P1_PRESET, - Presets.P2_PRESET_FT, - Presets.P2_PRESET_WF, - Presets.P3_PRESET_ALLIANCE, - Presets.P3_PRESET_HORDE, - Presets.P4_PRESET_FT, - Presets.P4_PRESET_WF, - ], - }, + autoRotation: (player: Player): APLRotation => { + const hasT94P = player.getCurrentStats().sets.includes('Triumphant Nobundo\'s Battlegear (4pc)') + || player.getCurrentStats().sets.includes('Nobundo\'s Battlegear (4pc)') + || player.getCurrentStats().sets.includes('Triumphant Thrall\'s Battlegear (4pc)') + || player.getCurrentStats().sets.includes('Thrall\'s Battlegear (4pc)'); + const options = player.getSpecOptions(); - autoRotation: (player: Player): APLRotation => { - const hasT94P = player.getCurrentStats().sets.includes('Triumphant Nobundo\'s Battlegear (4pc)') - || player.getCurrentStats().sets.includes('Nobundo\'s Battlegear (4pc)') - || player.getCurrentStats().sets.includes('Triumphant Thrall\'s Battlegear (4pc)') - || player.getCurrentStats().sets.includes('Thrall\'s Battlegear (4pc)'); - const options = player.getSpecOptions(); + if (hasT94P) { + console.log("has set"); + return Presets.ROTATION_PHASE_3.rotation.rotation!; + } else if (options.imbueMh == ShamanImbue.FlametongueWeapon) { + return Presets.ROTATION_FT_DEFAULT.rotation.rotation!; + } else { + return Presets.ROTATION_WF_DEFAULT.rotation.rotation!; + } + }, +}); - if (hasT94P) { - console.log("has set"); - return Presets.ROTATION_PHASE_3.rotation.rotation!; - } else if (options.imbueMh == ShamanImbue.FlametongueWeapon) { - return Presets.ROTATION_FT_DEFAULT.rotation.rotation!; - } else { - return Presets.ROTATION_WF_DEFAULT.rotation.rotation!; - } - }, - }); +export class EnhancementShamanSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/feral_druid/sim.ts b/ui/feral_druid/sim.ts index 2c5f57fa1c..fd3e77f31b 100644 --- a/ui/feral_druid/sim.ts +++ b/ui/feral_druid/sim.ts @@ -7,8 +7,8 @@ import { Stat, PseudoStat } from '../core/proto/common.js'; import { TristateEffect } from '../core/proto/common.js' import { Stats } from '../core/proto_utils/stats.js'; import { Player } from '../core/player.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; -import { EventID, TypedEvent } from '../core/typed_event.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; +import { TypedEvent } from '../core/typed_event.js'; import { Gear } from '../core/proto_utils/gear.js'; import { ItemSlot } from '../core/proto/common.js'; import { GemColor } from '../core/proto/common.js'; @@ -16,159 +16,160 @@ import { Profession } from '../core/proto/common.js'; import * as IconInputs from '../core/components/icon_inputs.js'; import * as OtherInputs from '../core/components/other_inputs.js'; -import * as Tooltips from '../core/constants/tooltips.js'; import * as DruidInputs from './inputs.js'; import * as Presets from './presets.js'; import { APLRotation } from 'ui/core/proto/apl.js'; +const SPEC_CONFIG = registerSpecConfig(Spec.SpecFeralDruid, { + cssClass: 'feral-druid-sim-ui', + cssScheme: 'druid', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + ], + warnings: [ + ], + + // All stats for which EP should be calculated. + epStats: [ + Stat.StatStrength, + Stat.StatAgility, + Stat.StatAttackPower, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatArmorPenetration, + Stat.StatExpertise, + ], + epPseudoStats: [ + PseudoStat.PseudoStatMainHandDps, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatAttackPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatStrength, + Stat.StatAgility, + Stat.StatAttackPower, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatArmorPenetration, + Stat.StatExpertise, + Stat.StatMana, + ], + + defaults: { + // Default equipped gear. + gear: Presets.P4_PRESET.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatStrength]: 2.40, + [Stat.StatAgility]: 2.39, + [Stat.StatAttackPower]: 1, + [Stat.StatMeleeHit]: 2.51, + [Stat.StatMeleeCrit]: 2.23, + [Stat.StatMeleeHaste]: 1.83, + [Stat.StatArmorPenetration]: 2.08, + [Stat.StatExpertise]: 2.44, + }, { + [PseudoStat.PseudoStatMainHandDps]: 16.5, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultRotation, + // Default talents. + talents: Presets.StandardTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + // Default raid/party buffs settings. + raidBuffs: RaidBuffs.create({ + arcaneBrilliance: true, + giftOfTheWild: TristateEffect.TristateEffectImproved, + bloodlust: true, + manaSpringTotem: TristateEffect.TristateEffectRegular, + strengthOfEarthTotem: TristateEffect.TristateEffectImproved, + battleShout: TristateEffect.TristateEffectImproved, + unleashedRage: true, + icyTalons: true, + swiftRetribution: true, + sanctifiedRetribution: true, + }), + partyBuffs: PartyBuffs.create({ + heroicPresence: true, + }), + individualBuffs: IndividualBuffs.create({ + blessingOfKings: true, + blessingOfMight: TristateEffect.TristateEffectImproved, + }), + debuffs: Debuffs.create({ + judgementOfWisdom: true, + bloodFrenzy: true, + giftOfArthas: true, + exposeArmor: true, + faerieFire: TristateEffect.TristateEffectImproved, + sunderArmor: true, + curseOfWeakness: TristateEffect.TristateEffectRegular, + heartOfTheCrusader: true, + }), + }, + + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: DruidInputs.FeralDruidRotationConfig, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + IconInputs.IntellectBuff, + IconInputs.MP5Buff, + IconInputs.JudgementOfWisdom, + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + DruidInputs.LatencyMs, + DruidInputs.AssumeBleedActive, + OtherInputs.TankAssignment, + OtherInputs.InFrontOfTarget, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, + + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.StandardTalents, + ], + rotations: [ + Presets.ROTATION_PRESET_LEGACY_DEFAULT, + Presets.APL_ROTATION_DEFAULT, + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.PRERAID_PRESET, + Presets.P1_PRESET, + Presets.P2_PRESET, + Presets.P3_PRESET, + Presets.P4_PRESET, + ], + }, + + autoRotation: (_player: Player): APLRotation => { + return Presets.ROTATION_PRESET_LEGACY_DEFAULT.rotation.rotation!; + } +}); + export class FeralDruidSimUI extends IndividualSimUI { constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'feral-druid-sim-ui', - cssScheme: 'druid', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], - warnings: [ - ], - - // All stats for which EP should be calculated. - epStats: [ - Stat.StatStrength, - Stat.StatAgility, - Stat.StatAttackPower, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatArmorPenetration, - Stat.StatExpertise, - ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatAttackPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatStrength, - Stat.StatAgility, - Stat.StatAttackPower, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatArmorPenetration, - Stat.StatExpertise, - Stat.StatMana, - ], - - defaults: { - // Default equipped gear. - gear: Presets.P4_PRESET.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatStrength]: 2.40, - [Stat.StatAgility]: 2.39, - [Stat.StatAttackPower]: 1, - [Stat.StatMeleeHit]: 2.51, - [Stat.StatMeleeCrit]: 2.23, - [Stat.StatMeleeHaste]: 1.83, - [Stat.StatArmorPenetration]: 2.08, - [Stat.StatExpertise]: 2.44, - }, { - [PseudoStat.PseudoStatMainHandDps]: 16.5, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultRotation, - // Default talents. - talents: Presets.StandardTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - // Default raid/party buffs settings. - raidBuffs: RaidBuffs.create({ - arcaneBrilliance: true, - giftOfTheWild: TristateEffect.TristateEffectImproved, - bloodlust: true, - manaSpringTotem: TristateEffect.TristateEffectRegular, - strengthOfEarthTotem: TristateEffect.TristateEffectImproved, - battleShout: TristateEffect.TristateEffectImproved, - unleashedRage: true, - icyTalons: true, - swiftRetribution: true, - sanctifiedRetribution: true, - }), - partyBuffs: PartyBuffs.create({ - heroicPresence: true, - }), - individualBuffs: IndividualBuffs.create({ - blessingOfKings: true, - blessingOfMight: TristateEffect.TristateEffectImproved, - }), - debuffs: Debuffs.create({ - judgementOfWisdom: true, - bloodFrenzy: true, - giftOfArthas: true, - exposeArmor: true, - faerieFire: TristateEffect.TristateEffectImproved, - sunderArmor: true, - curseOfWeakness: TristateEffect.TristateEffectRegular, - heartOfTheCrusader: true, - }), - }, - - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: DruidInputs.FeralDruidRotationConfig, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - IconInputs.IntellectBuff, - IconInputs.MP5Buff, - IconInputs.JudgementOfWisdom, - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - DruidInputs.LatencyMs, - DruidInputs.AssumeBleedActive, - OtherInputs.TankAssignment, - OtherInputs.InFrontOfTarget, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, - - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.StandardTalents, - ], - rotations: [ - Presets.ROTATION_PRESET_LEGACY_DEFAULT, - Presets.APL_ROTATION_DEFAULT, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.PRERAID_PRESET, - Presets.P1_PRESET, - Presets.P2_PRESET, - Presets.P3_PRESET, - Presets.P4_PRESET, - ], - }, - - autoRotation: (player: Player): APLRotation => { - return Presets.ROTATION_PRESET_LEGACY_DEFAULT.rotation.rotation!; - } - }); + super(parentElem, player, SPEC_CONFIG); this.addOptimizeGemsAction(); } diff --git a/ui/feral_tank_druid/sim.ts b/ui/feral_tank_druid/sim.ts index e0291ed795..401b0d0afc 100644 --- a/ui/feral_tank_druid/sim.ts +++ b/ui/feral_tank_druid/sim.ts @@ -13,231 +13,228 @@ import { } from '../core/proto/apl.js'; import { Stats } from '../core/proto_utils/stats.js'; import { Player } from '../core/player.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; -import { TypedEvent } from '../core/typed_event.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; import { - DruidTalents as DruidTalents, - FeralTankDruid, FeralTankDruid_Rotation as DruidRotation, - FeralTankDruid_Options as DruidOptions } from '../core/proto/druid.js'; import * as IconInputs from '../core/components/icon_inputs.js'; import * as OtherInputs from '../core/components/other_inputs.js'; -import * as Tooltips from '../core/constants/tooltips.js'; import * as AplUtils from '../core/proto_utils/apl_utils.js'; import * as DruidInputs from './inputs.js'; import * as Presets from './presets.js'; -export class FeralTankDruidSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'feral-tank-druid-sim-ui', - cssScheme: 'druid', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], +const SPEC_CONFIG = registerSpecConfig(Spec.SpecFeralTankDruid, { + cssClass: 'feral-tank-druid-sim-ui', + cssScheme: 'druid', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + ], - // All stats for which EP should be calculated. - epStats: [ - Stat.StatStamina, - Stat.StatStrength, - Stat.StatAgility, - Stat.StatAttackPower, - Stat.StatExpertise, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatArmor, - Stat.StatBonusArmor, - Stat.StatArmorPenetration, - Stat.StatDefense, - Stat.StatDodge, - Stat.StatNatureResistance, - Stat.StatShadowResistance, - Stat.StatFrostResistance, - ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatAttackPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatArmor, - Stat.StatBonusArmor, - Stat.StatStamina, - Stat.StatStrength, - Stat.StatAgility, - Stat.StatAttackPower, - Stat.StatExpertise, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatArmorPenetration, - Stat.StatDefense, - Stat.StatDodge, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatNatureResistance, - Stat.StatShadowResistance, - Stat.StatFrostResistance, - ], + // All stats for which EP should be calculated. + epStats: [ + Stat.StatStamina, + Stat.StatStrength, + Stat.StatAgility, + Stat.StatAttackPower, + Stat.StatExpertise, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatArmor, + Stat.StatBonusArmor, + Stat.StatArmorPenetration, + Stat.StatDefense, + Stat.StatDodge, + Stat.StatNatureResistance, + Stat.StatShadowResistance, + Stat.StatFrostResistance, + ], + epPseudoStats: [ + PseudoStat.PseudoStatMainHandDps, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatAttackPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatArmor, + Stat.StatBonusArmor, + Stat.StatStamina, + Stat.StatStrength, + Stat.StatAgility, + Stat.StatAttackPower, + Stat.StatExpertise, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatArmorPenetration, + Stat.StatDefense, + Stat.StatDodge, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatNatureResistance, + Stat.StatShadowResistance, + Stat.StatFrostResistance, + ], - defaults: { - // Default equipped gear. - gear: Presets.P1_PRESET.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatArmor]: 3.5665, - [Stat.StatBonusArmor]: 0.5187, - [Stat.StatStamina]: 7.3021, - [Stat.StatStrength]: 2.3786, - [Stat.StatAgility]: 4.4974, - [Stat.StatAttackPower]: 1, - [Stat.StatExpertise]: 2.6597, - [Stat.StatMeleeHit]: 2.9282, - [Stat.StatMeleeCrit]: 1.5143, - [Stat.StatMeleeHaste]: 2.0983, - [Stat.StatArmorPenetration]: 1.584, - [Stat.StatDefense]: 1.8171, - [Stat.StatDodge]: 2.0196, - [Stat.StatHealth]: 0.4465, - }, { - [PseudoStat.PseudoStatMainHandDps]: 0.0, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultSimpleRotation, - // Default talents. - talents: Presets.StandardTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - // Default raid/party buffs settings. - raidBuffs: RaidBuffs.create({ - powerWordFortitude: TristateEffect.TristateEffectImproved, - shadowProtection: true, - giftOfTheWild: TristateEffect.TristateEffectImproved, - thorns: TristateEffect.TristateEffectImproved, - bloodlust: true, - strengthOfEarthTotem: TristateEffect.TristateEffectImproved, - battleShout: TristateEffect.TristateEffectImproved, - unleashedRage: true, - windfuryTotem: TristateEffect.TristateEffectImproved, - arcaneEmpowerment: true, - moonkinAura: TristateEffect.TristateEffectImproved, - }), - partyBuffs: PartyBuffs.create({ - heroicPresence: true, - }), - individualBuffs: IndividualBuffs.create({ - blessingOfKings: true, - blessingOfMight: TristateEffect.TristateEffectImproved, - renewedHope: true, - }), - debuffs: Debuffs.create({ - savageCombat: true, - faerieFire: TristateEffect.TristateEffectImproved, - exposeArmor: true, - frostFever: TristateEffect.TristateEffectImproved, - masterPoisoner: true, - ebonPlaguebringer: true, - shadowMastery: true, - }), - }, + defaults: { + // Default equipped gear. + gear: Presets.P1_PRESET.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatArmor]: 3.5665, + [Stat.StatBonusArmor]: 0.5187, + [Stat.StatStamina]: 7.3021, + [Stat.StatStrength]: 2.3786, + [Stat.StatAgility]: 4.4974, + [Stat.StatAttackPower]: 1, + [Stat.StatExpertise]: 2.6597, + [Stat.StatMeleeHit]: 2.9282, + [Stat.StatMeleeCrit]: 1.5143, + [Stat.StatMeleeHaste]: 2.0983, + [Stat.StatArmorPenetration]: 1.584, + [Stat.StatDefense]: 1.8171, + [Stat.StatDodge]: 2.0196, + [Stat.StatHealth]: 0.4465, + }, { + [PseudoStat.PseudoStatMainHandDps]: 0.0, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultSimpleRotation, + // Default talents. + talents: Presets.StandardTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + // Default raid/party buffs settings. + raidBuffs: RaidBuffs.create({ + powerWordFortitude: TristateEffect.TristateEffectImproved, + shadowProtection: true, + giftOfTheWild: TristateEffect.TristateEffectImproved, + thorns: TristateEffect.TristateEffectImproved, + bloodlust: true, + strengthOfEarthTotem: TristateEffect.TristateEffectImproved, + battleShout: TristateEffect.TristateEffectImproved, + unleashedRage: true, + windfuryTotem: TristateEffect.TristateEffectImproved, + arcaneEmpowerment: true, + moonkinAura: TristateEffect.TristateEffectImproved, + }), + partyBuffs: PartyBuffs.create({ + heroicPresence: true, + }), + individualBuffs: IndividualBuffs.create({ + blessingOfKings: true, + blessingOfMight: TristateEffect.TristateEffectImproved, + renewedHope: true, + }), + debuffs: Debuffs.create({ + savageCombat: true, + faerieFire: TristateEffect.TristateEffectImproved, + exposeArmor: true, + frostFever: TristateEffect.TristateEffectImproved, + masterPoisoner: true, + ebonPlaguebringer: true, + shadowMastery: true, + }), + }, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: DruidInputs.FeralTankDruidRotationConfig, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - IconInputs.HealthBuff, - IconInputs.SpellCritBuff, - IconInputs.SpellCritDebuff, - IconInputs.SpellHitDebuff, - IconInputs.SpellDamageDebuff, - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - OtherInputs.TankAssignment, - OtherInputs.IncomingHps, - OtherInputs.HealingCadence, - OtherInputs.HealingCadenceVariation, - OtherInputs.BurstWindow, - OtherInputs.InspirationUptime, - OtherInputs.HpPercentForDefensives, - DruidInputs.StartingRage, - OtherInputs.InFrontOfTarget, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: DruidInputs.FeralTankDruidRotationConfig, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + IconInputs.HealthBuff, + IconInputs.SpellCritBuff, + IconInputs.SpellCritDebuff, + IconInputs.SpellHitDebuff, + IconInputs.SpellDamageDebuff, + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + OtherInputs.TankAssignment, + OtherInputs.IncomingHps, + OtherInputs.HealingCadence, + OtherInputs.HealingCadenceVariation, + OtherInputs.BurstWindow, + OtherInputs.InspirationUptime, + OtherInputs.HpPercentForDefensives, + DruidInputs.StartingRage, + OtherInputs.InFrontOfTarget, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.StandardTalents, - ], - // Preset rotations that the user can quickly select. - rotations: [ - Presets.ROTATION_PRESET_SIMPLE, - Presets.ROTATION_DEFAULT, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.P1_PRESET, Presets.P2_PRESET - ], - }, + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.StandardTalents, + ], + // Preset rotations that the user can quickly select. + rotations: [ + Presets.ROTATION_PRESET_SIMPLE, + Presets.ROTATION_DEFAULT, + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.P1_PRESET, Presets.P2_PRESET + ], + }, - autoRotation: (player: Player): APLRotation => { - return Presets.ROTATION_PRESET_SIMPLE.rotation.rotation!; - }, + autoRotation: (_player: Player): APLRotation => { + return Presets.ROTATION_PRESET_SIMPLE.rotation.rotation!; + }, - simpleRotation: (player: Player, simple: DruidRotation, cooldowns: Cooldowns): APLRotation => { - let [prepullActions, actions] = AplUtils.standardCooldownDefaults(cooldowns); + simpleRotation: (player: Player, simple: DruidRotation, cooldowns: Cooldowns): APLRotation => { + let [prepullActions, actions] = AplUtils.standardCooldownDefaults(cooldowns); - const emergencyLacerate = APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"cmp":{"op":"OpEq","lhs":{"auraNumStacks":{"sourceUnit":{"type":"CurrentTarget"},"auraId":{"spellId":48568}}},"rhs":{"const":{"val":"5"}}}},{"cmp":{"op":"OpLe","lhs":{"dotRemainingTime":{"spellId":{"spellId":48568}}},"rhs":{"const":{"val":"1.5s"}}}}]}},"castSpell":{"spellId":{"spellId":48568}}}`); - const demoRoar = APLAction.fromJsonString(`{"condition":{"auraShouldRefresh":{"auraId":{"spellId":48560},"maxOverlap":{"const":{"val":"1.5s"}}}},"castSpell":{"spellId":{"spellId":48560}}}`); - const mangle = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":48564}}}`); - const delayFaerieFireForMangle = APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"gcdIsReady":{}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":48564}}}}},{"cmp":{"op":"OpLt","lhs":{"spellTimeToReady":{"spellId":{"spellId":48564}}},"rhs":{"const":{"val":"1.0s"}}}}]}},"wait":{"duration":{"spellTimeToReady":{"spellId":{"spellId":48564}}}}}`); - const faerieFire = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":16857}}}`); - const delayFillersForMangle = APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"gcdIsReady":{}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":48564}}}}},{"cmp":{"op":"OpLt","lhs":{"spellTimeToReady":{"spellId":{"spellId":48564}}},"rhs":{"const":{"val":"1.5s"}}}}]}},"wait":{"duration":{"spellTimeToReady":{"spellId":{"spellId":48564}}}}}`); - const lacerate = APLAction.fromJsonString(`{"condition":{"or":{"vals":[{"cmp":{"op":"OpLt","lhs":{"auraNumStacks":{"sourceUnit":{"type":"CurrentTarget"},"auraId":{"spellId":48568}}},"rhs":{"const":{"val":"5"}}}},{"cmp":{"op":"OpLe","lhs":{"dotRemainingTime":{"spellId":{"spellId":48568}}},"rhs":{"const":{"val":"${simple.lacerateTime.toFixed(1)}s"}}}}]}},"castSpell":{"spellId":{"spellId":48568}}}`); - const swipe = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpGe","lhs":{"currentRage":{}},"rhs":{"const":{"val":"${(simple.maulRageThreshold + 15).toFixed(0)}"}}}},"castSpell":{"spellId":{"spellId":48562}}}`); - const queueMaul = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpGe","lhs":{"currentRage":{}},"rhs":{"const":{"val":"${simple.maulRageThreshold.toFixed(0)}"}}}},"castSpell":{"spellId":{"spellId":48480,"tag":1}}}`); - const waitForFaerieFire = APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"gcdIsReady":{}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":16857}}}}},{"cmp":{"op":"OpLt","lhs":{"spellTimeToReady":{"spellId":{"spellId":16857}}},"rhs":{"const":{"val":"1.5s"}}}}]}},"wait":{"duration":{"spellTimeToReady":{"spellId":{"spellId":16857}}}}}`); + const emergencyLacerate = APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"cmp":{"op":"OpEq","lhs":{"auraNumStacks":{"sourceUnit":{"type":"CurrentTarget"},"auraId":{"spellId":48568}}},"rhs":{"const":{"val":"5"}}}},{"cmp":{"op":"OpLe","lhs":{"dotRemainingTime":{"spellId":{"spellId":48568}}},"rhs":{"const":{"val":"1.5s"}}}}]}},"castSpell":{"spellId":{"spellId":48568}}}`); + const demoRoar = APLAction.fromJsonString(`{"condition":{"auraShouldRefresh":{"auraId":{"spellId":48560},"maxOverlap":{"const":{"val":"1.5s"}}}},"castSpell":{"spellId":{"spellId":48560}}}`); + const mangle = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":48564}}}`); + const delayFaerieFireForMangle = APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"gcdIsReady":{}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":48564}}}}},{"cmp":{"op":"OpLt","lhs":{"spellTimeToReady":{"spellId":{"spellId":48564}}},"rhs":{"const":{"val":"1.0s"}}}}]}},"wait":{"duration":{"spellTimeToReady":{"spellId":{"spellId":48564}}}}}`); + const faerieFire = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":16857}}}`); + const delayFillersForMangle = APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"gcdIsReady":{}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":48564}}}}},{"cmp":{"op":"OpLt","lhs":{"spellTimeToReady":{"spellId":{"spellId":48564}}},"rhs":{"const":{"val":"1.5s"}}}}]}},"wait":{"duration":{"spellTimeToReady":{"spellId":{"spellId":48564}}}}}`); + const lacerate = APLAction.fromJsonString(`{"condition":{"or":{"vals":[{"cmp":{"op":"OpLt","lhs":{"auraNumStacks":{"sourceUnit":{"type":"CurrentTarget"},"auraId":{"spellId":48568}}},"rhs":{"const":{"val":"5"}}}},{"cmp":{"op":"OpLe","lhs":{"dotRemainingTime":{"spellId":{"spellId":48568}}},"rhs":{"const":{"val":"${simple.lacerateTime.toFixed(1)}s"}}}}]}},"castSpell":{"spellId":{"spellId":48568}}}`); + const swipe = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpGe","lhs":{"currentRage":{}},"rhs":{"const":{"val":"${(simple.maulRageThreshold + 15).toFixed(0)}"}}}},"castSpell":{"spellId":{"spellId":48562}}}`); + const queueMaul = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpGe","lhs":{"currentRage":{}},"rhs":{"const":{"val":"${simple.maulRageThreshold.toFixed(0)}"}}}},"castSpell":{"spellId":{"spellId":48480,"tag":1}}}`); + const waitForFaerieFire = APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"gcdIsReady":{}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":16857}}}}},{"cmp":{"op":"OpLt","lhs":{"spellTimeToReady":{"spellId":{"spellId":16857}}},"rhs":{"const":{"val":"1.5s"}}}}]}},"wait":{"duration":{"spellTimeToReady":{"spellId":{"spellId":16857}}}}}`); - actions.push(...[ - emergencyLacerate, - simple.maintainDemoralizingRoar ? demoRoar : null, - mangle, - delayFaerieFireForMangle, - faerieFire, - delayFillersForMangle, - lacerate, - swipe, - queueMaul, - waitForFaerieFire, - ].filter(a => a) as Array) + actions.push(...[ + emergencyLacerate, + simple.maintainDemoralizingRoar ? demoRoar : null, + mangle, + delayFaerieFireForMangle, + faerieFire, + delayFillersForMangle, + lacerate, + swipe, + queueMaul, + waitForFaerieFire, + ].filter(a => a) as Array) - return APLRotation.create({ - prepullActions: prepullActions, - priorityList: actions.map(action => APLListItem.create({ - action: action, - })) - }); - }, + return APLRotation.create({ + prepullActions: prepullActions, + priorityList: actions.map(action => APLListItem.create({ + action: action, + })) }); + }, +}); + +export class FeralTankDruidSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/healing_priest/sim.ts b/ui/healing_priest/sim.ts index 089dec9e3b..118f3d81fd 100644 --- a/ui/healing_priest/sim.ts +++ b/ui/healing_priest/sim.ts @@ -3,135 +3,132 @@ import { Spec } from '../core/proto/common.js'; import { Stat } from '../core/proto/common.js'; import { Stats } from '../core/proto_utils/stats.js'; import { Player } from '../core/player.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; import { APLRotation, } from '../core/proto/apl.js'; -import * as IconInputs from '../core/components/icon_inputs.js'; -import * as OtherInputs from '../core/components/other_inputs.js'; -import * as Mechanics from '../core/constants/mechanics.js'; -import * as Tooltips from '../core/constants/tooltips.js'; - import * as HealingPriestInputs from './inputs.js'; import * as Presets from './presets.js'; -export class HealingPriestSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'healing-priest-sim-ui', - cssScheme: 'priest', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - 'Talents that apply to, "friendly targets at or below 50% health" are not implemented.', - 'Prayer of Mending always bounces the maximum number of times.', - ], +const SPEC_CONFIG = registerSpecConfig(Spec.SpecHealingPriest, { + cssClass: 'healing-priest-sim-ui', + cssScheme: 'priest', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + 'Talents that apply to, "friendly targets at or below 50% health" are not implemented.', + 'Prayer of Mending always bounces the maximum number of times.', + ], - // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatSpellPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatMana, - Stat.StatStamina, - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], + // All stats for which EP should be calculated. + epStats: [ + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatSpellPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatMana, + Stat.StatStamina, + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], - defaults: { - // Default equipped gear. - gear: Presets.DISC_P1_PRESET.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatIntellect]: 2.73, - [Stat.StatSpirit]: 1.63, - [Stat.StatSpellPower]: 1, - [Stat.StatSpellCrit]: 0.75, - [Stat.StatSpellHaste]: 0.28, - [Stat.StatMP5]: 2.05, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DiscDefaultRotation, - // Default talents. - talents: Presets.DiscTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - // Default raid/party buffs settings. - raidBuffs: Presets.DefaultRaidBuffs, - partyBuffs: PartyBuffs.create({}), - individualBuffs: Presets.DefaultIndividualBuffs, - debuffs: Presets.DefaultDebuffs, - }, + defaults: { + // Default equipped gear. + gear: Presets.DISC_P1_PRESET.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatIntellect]: 2.73, + [Stat.StatSpirit]: 1.63, + [Stat.StatSpellPower]: 1, + [Stat.StatSpellCrit]: 0.75, + [Stat.StatSpellHaste]: 0.28, + [Stat.StatMP5]: 2.05, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DiscDefaultRotation, + // Default talents. + talents: Presets.DiscTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + // Default raid/party buffs settings. + raidBuffs: Presets.DefaultRaidBuffs, + partyBuffs: PartyBuffs.create({}), + individualBuffs: Presets.DefaultIndividualBuffs, + debuffs: Presets.DefaultDebuffs, + }, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - HealingPriestInputs.SelfPowerInfusion, - HealingPriestInputs.InnerFire, - HealingPriestInputs.Shadowfiend, - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: HealingPriestInputs.HealingPriestRotationConfig, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - HealingPriestInputs.RapturesPerMinute, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + HealingPriestInputs.SelfPowerInfusion, + HealingPriestInputs.InnerFire, + HealingPriestInputs.Shadowfiend, + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: HealingPriestInputs.HealingPriestRotationConfig, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + HealingPriestInputs.RapturesPerMinute, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.DiscTalents, - Presets.HolyTalents, - ], - // Preset rotations that the user can quickly select. - rotations: [ - Presets.ROTATION_PRESET_DISC, - Presets.ROTATION_PRESET_HOLY, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.DISC_PRERAID_PRESET, - Presets.DISC_P1_PRESET, - Presets.DISC_P2_PRESET, - Presets.HOLY_PRERAID_PRESET, - Presets.HOLY_P1_PRESET, - Presets.HOLY_P2_PRESET, - ], - }, + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.DiscTalents, + Presets.HolyTalents, + ], + // Preset rotations that the user can quickly select. + rotations: [ + Presets.ROTATION_PRESET_DISC, + Presets.ROTATION_PRESET_HOLY, + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.DISC_PRERAID_PRESET, + Presets.DISC_P1_PRESET, + Presets.DISC_P2_PRESET, + Presets.HOLY_PRERAID_PRESET, + Presets.HOLY_P1_PRESET, + Presets.HOLY_P2_PRESET, + ], + }, - autoRotation: (player: Player): APLRotation => { - const talentTree = player.getTalentTree(); - if (talentTree == 0) { - return Presets.ROTATION_PRESET_DISC.rotation.rotation!; - } else { - return Presets.ROTATION_PRESET_HOLY.rotation.rotation!; - } - }, - }); + autoRotation: (player: Player): APLRotation => { + const talentTree = player.getTalentTree(); + if (talentTree == 0) { + return Presets.ROTATION_PRESET_DISC.rotation.rotation!; + } else { + return Presets.ROTATION_PRESET_HOLY.rotation.rotation!; + } + }, +}); + +export class HealingPriestSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/holy_paladin/sim.ts b/ui/holy_paladin/sim.ts index 1f709639c7..e3b1c74793 100644 --- a/ui/holy_paladin/sim.ts +++ b/ui/holy_paladin/sim.ts @@ -3,161 +3,162 @@ import { PartyBuffs } from '../core/proto/common.js'; import { IndividualBuffs } from '../core/proto/common.js'; import { Debuffs } from '../core/proto/common.js'; import { Spec } from '../core/proto/common.js'; -import { Stat, PseudoStat } from '../core/proto/common.js'; +import { Stat } from '../core/proto/common.js'; import { TristateEffect } from '../core/proto/common.js' import { APLRotation, } from '../core/proto/apl.js'; import { Stats } from '../core/proto_utils/stats.js'; import { Player } from '../core/player.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; -import { EventID, TypedEvent } from '../core/typed_event.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; -import * as IconInputs from '../core/components/icon_inputs.js'; import * as OtherInputs from '../core/components/other_inputs.js'; -import * as Mechanics from '../core/constants/mechanics.js'; import * as HolyPaladinInputs from './inputs.js'; import * as Presets from './presets.js'; -export class HolyPaladinSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'holy-paladin-sim-ui', - cssScheme: 'paladin', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], +const SPEC_CONFIG = registerSpecConfig(Spec.SpecHolyPaladin, { + cssClass: 'holy-paladin-sim-ui', + cssScheme: 'paladin', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + ], + + // All stats for which EP should be calculated. + epStats: [ + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatSpellPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatMana, + Stat.StatStamina, + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], + defaults: { + // Default equipped gear. + gear: Presets.P1_PRESET.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatIntellect]: 0.38, + [Stat.StatSpirit]: 0.34, + [Stat.StatSpellPower]: 1, + [Stat.StatSpellCrit]: 0.69, + [Stat.StatSpellHaste]: 0.77, + [Stat.StatMP5]: 0.00, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultRotation, + // Default talents. + talents: Presets.StandardTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + // Default raid/party buffs settings. + raidBuffs: RaidBuffs.create({ + giftOfTheWild: TristateEffect.TristateEffectImproved, + powerWordFortitude: TristateEffect.TristateEffectImproved, + strengthOfEarthTotem: TristateEffect.TristateEffectImproved, + arcaneBrilliance: true, + unleashedRage: true, + leaderOfThePack: TristateEffect.TristateEffectRegular, + icyTalons: true, + totemOfWrath: true, + demonicPact: 500, + swiftRetribution: true, + moonkinAura: TristateEffect.TristateEffectRegular, + sanctifiedRetribution: true, + manaSpringTotem: TristateEffect.TristateEffectRegular, + bloodlust: true, + thorns: TristateEffect.TristateEffectImproved, + devotionAura: TristateEffect.TristateEffectImproved, + shadowProtection: true, + }), + partyBuffs: PartyBuffs.create({ + }), + individualBuffs: IndividualBuffs.create({ + blessingOfKings: true, + blessingOfSanctuary: true, + blessingOfWisdom: TristateEffect.TristateEffectImproved, + blessingOfMight: TristateEffect.TristateEffectImproved, + }), + debuffs: Debuffs.create({ + judgementOfWisdom: true, + judgementOfLight: true, + misery: true, + faerieFire: TristateEffect.TristateEffectImproved, + ebonPlaguebringer: true, + totemOfWrath: true, + shadowMastery: true, + bloodFrenzy: true, + mangle: true, + exposeArmor: true, + sunderArmor: true, + vindication: true, + thunderClap: TristateEffect.TristateEffectImproved, + insectSwarm: true, + }), + }, - // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatSpellPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatMana, - Stat.StatStamina, - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], - defaults: { - // Default equipped gear. - gear: Presets.P1_PRESET.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatIntellect]: 0.38, - [Stat.StatSpirit]: 0.34, - [Stat.StatSpellPower]: 1, - [Stat.StatSpellCrit]: 0.69, - [Stat.StatSpellHaste]: 0.77, - [Stat.StatMP5]: 0.00, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultRotation, - // Default talents. - talents: Presets.StandardTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - // Default raid/party buffs settings. - raidBuffs: RaidBuffs.create({ - giftOfTheWild: TristateEffect.TristateEffectImproved, - powerWordFortitude: TristateEffect.TristateEffectImproved, - strengthOfEarthTotem: TristateEffect.TristateEffectImproved, - arcaneBrilliance: true, - unleashedRage: true, - leaderOfThePack: TristateEffect.TristateEffectRegular, - icyTalons: true, - totemOfWrath: true, - demonicPact: 500, - swiftRetribution: true, - moonkinAura: TristateEffect.TristateEffectRegular, - sanctifiedRetribution: true, - manaSpringTotem: TristateEffect.TristateEffectRegular, - bloodlust: true, - thorns: TristateEffect.TristateEffectImproved, - devotionAura: TristateEffect.TristateEffectImproved, - shadowProtection: true, - }), - partyBuffs: PartyBuffs.create({ - }), - individualBuffs: IndividualBuffs.create({ - blessingOfKings: true, - blessingOfSanctuary: true, - blessingOfWisdom: TristateEffect.TristateEffectImproved, - blessingOfMight: TristateEffect.TristateEffectImproved, - }), - debuffs: Debuffs.create({ - judgementOfWisdom: true, - judgementOfLight: true, - misery: true, - faerieFire: TristateEffect.TristateEffectImproved, - ebonPlaguebringer: true, - totemOfWrath: true, - shadowMastery: true, - bloodFrenzy: true, - mangle: true, - exposeArmor: true, - sunderArmor: true, - vindication: true, - thunderClap: TristateEffect.TristateEffectImproved, - insectSwarm: true, - }), - }, + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: HolyPaladinInputs.HolyPaladinRotationConfig, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + OtherInputs.TankAssignment, + OtherInputs.InspirationUptime, + HolyPaladinInputs.AuraSelection, + HolyPaladinInputs.JudgementSelection, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: HolyPaladinInputs.HolyPaladinRotationConfig, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - OtherInputs.TankAssignment, - OtherInputs.InspirationUptime, - HolyPaladinInputs.AuraSelection, - HolyPaladinInputs.JudgementSelection, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.StandardTalents, + ], + rotations: [ + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.PRERAID_PRESET, + Presets.P1_PRESET, + Presets.P2_PRESET, + ], + }, - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.StandardTalents, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.PRERAID_PRESET, - Presets.P1_PRESET, - Presets.P2_PRESET, - ], - }, + autoRotation: (_player: Player): APLRotation => { + return APLRotation.create(); + }, +}); - autoRotation: (_player: Player): APLRotation => { - return APLRotation.create(); - }, - }); +export class HolyPaladinSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/hunter/sim.ts b/ui/hunter/sim.ts index 3e8837c459..4b9755859e 100644 --- a/ui/hunter/sim.ts +++ b/ui/hunter/sim.ts @@ -19,16 +19,14 @@ import { import { Player } from '../core/player.js'; import { Stats } from '../core/proto_utils/stats.js'; import { getTalentPoints } from '../core/proto_utils/utils.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; -import { EventID, TypedEvent } from '../core/typed_event.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; +import { TypedEvent } from '../core/typed_event.js'; import { getPetTalentsConfig } from '../core/talents/hunter_pet.js'; import { protoToTalentString } from '../core/talents/factory.js'; import { - Hunter, Hunter_Rotation as HunterRotation, Hunter_Rotation_StingType as StingType, - Hunter_Options as HunterOptions, Hunter_Options_PetType as PetType, HunterPetTalents, Hunter_Rotation_RotationType, @@ -37,337 +35,338 @@ import { import * as IconInputs from '../core/components/icon_inputs.js'; import * as OtherInputs from '../core/components/other_inputs.js'; import * as Mechanics from '../core/constants/mechanics.js'; -import * as Tooltips from '../core/constants/tooltips.js'; import * as AplUtils from '../core/proto_utils/apl_utils.js'; import * as HunterInputs from './inputs.js'; import * as Presets from './presets.js'; -export class HunterSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'hunter-sim-ui', - cssScheme: 'hunter', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], - warnings: [ - // Warning when using exotic pet without BM talented. - (simUI: IndividualSimUI) => { - return { - updateOn: TypedEvent.onAny([simUI.player.talentsChangeEmitter, simUI.player.specOptionsChangeEmitter]), - getContent: () => { - const petIsExotic = [ - PetType.Chimaera, - PetType.CoreHound, - PetType.Devilsaur, - PetType.Silithid, - PetType.SpiritBeast, - PetType.Worm, - ].includes(simUI.player.getSpecOptions().petType); +const SPEC_CONFIG = registerSpecConfig(Spec.SpecHunter, { + cssClass: 'hunter-sim-ui', + cssScheme: 'hunter', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + ], + warnings: [ + // Warning when using exotic pet without BM talented. + (simUI: IndividualSimUI) => { + return { + updateOn: TypedEvent.onAny([simUI.player.talentsChangeEmitter, simUI.player.specOptionsChangeEmitter]), + getContent: () => { + const petIsExotic = [ + PetType.Chimaera, + PetType.CoreHound, + PetType.Devilsaur, + PetType.Silithid, + PetType.SpiritBeast, + PetType.Worm, + ].includes(simUI.player.getSpecOptions().petType); - const isBM = simUI.player.getTalents().beastMastery; + const isBM = simUI.player.getTalents().beastMastery; - if (petIsExotic && !isBM) { - return 'Cannot use exotic pets without the Beast Mastery talent.'; - } else { - return ''; - } - }, - }; + if (petIsExotic && !isBM) { + return 'Cannot use exotic pets without the Beast Mastery talent.'; + } else { + return ''; + } }, - // Warning when too many Pet talent points are used without BM talented. - (simUI: IndividualSimUI) => { - return { - updateOn: TypedEvent.onAny([simUI.player.talentsChangeEmitter, simUI.player.specOptionsChangeEmitter]), - getContent: () => { - const specOptions = simUI.player.getSpecOptions(); - const petTalents = specOptions.petTalents || HunterPetTalents.create(); - const petTalentString = protoToTalentString(petTalents, getPetTalentsConfig(specOptions.petType)); - const talentPoints = getTalentPoints(petTalentString); + }; + }, + // Warning when too many Pet talent points are used without BM talented. + (simUI: IndividualSimUI) => { + return { + updateOn: TypedEvent.onAny([simUI.player.talentsChangeEmitter, simUI.player.specOptionsChangeEmitter]), + getContent: () => { + const specOptions = simUI.player.getSpecOptions(); + const petTalents = specOptions.petTalents || HunterPetTalents.create(); + const petTalentString = protoToTalentString(petTalents, getPetTalentsConfig(specOptions.petType)); + const talentPoints = getTalentPoints(petTalentString); - const isBM = simUI.player.getTalents().beastMastery; - const maxPoints = isBM ? 20 : 16; + const isBM = simUI.player.getTalents().beastMastery; + const maxPoints = isBM ? 20 : 16; - if (talentPoints == 0) { - // Just return here, so we don't show a warning during page load. - return ''; - } else if (talentPoints < maxPoints) { - return 'Unspent pet talent points.'; - } else if (talentPoints > maxPoints) { - return 'More than 16 points spent in pet talents, but Beast Mastery is not talented.'; - } else { - return ''; - } - }, - }; + if (talentPoints == 0) { + // Just return here, so we don't show a warning during page load. + return ''; + } else if (talentPoints < maxPoints) { + return 'Unspent pet talent points.'; + } else if (talentPoints > maxPoints) { + return 'More than 16 points spent in pet talents, but Beast Mastery is not talented.'; + } else { + return ''; + } }, - ], + }; + }, + ], - // All stats for which EP should be calculated. - epStats: [ - Stat.StatStamina, - Stat.StatIntellect, - Stat.StatAgility, - Stat.StatRangedAttackPower, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatArmorPenetration, - Stat.StatMP5, - ], - epPseudoStats: [ - PseudoStat.PseudoStatRangedDps, - ], - // Reference stat against which to calculate EP. - epReferenceStat: Stat.StatRangedAttackPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatStamina, - Stat.StatAgility, - Stat.StatIntellect, - Stat.StatRangedAttackPower, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatArmorPenetration, - Stat.StatMP5, - ], - modifyDisplayStats: (player: Player) => { - let stats = new Stats(); - stats = stats.addStat(Stat.StatMeleeCrit, player.getTalents().lethalShots * 1 * Mechanics.MELEE_CRIT_RATING_PER_CRIT_CHANCE); + // All stats for which EP should be calculated. + epStats: [ + Stat.StatStamina, + Stat.StatIntellect, + Stat.StatAgility, + Stat.StatRangedAttackPower, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatArmorPenetration, + Stat.StatMP5, + ], + epPseudoStats: [ + PseudoStat.PseudoStatRangedDps, + ], + // Reference stat against which to calculate EP. + epReferenceStat: Stat.StatRangedAttackPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatStamina, + Stat.StatAgility, + Stat.StatIntellect, + Stat.StatRangedAttackPower, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatArmorPenetration, + Stat.StatMP5, + ], + modifyDisplayStats: (player: Player) => { + let stats = new Stats(); + stats = stats.addStat(Stat.StatMeleeCrit, player.getTalents().lethalShots * 1 * Mechanics.MELEE_CRIT_RATING_PER_CRIT_CHANCE); - const rangedWeapon = player.getEquippedItem(ItemSlot.ItemSlotRanged); - if (rangedWeapon?.enchant?.effectId == 3608) { - stats = stats.addStat(Stat.StatMeleeCrit, 40); - } - if (player.getRace() == Race.RaceDwarf && rangedWeapon?.item.rangedWeaponType == RangedWeaponType.RangedWeaponTypeGun) { - stats = stats.addStat(Stat.StatMeleeCrit, 1 * Mechanics.MELEE_CRIT_RATING_PER_CRIT_CHANCE); - } - if (player.getRace() == Race.RaceTroll && rangedWeapon?.item.rangedWeaponType == RangedWeaponType.RangedWeaponTypeBow) { - stats = stats.addStat(Stat.StatMeleeCrit, 1 * Mechanics.MELEE_CRIT_RATING_PER_CRIT_CHANCE); - } + const rangedWeapon = player.getEquippedItem(ItemSlot.ItemSlotRanged); + if (rangedWeapon?.enchant?.effectId == 3608) { + stats = stats.addStat(Stat.StatMeleeCrit, 40); + } + if (player.getRace() == Race.RaceDwarf && rangedWeapon?.item.rangedWeaponType == RangedWeaponType.RangedWeaponTypeGun) { + stats = stats.addStat(Stat.StatMeleeCrit, 1 * Mechanics.MELEE_CRIT_RATING_PER_CRIT_CHANCE); + } + if (player.getRace() == Race.RaceTroll && rangedWeapon?.item.rangedWeaponType == RangedWeaponType.RangedWeaponTypeBow) { + stats = stats.addStat(Stat.StatMeleeCrit, 1 * Mechanics.MELEE_CRIT_RATING_PER_CRIT_CHANCE); + } - return { - talents: stats, - }; - }, + return { + talents: stats, + }; + }, - defaults: { - // Default equipped gear. - gear: Presets.SV_P1_PRESET.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatStamina]: 0.5, - [Stat.StatAgility]: 2.65, - [Stat.StatIntellect]: 1.1, - [Stat.StatRangedAttackPower]: 1.0, - [Stat.StatMeleeHit]: 2, - [Stat.StatMeleeCrit]: 1.5, - [Stat.StatMeleeHaste]: 1.39, - [Stat.StatArmorPenetration]: 1.32, - }, { - [PseudoStat.PseudoStatRangedDps]: 6.32, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultRotation, - // Default talents. - talents: Presets.SurvivalTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - // Default raid/party buffs settings. - raidBuffs: RaidBuffs.create({ - arcaneBrilliance: true, - powerWordFortitude: TristateEffect.TristateEffectImproved, - giftOfTheWild: TristateEffect.TristateEffectImproved, - bloodlust: true, - strengthOfEarthTotem: TristateEffect.TristateEffectImproved, - windfuryTotem: TristateEffect.TristateEffectImproved, - battleShout: TristateEffect.TristateEffectImproved, - leaderOfThePack: TristateEffect.TristateEffectImproved, - sanctifiedRetribution: true, - unleashedRage: true, - moonkinAura: TristateEffect.TristateEffectImproved, - }), - partyBuffs: PartyBuffs.create({ - }), - individualBuffs: IndividualBuffs.create({ - blessingOfKings: true, - blessingOfWisdom: 2, - blessingOfMight: 2, - vampiricTouch: true, - }), - debuffs: Debuffs.create({ - sunderArmor: true, - faerieFire: TristateEffect.TristateEffectImproved, - judgementOfWisdom: true, - curseOfElements: true, - heartOfTheCrusader: true, - savageCombat: true, - }), - }, + defaults: { + // Default equipped gear. + gear: Presets.SV_P1_PRESET.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatStamina]: 0.5, + [Stat.StatAgility]: 2.65, + [Stat.StatIntellect]: 1.1, + [Stat.StatRangedAttackPower]: 1.0, + [Stat.StatMeleeHit]: 2, + [Stat.StatMeleeCrit]: 1.5, + [Stat.StatMeleeHaste]: 1.39, + [Stat.StatArmorPenetration]: 1.32, + }, { + [PseudoStat.PseudoStatRangedDps]: 6.32, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultRotation, + // Default talents. + talents: Presets.SurvivalTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + // Default raid/party buffs settings. + raidBuffs: RaidBuffs.create({ + arcaneBrilliance: true, + powerWordFortitude: TristateEffect.TristateEffectImproved, + giftOfTheWild: TristateEffect.TristateEffectImproved, + bloodlust: true, + strengthOfEarthTotem: TristateEffect.TristateEffectImproved, + windfuryTotem: TristateEffect.TristateEffectImproved, + battleShout: TristateEffect.TristateEffectImproved, + leaderOfThePack: TristateEffect.TristateEffectImproved, + sanctifiedRetribution: true, + unleashedRage: true, + moonkinAura: TristateEffect.TristateEffectImproved, + }), + partyBuffs: PartyBuffs.create({ + }), + individualBuffs: IndividualBuffs.create({ + blessingOfKings: true, + blessingOfWisdom: 2, + blessingOfMight: 2, + vampiricTouch: true, + }), + debuffs: Debuffs.create({ + sunderArmor: true, + faerieFire: TristateEffect.TristateEffectImproved, + judgementOfWisdom: true, + curseOfElements: true, + heartOfTheCrusader: true, + savageCombat: true, + }), + }, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - HunterInputs.PetTypeInput, - HunterInputs.WeaponAmmo, - HunterInputs.UseHuntersMark, - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: HunterInputs.HunterRotationConfig, - petConsumeInputs: [ - IconInputs.SpicedMammothTreats, - ], - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - IconInputs.StaminaBuff, - IconInputs.SpellDamageDebuff, - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - HunterInputs.PetUptime, - HunterInputs.TimeToTrapWeaveMs, - HunterInputs.SniperTrainingUptime, - OtherInputs.TankAssignment, - OtherInputs.InFrontOfTarget, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + HunterInputs.PetTypeInput, + HunterInputs.WeaponAmmo, + HunterInputs.UseHuntersMark, + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: HunterInputs.HunterRotationConfig, + petConsumeInputs: [ + IconInputs.SpicedMammothTreats, + ], + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + IconInputs.StaminaBuff, + IconInputs.SpellDamageDebuff, + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + HunterInputs.PetUptime, + HunterInputs.TimeToTrapWeaveMs, + HunterInputs.SniperTrainingUptime, + OtherInputs.TankAssignment, + OtherInputs.InFrontOfTarget, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.BeastMasteryTalents, - Presets.MarksmanTalents, - Presets.SurvivalTalents, - ], - // Preset rotations that the user can quickly select. - rotations: [ - Presets.ROTATION_PRESET_SIMPLE_DEFAULT, - Presets.ROTATION_PRESET_BM, - Presets.ROTATION_PRESET_MM, - Presets.ROTATION_PRESET_MM_ADVANCED, - Presets.ROTATION_PRESET_SV, - Presets.ROTATION_PRESET_SV_ADVANCED, - Presets.ROTATION_PRESET_AOE, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.MM_PRERAID_PRESET, - Presets.MM_P1_PRESET, - Presets.MM_P2_PRESET, - Presets.MM_P3_PRESET, - Presets.MM_P4_PRESET, - Presets.MM_P5_PRESET, - Presets.SV_PRERAID_PRESET, - Presets.SV_P1_PRESET, - Presets.SV_P2_PRESET, - Presets.SV_P3_PRESET, - Presets.SV_P4_PRESET, - Presets.SV_P5_PRESET, - ], - }, + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.BeastMasteryTalents, + Presets.MarksmanTalents, + Presets.SurvivalTalents, + ], + // Preset rotations that the user can quickly select. + rotations: [ + Presets.ROTATION_PRESET_SIMPLE_DEFAULT, + Presets.ROTATION_PRESET_BM, + Presets.ROTATION_PRESET_MM, + Presets.ROTATION_PRESET_MM_ADVANCED, + Presets.ROTATION_PRESET_SV, + Presets.ROTATION_PRESET_SV_ADVANCED, + Presets.ROTATION_PRESET_AOE, + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.MM_PRERAID_PRESET, + Presets.MM_P1_PRESET, + Presets.MM_P2_PRESET, + Presets.MM_P3_PRESET, + Presets.MM_P4_PRESET, + Presets.MM_P5_PRESET, + Presets.SV_PRERAID_PRESET, + Presets.SV_P1_PRESET, + Presets.SV_P2_PRESET, + Presets.SV_P3_PRESET, + Presets.SV_P4_PRESET, + Presets.SV_P5_PRESET, + ], + }, - autoRotation: (player: Player): APLRotation => { - const talentTree = player.getTalentTree(); - const numTargets = player.sim.encounter.targets.length; - if (numTargets >= 4) { - return Presets.ROTATION_PRESET_AOE.rotation.rotation!; - } else if (talentTree == 0) { - return Presets.ROTATION_PRESET_BM.rotation.rotation!; - } else if (talentTree == 1) { - return Presets.ROTATION_PRESET_MM.rotation.rotation!; - } else { - return Presets.ROTATION_PRESET_SV.rotation.rotation!; - } - }, + autoRotation: (player: Player): APLRotation => { + const talentTree = player.getTalentTree(); + const numTargets = player.sim.encounter.targets.length; + if (numTargets >= 4) { + return Presets.ROTATION_PRESET_AOE.rotation.rotation!; + } else if (talentTree == 0) { + return Presets.ROTATION_PRESET_BM.rotation.rotation!; + } else if (talentTree == 1) { + return Presets.ROTATION_PRESET_MM.rotation.rotation!; + } else { + return Presets.ROTATION_PRESET_SV.rotation.rotation!; + } + }, - simpleRotation: (player: Player, simple: HunterRotation, cooldowns: Cooldowns): APLRotation => { - let [prepullActions, actions] = AplUtils.standardCooldownDefaults(cooldowns); + simpleRotation: (player: Player, simple: HunterRotation, cooldowns: Cooldowns): APLRotation => { + let [prepullActions, actions] = AplUtils.standardCooldownDefaults(cooldowns); - const serpentSting = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpGt","lhs":{"remainingTime":{}},"rhs":{"const":{"val":"6s"}}}},"multidot":{"spellId":{"spellId":49001},"maxDots":${simple.multiDotSerpentSting ? 3 : 1},"maxOverlap":{"const":{"val":"0ms"}}}}`); - const scorpidSting = APLAction.fromJsonString(`{"condition":{"auraShouldRefresh":{"auraId":{"spellId":3043},"maxOverlap":{"const":{"val":"0ms"}}}},"castSpell":{"spellId":{"spellId":3043}}}`); - const trapWeave = APLAction.fromJsonString(`{"condition":{"not":{"val":{"dotIsActive":{"spellId":{"spellId":49067}}}}},"castSpell":{"spellId":{"tag":1,"spellId":49067}}}`); - const volley = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":58434}}}`); - const killShot = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":61006}}}`); - const aimedShot = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":49050}}}`); - const multiShot = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":49048}}}`); - const steadyShot = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":49052}}}`); - const silencingShot = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":34490}}}`); - const chimeraShot = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":53209}}}`); - const blackArrow = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":63672}}}`); - const explosiveShot4 = APLAction.fromJsonString(`{"condition":{"not":{"val":{"dotIsActive":{"spellId":{"spellId":60053}}}}},"castSpell":{"spellId":{"spellId":60053}}}`); - const explosiveShot3 = APLAction.fromJsonString(`{"condition":{"dotIsActive":{"spellId":{"spellId":60053}}},"castSpell":{"spellId":{"spellId":60052}}}`); - //const arcaneShot = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":49045}}}`); + const serpentSting = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpGt","lhs":{"remainingTime":{}},"rhs":{"const":{"val":"6s"}}}},"multidot":{"spellId":{"spellId":49001},"maxDots":${simple.multiDotSerpentSting ? 3 : 1},"maxOverlap":{"const":{"val":"0ms"}}}}`); + const scorpidSting = APLAction.fromJsonString(`{"condition":{"auraShouldRefresh":{"auraId":{"spellId":3043},"maxOverlap":{"const":{"val":"0ms"}}}},"castSpell":{"spellId":{"spellId":3043}}}`); + const trapWeave = APLAction.fromJsonString(`{"condition":{"not":{"val":{"dotIsActive":{"spellId":{"spellId":49067}}}}},"castSpell":{"spellId":{"tag":1,"spellId":49067}}}`); + const volley = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":58434}}}`); + const killShot = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":61006}}}`); + const aimedShot = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":49050}}}`); + const multiShot = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":49048}}}`); + const steadyShot = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":49052}}}`); + const silencingShot = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":34490}}}`); + const chimeraShot = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":53209}}}`); + const blackArrow = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":63672}}}`); + const explosiveShot4 = APLAction.fromJsonString(`{"condition":{"not":{"val":{"dotIsActive":{"spellId":{"spellId":60053}}}}},"castSpell":{"spellId":{"spellId":60053}}}`); + const explosiveShot3 = APLAction.fromJsonString(`{"condition":{"dotIsActive":{"spellId":{"spellId":60053}}},"castSpell":{"spellId":{"spellId":60052}}}`); + //const arcaneShot = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":49045}}}`); - if (simple.viperStartManaPercent != 0) { - actions.push(APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"not":{"val":{"auraIsActive":{"auraId":{"spellId":34074}}}}},{"cmp":{"op":"OpLt","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"${(simple.viperStartManaPercent * 100).toFixed(0)}%"}}}}]}},"castSpell":{"spellId":{"spellId":34074}}}`)); - } - if (simple.viperStopManaPercent != 0) { - actions.push(APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"not":{"val":{"auraIsActive":{"auraId":{"spellId":61847}}}}},{"cmp":{"op":"OpGt","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"${(simple.viperStopManaPercent * 100).toFixed(0)}%"}}}}]}},"castSpell":{"spellId":{"spellId":61847}}}`)); - } + if (simple.viperStartManaPercent != 0) { + actions.push(APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"not":{"val":{"auraIsActive":{"auraId":{"spellId":34074}}}}},{"cmp":{"op":"OpLt","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"${(simple.viperStartManaPercent * 100).toFixed(0)}%"}}}}]}},"castSpell":{"spellId":{"spellId":34074}}}`)); + } + if (simple.viperStopManaPercent != 0) { + actions.push(APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"not":{"val":{"auraIsActive":{"auraId":{"spellId":61847}}}}},{"cmp":{"op":"OpGt","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"${(simple.viperStopManaPercent * 100).toFixed(0)}%"}}}}]}},"castSpell":{"spellId":{"spellId":61847}}}`)); + } - const talentTree = player.getTalentTree(); - if (simple.type == Hunter_Rotation_RotationType.Aoe) { - actions.push(...[ - simple.sting == StingType.ScorpidSting ? scorpidSting : null, - simple.sting == StingType.SerpentSting ? serpentSting : null, - simple.trapWeave ? trapWeave : null, - volley, - ].filter(a => a) as Array) - } else if (talentTree == 0) { // BM - actions.push(...[ - killShot, - simple.trapWeave ? trapWeave : null, - simple.sting == StingType.ScorpidSting ? scorpidSting : null, - simple.sting == StingType.SerpentSting ? serpentSting : null, - aimedShot, - multiShot, - steadyShot, - ].filter(a => a) as Array) - } else if (talentTree == 1) { // MM - actions.push(...[ - silencingShot, - killShot, - simple.sting == StingType.ScorpidSting ? scorpidSting : null, - simple.sting == StingType.SerpentSting ? serpentSting : null, - simple.trapWeave ? trapWeave : null, - chimeraShot, - aimedShot, - multiShot, - steadyShot, - ].filter(a => a) as Array) - } else if (talentTree == 2) { // SV - actions.push(...[ - killShot, - explosiveShot4, - simple.allowExplosiveShotDownrank ? explosiveShot3 : null, - simple.trapWeave ? trapWeave : null, - simple.sting == StingType.ScorpidSting ? scorpidSting : null, - simple.sting == StingType.SerpentSting ? serpentSting : null, - blackArrow, - aimedShot, - multiShot, - steadyShot, - ].filter(a => a) as Array) - } + const talentTree = player.getTalentTree(); + if (simple.type == Hunter_Rotation_RotationType.Aoe) { + actions.push(...[ + simple.sting == StingType.ScorpidSting ? scorpidSting : null, + simple.sting == StingType.SerpentSting ? serpentSting : null, + simple.trapWeave ? trapWeave : null, + volley, + ].filter(a => a) as Array) + } else if (talentTree == 0) { // BM + actions.push(...[ + killShot, + simple.trapWeave ? trapWeave : null, + simple.sting == StingType.ScorpidSting ? scorpidSting : null, + simple.sting == StingType.SerpentSting ? serpentSting : null, + aimedShot, + multiShot, + steadyShot, + ].filter(a => a) as Array) + } else if (talentTree == 1) { // MM + actions.push(...[ + silencingShot, + killShot, + simple.sting == StingType.ScorpidSting ? scorpidSting : null, + simple.sting == StingType.SerpentSting ? serpentSting : null, + simple.trapWeave ? trapWeave : null, + chimeraShot, + aimedShot, + multiShot, + steadyShot, + ].filter(a => a) as Array) + } else if (talentTree == 2) { // SV + actions.push(...[ + killShot, + explosiveShot4, + simple.allowExplosiveShotDownrank ? explosiveShot3 : null, + simple.trapWeave ? trapWeave : null, + simple.sting == StingType.ScorpidSting ? scorpidSting : null, + simple.sting == StingType.SerpentSting ? serpentSting : null, + blackArrow, + aimedShot, + multiShot, + steadyShot, + ].filter(a => a) as Array) + } - return APLRotation.create({ - prepullActions: prepullActions, - priorityList: actions.map(action => APLListItem.create({ - action: action, - })) - }); - }, + return APLRotation.create({ + prepullActions: prepullActions, + priorityList: actions.map(action => APLListItem.create({ + action: action, + })) }); + }, +}); + +export class HunterSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/mage/sim.ts b/ui/mage/sim.ts index 8eb6d279bb..878f912486 100644 --- a/ui/mage/sim.ts +++ b/ui/mage/sim.ts @@ -2,7 +2,7 @@ import {Cooldowns, Debuffs, IndividualBuffs, PartyBuffs, RaidBuffs, Spec, Stat, import {APLAction, APLListItem, APLPrepullAction, APLRotation} from '../core/proto/apl.js'; import {Stats} from '../core/proto_utils/stats.js'; import {Player} from '../core/player.js'; -import {IndividualSimUI} from '../core/individual_sim_ui.js'; +import {IndividualSimUI, registerSpecConfig} from '../core/individual_sim_ui.js'; import { Mage_Rotation as MageRotation, Mage_Rotation_PrimaryFireSpell as PrimaryFireSpell, @@ -15,285 +15,287 @@ import * as AplUtils from '../core/proto_utils/apl_utils.js'; import * as MageInputs from './inputs.js'; import * as Presets from './presets.js'; -export class MageSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'mage-sim-ui', - cssScheme: 'mage', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], +const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { + cssClass: 'mage-sim-ui', + cssScheme: 'mage', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + ], - // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatSpellPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatMana, - Stat.StatStamina, - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], - modifyDisplayStats: (player: Player) => { - let stats = new Stats(); + // All stats for which EP should be calculated. + epStats: [ + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatSpellPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatMana, + Stat.StatStamina, + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], + modifyDisplayStats: (player: Player) => { + let stats = new Stats(); - if (player.getTalentTree() === 0) { - stats = stats.addStat(Stat.StatSpellHit, player.getTalents().arcaneFocus * 1 * Mechanics.SPELL_HIT_RATING_PER_HIT_CHANCE); - } + if (player.getTalentTree() === 0) { + stats = stats.addStat(Stat.StatSpellHit, player.getTalents().arcaneFocus * 1 * Mechanics.SPELL_HIT_RATING_PER_HIT_CHANCE); + } - return { - talents: stats, - }; - }, + return { + talents: stats, + }; + }, - defaults: { - // Default equipped gear. - gear: Presets.FIRE_P3_PRESET_HORDE.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatIntellect]: 0.48, - [Stat.StatSpirit]: 0.42, - [Stat.StatSpellPower]: 1, - [Stat.StatSpellHit]: 0.38, - [Stat.StatSpellCrit]: 0.58, - [Stat.StatSpellHaste]: 0.94, - [Stat.StatMP5]: 0.09, - }), - // Default consumes settings. - consumes: Presets.DefaultFireConsumes, - // Default rotation settings. - rotation: Presets.DefaultSimpleRotation, - // Default talents. - talents: Presets.Phase3FireTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultFireOptions, - other: Presets.OtherDefaults, - // Default raid/party buffs settings. - raidBuffs: RaidBuffs.create({ - giftOfTheWild: TristateEffect.TristateEffectImproved, - bloodlust: true, - manaSpringTotem: TristateEffect.TristateEffectImproved, - wrathOfAirTotem: true, - divineSpirit: true, - swiftRetribution: true, - sanctifiedRetribution: true, - demonicPact: 500, - moonkinAura: TristateEffect.TristateEffectImproved, - arcaneBrilliance: true, - }), - partyBuffs: PartyBuffs.create({ - manaTideTotems: 1, - }), - individualBuffs: IndividualBuffs.create({ - blessingOfKings: true, - blessingOfWisdom: TristateEffect.TristateEffectImproved, - innervates: 0, - vampiricTouch: true, - focusMagic: true, - }), - debuffs: Debuffs.create({ - judgementOfWisdom: true, - misery: true, - ebonPlaguebringer: true, - shadowMastery: true, - heartOfTheCrusader: true, - }), - }, + defaults: { + // Default equipped gear. + gear: Presets.FIRE_P3_PRESET_HORDE.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatIntellect]: 0.48, + [Stat.StatSpirit]: 0.42, + [Stat.StatSpellPower]: 1, + [Stat.StatSpellHit]: 0.38, + [Stat.StatSpellCrit]: 0.58, + [Stat.StatSpellHaste]: 0.94, + [Stat.StatMP5]: 0.09, + }), + // Default consumes settings. + consumes: Presets.DefaultFireConsumes, + // Default rotation settings. + rotation: Presets.DefaultSimpleRotation, + // Default talents. + talents: Presets.Phase3FireTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultFireOptions, + other: Presets.OtherDefaults, + // Default raid/party buffs settings. + raidBuffs: RaidBuffs.create({ + giftOfTheWild: TristateEffect.TristateEffectImproved, + bloodlust: true, + manaSpringTotem: TristateEffect.TristateEffectImproved, + wrathOfAirTotem: true, + divineSpirit: true, + swiftRetribution: true, + sanctifiedRetribution: true, + demonicPact: 500, + moonkinAura: TristateEffect.TristateEffectImproved, + arcaneBrilliance: true, + }), + partyBuffs: PartyBuffs.create({ + manaTideTotems: 1, + }), + individualBuffs: IndividualBuffs.create({ + blessingOfKings: true, + blessingOfWisdom: TristateEffect.TristateEffectImproved, + innervates: 0, + vampiricTouch: true, + focusMagic: true, + }), + debuffs: Debuffs.create({ + judgementOfWisdom: true, + misery: true, + ebonPlaguebringer: true, + shadowMastery: true, + heartOfTheCrusader: true, + }), + }, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - MageInputs.Armor, - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: MageInputs.MageRotationConfig, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - //Should add hymn of hope, revitalize, and - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - MageInputs.FocusMagicUptime, - MageInputs.WaterElementalDisobeyChance, - OtherInputs.ReactionTime, - OtherInputs.DistanceFromTarget, - OtherInputs.TankAssignment, - OtherInputs.nibelungAverageCasts, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: true, - }, + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + MageInputs.Armor, + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: MageInputs.MageRotationConfig, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + //Should add hymn of hope, revitalize, and + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + MageInputs.FocusMagicUptime, + MageInputs.WaterElementalDisobeyChance, + OtherInputs.ReactionTime, + OtherInputs.DistanceFromTarget, + OtherInputs.TankAssignment, + OtherInputs.nibelungAverageCasts, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: true, + }, - presets: { - // Preset rotations that the user can quickly select. - rotations: [ - Presets.ROTATION_PRESET_SIMPLE, - Presets.ARCANE_ROTATION_PRESET_DEFAULT, - Presets.FIRE_ROTATION_PRESET_DEFAULT, - Presets.FROSTFIRE_ROTATION_PRESET_DEFAULT, - Presets.FROST_ROTATION_PRESET_DEFAULT, - Presets.ARCANE_ROTATION_PRESET_AOE, - Presets.FIRE_ROTATION_PRESET_AOE, - Presets.FROST_ROTATION_PRESET_AOE, - ], - // Preset talents that the user can quickly select. - talents: [ - Presets.ArcaneTalents, - Presets.FireTalents, - Presets.FrostfireTalents, - Presets.FrostTalents, - Presets.Phase3FireTalents, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.ARCANE_PRERAID_PRESET, - Presets.FIRE_PRERAID_PRESET, - Presets.ARCANE_P1_PRESET, - Presets.FIRE_P1_PRESET, - Presets.FROST_P1_PRESET, - Presets.ARCANE_P2_PRESET, - Presets.FIRE_P2_PRESET, - Presets.FROST_P2_PRESET, - Presets.FFB_P2_PRESET, - Presets.ARCANE_P3_PRESET_ALLIANCE, - Presets.ARCANE_P3_PRESET_HORDE, - Presets.FROST_P3_PRESET_ALLIANCE, - Presets.FROST_P3_PRESET_HORDE, - Presets.FIRE_P3_PRESET_ALLIANCE, - Presets.FIRE_P3_PRESET_HORDE, - Presets.FFB_P3_PRESET_ALLIANCE, - Presets.FFB_P3_PRESET_HORDE, - Presets.FIRE_P4_PRESET_HORDE, - Presets.FIRE_P4_PRESET_ALLIANCE, - Presets.FFB_P4_PRESET_HORDE, - Presets.FFB_P4_PRESET_ALLIANCE, - Presets.ARCANE_P4_PRESET_HORDE, - Presets.ARCANE_P4_PRESET_ALLIANCE, - ], - }, + presets: { + // Preset rotations that the user can quickly select. + rotations: [ + Presets.ROTATION_PRESET_SIMPLE, + Presets.ARCANE_ROTATION_PRESET_DEFAULT, + Presets.FIRE_ROTATION_PRESET_DEFAULT, + Presets.FROSTFIRE_ROTATION_PRESET_DEFAULT, + Presets.FROST_ROTATION_PRESET_DEFAULT, + Presets.ARCANE_ROTATION_PRESET_AOE, + Presets.FIRE_ROTATION_PRESET_AOE, + Presets.FROST_ROTATION_PRESET_AOE, + ], + // Preset talents that the user can quickly select. + talents: [ + Presets.ArcaneTalents, + Presets.FireTalents, + Presets.FrostfireTalents, + Presets.FrostTalents, + Presets.Phase3FireTalents, + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.ARCANE_PRERAID_PRESET, + Presets.FIRE_PRERAID_PRESET, + Presets.ARCANE_P1_PRESET, + Presets.FIRE_P1_PRESET, + Presets.FROST_P1_PRESET, + Presets.ARCANE_P2_PRESET, + Presets.FIRE_P2_PRESET, + Presets.FROST_P2_PRESET, + Presets.FFB_P2_PRESET, + Presets.ARCANE_P3_PRESET_ALLIANCE, + Presets.ARCANE_P3_PRESET_HORDE, + Presets.FROST_P3_PRESET_ALLIANCE, + Presets.FROST_P3_PRESET_HORDE, + Presets.FIRE_P3_PRESET_ALLIANCE, + Presets.FIRE_P3_PRESET_HORDE, + Presets.FFB_P3_PRESET_ALLIANCE, + Presets.FFB_P3_PRESET_HORDE, + Presets.FIRE_P4_PRESET_HORDE, + Presets.FIRE_P4_PRESET_ALLIANCE, + Presets.FFB_P4_PRESET_HORDE, + Presets.FFB_P4_PRESET_ALLIANCE, + Presets.ARCANE_P4_PRESET_HORDE, + Presets.ARCANE_P4_PRESET_ALLIANCE, + ], + }, - autoRotation: (player: Player): APLRotation => { - const talentTree = player.getTalentTree(); - const numTargets = player.sim.encounter.targets.length; - if (numTargets > 3) { - if (talentTree == 0) { - return Presets.ARCANE_ROTATION_PRESET_AOE.rotation.rotation!; - } else if (talentTree == 1) { - return Presets.FIRE_ROTATION_PRESET_AOE.rotation.rotation!; - } else { - return Presets.FROST_ROTATION_PRESET_AOE.rotation.rotation!; - } - } else if (talentTree == 0) { - return Presets.ARCANE_ROTATION_PRESET_DEFAULT.rotation.rotation!; - } else if (talentTree == 1) { - if (player.getTalents().iceShards > 0) { - return Presets.FROSTFIRE_ROTATION_PRESET_DEFAULT.rotation.rotation!; - } - return Presets.FIRE_ROTATION_PRESET_DEFAULT.rotation.rotation!; - } else { - return Presets.FROST_ROTATION_PRESET_DEFAULT.rotation.rotation!; - } - }, + autoRotation: (player: Player): APLRotation => { + const talentTree = player.getTalentTree(); + const numTargets = player.sim.encounter.targets.length; + if (numTargets > 3) { + if (talentTree == 0) { + return Presets.ARCANE_ROTATION_PRESET_AOE.rotation.rotation!; + } else if (talentTree == 1) { + return Presets.FIRE_ROTATION_PRESET_AOE.rotation.rotation!; + } else { + return Presets.FROST_ROTATION_PRESET_AOE.rotation.rotation!; + } + } else if (talentTree == 0) { + return Presets.ARCANE_ROTATION_PRESET_DEFAULT.rotation.rotation!; + } else if (talentTree == 1) { + if (player.getTalents().iceShards > 0) { + return Presets.FROSTFIRE_ROTATION_PRESET_DEFAULT.rotation.rotation!; + } + return Presets.FIRE_ROTATION_PRESET_DEFAULT.rotation.rotation!; + } else { + return Presets.FROST_ROTATION_PRESET_DEFAULT.rotation.rotation!; + } + }, - simpleRotation: (player: Player, simple: MageRotation, cooldowns: Cooldowns): APLRotation => { - let [prepullActions, actions] = AplUtils.standardCooldownDefaults(cooldowns); + simpleRotation: (player: Player, simple: MageRotation, cooldowns: Cooldowns): APLRotation => { + let [prepullActions, actions] = AplUtils.standardCooldownDefaults(cooldowns); - const prepullMirrorImage = APLPrepullAction.fromJsonString(`{"action":{"castSpell":{"spellId":{"spellId":55342}}},"doAtValue":{"const":{"val":"-2s"}}}`); + const prepullMirrorImage = APLPrepullAction.fromJsonString(`{"action":{"castSpell":{"spellId":{"spellId":55342}}},"doAtValue":{"const":{"val":"-2s"}}}`); - const berserking = APLAction.fromJsonString(`{"condition":{"not":{"val":{"auraIsActive":{"auraId":{"spellId":12472}}}}},"castSpell":{"spellId":{"spellId":26297}}}`); - const hyperspeedAcceleration = APLAction.fromJsonString(`{"condition":{"not":{"val":{"auraIsActive":{"auraId":{"spellId":12472}}}}},"castSpell":{"spellId":{"spellId":54758}}}`); - const combatPot = APLAction.fromJsonString(`{"condition":{"not":{"val":{"auraIsActive":{"auraId":{"spellId":12472}}}}},"castSpell":{"spellId":{"otherId":"OtherActionPotion"}}}`); - const evocation = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpLe","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"25%"}}}},"castSpell":{"spellId":{"spellId":12051}}}`); + const berserking = APLAction.fromJsonString(`{"condition":{"not":{"val":{"auraIsActive":{"auraId":{"spellId":12472}}}}},"castSpell":{"spellId":{"spellId":26297}}}`); + const hyperspeedAcceleration = APLAction.fromJsonString(`{"condition":{"not":{"val":{"auraIsActive":{"auraId":{"spellId":12472}}}}},"castSpell":{"spellId":{"spellId":54758}}}`); + const combatPot = APLAction.fromJsonString(`{"condition":{"not":{"val":{"auraIsActive":{"auraId":{"spellId":12472}}}}},"castSpell":{"spellId":{"otherId":"OtherActionPotion"}}}`); + const evocation = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpLe","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"25%"}}}},"castSpell":{"spellId":{"spellId":12051}}}`); - const arcaneBlastBelowStacks = APLAction.fromJsonString(`{"condition":{"or":{"vals":[{"cmp":{"op":"OpLt","lhs":{"auraNumStacks":{"auraId":{"spellId":36032}}},"rhs":{"const":{"val":"4"}}}},{"and":{"vals":[{"cmp":{"op":"OpLt","lhs":{"auraNumStacks":{"auraId":{"spellId":36032}}},"rhs":{"const":{"val":"3"}}}},{"cmp":{"op":"OpLt","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"${(simple.only3ArcaneBlastStacksBelowManaPercent * 100).toFixed(0)}%"}}}}]}}]}},"castSpell":{"spellId":{"spellId":42897}}}`); - const arcaneMissilesWithMissileBarrageBelowMana = APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"auraIsActiveWithReactionTime":{"auraId":{"spellId":44401}}},{"cmp":{"op":"OpLt","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"${(simple.missileBarrageBelowManaPercent * 100).toFixed(0)}%"}}}}]}},"castSpell":{"spellId":{"spellId":42846}}}`); - const arcaneMisslesWithMissileBarrage = APLAction.fromJsonString(`{"condition":{"auraIsActiveWithReactionTime":{"auraId":{"spellId":44401}}},"castSpell":{"spellId":{"spellId":42846}}}`); - const arcaneBlastAboveMana = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpGt","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"${(simple.blastWithoutMissileBarrageAboveManaPercent * 100).toFixed(0)}%"}}}},"castSpell":{"spellId":{"spellId":42897}}}`); - const arcaneMissiles = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":42846}}}`); - const arcaneBarrage = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":44781}}}`); + const arcaneBlastBelowStacks = APLAction.fromJsonString(`{"condition":{"or":{"vals":[{"cmp":{"op":"OpLt","lhs":{"auraNumStacks":{"auraId":{"spellId":36032}}},"rhs":{"const":{"val":"4"}}}},{"and":{"vals":[{"cmp":{"op":"OpLt","lhs":{"auraNumStacks":{"auraId":{"spellId":36032}}},"rhs":{"const":{"val":"3"}}}},{"cmp":{"op":"OpLt","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"${(simple.only3ArcaneBlastStacksBelowManaPercent * 100).toFixed(0)}%"}}}}]}}]}},"castSpell":{"spellId":{"spellId":42897}}}`); + const arcaneMissilesWithMissileBarrageBelowMana = APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"auraIsActiveWithReactionTime":{"auraId":{"spellId":44401}}},{"cmp":{"op":"OpLt","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"${(simple.missileBarrageBelowManaPercent * 100).toFixed(0)}%"}}}}]}},"castSpell":{"spellId":{"spellId":42846}}}`); + const arcaneMisslesWithMissileBarrage = APLAction.fromJsonString(`{"condition":{"auraIsActiveWithReactionTime":{"auraId":{"spellId":44401}}},"castSpell":{"spellId":{"spellId":42846}}}`); + const arcaneBlastAboveMana = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpGt","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"${(simple.blastWithoutMissileBarrageAboveManaPercent * 100).toFixed(0)}%"}}}},"castSpell":{"spellId":{"spellId":42897}}}`); + const arcaneMissiles = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":42846}}}`); + const arcaneBarrage = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":44781}}}`); - const maintainImpScorch = APLAction.fromJsonString(`{"condition":{"auraShouldRefresh":{"auraId":{"spellId":12873},"maxOverlap":{"const":{"val":"4s"}}}},"castSpell":{"spellId":{"spellId":42859}}}`); - const pyroWithHotStreak = APLAction.fromJsonString(`{"condition":{"auraIsActiveWithReactionTime":{"auraId":{"spellId":44448}}},"castSpell":{"spellId":{"spellId":42891}}}`); - const livingBomb = APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"cmp":{"op":"OpGt","lhs":{"remainingTime":{}},"rhs":{"const":{"val":"12s"}}}}]}},"multidot":{"spellId":{"spellId":55360},"maxDots":10,"maxOverlap":{"const":{"val":"0ms"}}}}`); - const cheekyFireBlastFinisher = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpLe","lhs":{"remainingTime":{}},"rhs":{"spellCastTime":{"spellId":{"spellId":42859}}}}},"castSpell":{"spellId":{"spellId":42873}}}`); - const scorchFinisher = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpLe","lhs":{"remainingTime":{}},"rhs":{"const":{"val":"4s"}}}},"castSpell":{"spellId":{"spellId":42859}}}`); - const fireball = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":42833}}}`); - const frostfireBolt = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":47610}}}`); - const scorch = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":42859}}}`); + const maintainImpScorch = APLAction.fromJsonString(`{"condition":{"auraShouldRefresh":{"auraId":{"spellId":12873},"maxOverlap":{"const":{"val":"4s"}}}},"castSpell":{"spellId":{"spellId":42859}}}`); + const pyroWithHotStreak = APLAction.fromJsonString(`{"condition":{"auraIsActiveWithReactionTime":{"auraId":{"spellId":44448}}},"castSpell":{"spellId":{"spellId":42891}}}`); + const livingBomb = APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"cmp":{"op":"OpGt","lhs":{"remainingTime":{}},"rhs":{"const":{"val":"12s"}}}}]}},"multidot":{"spellId":{"spellId":55360},"maxDots":10,"maxOverlap":{"const":{"val":"0ms"}}}}`); + const cheekyFireBlastFinisher = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpLe","lhs":{"remainingTime":{}},"rhs":{"spellCastTime":{"spellId":{"spellId":42859}}}}},"castSpell":{"spellId":{"spellId":42873}}}`); + const scorchFinisher = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpLe","lhs":{"remainingTime":{}},"rhs":{"const":{"val":"4s"}}}},"castSpell":{"spellId":{"spellId":42859}}}`); + const fireball = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":42833}}}`); + const frostfireBolt = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":47610}}}`); + const scorch = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":42859}}}`); - const deepFreeze = APLAction.fromJsonString(`{"condition":{"auraIsActive":{"auraId":{"spellId":44545}}},"castSpell":{"spellId":{"spellId":44572}}}`); - const frostfireBoltWithBrainFreeze = APLAction.fromJsonString(`{"condition":{"auraIsActiveWithReactionTime":{"auraId":{"spellId":44549}}},"castSpell":{"spellId":{"spellId":47610}}}`); - const frostbolt = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":42842}}}`); - const iceLance = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpEq","lhs":{"auraNumStacks":{"auraId":{"spellId":44545}}},"rhs":{"const":{"val":"1"}}}},"castSpell":{"spellId":{"spellId":42914}}}`); + const deepFreeze = APLAction.fromJsonString(`{"condition":{"auraIsActive":{"auraId":{"spellId":44545}}},"castSpell":{"spellId":{"spellId":44572}}}`); + const frostfireBoltWithBrainFreeze = APLAction.fromJsonString(`{"condition":{"auraIsActiveWithReactionTime":{"auraId":{"spellId":44549}}},"castSpell":{"spellId":{"spellId":47610}}}`); + const frostbolt = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":42842}}}`); + const iceLance = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpEq","lhs":{"auraNumStacks":{"auraId":{"spellId":44545}}},"rhs":{"const":{"val":"1"}}}},"castSpell":{"spellId":{"spellId":42914}}}`); - prepullActions.push(prepullMirrorImage); - if (player.getTalents().improvedScorch > 0 && simple.maintainImprovedScorch) { - actions.push(maintainImpScorch); - } + prepullActions.push(prepullMirrorImage); + if (player.getTalents().improvedScorch > 0 && simple.maintainImprovedScorch) { + actions.push(maintainImpScorch); + } - const talentTree = player.getTalentTree(); - if (talentTree == 0) { // Arcane - actions.push(...[ - berserking, - hyperspeedAcceleration, - combatPot, - simple.missileBarrageBelowManaPercent > 0 ? arcaneMissilesWithMissileBarrageBelowMana : null, - arcaneBlastBelowStacks, - arcaneMisslesWithMissileBarrage, - evocation, - arcaneBlastAboveMana, - simple.useArcaneBarrage ? arcaneBarrage : null, - arcaneMissiles, - ].filter(a => a) as Array) - } else if (talentTree == 1) { // Fire - actions.push(...[ - pyroWithHotStreak, - livingBomb, - cheekyFireBlastFinisher, - scorchFinisher, - simple.primaryFireSpell == PrimaryFireSpell.Fireball - ? fireball - : (simple.primaryFireSpell == PrimaryFireSpell.FrostfireBolt - ? frostfireBolt : scorch), - ].filter(a => a) as Array) - } else if (talentTree == 2) { // Frost - actions.push(...[ - berserking, - hyperspeedAcceleration, - evocation, - deepFreeze, - frostfireBoltWithBrainFreeze, - simple.useIceLance ? iceLance : null, - frostbolt, - ].filter(a => a) as Array) - } + const talentTree = player.getTalentTree(); + if (talentTree == 0) { // Arcane + actions.push(...[ + berserking, + hyperspeedAcceleration, + combatPot, + simple.missileBarrageBelowManaPercent > 0 ? arcaneMissilesWithMissileBarrageBelowMana : null, + arcaneBlastBelowStacks, + arcaneMisslesWithMissileBarrage, + evocation, + arcaneBlastAboveMana, + simple.useArcaneBarrage ? arcaneBarrage : null, + arcaneMissiles, + ].filter(a => a) as Array) + } else if (talentTree == 1) { // Fire + actions.push(...[ + pyroWithHotStreak, + livingBomb, + cheekyFireBlastFinisher, + scorchFinisher, + simple.primaryFireSpell == PrimaryFireSpell.Fireball + ? fireball + : (simple.primaryFireSpell == PrimaryFireSpell.FrostfireBolt + ? frostfireBolt : scorch), + ].filter(a => a) as Array) + } else if (talentTree == 2) { // Frost + actions.push(...[ + berserking, + hyperspeedAcceleration, + evocation, + deepFreeze, + frostfireBoltWithBrainFreeze, + simple.useIceLance ? iceLance : null, + frostbolt, + ].filter(a => a) as Array) + } - return APLRotation.create({ - prepullActions: prepullActions, - priorityList: actions.map(action => APLListItem.create({ - action: action, - })) - }); - }, + return APLRotation.create({ + prepullActions: prepullActions, + priorityList: actions.map(action => APLListItem.create({ + action: action, + })) }); + }, +}); + +export class MageSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/protection_paladin/sim.ts b/ui/protection_paladin/sim.ts index 091b7c2231..02aef11e11 100644 --- a/ui/protection_paladin/sim.ts +++ b/ui/protection_paladin/sim.ts @@ -6,14 +6,12 @@ import { Spec } from '../core/proto/common.js'; import { Stat, PseudoStat } from '../core/proto/common.js'; import { TristateEffect } from '../core/proto/common.js' import { - APLAction, - APLListItem, APLRotation, } from '../core/proto/apl.js'; import { Stats } from '../core/proto_utils/stats.js'; import { Player } from '../core/player.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; -import { EventID, TypedEvent } from '../core/typed_event.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; +import { TypedEvent } from '../core/typed_event.js'; import * as IconInputs from '../core/components/icon_inputs.js'; import * as OtherInputs from '../core/components/other_inputs.js'; @@ -24,219 +22,221 @@ import { PaladinMajorGlyph, PaladinSeal } from '../core/proto/paladin.js'; import * as ProtectionPaladinInputs from './inputs.js'; import * as Presets from './presets.js'; -export class ProtectionPaladinSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'protection-paladin-sim-ui', - cssScheme: 'paladin', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], +const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionPaladin, { + cssClass: 'protection-paladin-sim-ui', + cssScheme: 'paladin', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + ], + + // All stats for which EP should be calculated. + epStats: [ + Stat.StatStamina, + Stat.StatStrength, + Stat.StatAgility, + Stat.StatAttackPower, + Stat.StatMeleeHit, + Stat.StatSpellHit, + Stat.StatMeleeCrit, + Stat.StatExpertise, + Stat.StatMeleeHaste, + Stat.StatArmorPenetration, + Stat.StatSpellPower, + Stat.StatArmor, + Stat.StatBonusArmor, + Stat.StatDefense, + Stat.StatBlock, + Stat.StatBlockValue, + Stat.StatDodge, + Stat.StatParry, + Stat.StatResilience, + Stat.StatNatureResistance, + Stat.StatShadowResistance, + Stat.StatFrostResistance, + ], + epPseudoStats: [ + PseudoStat.PseudoStatMainHandDps, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatSpellPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatArmor, + Stat.StatBonusArmor, + Stat.StatStamina, + Stat.StatStrength, + Stat.StatAgility, + Stat.StatAttackPower, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatExpertise, + Stat.StatArmorPenetration, + Stat.StatSpellPower, + Stat.StatSpellHit, + Stat.StatDefense, + Stat.StatBlock, + Stat.StatBlockValue, + Stat.StatDodge, + Stat.StatParry, + Stat.StatResilience, + Stat.StatNatureResistance, + Stat.StatShadowResistance, + Stat.StatFrostResistance, + ], + modifyDisplayStats: (player: Player) => { + let stats = new Stats(); - // All stats for which EP should be calculated. - epStats: [ - Stat.StatStamina, - Stat.StatStrength, - Stat.StatAgility, - Stat.StatAttackPower, - Stat.StatMeleeHit, - Stat.StatSpellHit, - Stat.StatMeleeCrit, - Stat.StatExpertise, - Stat.StatMeleeHaste, - Stat.StatArmorPenetration, - Stat.StatSpellPower, - Stat.StatArmor, - Stat.StatBonusArmor, - Stat.StatDefense, - Stat.StatBlock, - Stat.StatBlockValue, - Stat.StatDodge, - Stat.StatParry, - Stat.StatResilience, - Stat.StatNatureResistance, - Stat.StatShadowResistance, - Stat.StatFrostResistance, - ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatSpellPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatArmor, - Stat.StatBonusArmor, - Stat.StatStamina, - Stat.StatStrength, - Stat.StatAgility, - Stat.StatAttackPower, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatExpertise, - Stat.StatArmorPenetration, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatDefense, - Stat.StatBlock, - Stat.StatBlockValue, - Stat.StatDodge, - Stat.StatParry, - Stat.StatResilience, - Stat.StatNatureResistance, - Stat.StatShadowResistance, - Stat.StatFrostResistance, - ], - modifyDisplayStats: (player: Player) => { - let stats = new Stats(); + TypedEvent.freezeAllAndDo(() => { + if (player.getMajorGlyphs().includes(PaladinMajorGlyph.GlyphOfSealOfVengeance) && (player.getSpecOptions().seal == PaladinSeal.Vengeance)) { + stats = stats.addStat(Stat.StatExpertise, 10 * Mechanics.EXPERTISE_PER_QUARTER_PERCENT_REDUCTION); + } + }) - TypedEvent.freezeAllAndDo(() => { - if (player.getMajorGlyphs().includes(PaladinMajorGlyph.GlyphOfSealOfVengeance) && (player.getSpecOptions().seal == PaladinSeal.Vengeance)) { - stats = stats.addStat(Stat.StatExpertise, 10 * Mechanics.EXPERTISE_PER_QUARTER_PERCENT_REDUCTION); - } - }) + return { + talents: stats, + }; + }, + defaults: { + // Default equipped gear. + gear: Presets.P3_PRESET.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatArmor]: 0.07, + [Stat.StatBonusArmor]: 0.06, + [Stat.StatStamina]: 1.14, + [Stat.StatStrength]: 1.00, + [Stat.StatAgility]: 0.62, + [Stat.StatAttackPower]: 0.26, + [Stat.StatExpertise]: 0.69, + [Stat.StatMeleeHit]: 0.79, + [Stat.StatMeleeCrit]: 0.30, + [Stat.StatMeleeHaste]: 0.17, + [Stat.StatArmorPenetration]: 0.04, + [Stat.StatSpellPower]: 0.13, + [Stat.StatBlock]: 0.52, + [Stat.StatBlockValue]: 0.28, + [Stat.StatDodge]: 0.46, + [Stat.StatParry]: 0.61, + [Stat.StatDefense]: 0.54, + }, { + [PseudoStat.PseudoStatMainHandDps]: 3.33, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultRotation, + // Default talents. + talents: Presets.GenericAoeTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + // Default raid/party buffs settings. + raidBuffs: RaidBuffs.create({ + giftOfTheWild: TristateEffect.TristateEffectImproved, + powerWordFortitude: TristateEffect.TristateEffectImproved, + strengthOfEarthTotem: TristateEffect.TristateEffectImproved, + arcaneBrilliance: true, + unleashedRage: true, + leaderOfThePack: TristateEffect.TristateEffectRegular, + icyTalons: true, + totemOfWrath: true, + demonicPact: 500, + swiftRetribution: true, + moonkinAura: TristateEffect.TristateEffectRegular, + sanctifiedRetribution: true, + manaSpringTotem: TristateEffect.TristateEffectRegular, + bloodlust: true, + thorns: TristateEffect.TristateEffectImproved, + devotionAura: TristateEffect.TristateEffectImproved, + shadowProtection: true, + }), + partyBuffs: PartyBuffs.create({ + }), + individualBuffs: IndividualBuffs.create({ + blessingOfKings: true, + blessingOfSanctuary: true, + blessingOfWisdom: TristateEffect.TristateEffectImproved, + blessingOfMight: TristateEffect.TristateEffectImproved, + }), + debuffs: Debuffs.create({ + judgementOfWisdom: true, + judgementOfLight: true, + misery: true, + faerieFire: TristateEffect.TristateEffectImproved, + ebonPlaguebringer: true, + totemOfWrath: true, + shadowMastery: true, + bloodFrenzy: true, + mangle: true, + exposeArmor: true, + sunderArmor: true, + vindication: true, + thunderClap: TristateEffect.TristateEffectImproved, + insectSwarm: true, + }), + }, - return { - talents: stats, - }; - }, - defaults: { - // Default equipped gear. - gear: Presets.P3_PRESET.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatArmor]: 0.07, - [Stat.StatBonusArmor]: 0.06, - [Stat.StatStamina]: 1.14, - [Stat.StatStrength]: 1.00, - [Stat.StatAgility]: 0.62, - [Stat.StatAttackPower]: 0.26, - [Stat.StatExpertise]: 0.69, - [Stat.StatMeleeHit]: 0.79, - [Stat.StatMeleeCrit]: 0.30, - [Stat.StatMeleeHaste]: 0.17, - [Stat.StatArmorPenetration]: 0.04, - [Stat.StatSpellPower]: 0.13, - [Stat.StatBlock]: 0.52, - [Stat.StatBlockValue]: 0.28, - [Stat.StatDodge]: 0.46, - [Stat.StatParry]: 0.61, - [Stat.StatDefense]: 0.54, - }, { - [PseudoStat.PseudoStatMainHandDps]: 3.33, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultRotation, - // Default talents. - talents: Presets.GenericAoeTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - // Default raid/party buffs settings. - raidBuffs: RaidBuffs.create({ - giftOfTheWild: TristateEffect.TristateEffectImproved, - powerWordFortitude: TristateEffect.TristateEffectImproved, - strengthOfEarthTotem: TristateEffect.TristateEffectImproved, - arcaneBrilliance: true, - unleashedRage: true, - leaderOfThePack: TristateEffect.TristateEffectRegular, - icyTalons: true, - totemOfWrath: true, - demonicPact: 500, - swiftRetribution: true, - moonkinAura: TristateEffect.TristateEffectRegular, - sanctifiedRetribution: true, - manaSpringTotem: TristateEffect.TristateEffectRegular, - bloodlust: true, - thorns: TristateEffect.TristateEffectImproved, - devotionAura: TristateEffect.TristateEffectImproved, - shadowProtection: true, - }), - partyBuffs: PartyBuffs.create({ - }), - individualBuffs: IndividualBuffs.create({ - blessingOfKings: true, - blessingOfSanctuary: true, - blessingOfWisdom: TristateEffect.TristateEffectImproved, - blessingOfMight: TristateEffect.TristateEffectImproved, - }), - debuffs: Debuffs.create({ - judgementOfWisdom: true, - judgementOfLight: true, - misery: true, - faerieFire: TristateEffect.TristateEffectImproved, - ebonPlaguebringer: true, - totemOfWrath: true, - shadowMastery: true, - bloodFrenzy: true, - mangle: true, - exposeArmor: true, - sunderArmor: true, - vindication: true, - thunderClap: TristateEffect.TristateEffectImproved, - insectSwarm: true, - }), - }, + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: ProtectionPaladinInputs.ProtectionPaladinRotationConfig, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + IconInputs.HealthBuff, + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + OtherInputs.TankAssignment, + OtherInputs.IncomingHps, + OtherInputs.HealingCadence, + OtherInputs.HealingCadenceVariation, + OtherInputs.BurstWindow, + OtherInputs.HpPercentForDefensives, + OtherInputs.InspirationUptime, + ProtectionPaladinInputs.AuraSelection, + ProtectionPaladinInputs.UseAvengingWrath, + ProtectionPaladinInputs.JudgementSelection, + ProtectionPaladinInputs.StartingSealSelection, + OtherInputs.InFrontOfTarget, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: ProtectionPaladinInputs.ProtectionPaladinRotationConfig, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - IconInputs.HealthBuff, - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - OtherInputs.TankAssignment, - OtherInputs.IncomingHps, - OtherInputs.HealingCadence, - OtherInputs.HealingCadenceVariation, - OtherInputs.BurstWindow, - OtherInputs.HpPercentForDefensives, - OtherInputs.InspirationUptime, - ProtectionPaladinInputs.AuraSelection, - ProtectionPaladinInputs.UseAvengingWrath, - ProtectionPaladinInputs.JudgementSelection, - ProtectionPaladinInputs.StartingSealSelection, - OtherInputs.InFrontOfTarget, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.GenericAoeTalents, + ], + // Preset rotations that the user can quickly select. + rotations: [ + Presets.ROTATION_DEFAULT, + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.PRERAID_PRESET, + Presets.P4_PRERAID_PRESET, + Presets.P1_PRESET, + Presets.P2_PRESET, + Presets.P3_PRESET, + Presets.P4_PRESET, + ], + }, - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.GenericAoeTalents, - ], - // Preset rotations that the user can quickly select. - rotations: [ - Presets.ROTATION_DEFAULT, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.PRERAID_PRESET, - Presets.P4_PRERAID_PRESET, - Presets.P1_PRESET, - Presets.P2_PRESET, - Presets.P3_PRESET, - Presets.P4_PRESET, - ], - }, + autoRotation: (_player: Player): APLRotation => { + return Presets.ROTATION_DEFAULT.rotation.rotation!; + }, +}); - autoRotation: (player: Player): APLRotation => { - return Presets.ROTATION_DEFAULT.rotation.rotation!; - }, - }); +export class ProtectionPaladinSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/protection_warrior/sim.ts b/ui/protection_warrior/sim.ts index f86d495166..45634c4ea4 100644 --- a/ui/protection_warrior/sim.ts +++ b/ui/protection_warrior/sim.ts @@ -18,240 +18,240 @@ import { } from '../core/proto/apl.js'; import { Stats } from '../core/proto_utils/stats.js'; import { Player } from '../core/player.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; -import { ProtectionWarrior, ProtectionWarrior_Rotation as ProtectionWarriorRotation, WarriorTalents as WarriorTalents, ProtectionWarrior_Options as ProtectionWarriorOptions } from '../core/proto/warrior.js'; +import { ProtectionWarrior_Rotation as ProtectionWarriorRotation } from '../core/proto/warrior.js'; import * as IconInputs from '../core/components/icon_inputs.js'; import * as OtherInputs from '../core/components/other_inputs.js'; -import * as Tooltips from '../core/constants/tooltips.js'; import * as AplUtils from '../core/proto_utils/apl_utils.js'; import * as ProtectionWarriorInputs from './inputs.js'; import * as Presets from './presets.js'; -export class ProtectionWarriorSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'protection-warrior-sim-ui', - cssScheme: 'warrior', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], +const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionWarrior, { + cssClass: 'protection-warrior-sim-ui', + cssScheme: 'warrior', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + ], - // All stats for which EP should be calculated. - epStats: [ - Stat.StatStamina, - Stat.StatStrength, - Stat.StatAgility, - Stat.StatAttackPower, - Stat.StatExpertise, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatArmor, - Stat.StatBonusArmor, - Stat.StatArmorPenetration, - Stat.StatDefense, - Stat.StatBlock, - Stat.StatBlockValue, - Stat.StatDodge, - Stat.StatParry, - Stat.StatResilience, - Stat.StatNatureResistance, - Stat.StatShadowResistance, - Stat.StatFrostResistance, - ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatAttackPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatArmor, - Stat.StatBonusArmor, - Stat.StatStamina, - Stat.StatStrength, - Stat.StatAgility, - Stat.StatAttackPower, - Stat.StatExpertise, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatArmorPenetration, - Stat.StatDefense, - Stat.StatBlock, - Stat.StatBlockValue, - Stat.StatDodge, - Stat.StatParry, - Stat.StatResilience, - Stat.StatNatureResistance, - Stat.StatShadowResistance, - Stat.StatFrostResistance, - ], + // All stats for which EP should be calculated. + epStats: [ + Stat.StatStamina, + Stat.StatStrength, + Stat.StatAgility, + Stat.StatAttackPower, + Stat.StatExpertise, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatArmor, + Stat.StatBonusArmor, + Stat.StatArmorPenetration, + Stat.StatDefense, + Stat.StatBlock, + Stat.StatBlockValue, + Stat.StatDodge, + Stat.StatParry, + Stat.StatResilience, + Stat.StatNatureResistance, + Stat.StatShadowResistance, + Stat.StatFrostResistance, + ], + epPseudoStats: [ + PseudoStat.PseudoStatMainHandDps, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatAttackPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatArmor, + Stat.StatBonusArmor, + Stat.StatStamina, + Stat.StatStrength, + Stat.StatAgility, + Stat.StatAttackPower, + Stat.StatExpertise, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatArmorPenetration, + Stat.StatDefense, + Stat.StatBlock, + Stat.StatBlockValue, + Stat.StatDodge, + Stat.StatParry, + Stat.StatResilience, + Stat.StatNatureResistance, + Stat.StatShadowResistance, + Stat.StatFrostResistance, + ], - defaults: { - // Default equipped gear. - gear: Presets.P3_PRESET.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatArmor]: 0.174, - [Stat.StatBonusArmor]: 0.155, - [Stat.StatStamina]: 2.336, - [Stat.StatStrength]: 1.555, - [Stat.StatAgility]: 2.771, - [Stat.StatAttackPower]: 0.32, - [Stat.StatExpertise]: 1.44, - [Stat.StatMeleeHit]: 1.432, - [Stat.StatMeleeCrit]: 0.925, - [Stat.StatMeleeHaste]: 0.431, - [Stat.StatArmorPenetration]: 1.055, - [Stat.StatBlock]: 1.320, - [Stat.StatBlockValue]: 1.373, - [Stat.StatDodge]: 2.606, - [Stat.StatParry]: 2.649, - [Stat.StatDefense]: 3.305, - }, { - [PseudoStat.PseudoStatMainHandDps]: 6.081, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultRotation, - // Default talents. - talents: Presets.StandardTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - // Default raid/party buffs settings. - raidBuffs: RaidBuffs.create({ - giftOfTheWild: TristateEffect.TristateEffectImproved, - powerWordFortitude: TristateEffect.TristateEffectImproved, - abominationsMight: true, - swiftRetribution: true, - bloodlust: true, - strengthOfEarthTotem: TristateEffect.TristateEffectImproved, - leaderOfThePack: TristateEffect.TristateEffectImproved, - sanctifiedRetribution: true, - devotionAura: TristateEffect.TristateEffectImproved, - stoneskinTotem: TristateEffect.TristateEffectImproved, - icyTalons: true, - retributionAura: true, - thorns: TristateEffect.TristateEffectImproved, - shadowProtection: true, - }), - partyBuffs: PartyBuffs.create({ - }), - individualBuffs: IndividualBuffs.create({ - blessingOfKings: true, - blessingOfMight: TristateEffect.TristateEffectImproved, - blessingOfSanctuary: true, - }), - debuffs: Debuffs.create({ - sunderArmor: true, - mangle: true, - vindication: true, - faerieFire: TristateEffect.TristateEffectImproved, - insectSwarm: true, - bloodFrenzy: true, - judgementOfLight: true, - heartOfTheCrusader: true, - frostFever: TristateEffect.TristateEffectImproved, - }), - }, + defaults: { + // Default equipped gear. + gear: Presets.P3_PRESET.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatArmor]: 0.174, + [Stat.StatBonusArmor]: 0.155, + [Stat.StatStamina]: 2.336, + [Stat.StatStrength]: 1.555, + [Stat.StatAgility]: 2.771, + [Stat.StatAttackPower]: 0.32, + [Stat.StatExpertise]: 1.44, + [Stat.StatMeleeHit]: 1.432, + [Stat.StatMeleeCrit]: 0.925, + [Stat.StatMeleeHaste]: 0.431, + [Stat.StatArmorPenetration]: 1.055, + [Stat.StatBlock]: 1.320, + [Stat.StatBlockValue]: 1.373, + [Stat.StatDodge]: 2.606, + [Stat.StatParry]: 2.649, + [Stat.StatDefense]: 3.305, + }, { + [PseudoStat.PseudoStatMainHandDps]: 6.081, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultRotation, + // Default talents. + talents: Presets.StandardTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + // Default raid/party buffs settings. + raidBuffs: RaidBuffs.create({ + giftOfTheWild: TristateEffect.TristateEffectImproved, + powerWordFortitude: TristateEffect.TristateEffectImproved, + abominationsMight: true, + swiftRetribution: true, + bloodlust: true, + strengthOfEarthTotem: TristateEffect.TristateEffectImproved, + leaderOfThePack: TristateEffect.TristateEffectImproved, + sanctifiedRetribution: true, + devotionAura: TristateEffect.TristateEffectImproved, + stoneskinTotem: TristateEffect.TristateEffectImproved, + icyTalons: true, + retributionAura: true, + thorns: TristateEffect.TristateEffectImproved, + shadowProtection: true, + }), + partyBuffs: PartyBuffs.create({ + }), + individualBuffs: IndividualBuffs.create({ + blessingOfKings: true, + blessingOfMight: TristateEffect.TristateEffectImproved, + blessingOfSanctuary: true, + }), + debuffs: Debuffs.create({ + sunderArmor: true, + mangle: true, + vindication: true, + faerieFire: TristateEffect.TristateEffectImproved, + insectSwarm: true, + bloodFrenzy: true, + judgementOfLight: true, + heartOfTheCrusader: true, + frostFever: TristateEffect.TristateEffectImproved, + }), + }, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ProtectionWarriorInputs.ShoutPicker, - ProtectionWarriorInputs.ShatteringThrow, - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: ProtectionWarriorInputs.ProtectionWarriorRotationConfig, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - IconInputs.HealthBuff, - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - OtherInputs.TankAssignment, - OtherInputs.IncomingHps, - OtherInputs.HealingCadence, - OtherInputs.HealingCadenceVariation, - OtherInputs.BurstWindow, - OtherInputs.HpPercentForDefensives, - OtherInputs.InspirationUptime, - ProtectionWarriorInputs.StartingRage, - OtherInputs.InFrontOfTarget, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + ProtectionWarriorInputs.ShoutPicker, + ProtectionWarriorInputs.ShatteringThrow, + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: ProtectionWarriorInputs.ProtectionWarriorRotationConfig, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + IconInputs.HealthBuff, + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + OtherInputs.TankAssignment, + OtherInputs.IncomingHps, + OtherInputs.HealingCadence, + OtherInputs.HealingCadenceVariation, + OtherInputs.BurstWindow, + OtherInputs.HpPercentForDefensives, + OtherInputs.InspirationUptime, + ProtectionWarriorInputs.StartingRage, + OtherInputs.InFrontOfTarget, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.StandardTalents, - Presets.UATalents, - ], - // Preset rotations that the user can quickly select. - rotations: [ - Presets.ROTATION_DEFAULT, - Presets.ROTATION_PRESET_SIMPLE, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.PRERAID_BALANCED_PRESET, - Presets.P4_PRERAID_PRESET, - Presets.P1_BALANCED_PRESET, - Presets.P2_SURVIVAL_PRESET, - Presets.P3_PRESET, - Presets.P4_PRESET, - ], - }, + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.StandardTalents, + Presets.UATalents, + ], + // Preset rotations that the user can quickly select. + rotations: [ + Presets.ROTATION_DEFAULT, + Presets.ROTATION_PRESET_SIMPLE, + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.PRERAID_BALANCED_PRESET, + Presets.P4_PRERAID_PRESET, + Presets.P1_BALANCED_PRESET, + Presets.P2_SURVIVAL_PRESET, + Presets.P3_PRESET, + Presets.P4_PRESET, + ], + }, - autoRotation: (player: Player): APLRotation => { - return Presets.ROTATION_DEFAULT.rotation.rotation!; - }, - - simpleRotation: (player: Player, simple: ProtectionWarriorRotation, cooldowns: Cooldowns): APLRotation => { - let [prepullActions, actions] = AplUtils.standardCooldownDefaults(cooldowns); + autoRotation: (_player: Player): APLRotation => { + return Presets.ROTATION_DEFAULT.rotation.rotation!; + }, + + simpleRotation: (player: Player, simple: ProtectionWarriorRotation, cooldowns: Cooldowns): APLRotation => { + let [prepullActions, actions] = AplUtils.standardCooldownDefaults(cooldowns); - const preShout = APLPrepullAction.fromJsonString(`{"action":{"castSpell":{"spellId":{"spellId":47440}}},"doAtValue":{"const":{"val":"-10s"}}}`); + const preShout = APLPrepullAction.fromJsonString(`{"action":{"castSpell":{"spellId":{"spellId":47440}}},"doAtValue":{"const":{"val":"-10s"}}}`); - const heroicStrike = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpGe","lhs":{"currentRage":{}},"rhs":{"const":{"val":"30"}}}},"castSpell":{"spellId":{"tag":1,"spellId":47450}}}`); - const shieldSlam = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":47488}}}`); - const revenge = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":57823}}}`); - const refreshShout = APLAction.fromJsonString(`{"condition":{"auraShouldRefresh":{"sourceUnit":{"type":"Self"},"auraId":{"spellId":47440},"maxOverlap":{"const":{"val":"3s"}}}},"castSpell":{"spellId":{"spellId":47440}}}`); - const refreshTclap = APLAction.fromJsonString(`{"condition":{"auraShouldRefresh":{"auraId":{"spellId":47502},"maxOverlap":{"const":{"val":"2s"}}}},"castSpell":{"spellId":{"spellId":47502}}}`); - const refreshDemo = APLAction.fromJsonString(`{"condition":{"auraShouldRefresh":{"auraId":{"spellId":47437},"maxOverlap":{"const":{"val":"2s"}}}},"castSpell":{"spellId":{"spellId":25203}}}`); - const devastate = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":47498}}}`); + const heroicStrike = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpGe","lhs":{"currentRage":{}},"rhs":{"const":{"val":"30"}}}},"castSpell":{"spellId":{"tag":1,"spellId":47450}}}`); + const shieldSlam = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":47488}}}`); + const revenge = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":57823}}}`); + const refreshShout = APLAction.fromJsonString(`{"condition":{"auraShouldRefresh":{"sourceUnit":{"type":"Self"},"auraId":{"spellId":47440},"maxOverlap":{"const":{"val":"3s"}}}},"castSpell":{"spellId":{"spellId":47440}}}`); + const refreshTclap = APLAction.fromJsonString(`{"condition":{"auraShouldRefresh":{"auraId":{"spellId":47502},"maxOverlap":{"const":{"val":"2s"}}}},"castSpell":{"spellId":{"spellId":47502}}}`); + const refreshDemo = APLAction.fromJsonString(`{"condition":{"auraShouldRefresh":{"auraId":{"spellId":47437},"maxOverlap":{"const":{"val":"2s"}}}},"castSpell":{"spellId":{"spellId":25203}}}`); + const devastate = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":47498}}}`); - prepullActions.push(preShout); + prepullActions.push(preShout); - actions.push(...[ - heroicStrike, - shieldSlam, - revenge, - refreshShout, - refreshTclap, - refreshDemo, - devastate, - ].filter(a => a) as Array) + actions.push(...[ + heroicStrike, + shieldSlam, + revenge, + refreshShout, + refreshTclap, + refreshDemo, + devastate, + ].filter(a => a) as Array) - return APLRotation.create({ - prepullActions: prepullActions, - priorityList: actions.map(action => APLListItem.create({ - action: action, - })) - }); - }, - + return APLRotation.create({ + prepullActions: prepullActions, + priorityList: actions.map(action => APLListItem.create({ + action: action, + })) }); + }, +}); + +export class ProtectionWarriorSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/restoration_druid/sim.ts b/ui/restoration_druid/sim.ts index 053ec3d528..3c49f2583d 100644 --- a/ui/restoration_druid/sim.ts +++ b/ui/restoration_druid/sim.ts @@ -5,116 +5,120 @@ import { } from '../core/proto/apl.js'; import { Stats } from '../core/proto_utils/stats.js'; import { Player } from '../core/player.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; import * as OtherInputs from '../core/components/other_inputs.js'; import * as DruidInputs from './inputs.js'; import * as Presets from './presets.js'; -export class RestorationDruidSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'restoration-druid-sim-ui', - cssScheme: 'druid', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], +const SPEC_CONFIG = registerSpecConfig(Spec.SpecRestorationDruid, { + cssClass: 'restoration-druid-sim-ui', + cssScheme: 'druid', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + ], + + // All stats for which EP should be calculated. + epStats: [ + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatSpellPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatMana, + Stat.StatStamina, + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], - // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatSpellPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatMana, - Stat.StatStamina, - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], + defaults: { + // Default equipped gear. + gear: Presets.P1_PRESET.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatIntellect]: 0.38, + [Stat.StatSpirit]: 0.34, + [Stat.StatSpellPower]: 1, + [Stat.StatSpellCrit]: 0.69, + [Stat.StatSpellHaste]: 0.77, + [Stat.StatMP5]: 0.00, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultRotation, + // Default talents. + talents: Presets.CelestialFocusTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + // Default raid/party buffs settings. + raidBuffs: Presets.DefaultRaidBuffs, - defaults: { - // Default equipped gear. - gear: Presets.P1_PRESET.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatIntellect]: 0.38, - [Stat.StatSpirit]: 0.34, - [Stat.StatSpellPower]: 1, - [Stat.StatSpellCrit]: 0.69, - [Stat.StatSpellHaste]: 0.77, - [Stat.StatMP5]: 0.00, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultRotation, - // Default talents. - talents: Presets.CelestialFocusTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - // Default raid/party buffs settings. - raidBuffs: Presets.DefaultRaidBuffs, + partyBuffs: Presets.DefaultPartyBuffs, - partyBuffs: Presets.DefaultPartyBuffs, + individualBuffs: Presets.DefaultIndividualBuffs, - individualBuffs: Presets.DefaultIndividualBuffs, + debuffs: Presets.DefaultDebuffs, - debuffs: Presets.DefaultDebuffs, + other: Presets.OtherDefaults, + }, - other: Presets.OtherDefaults, - }, + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + DruidInputs.SelfInnervate, + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: DruidInputs.RestorationDruidRotationConfig, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + OtherInputs.TankAssignment, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - DruidInputs.SelfInnervate, - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: DruidInputs.RestorationDruidRotationConfig, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - OtherInputs.TankAssignment, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.CelestialFocusTalents, + Presets.ThiccRestoTalents, + ], + rotations: [ + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.PRERAID_PRESET, + Presets.P1_PRESET, + Presets.P2_PRESET, + ], + }, - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.CelestialFocusTalents, - Presets.ThiccRestoTalents, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.PRERAID_PRESET, - Presets.P1_PRESET, - Presets.P2_PRESET, - ], - }, + autoRotation: (_player: Player): APLRotation => { + return APLRotation.create(); + }, +}); - autoRotation: (_player: Player): APLRotation => { - return APLRotation.create(); - }, - }); +export class RestorationDruidSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/restoration_shaman/sim.ts b/ui/restoration_shaman/sim.ts index 51aa936a10..31f368684d 100644 --- a/ui/restoration_shaman/sim.ts +++ b/ui/restoration_shaman/sim.ts @@ -10,7 +10,7 @@ import { } from '../core/proto/apl.js'; import { Player } from '../core/player.js'; import { Stats } from '../core/proto_utils/stats.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; import { TotemsSection } from '../core/components/totem_inputs.js'; import * as OtherInputs from '../core/components/other_inputs.js'; @@ -19,133 +19,137 @@ import * as Mechanics from '../core/constants/mechanics.js'; import * as ShamanInputs from './inputs.js'; import * as Presets from './presets.js'; -export class RestorationShamanSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'restoration-shaman-sim-ui', - cssScheme: 'shaman', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], - warnings: [ - ], +const SPEC_CONFIG = registerSpecConfig(Spec.SpecRestorationShaman, { + cssClass: 'restoration-shaman-sim-ui', + cssScheme: 'shaman', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + ], + warnings: [ + ], + + // All stats for which EP should be calculated. + epStats: [ + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatSpellPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatMana, + Stat.StatStamina, + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], + modifyDisplayStats: (player: Player) => { + let stats = new Stats(); + stats = stats.addStat(Stat.StatSpellCrit, player.getTalents().tidalMastery * 1 * Mechanics.SPELL_CRIT_RATING_PER_CRIT_CHANCE); + return { + talents: stats, + }; + }, - // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatSpellPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatMana, - Stat.StatStamina, - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], - modifyDisplayStats: (player: Player) => { - let stats = new Stats(); - stats = stats.addStat(Stat.StatSpellCrit, player.getTalents().tidalMastery * 1 * Mechanics.SPELL_CRIT_RATING_PER_CRIT_CHANCE); - return { - talents: stats, - }; - }, + defaults: { + // Default equipped gear. + gear: Presets.P1_PRESET.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatIntellect]: 0.22, + [Stat.StatSpirit]: 0.05, + [Stat.StatSpellPower]: 1, + [Stat.StatSpellCrit]: 0.67, + [Stat.StatSpellHaste]: 1.29, + [Stat.StatMP5]: 0.08, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultRotation, + // Default talents. + talents: Presets.RaidHealingTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + // Default raid/party buffs settings. + raidBuffs: RaidBuffs.create({ + arcaneBrilliance: true, + divineSpirit: true, + giftOfTheWild: TristateEffect.TristateEffectImproved, + moonkinAura: TristateEffect.TristateEffectImproved, + sanctifiedRetribution: true, + }), + partyBuffs: PartyBuffs.create({ + }), + individualBuffs: IndividualBuffs.create({ + blessingOfKings: true, + blessingOfWisdom: 2, + vampiricTouch: true, + }), + debuffs: Debuffs.create({ + faerieFire: TristateEffect.TristateEffectImproved, + judgementOfWisdom: true, + misery: true, + curseOfElements: true, + shadowMastery: true, + }), + }, + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + ShamanInputs.ShamanShieldInput, + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: ShamanInputs.RestorationShamanRotationConfig, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + OtherInputs.TankAssignment + ], + }, + customSections: [ + TotemsSection, + ], + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, - defaults: { - // Default equipped gear. - gear: Presets.P1_PRESET.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatIntellect]: 0.22, - [Stat.StatSpirit]: 0.05, - [Stat.StatSpellPower]: 1, - [Stat.StatSpellCrit]: 0.67, - [Stat.StatSpellHaste]: 1.29, - [Stat.StatMP5]: 0.08, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultRotation, - // Default talents. - talents: Presets.RaidHealingTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - // Default raid/party buffs settings. - raidBuffs: RaidBuffs.create({ - arcaneBrilliance: true, - divineSpirit: true, - giftOfTheWild: TristateEffect.TristateEffectImproved, - moonkinAura: TristateEffect.TristateEffectImproved, - sanctifiedRetribution: true, - }), - partyBuffs: PartyBuffs.create({ - }), - individualBuffs: IndividualBuffs.create({ - blessingOfKings: true, - blessingOfWisdom: 2, - vampiricTouch: true, - }), - debuffs: Debuffs.create({ - faerieFire: TristateEffect.TristateEffectImproved, - judgementOfWisdom: true, - misery: true, - curseOfElements: true, - shadowMastery: true, - }), - }, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ShamanInputs.ShamanShieldInput, - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: ShamanInputs.RestorationShamanRotationConfig, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - OtherInputs.TankAssignment - ], - }, - customSections: [ - TotemsSection, - ], - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.RaidHealingTalents, + Presets.TankHealingTalents, + ], + rotations: [ + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.PRERAID_PRESET, + Presets.P1_PRESET, + Presets.P2_PRESET, + ], + }, - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.RaidHealingTalents, - Presets.TankHealingTalents, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.PRERAID_PRESET, - Presets.P1_PRESET, - Presets.P2_PRESET, - ], - }, + autoRotation: (_player: Player): APLRotation => { + return APLRotation.create(); + }, +}); - autoRotation: (_player: Player): APLRotation => { - return APLRotation.create(); - }, - }); +export class RestorationShamanSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/retribution_paladin/sim.ts b/ui/retribution_paladin/sim.ts index 0fced5644e..52b1c06596 100644 --- a/ui/retribution_paladin/sim.ts +++ b/ui/retribution_paladin/sim.ts @@ -6,14 +6,12 @@ import { Spec } from '../core/proto/common.js'; import { Stat, PseudoStat } from '../core/proto/common.js'; import { TristateEffect } from '../core/proto/common.js' import { - APLAction, - APLListItem, APLRotation, } from '../core/proto/apl.js'; import { Stats } from '../core/proto_utils/stats.js'; import { Player } from '../core/player.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; -import { EventID, TypedEvent } from '../core/typed_event.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; +import { TypedEvent } from '../core/typed_event.js'; import * as IconInputs from '../core/components/icon_inputs.js'; import * as OtherInputs from '../core/components/other_inputs.js'; @@ -24,207 +22,209 @@ import { PaladinMajorGlyph, PaladinSeal } from '../core/proto/paladin.js'; import * as RetributionPaladinInputs from './inputs.js'; import * as Presets from './presets.js'; -export class RetributionPaladinSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'retribution-paladin-sim-ui', - cssScheme: 'paladin', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], +const SPEC_CONFIG = registerSpecConfig(Spec.SpecRetributionPaladin, { + cssClass: 'retribution-paladin-sim-ui', + cssScheme: 'paladin', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + ], + + // All stats for which EP should be calculated. + epStats: [ + Stat.StatStrength, + Stat.StatAgility, + Stat.StatIntellect, + Stat.StatMP5, + Stat.StatAttackPower, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatExpertise, + Stat.StatArmorPenetration, + Stat.StatSpellPower, + Stat.StatSpellCrit, + Stat.StatSpellHit, + Stat.StatSpellHaste, + ], + epPseudoStats: [ + PseudoStat.PseudoStatMainHandDps, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatAttackPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatStrength, + Stat.StatAgility, + Stat.StatIntellect, + Stat.StatMP5, + Stat.StatAttackPower, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatExpertise, + Stat.StatArmorPenetration, + Stat.StatSpellHaste, + Stat.StatSpellPower, + Stat.StatSpellCrit, + Stat.StatSpellHit, + Stat.StatMana, + Stat.StatHealth, + ], + modifyDisplayStats: (player: Player) => { + let stats = new Stats(); - // All stats for which EP should be calculated. - epStats: [ - Stat.StatStrength, - Stat.StatAgility, - Stat.StatIntellect, - Stat.StatMP5, - Stat.StatAttackPower, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatExpertise, - Stat.StatArmorPenetration, - Stat.StatSpellPower, - Stat.StatSpellCrit, - Stat.StatSpellHit, - Stat.StatSpellHaste, - ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatAttackPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatStrength, - Stat.StatAgility, - Stat.StatIntellect, - Stat.StatMP5, - Stat.StatAttackPower, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatExpertise, - Stat.StatArmorPenetration, - Stat.StatSpellHaste, - Stat.StatSpellPower, - Stat.StatSpellCrit, - Stat.StatSpellHit, - Stat.StatMana, - Stat.StatHealth, - ], - modifyDisplayStats: (player: Player) => { - let stats = new Stats(); + TypedEvent.freezeAllAndDo(() => { + if (player.getMajorGlyphs().includes(PaladinMajorGlyph.GlyphOfSealOfVengeance) && (player.getSpecOptions().seal == PaladinSeal.Vengeance)) { + stats = stats.addStat(Stat.StatExpertise, 10 * Mechanics.EXPERTISE_PER_QUARTER_PERCENT_REDUCTION); + } + }) - TypedEvent.freezeAllAndDo(() => { - if (player.getMajorGlyphs().includes(PaladinMajorGlyph.GlyphOfSealOfVengeance) && (player.getSpecOptions().seal == PaladinSeal.Vengeance)) { - stats = stats.addStat(Stat.StatExpertise, 10 * Mechanics.EXPERTISE_PER_QUARTER_PERCENT_REDUCTION); - } - }) + return { + talents: stats, + }; + }, - return { - talents: stats, - }; - }, + defaults: { + // Default equipped gear. + gear: Presets.P1_PRESET.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatStrength]: 2.53, + [Stat.StatAgility]: 1.13, + [Stat.StatIntellect]: 0.15, + [Stat.StatSpellPower]: 0.32, + [Stat.StatSpellHit]: 0.41, + [Stat.StatSpellCrit]: 0.01, + [Stat.StatSpellHaste]: 0.12, + [Stat.StatMP5]: 0.05, + [Stat.StatAttackPower]: 1, + [Stat.StatMeleeHit]: 1.96, + [Stat.StatMeleeCrit]: 1.16, + [Stat.StatMeleeHaste]: 1.44, + [Stat.StatArmorPenetration]: 0.76, + [Stat.StatExpertise]: 1.80, + }, { + [PseudoStat.PseudoStatMainHandDps]: 7.33, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultRotation, + // Default talents. + talents: Presets.AuraMasteryTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + // Default raid/party buffs settings. + raidBuffs: RaidBuffs.create({ + arcaneBrilliance: true, + divineSpirit: true, + giftOfTheWild: TristateEffect.TristateEffectImproved, + bloodlust: true, + manaSpringTotem: TristateEffect.TristateEffectRegular, + hornOfWinter: true, + battleShout: TristateEffect.TristateEffectImproved, + sanctifiedRetribution: true, + swiftRetribution: true, + elementalOath: true, + rampage: true, + trueshotAura: true, + icyTalons: true, + totemOfWrath: true, + wrathOfAirTotem: true, + demonicPact: 500, + }), + partyBuffs: PartyBuffs.create({ + }), + individualBuffs: IndividualBuffs.create({ + judgementsOfTheWise: true, + blessingOfKings: true, + blessingOfMight: TristateEffect.TristateEffectImproved, + }), + debuffs: Debuffs.create({ + shadowMastery: true, + totemOfWrath: true, + judgementOfWisdom: true, + judgementOfLight: true, + misery: true, + curseOfElements: true, + bloodFrenzy: true, + exposeArmor: true, + sunderArmor: true, + faerieFire: TristateEffect.TristateEffectImproved, + curseOfWeakness: TristateEffect.TristateEffectRegular, + }), + }, - defaults: { - // Default equipped gear. - gear: Presets.P1_PRESET.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatStrength]: 2.53, - [Stat.StatAgility]: 1.13, - [Stat.StatIntellect]: 0.15, - [Stat.StatSpellPower]: 0.32, - [Stat.StatSpellHit]: 0.41, - [Stat.StatSpellCrit]: 0.01, - [Stat.StatSpellHaste]: 0.12, - [Stat.StatMP5]: 0.05, - [Stat.StatAttackPower]: 1, - [Stat.StatMeleeHit]: 1.96, - [Stat.StatMeleeCrit]: 1.16, - [Stat.StatMeleeHaste]: 1.44, - [Stat.StatArmorPenetration]: 0.76, - [Stat.StatExpertise]: 1.80, - }, { - [PseudoStat.PseudoStatMainHandDps]: 7.33, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultRotation, - // Default talents. - talents: Presets.AuraMasteryTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - // Default raid/party buffs settings. - raidBuffs: RaidBuffs.create({ - arcaneBrilliance: true, - divineSpirit: true, - giftOfTheWild: TristateEffect.TristateEffectImproved, - bloodlust: true, - manaSpringTotem: TristateEffect.TristateEffectRegular, - hornOfWinter: true, - battleShout: TristateEffect.TristateEffectImproved, - sanctifiedRetribution: true, - swiftRetribution: true, - elementalOath: true, - rampage: true, - trueshotAura: true, - icyTalons: true, - totemOfWrath: true, - wrathOfAirTotem: true, - demonicPact: 500, - }), - partyBuffs: PartyBuffs.create({ - }), - individualBuffs: IndividualBuffs.create({ - judgementsOfTheWise: true, - blessingOfKings: true, - blessingOfMight: TristateEffect.TristateEffectImproved, - }), - debuffs: Debuffs.create({ - shadowMastery: true, - totemOfWrath: true, - judgementOfWisdom: true, - judgementOfLight: true, - misery: true, - curseOfElements: true, - bloodFrenzy: true, - exposeArmor: true, - sunderArmor: true, - faerieFire: TristateEffect.TristateEffectImproved, - curseOfWeakness: TristateEffect.TristateEffectRegular, - }), - }, + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + RetributionPaladinInputs.AuraSelection, + RetributionPaladinInputs.JudgementSelection, + RetributionPaladinInputs.StartingSealSelection, + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: { + inputs: [ + RetributionPaladinInputs.RotationSelector, + RetributionPaladinInputs.RetributionPaladinRotationDivinePleaSelection, + RetributionPaladinInputs.RetributionPaladinRotationAvoidClippingConsecration, + RetributionPaladinInputs.RetributionPaladinRotationHoldLastAvengingWrathUntilExecution, + RetributionPaladinInputs.RetributionPaladinRotationCancelChaosBane, + RetributionPaladinInputs.RetributionPaladinRotationDivinePleaSelectionAlternate, + RetributionPaladinInputs.RetributionPaladinRotationDivinePleaPercentageConfig, + RetributionPaladinInputs.RetributionPaladinRotationConsSlackConfig, + RetributionPaladinInputs.RetributionPaladinRotationExoSlackConfig, + RetributionPaladinInputs.RetributionPaladinRotationHolyWrathConfig, + RetributionPaladinInputs.RetributionPaladinSoVTargets, + RetributionPaladinInputs.RetributionPaladinRotationPriorityConfig, + RetributionPaladinInputs.RetributionPaladinCastSequenceConfig + ] + }, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + IconInputs.ReplenishmentBuff, + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + OtherInputs.TankAssignment, + OtherInputs.InFrontOfTarget, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - RetributionPaladinInputs.AuraSelection, - RetributionPaladinInputs.JudgementSelection, - RetributionPaladinInputs.StartingSealSelection, - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: { - inputs: [ - RetributionPaladinInputs.RotationSelector, - RetributionPaladinInputs.RetributionPaladinRotationDivinePleaSelection, - RetributionPaladinInputs.RetributionPaladinRotationAvoidClippingConsecration, - RetributionPaladinInputs.RetributionPaladinRotationHoldLastAvengingWrathUntilExecution, - RetributionPaladinInputs.RetributionPaladinRotationCancelChaosBane, - RetributionPaladinInputs.RetributionPaladinRotationDivinePleaSelectionAlternate, - RetributionPaladinInputs.RetributionPaladinRotationDivinePleaPercentageConfig, - RetributionPaladinInputs.RetributionPaladinRotationConsSlackConfig, - RetributionPaladinInputs.RetributionPaladinRotationExoSlackConfig, - RetributionPaladinInputs.RetributionPaladinRotationHolyWrathConfig, - RetributionPaladinInputs.RetributionPaladinSoVTargets, - RetributionPaladinInputs.RetributionPaladinRotationPriorityConfig, - RetributionPaladinInputs.RetributionPaladinCastSequenceConfig - ] - }, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - IconInputs.ReplenishmentBuff, - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - OtherInputs.TankAssignment, - OtherInputs.InFrontOfTarget, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, + presets: { + rotations: [ + Presets.ROTATION_PRESET_LEGACY_DEFAULT, + Presets.ROTATION_PRESET_DEFAULT, + ], + // Preset talents that the user can quickly select. + talents: [ + Presets.AuraMasteryTalents, + Presets.DivineSacTalents, + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.PRERAID_PRESET, + Presets.P1_PRESET, + Presets.P2_PRESET, + Presets.P3_PRESET, + Presets.P4_PRESET, + Presets.P5_PRESET, + ], + }, - presets: { - rotations: [ - Presets.ROTATION_PRESET_LEGACY_DEFAULT, - Presets.ROTATION_PRESET_DEFAULT, - ], - // Preset talents that the user can quickly select. - talents: [ - Presets.AuraMasteryTalents, - Presets.DivineSacTalents, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.PRERAID_PRESET, - Presets.P1_PRESET, - Presets.P2_PRESET, - Presets.P3_PRESET, - Presets.P4_PRESET, - Presets.P5_PRESET, - ], - }, + autoRotation: (_player: Player): APLRotation => { + return Presets.ROTATION_PRESET_DEFAULT.rotation.rotation!; + }, +}); - autoRotation: (player: Player): APLRotation => { - return Presets.ROTATION_PRESET_DEFAULT.rotation.rotation!; - }, - }); +export class RetributionPaladinSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/rogue/sim.ts b/ui/rogue/sim.ts index 9738566ffe..5b1f5b28cd 100644 --- a/ui/rogue/sim.ts +++ b/ui/rogue/sim.ts @@ -11,13 +11,11 @@ import { WeaponType } from '../core/proto/common.js'; import { - APLAction, - APLListItem, APLRotation, } from '../core/proto/apl.js'; import { Player } from '../core/player.js'; import { Stats } from '../core/proto_utils/stats.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; import { Rogue_Options_PoisonImbue, @@ -36,390 +34,392 @@ import * as RogueInputs from './inputs.js'; import * as Presets from './presets.js'; import { DefaultOptions } from './presets.js'; -export class RogueSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'rogue-sim-ui', - cssScheme: 'rogue', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - 'Rotations are not fully optimized, especially for non-standard setups.', - ], - warnings: [ - (simUI: IndividualSimUI) => { - return { - updateOn: simUI.sim.encounter.changeEmitter, - getContent: () => { - let hasNoArmor = false - for (const target of simUI.sim.encounter.targets) { - if (new Stats(target.stats).getStat(Stat.StatArmor) <= 0) { - hasNoArmor = true - break - } - } - if (hasNoArmor) { - return 'One or more targets have no armor. Check advanced encounter settings.'; - } else { - return ''; - } - }, - }; +const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { + cssClass: 'rogue-sim-ui', + cssScheme: 'rogue', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + 'Rotations are not fully optimized, especially for non-standard setups.', + ], + warnings: [ + (simUI: IndividualSimUI) => { + return { + updateOn: simUI.sim.encounter.changeEmitter, + getContent: () => { + let hasNoArmor = false + for (const target of simUI.sim.encounter.targets) { + if (new Stats(target.stats).getStat(Stat.StatArmor) <= 0) { + hasNoArmor = true + break + } + } + if (hasNoArmor) { + return 'One or more targets have no armor. Check advanced encounter settings.'; + } else { + return ''; + } }, - (simUI: IndividualSimUI) => { - return { - updateOn: simUI.player.changeEmitter, - getContent: () => { - if ( - simUI.player.getTalents().mutilate && - (simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType != WeaponType.WeaponTypeDagger || - simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType != WeaponType.WeaponTypeDagger) - ) { - return '"Mutilate" talent selected, but daggers not equipped in both hands.'; - } else { - return ''; - } - }, - }; + }; + }, + (simUI: IndividualSimUI) => { + return { + updateOn: simUI.player.changeEmitter, + getContent: () => { + if ( + simUI.player.getTalents().mutilate && + (simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType != WeaponType.WeaponTypeDagger || + simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType != WeaponType.WeaponTypeDagger) + ) { + return '"Mutilate" talent selected, but daggers not equipped in both hands.'; + } else { + return ''; + } }, - (simUI: IndividualSimUI) => { - return { - updateOn: simUI.player.changeEmitter, - getContent: () => { - if (simUI.player.getRotation().combatBuilder == CombatBuilder.Backstab && - simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType != WeaponType.WeaponTypeDagger) { - return 'Builder "Backstab" selected, but no dagger equipped.'; - } else { - return ''; - } - }, - }; + }; + }, + (simUI: IndividualSimUI) => { + return { + updateOn: simUI.player.changeEmitter, + getContent: () => { + if (simUI.player.getRotation().combatBuilder == CombatBuilder.Backstab && + simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType != WeaponType.WeaponTypeDagger) { + return 'Builder "Backstab" selected, but no dagger equipped.'; + } else { + return ''; + } }, - (simUI: IndividualSimUI) => { - return { - updateOn: simUI.player.changeEmitter, - getContent: () => { - if (simUI.player.getTalents().hackAndSlash) { - if (simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType == WeaponType.WeaponTypeSword || - simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType == WeaponType.WeaponTypeAxe || - simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType == WeaponType.WeaponTypeSword || - simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType == WeaponType.WeaponTypeAxe) { - return ''; - } else { - return '"Hack and Slash" talent selected, but swords or axes not equipped.'; - } - } else { - return ''; - } - }, - }; + }; + }, + (simUI: IndividualSimUI) => { + return { + updateOn: simUI.player.changeEmitter, + getContent: () => { + if (simUI.player.getTalents().hackAndSlash) { + if (simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType == WeaponType.WeaponTypeSword || + simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType == WeaponType.WeaponTypeAxe || + simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType == WeaponType.WeaponTypeSword || + simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType == WeaponType.WeaponTypeAxe) { + return ''; + } else { + return '"Hack and Slash" talent selected, but swords or axes not equipped.'; + } + } else { + return ''; + } }, - (simUI: IndividualSimUI) => { - return { - updateOn: simUI.player.changeEmitter, - getContent: () => { - if (simUI.player.getTalents().closeQuartersCombat) { - if (simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType == WeaponType.WeaponTypeFist || - simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType == WeaponType.WeaponTypeDagger || - simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType == WeaponType.WeaponTypeFist || - simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType == WeaponType.WeaponTypeDagger) { - return ''; - } else { - return '"Close Quarters Combat" talent selected, but fists or daggers not equipped.'; - } - } else { - return ''; - } - }, - }; + }; + }, + (simUI: IndividualSimUI) => { + return { + updateOn: simUI.player.changeEmitter, + getContent: () => { + if (simUI.player.getTalents().closeQuartersCombat) { + if (simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType == WeaponType.WeaponTypeFist || + simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType == WeaponType.WeaponTypeDagger || + simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType == WeaponType.WeaponTypeFist || + simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType == WeaponType.WeaponTypeDagger) { + return ''; + } else { + return '"Close Quarters Combat" talent selected, but fists or daggers not equipped.'; + } + } else { + return ''; + } }, - (simUI: IndividualSimUI) => { - return { - updateOn: simUI.player.changeEmitter, - getContent: () => { - if (simUI.player.getTalents().maceSpecialization) { - if (simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType == WeaponType.WeaponTypeMace || - simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType == WeaponType.WeaponTypeMace) { - return ''; - } else { - return '"Mace Specialization" talent selected, but maces not equipped.'; - } - } else { - return ''; - } - }, - }; + }; + }, + (simUI: IndividualSimUI) => { + return { + updateOn: simUI.player.changeEmitter, + getContent: () => { + if (simUI.player.getTalents().maceSpecialization) { + if (simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType == WeaponType.WeaponTypeMace || + simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType == WeaponType.WeaponTypeMace) { + return ''; + } else { + return '"Mace Specialization" talent selected, but maces not equipped.'; + } + } else { + return ''; + } }, - (simUI: IndividualSimUI) => { - return { - updateOn: simUI.player.changeEmitter, - getContent: () => { - if (simUI.player.getInFrontOfTarget() && (simUI.player.getRotation().combatBuilder == CombatBuilder.Backstab || - simUI.player.getRotation().openWithGarrote)) { - return 'Option "In Front of Target" selected, but using Backstab or Garrote as builder or opener.'; - } else { - return ''; - } - }, - }; + }; + }, + (simUI: IndividualSimUI) => { + return { + updateOn: simUI.player.changeEmitter, + getContent: () => { + if (simUI.player.getInFrontOfTarget() && (simUI.player.getRotation().combatBuilder == CombatBuilder.Backstab || + simUI.player.getRotation().openWithGarrote)) { + return 'Option "In Front of Target" selected, but using Backstab or Garrote as builder or opener.'; + } else { + return ''; + } }, - (simUI: IndividualSimUI) => { - return { - updateOn: simUI.player.changeEmitter, - getContent: () => { - if (simUI.player.getRotation().combatBuilder == CombatBuilder.HemorrhageCombat && !simUI.player.getTalents().hemorrhage) { - return 'Builder "Hemorrhage" selected, but Hemorrhage is not talented.'; - } else { - return ''; - } - }, - }; + }; + }, + (simUI: IndividualSimUI) => { + return { + updateOn: simUI.player.changeEmitter, + getContent: () => { + if (simUI.player.getRotation().combatBuilder == CombatBuilder.HemorrhageCombat && !simUI.player.getTalents().hemorrhage) { + return 'Builder "Hemorrhage" selected, but Hemorrhage is not talented.'; + } else { + return ''; + } }, - (simUI: IndividualSimUI) => { - return { - updateOn: simUI.player.changeEmitter, - getContent: () => { - if (simUI.player.getRotation().useGhostlyStrike && !simUI.player.getMajorGlyphs().includes(RogueMajorGlyph.GlyphOfGhostlyStrike)) { - return '"Use Ghostly Strike" selected, but missing Glyph of Ghostly Strike.'; - } else { - return ''; - } - }, - }; + }; + }, + (simUI: IndividualSimUI) => { + return { + updateOn: simUI.player.changeEmitter, + getContent: () => { + if (simUI.player.getRotation().useGhostlyStrike && !simUI.player.getMajorGlyphs().includes(RogueMajorGlyph.GlyphOfGhostlyStrike)) { + return '"Use Ghostly Strike" selected, but missing Glyph of Ghostly Strike.'; + } else { + return ''; + } }, - (simUI: IndividualSimUI) => { - return { - updateOn: simUI.player.changeEmitter, - getContent: () => { - if (simUI.player.getRotation().useFeint && !simUI.player.getMajorGlyphs().includes(RogueMajorGlyph.GlyphOfFeint)) { - return '"Use Feint" selected, but missing Glyph of Feint.'; - } else { - return ''; - } - }, - }; + }; + }, + (simUI: IndividualSimUI) => { + return { + updateOn: simUI.player.changeEmitter, + getContent: () => { + if (simUI.player.getRotation().useFeint && !simUI.player.getMajorGlyphs().includes(RogueMajorGlyph.GlyphOfFeint)) { + return '"Use Feint" selected, but missing Glyph of Feint.'; + } else { + return ''; + } }, - (simUI: IndividualSimUI) => { - return { - updateOn: simUI.player.changeEmitter, - getContent: () => { - if (simUI.player.getRotation().exposeArmorFrequency == 2 && !simUI.player.getMajorGlyphs().includes(RogueMajorGlyph.GlyphOfExposeArmor) && simUI.player.getTalentTree() == 1) { - return '"Maintain Expose Armor" selected, but missing Glyph of Expose Armor.'; - } else { - return ''; - } - }, - }; + }; + }, + (simUI: IndividualSimUI) => { + return { + updateOn: simUI.player.changeEmitter, + getContent: () => { + if (simUI.player.getRotation().exposeArmorFrequency == 2 && !simUI.player.getMajorGlyphs().includes(RogueMajorGlyph.GlyphOfExposeArmor) && simUI.player.getTalentTree() == 1) { + return '"Maintain Expose Armor" selected, but missing Glyph of Expose Armor.'; + } else { + return ''; + } }, - (simUI: IndividualSimUI) => { - return { - updateOn: simUI.player.changeEmitter, - getContent: () => { - const mhWeaponSpeed = simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponSpeed; - const ohWeaponSpeed = simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponSpeed; - const mhImbue = simUI.player.getSpecOptions().mhImbue; - const ohImbue = simUI.player.getSpecOptions().ohImbue; - if (typeof mhWeaponSpeed == 'undefined' || typeof ohWeaponSpeed == 'undefined' || !simUI.player.getSpecOptions().applyPoisonsManually) { - return ''; - } - if (mhWeaponSpeed < ohWeaponSpeed && ohImbue == Rogue_Options_PoisonImbue.DeadlyPoison) { - return 'Deadly poison applied to slower (off hand) weapon.'; - } - if (ohWeaponSpeed < mhWeaponSpeed && mhImbue == Rogue_Options_PoisonImbue.DeadlyPoison) { - return 'Deadly poison applied to slower (main hand) weapon.'; - } - return ''; - }, - }; + }; + }, + (simUI: IndividualSimUI) => { + return { + updateOn: simUI.player.changeEmitter, + getContent: () => { + const mhWeaponSpeed = simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponSpeed; + const ohWeaponSpeed = simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponSpeed; + const mhImbue = simUI.player.getSpecOptions().mhImbue; + const ohImbue = simUI.player.getSpecOptions().ohImbue; + if (typeof mhWeaponSpeed == 'undefined' || typeof ohWeaponSpeed == 'undefined' || !simUI.player.getSpecOptions().applyPoisonsManually) { + return ''; + } + if (mhWeaponSpeed < ohWeaponSpeed && ohImbue == Rogue_Options_PoisonImbue.DeadlyPoison) { + return 'Deadly poison applied to slower (off hand) weapon.'; + } + if (ohWeaponSpeed < mhWeaponSpeed && mhImbue == Rogue_Options_PoisonImbue.DeadlyPoison) { + return 'Deadly poison applied to slower (main hand) weapon.'; + } + return ''; }, - ], + }; + }, + ], - // All stats for which EP should be calculated. - epStats: [ - Stat.StatAgility, - Stat.StatStrength, - Stat.StatAttackPower, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatMeleeHaste, - Stat.StatArmorPenetration, - Stat.StatExpertise, - ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - PseudoStat.PseudoStatOffHandDps, - ], - // Reference stat against which to calculate EP. - epReferenceStat: Stat.StatAttackPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatStamina, - Stat.StatAgility, - Stat.StatStrength, - Stat.StatAttackPower, - Stat.StatMeleeHit, - Stat.StatSpellHit, - Stat.StatMeleeCrit, - Stat.StatSpellCrit, - Stat.StatMeleeHaste, - Stat.StatArmorPenetration, - Stat.StatExpertise, - ], + // All stats for which EP should be calculated. + epStats: [ + Stat.StatAgility, + Stat.StatStrength, + Stat.StatAttackPower, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatMeleeHaste, + Stat.StatArmorPenetration, + Stat.StatExpertise, + ], + epPseudoStats: [ + PseudoStat.PseudoStatMainHandDps, + PseudoStat.PseudoStatOffHandDps, + ], + // Reference stat against which to calculate EP. + epReferenceStat: Stat.StatAttackPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatStamina, + Stat.StatAgility, + Stat.StatStrength, + Stat.StatAttackPower, + Stat.StatMeleeHit, + Stat.StatSpellHit, + Stat.StatMeleeCrit, + Stat.StatSpellCrit, + Stat.StatMeleeHaste, + Stat.StatArmorPenetration, + Stat.StatExpertise, + ], - defaults: { - // Default equipped gear. - gear: Presets.PRERAID_PRESET_ASSASSINATION.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatAgility]: 1.86, - [Stat.StatStrength]: 1.14, - [Stat.StatAttackPower]: 1, - [Stat.StatSpellCrit]: 0.28, - [Stat.StatSpellHit]: 0.08, - [Stat.StatMeleeHit]: 1.39, - [Stat.StatMeleeCrit]: 1.32, - [Stat.StatMeleeHaste]: 1.48, - [Stat.StatArmorPenetration]: 0.84, - [Stat.StatExpertise]: 0.98, - }, { - [PseudoStat.PseudoStatMainHandDps]: 2.94, - [PseudoStat.PseudoStatOffHandDps]: 2.45, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultRotation, - // Default talents. - talents: Presets.AssassinationTalents137.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - // Default raid/party buffs settings. - raidBuffs: RaidBuffs.create({ - giftOfTheWild: TristateEffect.TristateEffectImproved, - bloodlust: true, - strengthOfEarthTotem: TristateEffect.TristateEffectImproved, - icyTalons: true, - leaderOfThePack: TristateEffect.TristateEffectImproved, - abominationsMight: true, - swiftRetribution: true, - elementalOath: true, - sanctifiedRetribution: true, - }), - partyBuffs: PartyBuffs.create({ - }), - individualBuffs: IndividualBuffs.create({ - blessingOfKings: true, - blessingOfMight: TristateEffect.TristateEffectImproved, - }), - debuffs: Debuffs.create({ - heartOfTheCrusader: true, - mangle: true, - sunderArmor: true, - faerieFire: TristateEffect.TristateEffectImproved, - shadowMastery: true, - earthAndMoon: true, - bloodFrenzy: true, - }), - }, + defaults: { + // Default equipped gear. + gear: Presets.PRERAID_PRESET_ASSASSINATION.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatAgility]: 1.86, + [Stat.StatStrength]: 1.14, + [Stat.StatAttackPower]: 1, + [Stat.StatSpellCrit]: 0.28, + [Stat.StatSpellHit]: 0.08, + [Stat.StatMeleeHit]: 1.39, + [Stat.StatMeleeCrit]: 1.32, + [Stat.StatMeleeHaste]: 1.48, + [Stat.StatArmorPenetration]: 0.84, + [Stat.StatExpertise]: 0.98, + }, { + [PseudoStat.PseudoStatMainHandDps]: 2.94, + [PseudoStat.PseudoStatOffHandDps]: 2.45, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultRotation, + // Default talents. + talents: Presets.AssassinationTalents137.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + // Default raid/party buffs settings. + raidBuffs: RaidBuffs.create({ + giftOfTheWild: TristateEffect.TristateEffectImproved, + bloodlust: true, + strengthOfEarthTotem: TristateEffect.TristateEffectImproved, + icyTalons: true, + leaderOfThePack: TristateEffect.TristateEffectImproved, + abominationsMight: true, + swiftRetribution: true, + elementalOath: true, + sanctifiedRetribution: true, + }), + partyBuffs: PartyBuffs.create({ + }), + individualBuffs: IndividualBuffs.create({ + blessingOfKings: true, + blessingOfMight: TristateEffect.TristateEffectImproved, + }), + debuffs: Debuffs.create({ + heartOfTheCrusader: true, + mangle: true, + sunderArmor: true, + faerieFire: TristateEffect.TristateEffectImproved, + shadowMastery: true, + earthAndMoon: true, + bloodFrenzy: true, + }), + }, - playerInputs: { - inputs: [ - RogueInputs.ApplyPoisonsManually - ] - }, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - RogueInputs.MainHandImbue, - RogueInputs.OffHandImbue, - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: RogueInputs.RogueRotationConfig, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - IconInputs.SpellCritBuff, - IconInputs.SpellCritDebuff, - IconInputs.SpellHitDebuff, - IconInputs.SpellDamageDebuff - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - RogueInputs.StartingOverkillDuration, - RogueInputs.VanishBreakTime, - RogueInputs.AssumeBleedActive, - RogueInputs.HonorOfThievesCritRate, - OtherInputs.TankAssignment, - OtherInputs.InFrontOfTarget, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, + playerInputs: { + inputs: [ + RogueInputs.ApplyPoisonsManually + ] + }, + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + RogueInputs.MainHandImbue, + RogueInputs.OffHandImbue, + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: RogueInputs.RogueRotationConfig, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + IconInputs.SpellCritBuff, + IconInputs.SpellCritDebuff, + IconInputs.SpellHitDebuff, + IconInputs.SpellDamageDebuff + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + RogueInputs.StartingOverkillDuration, + RogueInputs.VanishBreakTime, + RogueInputs.AssumeBleedActive, + RogueInputs.HonorOfThievesCritRate, + OtherInputs.TankAssignment, + OtherInputs.InFrontOfTarget, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.AssassinationTalents137, - Presets.AssassinationTalents182, - Presets.AssassinationTalentsBF, - Presets.CombatHackTalents, - Presets.CombatCQCTalents, - Presets.SubtletyTalents, - Presets.HemoSubtletyTalents, - ], - // Preset rotations that the user can quickly select. - rotations: [ - Presets.ROTATION_PRESET_MUTILATE, - Presets.ROTATION_PRESET_MUTILATE_EXPOSE, - Presets.ROTATION_PRESET_RUPTURE_MUTILATE, - Presets.ROTATION_PRESET_RUPTURE_MUTILATE_EXPOSE, - Presets.ROTATION_PRESET_COMBAT, - Presets.ROTATION_PRESET_COMBAT_EXPOSE, - Presets.ROTATION_PRESET_COMBAT_CLEAVE_SND, - Presets.ROTATION_PRESET_COMBAT_CLEAVE_SND_EXPOSE, - Presets.ROTATION_PRESET_AOE, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.PRERAID_PRESET_ASSASSINATION, - Presets.PRERAID_PRESET_COMBAT, - Presets.P1_PRESET_ASSASSINATION, - Presets.P1_PRESET_COMBAT, - Presets.P1_PRESET_HEMO_SUB, - Presets.P2_PRESET_ASSASSINATION, - Presets.P2_PRESET_COMBAT, - Presets.P3_PRESET_ASSASSINATION, - Presets.P3_PRESET_COMBAT, - Presets.P4_PRESET_ASSASSINATION, - Presets.P4_PRESET_COMBAT, - Presets.P5_PRESET_ASSASSINATION, - Presets.P5_PRESET_COMBAT, - Presets.P2_PRESET_HEMO_SUB, - Presets.P3_PRESET_HEMO_SUB, - Presets.P3_PRESET_DANCE_SUB, - ], - }, + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.AssassinationTalents137, + Presets.AssassinationTalents182, + Presets.AssassinationTalentsBF, + Presets.CombatHackTalents, + Presets.CombatCQCTalents, + Presets.SubtletyTalents, + Presets.HemoSubtletyTalents, + ], + // Preset rotations that the user can quickly select. + rotations: [ + Presets.ROTATION_PRESET_MUTILATE, + Presets.ROTATION_PRESET_MUTILATE_EXPOSE, + Presets.ROTATION_PRESET_RUPTURE_MUTILATE, + Presets.ROTATION_PRESET_RUPTURE_MUTILATE_EXPOSE, + Presets.ROTATION_PRESET_COMBAT, + Presets.ROTATION_PRESET_COMBAT_EXPOSE, + Presets.ROTATION_PRESET_COMBAT_CLEAVE_SND, + Presets.ROTATION_PRESET_COMBAT_CLEAVE_SND_EXPOSE, + Presets.ROTATION_PRESET_AOE, + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.PRERAID_PRESET_ASSASSINATION, + Presets.PRERAID_PRESET_COMBAT, + Presets.P1_PRESET_ASSASSINATION, + Presets.P1_PRESET_COMBAT, + Presets.P1_PRESET_HEMO_SUB, + Presets.P2_PRESET_ASSASSINATION, + Presets.P2_PRESET_COMBAT, + Presets.P3_PRESET_ASSASSINATION, + Presets.P3_PRESET_COMBAT, + Presets.P4_PRESET_ASSASSINATION, + Presets.P4_PRESET_COMBAT, + Presets.P5_PRESET_ASSASSINATION, + Presets.P5_PRESET_COMBAT, + Presets.P2_PRESET_HEMO_SUB, + Presets.P3_PRESET_HEMO_SUB, + Presets.P3_PRESET_DANCE_SUB, + ], + }, - autoRotation: (player: Player): APLRotation => { - const talentTree = player.getTalentTree(); - const numTargets = player.sim.encounter.targets.length; - if (numTargets >= 5) { - return Presets.ROTATION_PRESET_AOE.rotation.rotation!; - } else if (talentTree == 0) { - return Presets.ROTATION_PRESET_MUTILATE_EXPOSE.rotation.rotation!; - } else if (talentTree == 1) { - return Presets.ROTATION_PRESET_COMBAT_EXPOSE.rotation.rotation!; - } else { - // TODO: Need a sub rotation here - return Presets.ROTATION_PRESET_MUTILATE_EXPOSE.rotation.rotation!; - } - }, - }) + autoRotation: (player: Player): APLRotation => { + const talentTree = player.getTalentTree(); + const numTargets = player.sim.encounter.targets.length; + if (numTargets >= 5) { + return Presets.ROTATION_PRESET_AOE.rotation.rotation!; + } else if (talentTree == 0) { + return Presets.ROTATION_PRESET_MUTILATE_EXPOSE.rotation.rotation!; + } else if (talentTree == 1) { + return Presets.ROTATION_PRESET_COMBAT_EXPOSE.rotation.rotation!; + } else { + // TODO: Need a sub rotation here + return Presets.ROTATION_PRESET_MUTILATE_EXPOSE.rotation.rotation!; + } + }, +}); + +export class RogueSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); this.player.changeEmitter.on((c) => { const rotation = this.player.getRotation() const options = this.player.getSpecOptions() diff --git a/ui/shadow_priest/sim.ts b/ui/shadow_priest/sim.ts index 676f209524..8a233e69c8 100644 --- a/ui/shadow_priest/sim.ts +++ b/ui/shadow_priest/sim.ts @@ -4,14 +4,12 @@ import { Stat, } from '../core/proto/common.js'; import { - APLAction, - APLListItem, APLRotation, } from '../core/proto/apl.js'; import { Stats } from '../core/proto_utils/stats.js'; import { Player } from '../core/player.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; import * as IconInputs from '../core/components/icon_inputs.js'; import * as OtherInputs from '../core/components/other_inputs.js'; import * as Mechanics from '../core/constants/mechanics.js'; @@ -19,149 +17,151 @@ import * as Mechanics from '../core/constants/mechanics.js'; import * as ShadowPriestInputs from './inputs.js'; import * as Presets from './presets.js'; -export class ShadowPriestSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'shadow-priest-sim-ui', - cssScheme: 'priest', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], +const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { + cssClass: 'shadow-priest-sim-ui', + cssScheme: 'priest', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + ], + + // All stats for which EP should be calculated. + epStats: [ + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatSpellPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatMana, + Stat.StatStamina, + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], + modifyDisplayStats: (player: Player) => { + let stats = new Stats(); + stats = stats.addStat(Stat.StatSpellHit, player.getTalents().shadowFocus * 1 * Mechanics.SPELL_HIT_RATING_PER_HIT_CHANCE); - // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatSpellPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatMana, - Stat.StatStamina, - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], - modifyDisplayStats: (player: Player) => { - let stats = new Stats(); - stats = stats.addStat(Stat.StatSpellHit, player.getTalents().shadowFocus * 1 * Mechanics.SPELL_HIT_RATING_PER_HIT_CHANCE); + return { + talents: stats, + }; + }, - return { - talents: stats, - }; - }, + defaults: { + // Default equipped gear. + gear: Presets.P4_PRESET.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatIntellect]: 0.11, + [Stat.StatSpirit]: 0.47, + [Stat.StatSpellPower]: 1, + [Stat.StatSpellHit]: 0.87, + [Stat.StatSpellCrit]: 0.74, + [Stat.StatSpellHaste]: 1.65, + [Stat.StatMP5]: 0.00, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultRotation, + // Default talents. + talents: Presets.StandardTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + // Default raid/party buffs settings. + raidBuffs: Presets.DefaultRaidBuffs, - defaults: { - // Default equipped gear. - gear: Presets.P4_PRESET.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatIntellect]: 0.11, - [Stat.StatSpirit]: 0.47, - [Stat.StatSpellPower]: 1, - [Stat.StatSpellHit]: 0.87, - [Stat.StatSpellCrit]: 0.74, - [Stat.StatSpellHaste]: 1.65, - [Stat.StatMP5]: 0.00, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultRotation, - // Default talents. - talents: Presets.StandardTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - // Default raid/party buffs settings. - raidBuffs: Presets.DefaultRaidBuffs, + partyBuffs: PartyBuffs.create({}), - partyBuffs: PartyBuffs.create({}), + individualBuffs: Presets.DefaultIndividualBuffs, - individualBuffs: Presets.DefaultIndividualBuffs, + debuffs: Presets.DefaultDebuffs, - debuffs: Presets.DefaultDebuffs, + other: Presets.OtherDefaults, + }, - other: Presets.OtherDefaults, - }, + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + ShadowPriestInputs.ArmorInput, + ], + rotationIconInputs: [ + ShadowPriestInputs.MindBlastInput, + ShadowPriestInputs.ShadowWordDeathInput, + ShadowPriestInputs.ShadowfiendInput, + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: ShadowPriestInputs.ShadowPriestRotationConfig, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + IconInputs.ReplenishmentBuff, + IconInputs.MeleeHasteBuff, + IconInputs.MeleeCritBuff, + IconInputs.MP5Buff, + IconInputs.AttackPowerPercentBuff, + IconInputs.AttackPowerBuff, + IconInputs.StaminaBuff, + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + OtherInputs.TankAssignment, + OtherInputs.ChannelClipDelay, + OtherInputs.nibelungAverageCasts, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ShadowPriestInputs.ArmorInput, - ], - rotationIconInputs: [ - ShadowPriestInputs.MindBlastInput, - ShadowPriestInputs.ShadowWordDeathInput, - ShadowPriestInputs.ShadowfiendInput, - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: ShadowPriestInputs.ShadowPriestRotationConfig, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - IconInputs.ReplenishmentBuff, - IconInputs.MeleeHasteBuff, - IconInputs.MeleeCritBuff, - IconInputs.MP5Buff, - IconInputs.AttackPowerPercentBuff, - IconInputs.AttackPowerBuff, - IconInputs.StaminaBuff, - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - OtherInputs.TankAssignment, - OtherInputs.ChannelClipDelay, - OtherInputs.nibelungAverageCasts, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.StandardTalents, + Presets.EnlightenmentTalents, + ], + rotations: [ + Presets.ROTATION_PRESET_DEFAULT, + Presets.ROTATION_PRESET_AOE24, + Presets.ROTATION_PRESET_AOE4PLUS, + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.PRERAID_PRESET, + Presets.P1_PRESET, + Presets.P2_PRESET, + Presets.P3_PRESET, + Presets.P4_PRESET, + ], + }, - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.StandardTalents, - Presets.EnlightenmentTalents, - ], - rotations: [ - Presets.ROTATION_PRESET_DEFAULT, - Presets.ROTATION_PRESET_AOE24, - Presets.ROTATION_PRESET_AOE4PLUS, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.PRERAID_PRESET, - Presets.P1_PRESET, - Presets.P2_PRESET, - Presets.P3_PRESET, - Presets.P4_PRESET, - ], - }, + autoRotation: (player: Player): APLRotation => { + const numTargets = player.sim.encounter.targets.length; + if (numTargets > 4) { + return Presets.ROTATION_PRESET_AOE4PLUS.rotation.rotation!; + } else if (numTargets > 1) { + return Presets.ROTATION_PRESET_AOE24.rotation.rotation!; + } else { + return Presets.ROTATION_PRESET_DEFAULT.rotation.rotation!; + } + }, +}); - autoRotation: (player: Player): APLRotation => { - const numTargets = player.sim.encounter.targets.length; - if (numTargets > 4) { - return Presets.ROTATION_PRESET_AOE4PLUS.rotation.rotation!; - } else if (numTargets > 1) { - return Presets.ROTATION_PRESET_AOE24.rotation.rotation!; - } else { - return Presets.ROTATION_PRESET_DEFAULT.rotation.rotation!; - } - }, - }); +export class ShadowPriestSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/smite_priest/sim.ts b/ui/smite_priest/sim.ts index 1a30f4e262..31e6687a9c 100644 --- a/ui/smite_priest/sim.ts +++ b/ui/smite_priest/sim.ts @@ -6,7 +6,7 @@ import { } from '../core/proto/apl.js'; import { Stats } from '../core/proto_utils/stats.js'; import { Player } from '../core/player.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; import * as OtherInputs from '../core/components/other_inputs.js'; import * as Mechanics from '../core/constants/mechanics.js'; @@ -14,120 +14,122 @@ import * as Mechanics from '../core/constants/mechanics.js'; import * as SmitePriestInputs from './inputs.js'; import * as Presets from './presets.js'; -export class SmitePriestSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'smite-priest-sim-ui', - cssScheme: 'priest', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], +const SPEC_CONFIG = registerSpecConfig(Spec.SpecSmitePriest, { + cssClass: 'smite-priest-sim-ui', + cssScheme: 'priest', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + ], + + // All stats for which EP should be calculated. + epStats: [ + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatSpellPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatStamina, + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + ], + modifyDisplayStats: (player: Player) => { + let stats = new Stats(); + stats = stats.addStat(Stat.StatSpellHit, player.getTalents().shadowFocus * 1 * Mechanics.SPELL_HIT_RATING_PER_HIT_CHANCE); - // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatSpellPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatStamina, - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], - modifyDisplayStats: (player: Player) => { - let stats = new Stats(); - stats = stats.addStat(Stat.StatSpellHit, player.getTalents().shadowFocus * 1 * Mechanics.SPELL_HIT_RATING_PER_HIT_CHANCE); + return { + talents: stats, + }; + }, - return { - talents: stats, - }; - }, + defaults: { + // Default equipped gear. + gear: Presets.P1_PRESET.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatIntellect]: 0.38, + [Stat.StatSpirit]: 0.38, + [Stat.StatSpellPower]: 1, + [Stat.StatSpellHit]: 1.65, + [Stat.StatSpellCrit]: 0.32, + [Stat.StatSpellHaste]: 0.78, + [Stat.StatMP5]: 0.35, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultRotation, + // Default talents. + talents: Presets.StandardTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + // Default raid/party buffs settings. + raidBuffs: Presets.DefaultRaidBuffs, + partyBuffs: PartyBuffs.create({}), + individualBuffs: Presets.DefaultIndividualBuffs, + debuffs: Presets.DefaultDebuffs, + }, - defaults: { - // Default equipped gear. - gear: Presets.P1_PRESET.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatIntellect]: 0.38, - [Stat.StatSpirit]: 0.38, - [Stat.StatSpellPower]: 1, - [Stat.StatSpellHit]: 1.65, - [Stat.StatSpellCrit]: 0.32, - [Stat.StatSpellHaste]: 0.78, - [Stat.StatMP5]: 0.35, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultRotation, - // Default talents. - talents: Presets.StandardTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - // Default raid/party buffs settings. - raidBuffs: Presets.DefaultRaidBuffs, - partyBuffs: PartyBuffs.create({}), - individualBuffs: Presets.DefaultIndividualBuffs, - debuffs: Presets.DefaultDebuffs, - }, + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + SmitePriestInputs.SelfPowerInfusion, + SmitePriestInputs.InnerFire, + SmitePriestInputs.Shadowfiend, + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: SmitePriestInputs.SmitePriestRotationConfig, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + OtherInputs.TankAssignment, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - SmitePriestInputs.SelfPowerInfusion, - SmitePriestInputs.InnerFire, - SmitePriestInputs.Shadowfiend, - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: SmitePriestInputs.SmitePriestRotationConfig, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - OtherInputs.TankAssignment, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.StandardTalents, + ], + // Preset rotations that the user can quickly select. + rotations: [ + Presets.ROTATION_PRESET_LEGACY_DEFAULT, + Presets.ROTATION_PRESET_APL, + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.PRERAID_PRESET, + Presets.P1_PRESET, + ], + }, - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.StandardTalents, - ], - // Preset rotations that the user can quickly select. - rotations: [ - Presets.ROTATION_PRESET_LEGACY_DEFAULT, - Presets.ROTATION_PRESET_APL, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.PRERAID_PRESET, - Presets.P1_PRESET, - ], - }, + autoRotation: (_player: Player): APLRotation => { + return Presets.ROTATION_PRESET_APL.rotation.rotation!; + }, +}); - autoRotation: (_player: Player): APLRotation => { - return Presets.ROTATION_PRESET_APL.rotation.rotation!; - }, - }); +export class SmitePriestSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/tank_deathknight/sim.ts b/ui/tank_deathknight/sim.ts index f2a89e3f44..6e41515bc9 100644 --- a/ui/tank_deathknight/sim.ts +++ b/ui/tank_deathknight/sim.ts @@ -6,216 +6,213 @@ import { Spec } from '../core/proto/common.js'; import { Stat, PseudoStat } from '../core/proto/common.js'; import { TristateEffect } from '../core/proto/common.js' import { - APLAction, - APLListItem, APLRotation, } from '../core/proto/apl.js'; import { Player } from '../core/player.js'; import { Stats } from '../core/proto_utils/stats.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; - -import { TankDeathknight, TankDeathknight_Rotation as DeathKnightRotation, DeathknightTalents as DeathKnightTalents, TankDeathknight_Options as DeathKnightOptions } from '../core/proto/deathknight.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; import * as IconInputs from '../core/components/icon_inputs.js'; import * as OtherInputs from '../core/components/other_inputs.js'; -import * as Tooltips from '../core/constants/tooltips.js'; import * as DeathKnightInputs from './inputs.js'; import * as Presets from './presets.js'; -export class TankDeathknightSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'tank-deathknight-sim-ui', - cssScheme: 'death-knight', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - "

Defensive CDs use is very basic and wip.

" - ], +const SPEC_CONFIG = registerSpecConfig(Spec.SpecTankDeathknight, { + cssClass: 'tank-deathknight-sim-ui', + cssScheme: 'death-knight', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + "

Defensive CDs use is very basic and wip.

" + ], - // All stats for which EP should be calculated. - epStats: [ - Stat.StatStamina, - Stat.StatStrength, - Stat.StatAgility, - Stat.StatAttackPower, - Stat.StatExpertise, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatHealth, - Stat.StatArmor, - Stat.StatBonusArmor, - Stat.StatArmorPenetration, - Stat.StatDefense, - Stat.StatDodge, - Stat.StatParry, - Stat.StatResilience, - Stat.StatSpellHit, - Stat.StatNatureResistance, - Stat.StatShadowResistance, - Stat.StatFrostResistance, - ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - PseudoStat.PseudoStatOffHandDps, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatAttackPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatArmor, - Stat.StatBonusArmor, - Stat.StatStamina, - Stat.StatStrength, - Stat.StatAgility, - Stat.StatAttackPower, - Stat.StatExpertise, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatArmorPenetration, - Stat.StatDefense, - Stat.StatDodge, - Stat.StatParry, - Stat.StatResilience, - Stat.StatNatureResistance, - Stat.StatShadowResistance, - Stat.StatFrostResistance, - ], - defaults: { - // Default equipped gear. - gear: Presets.P2_BLOOD_PRESET.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatArmor]: 0.05, - [Stat.StatBonusArmor]: 0.03, - [Stat.StatStamina]: 1, - [Stat.StatStrength]: 0.33, - [Stat.StatAgility]: 0.6, - [Stat.StatAttackPower]: 0.06, - [Stat.StatExpertise]: 0.67, - [Stat.StatMeleeHit]: 0.67, - [Stat.StatMeleeCrit]: 0.28, - [Stat.StatMeleeHaste]: 0.21, - [Stat.StatArmorPenetration]: 0.19, - [Stat.StatBlock]: 0.35, - [Stat.StatBlockValue]: 0.59, - [Stat.StatDodge]: 0.7, - [Stat.StatParry]: 0.58, - [Stat.StatDefense]: 0.8, - }, { - [PseudoStat.PseudoStatMainHandDps]: 3.10, - [PseudoStat.PseudoStatOffHandDps]: 0.0, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultRotation, - // Default talents. - talents: Presets.BloodTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - // Default raid/party buffs settings. - raidBuffs: RaidBuffs.create({ - retributionAura: true, - powerWordFortitude: TristateEffect.TristateEffectImproved, - giftOfTheWild: TristateEffect.TristateEffectImproved, - swiftRetribution: true, - strengthOfEarthTotem: TristateEffect.TristateEffectImproved, - icyTalons: true, - abominationsMight: true, - leaderOfThePack: TristateEffect.TristateEffectRegular, - sanctifiedRetribution: true, - bloodlust: true, - devotionAura: TristateEffect.TristateEffectImproved, - stoneskinTotem: TristateEffect.TristateEffectImproved, - }), - partyBuffs: PartyBuffs.create({ - }), - individualBuffs: IndividualBuffs.create({ - blessingOfKings: true, - blessingOfMight: TristateEffect.TristateEffectImproved, - blessingOfSanctuary: true, - }), - debuffs: Debuffs.create({ - bloodFrenzy: true, - faerieFire: TristateEffect.TristateEffectRegular, - sunderArmor: true, - misery: true, - ebonPlaguebringer: true, - mangle: true, - heartOfTheCrusader: true, - demoralizingShout: TristateEffect.TristateEffectImproved, - frostFever: TristateEffect.TristateEffectImproved, - insectSwarm: true, - judgementOfLight: true, - }), - }, + // All stats for which EP should be calculated. + epStats: [ + Stat.StatStamina, + Stat.StatStrength, + Stat.StatAgility, + Stat.StatAttackPower, + Stat.StatExpertise, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatHealth, + Stat.StatArmor, + Stat.StatBonusArmor, + Stat.StatArmorPenetration, + Stat.StatDefense, + Stat.StatDodge, + Stat.StatParry, + Stat.StatResilience, + Stat.StatSpellHit, + Stat.StatNatureResistance, + Stat.StatShadowResistance, + Stat.StatFrostResistance, + ], + epPseudoStats: [ + PseudoStat.PseudoStatMainHandDps, + PseudoStat.PseudoStatOffHandDps, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatAttackPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatArmor, + Stat.StatBonusArmor, + Stat.StatStamina, + Stat.StatStrength, + Stat.StatAgility, + Stat.StatAttackPower, + Stat.StatExpertise, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatArmorPenetration, + Stat.StatDefense, + Stat.StatDodge, + Stat.StatParry, + Stat.StatResilience, + Stat.StatNatureResistance, + Stat.StatShadowResistance, + Stat.StatFrostResistance, + ], + defaults: { + // Default equipped gear. + gear: Presets.P2_BLOOD_PRESET.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatArmor]: 0.05, + [Stat.StatBonusArmor]: 0.03, + [Stat.StatStamina]: 1, + [Stat.StatStrength]: 0.33, + [Stat.StatAgility]: 0.6, + [Stat.StatAttackPower]: 0.06, + [Stat.StatExpertise]: 0.67, + [Stat.StatMeleeHit]: 0.67, + [Stat.StatMeleeCrit]: 0.28, + [Stat.StatMeleeHaste]: 0.21, + [Stat.StatArmorPenetration]: 0.19, + [Stat.StatBlock]: 0.35, + [Stat.StatBlockValue]: 0.59, + [Stat.StatDodge]: 0.7, + [Stat.StatParry]: 0.58, + [Stat.StatDefense]: 0.8, + }, { + [PseudoStat.PseudoStatMainHandDps]: 3.10, + [PseudoStat.PseudoStatOffHandDps]: 0.0, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultRotation, + // Default talents. + talents: Presets.BloodTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + // Default raid/party buffs settings. + raidBuffs: RaidBuffs.create({ + retributionAura: true, + powerWordFortitude: TristateEffect.TristateEffectImproved, + giftOfTheWild: TristateEffect.TristateEffectImproved, + swiftRetribution: true, + strengthOfEarthTotem: TristateEffect.TristateEffectImproved, + icyTalons: true, + abominationsMight: true, + leaderOfThePack: TristateEffect.TristateEffectRegular, + sanctifiedRetribution: true, + bloodlust: true, + devotionAura: TristateEffect.TristateEffectImproved, + stoneskinTotem: TristateEffect.TristateEffectImproved, + }), + partyBuffs: PartyBuffs.create({ + }), + individualBuffs: IndividualBuffs.create({ + blessingOfKings: true, + blessingOfMight: TristateEffect.TristateEffectImproved, + blessingOfSanctuary: true, + }), + debuffs: Debuffs.create({ + bloodFrenzy: true, + faerieFire: TristateEffect.TristateEffectRegular, + sunderArmor: true, + misery: true, + ebonPlaguebringer: true, + mangle: true, + heartOfTheCrusader: true, + demoralizingShout: TristateEffect.TristateEffectImproved, + frostFever: TristateEffect.TristateEffectImproved, + insectSwarm: true, + judgementOfLight: true, + }), + }, - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: DeathKnightInputs.TankDeathKnightRotationConfig, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - IconInputs.SpellDamageDebuff, - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - OtherInputs.TankAssignment, - OtherInputs.HpPercentForDefensives, - OtherInputs.IncomingHps, - OtherInputs.HealingCadence, - OtherInputs.HealingCadenceVariation, - OtherInputs.BurstWindow, - OtherInputs.InspirationUptime, - OtherInputs.InFrontOfTarget, - DeathKnightInputs.StartingRunicPower, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: DeathKnightInputs.TankDeathKnightRotationConfig, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + IconInputs.SpellDamageDebuff, + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + OtherInputs.TankAssignment, + OtherInputs.HpPercentForDefensives, + OtherInputs.IncomingHps, + OtherInputs.HealingCadence, + OtherInputs.HealingCadenceVariation, + OtherInputs.BurstWindow, + OtherInputs.InspirationUptime, + OtherInputs.InFrontOfTarget, + DeathKnightInputs.StartingRunicPower, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, - presets: { - // Preset rotations that the user can quickly select. - rotations: [ - Presets.BLOOD_LEGACY_PRESET_LEGACY_DEFAULT, - Presets.BLOOD_IT_SPAM_ROTATION_PRESET_DEFAULT, - Presets.BLOOD_AGGRO_ROTATION_PRESET_DEFAULT, - ], - // Preset talents that the user can quickly select. - talents: [ - Presets.BloodTalents, - Presets.BloodAggroTalents, - Presets.DoubleBuffBloodTalents, - Presets.FrostTalents, - Presets.DoubleBuffFrostTalents, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.P1_BLOOD_PRESET, - Presets.P1_FROST_PRESET, - Presets.P2_BLOOD_PRESET, - Presets.P2_FROST_PRESET, - ], - }, + presets: { + // Preset rotations that the user can quickly select. + rotations: [ + Presets.BLOOD_LEGACY_PRESET_LEGACY_DEFAULT, + Presets.BLOOD_IT_SPAM_ROTATION_PRESET_DEFAULT, + Presets.BLOOD_AGGRO_ROTATION_PRESET_DEFAULT, + ], + // Preset talents that the user can quickly select. + talents: [ + Presets.BloodTalents, + Presets.BloodAggroTalents, + Presets.DoubleBuffBloodTalents, + Presets.FrostTalents, + Presets.DoubleBuffFrostTalents, + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.P1_BLOOD_PRESET, + Presets.P1_FROST_PRESET, + Presets.P2_BLOOD_PRESET, + Presets.P2_FROST_PRESET, + ], + }, - autoRotation: (player: Player): APLRotation => { - return Presets.BLOOD_IT_SPAM_ROTATION_PRESET_DEFAULT.rotation.rotation!; - }, - }); + autoRotation: (_player: Player): APLRotation => { + return Presets.BLOOD_IT_SPAM_ROTATION_PRESET_DEFAULT.rotation.rotation!; + }, +}); + +export class TankDeathknightSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/warlock/sim.ts b/ui/warlock/sim.ts index cb3fc6c01f..9449b02034 100644 --- a/ui/warlock/sim.ts +++ b/ui/warlock/sim.ts @@ -4,186 +4,186 @@ import { Stat, } from '../core/proto/common.js'; import { - APLAction, - APLListItem, APLRotation, } from '../core/proto/apl.js'; import { Stats } from '../core/proto_utils/stats.js'; import { Player } from '../core/player.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; import * as IconInputs from '../core/components/icon_inputs.js'; import * as OtherInputs from '../core/components/other_inputs.js'; import * as WarlockInputs from './inputs.js'; import * as Presets from './presets.js'; +const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { + cssClass: 'warlock-sim-ui', + cssScheme: 'warlock', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + "Drain Soul is currently disabled for APL rotations" + ], + + // All stats for which EP should be calculated. + epStats: [ + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatStamina, + ], + // Reference stat against which to calculate EP. DPS classes use either spell power or attack power. + epReferenceStat: Stat.StatSpellPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatIntellect, + Stat.StatSpirit, + Stat.StatSpellPower, + Stat.StatSpellHit, + Stat.StatSpellCrit, + Stat.StatSpellHaste, + Stat.StatMP5, + Stat.StatStamina, + ], + + defaults: { + // Default equipped gear. + gear: Presets.P3_AFFLICTION_HORDE_PRESET.gear, + + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatIntellect]: 0.18, + [Stat.StatSpirit]: 0.54, + [Stat.StatSpellPower]: 1, + [Stat.StatSpellHit]: 0.93, + [Stat.StatSpellCrit]: 0.53, + [Stat.StatSpellHaste]: 0.81, + [Stat.StatStamina]: 0.01, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + + // Default rotation settings. + rotation: Presets.AfflictionRotation, + // Default talents. + talents: Presets.AfflictionTalents.data, + // Default spec-specific settings. + specOptions: Presets.AfflictionOptions, + + // Default buffs and debuffs settings. + raidBuffs: Presets.DefaultRaidBuffs, + + partyBuffs: PartyBuffs.create({}), + + individualBuffs: Presets.DefaultIndividualBuffs, + + debuffs: Presets.DefaultDebuffs, + + other: Presets.OtherDefaults, + }, + + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + WarlockInputs.PetInput, + WarlockInputs.ArmorInput, + WarlockInputs.WeaponImbueInput, + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationIconInputs: [ + WarlockInputs.PrimarySpellInput, + WarlockInputs.CorruptionSpell, + WarlockInputs.SecondaryDotInput, + WarlockInputs.SpecSpellInput, + ], + rotationInputs: WarlockInputs.WarlockRotationConfig, + + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + IconInputs.ReplenishmentBuff, + IconInputs.MajorArmorDebuff, + IconInputs.MinorArmorDebuff, + IconInputs.PhysicalDamageDebuff, + IconInputs.MeleeHasteBuff, + IconInputs.MeleeCritBuff, + IconInputs.MP5Buff, + IconInputs.AttackPowerPercentBuff, + IconInputs.AttackPowerBuff, + IconInputs.StrengthAndAgilityBuff, + IconInputs.StaminaBuff, + ], + excludeBuffDebuffInputs: [ + ], + petConsumeInputs: [ + IconInputs.SpicedMammothTreats, + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + OtherInputs.DistanceFromTarget, + OtherInputs.TankAssignment, + OtherInputs.ChannelClipDelay, + OtherInputs.nibelungAverageCasts, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: false, + }, + + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.AfflictionTalents, + Presets.DemonologyTalents, + Presets.DestructionTalents, + ], + // Preset rotations that the user can quickly select. + rotations: [ + Presets.APL_Affliction_Legacy, + Presets.APL_Affliction_Default, + Presets.APL_Demo_Legacy, + Presets.APL_Demo_Default, + Presets.APL_Destro_Legacy, + Presets.APL_Destro_Default, + ], + + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.SWP_BIS, + Presets.PRERAID_AFFLICTION_PRESET, + Presets.P1_AFFLICTION_PRESET, + Presets.P2_AFFLICTION_PRESET, + Presets.P3_AFFLICTION_ALLIANCE_PRESET, + Presets.P3_AFFLICTION_HORDE_PRESET, + Presets.P4_AFFLICTION_PRESET, + Presets.PRERAID_DEMODESTRO_PRESET, + Presets.P1_DEMODESTRO_PRESET, + Presets.P2_DEMODESTRO_PRESET, + Presets.P3_DEMO_ALLIANCE_PRESET, + Presets.P3_DEMO_HORDE_PRESET, + Presets.P4_DEMO_PRESET, + Presets.P3_DESTRO_ALLIANCE_PRESET, + Presets.P3_DESTRO_HORDE_PRESET, + Presets.P4_DESTRO_PRESET, + ], + }, + + autoRotation: (player: Player): APLRotation => { + const talentTree = player.getTalentTree(); + if (talentTree == 0) { + return Presets.APL_Affliction_Default.rotation.rotation!; + } else if (talentTree == 1) { + return Presets.APL_Demo_Default.rotation.rotation!; + } else { + return Presets.APL_Destro_Default.rotation.rotation!; + } + }, +}); + export class WarlockSimUI extends IndividualSimUI { constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'warlock-sim-ui', - cssScheme: 'warlock', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - "Drain Soul is currently disabled for APL rotations" - ], - - // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatStamina, - ], - // Reference stat against which to calculate EP. DPS classes use either spell power or attack power. - epReferenceStat: Stat.StatSpellPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - Stat.StatStamina, - ], - - defaults: { - // Default equipped gear. - gear: Presets.P3_AFFLICTION_HORDE_PRESET.gear, - - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatIntellect]: 0.18, - [Stat.StatSpirit]: 0.54, - [Stat.StatSpellPower]: 1, - [Stat.StatSpellHit]: 0.93, - [Stat.StatSpellCrit]: 0.53, - [Stat.StatSpellHaste]: 0.81, - [Stat.StatStamina]: 0.01, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - - // Default rotation settings. - rotation: Presets.AfflictionRotation, - // Default talents. - talents: Presets.AfflictionTalents.data, - // Default spec-specific settings. - specOptions: Presets.AfflictionOptions, - - // Default buffs and debuffs settings. - raidBuffs: Presets.DefaultRaidBuffs, - - partyBuffs: PartyBuffs.create({}), - - individualBuffs: Presets.DefaultIndividualBuffs, - - debuffs: Presets.DefaultDebuffs, - - other: Presets.OtherDefaults, - }, - - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - WarlockInputs.PetInput, - WarlockInputs.ArmorInput, - WarlockInputs.WeaponImbueInput, - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationIconInputs: [ - WarlockInputs.PrimarySpellInput, - WarlockInputs.CorruptionSpell, - WarlockInputs.SecondaryDotInput, - WarlockInputs.SpecSpellInput, - ], - rotationInputs: WarlockInputs.WarlockRotationConfig, - - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - IconInputs.ReplenishmentBuff, - IconInputs.MajorArmorDebuff, - IconInputs.MinorArmorDebuff, - IconInputs.PhysicalDamageDebuff, - IconInputs.MeleeHasteBuff, - IconInputs.MeleeCritBuff, - IconInputs.MP5Buff, - IconInputs.AttackPowerPercentBuff, - IconInputs.AttackPowerBuff, - IconInputs.StrengthAndAgilityBuff, - IconInputs.StaminaBuff, - ], - excludeBuffDebuffInputs: [ - ], - petConsumeInputs: [ - IconInputs.SpicedMammothTreats, - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - OtherInputs.DistanceFromTarget, - OtherInputs.TankAssignment, - OtherInputs.ChannelClipDelay, - OtherInputs.nibelungAverageCasts, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: false, - }, - - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.AfflictionTalents, - Presets.DemonologyTalents, - Presets.DestructionTalents, - ], - // Preset rotations that the user can quickly select. - rotations: [ - Presets.APL_Affliction_Legacy, - Presets.APL_Affliction_Default, - Presets.APL_Demo_Legacy, - Presets.APL_Demo_Default, - Presets.APL_Destro_Legacy, - Presets.APL_Destro_Default, - ], - - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.SWP_BIS, - Presets.PRERAID_AFFLICTION_PRESET, - Presets.P1_AFFLICTION_PRESET, - Presets.P2_AFFLICTION_PRESET, - Presets.P3_AFFLICTION_ALLIANCE_PRESET, - Presets.P3_AFFLICTION_HORDE_PRESET, - Presets.P4_AFFLICTION_PRESET, - Presets.PRERAID_DEMODESTRO_PRESET, - Presets.P1_DEMODESTRO_PRESET, - Presets.P2_DEMODESTRO_PRESET, - Presets.P3_DEMO_ALLIANCE_PRESET, - Presets.P3_DEMO_HORDE_PRESET, - Presets.P4_DEMO_PRESET, - Presets.P3_DESTRO_ALLIANCE_PRESET, - Presets.P3_DESTRO_HORDE_PRESET, - Presets.P4_DESTRO_PRESET, - ], - }, - - autoRotation: (player: Player): APLRotation => { - const talentTree = player.getTalentTree(); - if (talentTree == 0) { - return Presets.APL_Affliction_Default.rotation.rotation!; - } else if (talentTree == 1) { - return Presets.APL_Demo_Default.rotation.rotation!; - } else { - return Presets.APL_Destro_Default.rotation.rotation!; - } - }, - }); + super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/warrior/sim.ts b/ui/warrior/sim.ts index 3f6e90b60d..020f787af9 100644 --- a/ui/warrior/sim.ts +++ b/ui/warrior/sim.ts @@ -11,13 +11,11 @@ import { TristateEffect, } from '../core/proto/common.js'; import { - APLAction, - APLListItem, APLRotation, } from '../core/proto/apl.js'; import { Stats } from '../core/proto_utils/stats.js'; import { Player } from '../core/player.js'; -import { IndividualSimUI } from '../core/individual_sim_ui.js'; +import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; import { TypedEvent } from '../core/typed_event.js'; import { Gear } from '../core/proto_utils/gear.js'; @@ -29,185 +27,187 @@ import * as Mechanics from '../core/constants/mechanics.js'; import * as WarriorInputs from './inputs.js'; import * as Presets from './presets.js'; +const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, { + cssClass: 'warrior-sim-ui', + cssScheme: 'warrior', + // List any known bugs / issues here and they'll be shown on the site. + knownIssues: [ + ], + + // All stats for which EP should be calculated. + epStats: [ + Stat.StatStrength, + Stat.StatAgility, + Stat.StatAttackPower, + Stat.StatExpertise, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatArmorPenetration, + Stat.StatArmor, + ], + epPseudoStats: [ + PseudoStat.PseudoStatMainHandDps, + PseudoStat.PseudoStatOffHandDps, + ], + // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. + epReferenceStat: Stat.StatAttackPower, + // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. + displayStats: [ + Stat.StatHealth, + Stat.StatStamina, + Stat.StatStrength, + Stat.StatAgility, + Stat.StatAttackPower, + Stat.StatExpertise, + Stat.StatMeleeHit, + Stat.StatMeleeCrit, + Stat.StatMeleeHaste, + Stat.StatArmorPenetration, + Stat.StatArmor, + ], + modifyDisplayStats: (player: Player) => { + let stats = new Stats(); + if (!player.getInFrontOfTarget()) { + // When behind target, dodge is the only outcome affected by Expertise. + stats = stats.addStat(Stat.StatExpertise, player.getTalents().weaponMastery * 4 * Mechanics.EXPERTISE_PER_QUARTER_PERCENT_REDUCTION); + } + return { + talents: stats, + }; + }, + + defaults: { + // Default equipped gear. + gear: Presets.P3_FURY_PRESET_ALLIANCE.gear, + // Default EP weights for sorting gear in the gear picker. + epWeights: Stats.fromMap({ + [Stat.StatStrength]: 2.72, + [Stat.StatAgility]: 1.82, + [Stat.StatAttackPower]: 1, + [Stat.StatExpertise]: 2.55, + [Stat.StatMeleeHit]: 0.79, + [Stat.StatMeleeCrit]: 2.12, + [Stat.StatMeleeHaste]: 1.72, + [Stat.StatArmorPenetration]: 2.17, + [Stat.StatArmor]: 0.03, + }, { + [PseudoStat.PseudoStatMainHandDps]: 6.29, + [PseudoStat.PseudoStatOffHandDps]: 3.58, + }), + // Default consumes settings. + consumes: Presets.DefaultConsumes, + // Default rotation settings. + rotation: Presets.DefaultRotation, + // Default talents. + talents: Presets.FuryTalents.data, + // Default spec-specific settings. + specOptions: Presets.DefaultOptions, + // Default raid/party buffs settings. + raidBuffs: RaidBuffs.create({ + giftOfTheWild: TristateEffect.TristateEffectImproved, + swiftRetribution: true, + strengthOfEarthTotem: TristateEffect.TristateEffectImproved, + icyTalons: true, + abominationsMight: true, + leaderOfThePack: TristateEffect.TristateEffectRegular, + sanctifiedRetribution: true, + bloodlust: true, + devotionAura: TristateEffect.TristateEffectImproved, + stoneskinTotem: TristateEffect.TristateEffectImproved, + }), + partyBuffs: PartyBuffs.create({ + heroicPresence: false, + }), + individualBuffs: IndividualBuffs.create({ + blessingOfKings: true, + blessingOfMight: TristateEffect.TristateEffectImproved, + }), + debuffs: Debuffs.create({ + bloodFrenzy: true, + heartOfTheCrusader: true, + mangle: true, + sunderArmor: true, + curseOfWeakness: TristateEffect.TristateEffectRegular, + faerieFire: TristateEffect.TristateEffectImproved, + ebonPlaguebringer: true, + }), + }, + + // IconInputs to include in the 'Player' section on the settings tab. + playerIconInputs: [ + WarriorInputs.ShoutPicker, + WarriorInputs.Recklessness, + WarriorInputs.ShatteringThrow, + ], + // Inputs to include in the 'Rotation' section on the settings tab. + rotationInputs: WarriorInputs.WarriorRotationConfig, + // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. + includeBuffDebuffInputs: [ + // just for Bryntroll + IconInputs.SpellDamageDebuff, + IconInputs.SpellHitDebuff, + ], + excludeBuffDebuffInputs: [ + ], + // Inputs to include in the 'Other' section on the settings tab. + otherInputs: { + inputs: [ + WarriorInputs.StartingRage, + WarriorInputs.StanceSnapshot, + WarriorInputs.DisableExpertiseGemming, + OtherInputs.TankAssignment, + OtherInputs.InFrontOfTarget, + ], + }, + encounterPicker: { + // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. + showExecuteProportion: true, + }, + + presets: { + // Preset talents that the user can quickly select. + talents: [ + Presets.ArmsTalents, + Presets.FuryTalents, + ], + // Preset rotations that the user can quickly select. + rotations: [ + Presets.ROTATION_FURY, + Presets.ROTATION_FURY_SUNDER, + Presets.ROTATION_ARMS, + Presets.ROTATION_ARMS_SUNDER, + ], + // Preset gear configurations that the user can quickly select. + gear: [ + Presets.PRERAID_FURY_PRESET, + Presets.P1_FURY_PRESET, + Presets.P2_FURY_PRESET, + Presets.P3_FURY_PRESET_ALLIANCE, + Presets.P3_FURY_PRESET_HORDE, + Presets.PRERAID_ARMS_PRESET, + Presets.P1_ARMS_PRESET, + Presets.P2_ARMS_PRESET, + Presets.P3_ARMS_2P_PRESET_ALLIANCE, + Presets.P3_ARMS_4P_PRESET_ALLIANCE, + Presets.P3_ARMS_2P_PRESET_HORDE, + Presets.P3_ARMS_4P_PRESET_HORDE, + ], + }, + + autoRotation: (player: Player): APLRotation => { + const talentTree = player.getTalentTree(); + if (talentTree == 0) { + return Presets.ROTATION_ARMS_SUNDER.rotation.rotation!; + } else { + return Presets.ROTATION_FURY_SUNDER.rotation.rotation!; + } + }, +}); + export class WarriorSimUI extends IndividualSimUI { constructor(parentElem: HTMLElement, player: Player) { - super(parentElem, player, { - cssClass: 'warrior-sim-ui', - cssScheme: 'warrior', - // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], - - // All stats for which EP should be calculated. - epStats: [ - Stat.StatStrength, - Stat.StatAgility, - Stat.StatAttackPower, - Stat.StatExpertise, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatArmorPenetration, - Stat.StatArmor, - ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - PseudoStat.PseudoStatOffHandDps, - ], - // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. - epReferenceStat: Stat.StatAttackPower, - // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. - displayStats: [ - Stat.StatHealth, - Stat.StatStamina, - Stat.StatStrength, - Stat.StatAgility, - Stat.StatAttackPower, - Stat.StatExpertise, - Stat.StatMeleeHit, - Stat.StatMeleeCrit, - Stat.StatMeleeHaste, - Stat.StatArmorPenetration, - Stat.StatArmor, - ], - modifyDisplayStats: (player: Player) => { - let stats = new Stats(); - if (!player.getInFrontOfTarget()) { - // When behind target, dodge is the only outcome affected by Expertise. - stats = stats.addStat(Stat.StatExpertise, player.getTalents().weaponMastery * 4 * Mechanics.EXPERTISE_PER_QUARTER_PERCENT_REDUCTION); - } - return { - talents: stats, - }; - }, - - defaults: { - // Default equipped gear. - gear: Presets.P3_FURY_PRESET_ALLIANCE.gear, - // Default EP weights for sorting gear in the gear picker. - epWeights: Stats.fromMap({ - [Stat.StatStrength]: 2.72, - [Stat.StatAgility]: 1.82, - [Stat.StatAttackPower]: 1, - [Stat.StatExpertise]: 2.55, - [Stat.StatMeleeHit]: 0.79, - [Stat.StatMeleeCrit]: 2.12, - [Stat.StatMeleeHaste]: 1.72, - [Stat.StatArmorPenetration]: 2.17, - [Stat.StatArmor]: 0.03, - }, { - [PseudoStat.PseudoStatMainHandDps]: 6.29, - [PseudoStat.PseudoStatOffHandDps]: 3.58, - }), - // Default consumes settings. - consumes: Presets.DefaultConsumes, - // Default rotation settings. - rotation: Presets.DefaultRotation, - // Default talents. - talents: Presets.FuryTalents.data, - // Default spec-specific settings. - specOptions: Presets.DefaultOptions, - // Default raid/party buffs settings. - raidBuffs: RaidBuffs.create({ - giftOfTheWild: TristateEffect.TristateEffectImproved, - swiftRetribution: true, - strengthOfEarthTotem: TristateEffect.TristateEffectImproved, - icyTalons: true, - abominationsMight: true, - leaderOfThePack: TristateEffect.TristateEffectRegular, - sanctifiedRetribution: true, - bloodlust: true, - devotionAura: TristateEffect.TristateEffectImproved, - stoneskinTotem: TristateEffect.TristateEffectImproved, - }), - partyBuffs: PartyBuffs.create({ - heroicPresence: false, - }), - individualBuffs: IndividualBuffs.create({ - blessingOfKings: true, - blessingOfMight: TristateEffect.TristateEffectImproved, - }), - debuffs: Debuffs.create({ - bloodFrenzy: true, - heartOfTheCrusader: true, - mangle: true, - sunderArmor: true, - curseOfWeakness: TristateEffect.TristateEffectRegular, - faerieFire: TristateEffect.TristateEffectImproved, - ebonPlaguebringer: true, - }), - }, - - // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - WarriorInputs.ShoutPicker, - WarriorInputs.Recklessness, - WarriorInputs.ShatteringThrow, - ], - // Inputs to include in the 'Rotation' section on the settings tab. - rotationInputs: WarriorInputs.WarriorRotationConfig, - // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - // just for Bryntroll - IconInputs.SpellDamageDebuff, - IconInputs.SpellHitDebuff, - ], - excludeBuffDebuffInputs: [ - ], - // Inputs to include in the 'Other' section on the settings tab. - otherInputs: { - inputs: [ - WarriorInputs.StartingRage, - WarriorInputs.StanceSnapshot, - WarriorInputs.DisableExpertiseGemming, - OtherInputs.TankAssignment, - OtherInputs.InFrontOfTarget, - ], - }, - encounterPicker: { - // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. - showExecuteProportion: true, - }, - - presets: { - // Preset talents that the user can quickly select. - talents: [ - Presets.ArmsTalents, - Presets.FuryTalents, - ], - // Preset rotations that the user can quickly select. - rotations: [ - Presets.ROTATION_FURY, - Presets.ROTATION_FURY_SUNDER, - Presets.ROTATION_ARMS, - Presets.ROTATION_ARMS_SUNDER, - ], - // Preset gear configurations that the user can quickly select. - gear: [ - Presets.PRERAID_FURY_PRESET, - Presets.P1_FURY_PRESET, - Presets.P2_FURY_PRESET, - Presets.P3_FURY_PRESET_ALLIANCE, - Presets.P3_FURY_PRESET_HORDE, - Presets.PRERAID_ARMS_PRESET, - Presets.P1_ARMS_PRESET, - Presets.P2_ARMS_PRESET, - Presets.P3_ARMS_2P_PRESET_ALLIANCE, - Presets.P3_ARMS_4P_PRESET_ALLIANCE, - Presets.P3_ARMS_2P_PRESET_HORDE, - Presets.P3_ARMS_4P_PRESET_HORDE, - ], - }, - - autoRotation: (player: Player): APLRotation => { - const talentTree = player.getTalentTree(); - if (talentTree == 0) { - return Presets.ROTATION_ARMS_SUNDER.rotation.rotation!; - } else { - return Presets.ROTATION_FURY_SUNDER.rotation.rotation!; - } - }, - }); + super(parentElem, player, SPEC_CONFIG); this.addOptimizeGemsAction(); } addOptimizeGemsAction() {