diff --git a/proto/mage.proto b/proto/mage.proto index 5f54d62a66..23ac4481c8 100644 --- a/proto/mage.proto +++ b/proto/mage.proto @@ -158,7 +158,12 @@ message MageOptions { } message ArcaneMage { - message Rotation {} + message Rotation { + double missile_barrage_below_mana_percent = 1; + double blast_without_missile_barrage_above_mana_percent = 2; + double only_3_arcane_blast_stacks_below_mana_percent = 3; + bool use_arcane_barrage = 4; + } message Options { MageOptions mage_options = 1; @@ -167,7 +172,15 @@ message ArcaneMage { } message FireMage { - message Rotation {} + message Rotation { + enum PrimaryFireSpell { + Fireball = 0; + FrostfireBolt = 1; + Scorch = 2; + } + PrimaryFireSpell primary_fire_spell = 1; + bool maintain_improved_scorch = 2; + } message Options { MageOptions mage_options = 1; @@ -176,7 +189,9 @@ message FireMage { } message FrostMage { - message Rotation {} + message Rotation { + bool use_ice_lance = 1; + } message Options { MageOptions mage_options = 1; diff --git a/ui/core/components/fire_elemental_inputs.ts b/ui/core/components/fire_elemental_inputs.ts index 2569616064..7f37a76a9a 100644 --- a/ui/core/components/fire_elemental_inputs.ts +++ b/ui/core/components/fire_elemental_inputs.ts @@ -1,82 +1,85 @@ -import { IndividualSimUI } from "../individual_sim_ui"; -import { Player } from "../player"; -import { ShamanTotems } from "../proto/shaman"; -import { ShamanSpecs } from "../proto_utils/utils"; -import { EventID } from "../typed_event"; -import { ContentBlock } from "./content_block"; -import { IconPicker } from "./icon_picker"; import * as InputHelpers from '../components/input_helpers.js'; +import { IndividualSimUI } from '../individual_sim_ui'; +import { Player } from '../player'; +import { ShamanTotems } from '../proto/shaman'; import { ActionId } from '../proto_utils/action_id.js'; -import { Input } from "./input"; -import { NumberPicker } from "./number_picker"; -import { BooleanPicker } from "./boolean_picker"; +import { ShamanSpecs } from '../proto_utils/utils'; +import { EventID } from '../typed_event'; +import { BooleanPicker } from './boolean_picker'; +import { ContentBlock } from './content_block'; +import { IconPicker } from './icon_picker'; +import { Input } from './input'; +import { NumberPicker } from './number_picker'; -export function FireElementalSection(parentElem: HTMLElement, simUI: IndividualSimUI): ContentBlock { - let contentBlock = new ContentBlock(parentElem, 'fire-elemental-settings', { - header: { title: 'Fire Elemental' } +export function FireElementalSection(parentElem: HTMLElement, simUI: IndividualSimUI): ContentBlock { + const contentBlock = new ContentBlock(parentElem, 'fire-elemental-settings', { + header: { title: 'Fire Elemental' }, }); - let fireElementalIconContainer = Input.newGroupContainer(); + const fireElementalIconContainer = Input.newGroupContainer(); fireElementalIconContainer.classList.add('fire-elemental-icon-container'); contentBlock.bodyElement.appendChild(fireElementalIconContainer); - const fireElementalBooleanIconInput = InputHelpers.makeBooleanIconInput>({ - getModObject: (player: Player) => player, - getValue: (player: Player) => player.getSpecOptions().totems || ShamanTotems.create(), - setValue: (eventID: EventID, player: Player, newVal: ShamanTotems) => { - const newOptions = player.getSpecOptions(); - newOptions.totems = newVal; - player.setSpecOptions(eventID, newOptions); + const fireElementalBooleanIconInput = InputHelpers.makeBooleanIconInput>( + { + getModObject: (player: Player) => player, + getValue: (player: Player) => player.getSpecOptions().totems || ShamanTotems.create(), + setValue: (eventID: EventID, player: Player, newVal: ShamanTotems) => { + const newOptions = player.getSpecOptions(); + newOptions.totems = newVal; + player.setSpecOptions(eventID, newOptions); - // Hacky fix ItemSwapping is in the Rotation proto, this will let the Rotation know to update showWhen - // TODO move the ItemSwap enabled to a spec option and have the ItemSwap proto be apart of player. - player.rotationChangeEmitter.emit(eventID) + // Hacky fix ItemSwapping is in the Rotation proto, this will let the Rotation know to update showWhen + // TODO move the ItemSwap enabled to a spec option and have the ItemSwap proto be apart of player. + player.rotationChangeEmitter.emit(eventID); + }, + changeEmitter: (player: Player) => player.specOptionsChangeEmitter, }, - changeEmitter: (player: Player) => player.specOptionsChangeEmitter, - }, ActionId.fromSpellId(2894), "useFireElemental"); + ActionId.fromSpellId(2894), + 'useFireElemental', + ); new IconPicker(fireElementalIconContainer, simUI.player, fireElementalBooleanIconInput); new NumberPicker(contentBlock.bodyElement, simUI.player, { positive: true, - label: "Bonus spell power", - labelTooltip: "Bonus spell power to snapshot Fire Elemental with. Will prioritize dropping Fire Elemental if greater then 0", + label: 'Bonus spell power', + labelTooltip: 'Bonus spell power to snapshot Fire Elemental with. Will prioritize dropping Fire Elemental if greater then 0', inline: true, getValue: (player: Player) => player.getSpecOptions().totems?.bonusSpellpower || 0, setValue: (eventID: EventID, player: Player, newVal: number) => { const newOptions = player.getSpecOptions(); if (newOptions.totems) { - newOptions.totems.bonusSpellpower = newVal + newOptions.totems.bonusSpellpower = newVal; } player.setSpecOptions(eventID, newOptions); }, changedEvent: (player: Player) => player.specOptionsChangeEmitter, - }) + }); new BooleanPicker(contentBlock.bodyElement, simUI.player, { - label: "Use Tier 10 (4pc)", - labelTooltip: "Will use Tier 10 (4pc) to snapshot Fire Elemental.", + label: 'Use Tier 10 (4pc)', + labelTooltip: 'Will use Tier 10 (4pc) to snapshot Fire Elemental.', inline: true, getValue: (player: Player) => player.getSpecOptions().totems?.enhTierTenBonus || false, setValue: (eventID: EventID, player: Player, newVal: boolean) => { const newOptions = player.getSpecOptions(); if (newOptions.totems) { - newOptions.totems.enhTierTenBonus = newVal + newOptions.totems.enhTierTenBonus = newVal; } player.setSpecOptions(eventID, newOptions); }, changedEvent: (player: Player) => player.currentStatsEmitter, showWhen: (player: Player) => { - const hasBonus = player.getCurrentStats().sets.includes('Frost Witch\'s Battlegear (4pc)'); - return hasBonus - } - }) - + const hasBonus = player.getCurrentStats().sets.includes("Frost Witch's Battlegear (4pc)"); + return hasBonus; + }, + }); return contentBlock; -} \ No newline at end of file +} diff --git a/ui/core/components/individual_sim_ui/apl_actions.ts b/ui/core/components/individual_sim_ui/apl_actions.ts index a78d4a2bbb..13b273aedc 100644 --- a/ui/core/components/individual_sim_ui/apl_actions.ts +++ b/ui/core/components/individual_sim_ui/apl_actions.ts @@ -32,18 +32,16 @@ import { ListItemPickerConfig, ListPicker } from '../list_picker.js'; import * as AplHelpers from './apl_helpers.js'; import * as AplValues from './apl_values.js'; -export interface APLActionPickerConfig extends InputConfig, APLAction> { -} +export interface APLActionPickerConfig extends InputConfig, APLAction> {} export type APLActionKind = APLAction['action']['oneofKind']; -type APLActionImplStruct = Extract; +type APLActionImplStruct = Extract; type APLActionImplTypesUnion = { [f in NonNullable]: f extends keyof APLActionImplStruct ? APLActionImplStruct[f] : never; }; -export type APLActionImplType = APLActionImplTypesUnion[NonNullable]|undefined; +export type APLActionImplType = APLActionImplTypesUnion[NonNullable] | undefined; export class APLActionPicker extends Input, APLAction> { - private kindPicker: TextDropdownPicker, APLActionKind>; private readonly actionDiv: HTMLElement; @@ -65,9 +63,12 @@ export class APLActionPicker extends Input, APLAction> { srcVal.condition = newValue; player.rotationChangeEmitter.emit(eventID); } else { - this.setSourceValue(eventID, APLAction.create({ - condition: newValue, - })); + this.setSourceValue( + eventID, + APLAction.create({ + condition: newValue, + }), + ); } }, }); @@ -79,21 +80,21 @@ export class APLActionPicker extends Input, APLAction> { const isPrepull = this.rootElem.closest('.apl-prepull-action-picker') != null; - const allActionKinds = (Object.keys(actionKindFactories) as Array>) - .filter(actionKind => actionKindFactories[actionKind].includeIf?.(player, isPrepull) ?? true); + const allActionKinds = (Object.keys(actionKindFactories) as Array>).filter( + actionKind => actionKindFactories[actionKind].includeIf?.(player, isPrepull) ?? true, + ); this.kindPicker = new TextDropdownPicker(this.actionDiv, player, { defaultLabel: 'Action', - values: allActionKinds - .map(actionKind => { - const factory = actionKindFactories[actionKind]; - return { - value: actionKind, - label: factory.label, - submenu: factory.submenu, - tooltip: factory.fullDescription ? `

${factory.shortDescription}

${factory.fullDescription}` : factory.shortDescription, - }; - }), + values: allActionKinds.map(actionKind => { + const factory = actionKindFactories[actionKind]; + return { + value: actionKind, + label: factory.label, + submenu: factory.submenu, + tooltip: factory.fullDescription ? `

${factory.shortDescription}

${factory.fullDescription}` : factory.shortDescription, + }; + }), equals: (a, b) => a == b, changedEvent: (player: Player) => player.rotationChangeEmitter, getValue: (_player: Player) => this.getSourceValue()?.action.oneofKind, @@ -114,17 +115,25 @@ export class APLActionPicker extends Input, APLAction> { if (sourceValue.action.oneofKind == 'strictSequence') { (newSourceValue.action as APLActionImplStruct<'sequence'>).sequence.actions = sourceValue.action.strictSequence.actions; } else { - (newSourceValue.action as APLActionImplStruct<'sequence'>).sequence.actions = [this.makeAPLAction(oldKind, this.actionPicker.getInputValue())]; + (newSourceValue.action as APLActionImplStruct<'sequence'>).sequence.actions = [ + this.makeAPLAction(oldKind, this.actionPicker.getInputValue()), + ]; } } else if (newKind == 'strictSequence') { if (sourceValue.action.oneofKind == 'sequence') { - (newSourceValue.action as APLActionImplStruct<'strictSequence'>).strictSequence.actions = sourceValue.action.sequence.actions; + (newSourceValue.action as APLActionImplStruct<'strictSequence'>).strictSequence.actions = + sourceValue.action.sequence.actions; } else { - (newSourceValue.action as APLActionImplStruct<'strictSequence'>).strictSequence.actions = [this.makeAPLAction(oldKind, this.actionPicker.getInputValue())]; + (newSourceValue.action as APLActionImplStruct<'strictSequence'>).strictSequence.actions = [ + this.makeAPLAction(oldKind, this.actionPicker.getInputValue()), + ]; } } else if (sourceValue.action.oneofKind == 'sequence' && sourceValue.action.sequence.actions?.[0]?.action.oneofKind == newKind) { newSourceValue = sourceValue.action.sequence.actions[0]; - } else if (sourceValue.action.oneofKind == 'strictSequence' && sourceValue.action.strictSequence.actions?.[0]?.action.oneofKind == newKind) { + } else if ( + sourceValue.action.oneofKind == 'strictSequence' && + sourceValue.action.strictSequence.actions?.[0]?.action.oneofKind == newKind + ) { newSourceValue = sourceValue.action.strictSequence.actions[0]; } } @@ -159,15 +168,15 @@ export class APLActionPicker extends Input, APLAction> { condition: this.conditionPicker.getInputValue(), action: { oneofKind: actionKind, - ...((() => { + ...(() => { const val: any = {}; if (actionKind && this.actionPicker) { val[actionKind] = this.actionPicker.getInputValue(); } return val; - })()), + })(), }, - }) + }); } setInputValue(newValue: APLAction) { @@ -191,7 +200,7 @@ export class APLActionPicker extends Input, APLAction> { } const obj: any = { oneofKind: kind }; obj[kind] = implVal; - return APLAction.create({action: obj}); + return APLAction.create({ action: obj }); } private updateActionPicker(newActionKind: APLActionKind) { @@ -229,28 +238,29 @@ export class APLActionPicker extends Input, APLAction> { } type ActionKindConfig = { - label: string, - submenu?: Array, - shortDescription: string, - fullDescription?: string, - includeIf?: (player: Player, isPrepull: boolean) => boolean, - newValue: () => T, - factory: (parent: HTMLElement, player: Player, config: InputConfig, T>) => Input, T>, + label: string; + submenu?: Array; + shortDescription: string; + fullDescription?: string; + includeIf?: (player: Player, isPrepull: boolean) => boolean; + newValue: () => T; + factory: (parent: HTMLElement, player: Player, config: InputConfig, T>) => Input, T>; }; function itemSwapSetFieldConfig(field: string): AplHelpers.APLPickerBuilderFieldConfig { return { field: field, newValue: () => ItemSwapSet.Swap1, - factory: (parent, player, config) => new TextDropdownPicker(parent, player, { - ...config, - defaultLabel: 'None', - equals: (a, b) => a == b, - values: [ - { value: ItemSwapSet.Main, label: 'Main' }, - { value: ItemSwapSet.Swap1, label: 'Swapped' }, - ], - }), + factory: (parent, player, config) => + new TextDropdownPicker(parent, player, { + ...config, + defaultLabel: 'None', + equals: (a, b) => a == b, + values: [ + { value: ItemSwapSet.Main, label: 'Main' }, + { value: ItemSwapSet.Swap1, label: 'Swapped' }, + ], + }), }; } @@ -266,34 +276,44 @@ function actionListFieldConfig(field: string): AplHelpers.APLPickerBuilderFieldC return { field: field, newValue: () => [], - factory: (parent, player, config) => new ListPicker, APLAction>(parent, player, { - ...config, - // Override setValue to replace undefined elements with default messages. - setValue: (eventID: EventID, player: Player, newValue: Array) => { - config.setValue(eventID, player, newValue.map(val => val || APLAction.create())); - }, - itemLabel: 'Action', - newItem: APLAction.create, - copyItem: (oldValue: APLAction) => oldValue ? APLAction.clone(oldValue) : oldValue, - newItemPicker: (parent: HTMLElement, listPicker: ListPicker, APLAction>, index: number, config: ListItemPickerConfig, APLAction>) => new APLActionPicker(parent, player, config), - allowedActions: ['create', 'delete', 'move'], - actions: { - create: { - useIcon: true, - } - } - }), + factory: (parent, player, config) => + new ListPicker, APLAction>(parent, player, { + ...config, + // Override setValue to replace undefined elements with default messages. + setValue: (eventID: EventID, player: Player, newValue: Array) => { + config.setValue( + eventID, + player, + newValue.map(val => val || APLAction.create()), + ); + }, + itemLabel: 'Action', + newItem: APLAction.create, + copyItem: (oldValue: APLAction) => (oldValue ? APLAction.clone(oldValue) : oldValue), + newItemPicker: ( + parent: HTMLElement, + listPicker: ListPicker, APLAction>, + index: number, + config: ListItemPickerConfig, APLAction>, + ) => new APLActionPicker(parent, player, config), + allowedActions: ['create', 'delete', 'move'], + actions: { + create: { + useIcon: true, + }, + }, + }), }; } function inputBuilder(config: { - label: string, - submenu?: Array, - shortDescription: string, - fullDescription?: string, - includeIf?: (player: Player, isPrepull: boolean) => boolean, - newValue: () => T, - fields: Array>, + label: string; + submenu?: Array; + shortDescription: string; + fullDescription?: string; + includeIf?: (player: Player, isPrepull: boolean) => boolean; + newValue: () => T; + fields: Array>; }): ActionKindConfig { return { label: config.label, @@ -306,32 +326,30 @@ function inputBuilder(config: { }; } -const actionKindFactories: {[f in NonNullable]: ActionKindConfig} = { +const actionKindFactories: { [f in NonNullable]: ActionKindConfig } = { ['castSpell']: inputBuilder({ label: 'Cast', shortDescription: 'Casts the spell if possible, i.e. resource/cooldown/GCD/etc requirements are all met.', newValue: APLActionCastSpell.create, - fields: [ - AplHelpers.actionIdFieldConfig('spellId', 'castable_spells', ''), - AplHelpers.unitFieldConfig('target', 'targets'), - ], + fields: [AplHelpers.actionIdFieldConfig('spellId', 'castable_spells', ''), AplHelpers.unitFieldConfig('target', 'targets')], }), ['multidot']: inputBuilder({ label: 'Multi Dot', submenu: ['Casting'], shortDescription: 'Keeps a DoT active on multiple targets by casting the specified spell.', includeIf: (player: Player, isPrepull: boolean) => !isPrepull, - newValue: () => APLActionMultidot.create({ - maxDots: 3, - maxOverlap: { - value: { - oneofKind: 'const', - const: { - val: '0ms', + newValue: () => + APLActionMultidot.create({ + maxDots: 3, + maxOverlap: { + value: { + oneofKind: 'const', + const: { + val: '0ms', + }, }, }, - }, - }), + }), fields: [ AplHelpers.actionIdFieldConfig('spellId', 'dot_spells', ''), AplHelpers.numberFieldConfig('maxDots', false, { @@ -349,17 +367,18 @@ const actionKindFactories: {[f in NonNullable]: ActionKindConfig< submenu: ['Casting'], shortDescription: 'Keeps a Shield active on multiple targets by casting the specified spell.', includeIf: (player: Player, isPrepull: boolean) => !isPrepull && player.spec.isHealingSpec, - newValue: () => APLActionMultishield.create({ - maxShields: 3, - maxOverlap: { - value: { - oneofKind: 'const', - const: { - val: '0ms', + newValue: () => + APLActionMultishield.create({ + maxShields: 3, + maxOverlap: { + value: { + oneofKind: 'const', + const: { + val: '0ms', + }, }, }, - }, - }), + }), fields: [ AplHelpers.actionIdFieldConfig('spellId', 'shield_spells', ''), AplHelpers.numberFieldConfig('maxShields', false, { @@ -386,14 +405,15 @@ const actionKindFactories: {[f in NonNullable]: ActionKindConfig<

Note that if you simply want to allow other actions to interrupt the channel, set Interrupt If to True.

`, - newValue: () => APLActionChannelSpell.create({ - interruptIf: { - value: { - oneofKind: 'gcdIsReady', - gcdIsReady: {}, - } - }, - }), + newValue: () => + APLActionChannelSpell.create({ + interruptIf: { + value: { + oneofKind: 'gcdIsReady', + gcdIsReady: {}, + }, + }, + }), fields: [ AplHelpers.actionIdFieldConfig('spellId', 'channel_spells', ''), AplHelpers.unitFieldConfig('target', 'targets'), @@ -425,19 +445,18 @@ const actionKindFactories: {[f in NonNullable]: ActionKindConfig< submenu: ['Timing'], shortDescription: 'Pauses all APL actions for a specified amount of time.', includeIf: (player: Player, isPrepull: boolean) => !isPrepull, - newValue: () => APLActionWait.create({ - duration: { - value: { - oneofKind: 'const', - const: { - val: '1000ms', + newValue: () => + APLActionWait.create({ + duration: { + value: { + oneofKind: 'const', + const: { + val: '1000ms', + }, }, }, - }, - }), - fields: [ - AplValues.valueFieldConfig('duration'), - ], + }), + fields: [AplValues.valueFieldConfig('duration')], }), ['waitUntil']: inputBuilder({ label: 'Wait Until', @@ -445,21 +464,20 @@ const actionKindFactories: {[f in NonNullable]: ActionKindConfig< shortDescription: 'Pauses all APL actions until the specified condition is True.', includeIf: (player: Player, isPrepull: boolean) => !isPrepull, newValue: () => APLActionWaitUntil.create(), - fields: [ - AplValues.valueFieldConfig('condition'), - ], + fields: [AplValues.valueFieldConfig('condition')], }), ['schedule']: inputBuilder({ label: 'Scheduled Action', submenu: ['Timing'], shortDescription: 'Executes the inner action once at each specified timing.', includeIf: (player: Player, isPrepull: boolean) => !isPrepull, - newValue: () => APLActionSchedule.create({ - schedule: '0s, 60s', - innerAction: { - action: {oneofKind: 'castSpell', castSpell: {}}, - }, - }), + newValue: () => + APLActionSchedule.create({ + schedule: '0s, 60s', + innerAction: { + action: { oneofKind: 'castSpell', castSpell: {} }, + }, + }), fields: [ AplHelpers.stringFieldConfig('schedule', { label: 'Do At', @@ -478,10 +496,7 @@ const actionKindFactories: {[f in NonNullable]: ActionKindConfig< `, includeIf: (player: Player, isPrepull: boolean) => !isPrepull, newValue: APLActionSequence.create, - fields: [ - AplHelpers.stringFieldConfig('name'), - actionListFieldConfig('actions'), - ], + fields: [AplHelpers.stringFieldConfig('name'), actionListFieldConfig('actions')], }), ['resetSequence']: inputBuilder({ label: 'Reset Sequence', @@ -492,31 +507,26 @@ const actionKindFactories: {[f in NonNullable]: ActionKindConfig< `, includeIf: (player: Player, isPrepull: boolean) => !isPrepull, newValue: APLActionResetSequence.create, - fields: [ - AplHelpers.stringFieldConfig('sequenceName'), - ], + fields: [AplHelpers.stringFieldConfig('sequenceName')], }), ['strictSequence']: inputBuilder({ label: 'Strict Sequence', submenu: ['Sequences'], - shortDescription: 'Like a regular Sequence, except all sub-actions are executed immediately after each other and the sequence resets automatically upon completion.', + shortDescription: + 'Like a regular Sequence, except all sub-actions are executed immediately after each other and the sequence resets automatically upon completion.', fullDescription: `

Strict Sequences do not begin unless ALL sub-actions are ready.

`, includeIf: (player: Player, isPrepull: boolean) => !isPrepull, newValue: APLActionStrictSequence.create, - fields: [ - actionListFieldConfig('actions'), - ], + fields: [actionListFieldConfig('actions')], }), ['changeTarget']: inputBuilder({ label: 'Change Target', submenu: ['Misc'], shortDescription: 'Sets the current target, which is the target of auto attacks and most casts by default.', newValue: () => APLActionChangeTarget.create(), - fields: [ - AplHelpers.unitFieldConfig('newTarget', 'targets'), - ], + fields: [AplHelpers.unitFieldConfig('newTarget', 'targets')], }), ['activateAura']: inputBuilder({ label: 'Activate Aura', @@ -524,38 +534,30 @@ const actionKindFactories: {[f in NonNullable]: ActionKindConfig< shortDescription: 'Activates an aura', includeIf: (player: Player, isPrepull: boolean) => isPrepull, newValue: () => APLActionActivateAura.create(), - fields: [ - AplHelpers.actionIdFieldConfig('auraId', 'auras'), - ], + fields: [AplHelpers.actionIdFieldConfig('auraId', 'auras')], }), ['cancelAura']: inputBuilder({ label: 'Cancel Aura', submenu: ['Misc'], shortDescription: 'Deactivates an aura, equivalent to /cancelaura.', newValue: () => APLActionCancelAura.create(), - fields: [ - AplHelpers.actionIdFieldConfig('auraId', 'auras'), - ], + fields: [AplHelpers.actionIdFieldConfig('auraId', 'auras')], }), ['triggerIcd']: inputBuilder({ label: 'Trigger ICD', submenu: ['Misc'], - shortDescription: 'Triggers an aura\'s ICD, putting it on cooldown. Example usage would be to desync an ICD cooldown before combat starts.', + shortDescription: "Triggers an aura's ICD, putting it on cooldown. Example usage would be to desync an ICD cooldown before combat starts.", includeIf: (player: Player, isPrepull: boolean) => isPrepull, newValue: () => APLActionTriggerICD.create(), - fields: [ - AplHelpers.actionIdFieldConfig('auraId', 'icd_auras'), - ], + fields: [AplHelpers.actionIdFieldConfig('auraId', 'icd_auras')], }), ['itemSwap']: inputBuilder({ label: 'Item Swap', submenu: ['Misc'], shortDescription: 'Swaps items, using the swap set specified in Settings.', - includeIf: (player: Player, _isPrepull: boolean) => itemSwapEnabledSpecs.includes(player.spec.protoID), + includeIf: (player: Player, _isPrepull: boolean) => itemSwapEnabledSpecs.includes(player.getSpec()), newValue: () => APLActionItemSwap.create(), - fields: [ - itemSwapSetFieldConfig('swapSet'), - ], + fields: [itemSwapSetFieldConfig('swapSet')], }), ['customRotation']: inputBuilder({ @@ -564,8 +566,7 @@ const actionKindFactories: {[f in NonNullable]: ActionKindConfig< shortDescription: 'INTERNAL ONLY', includeIf: (_player: Player, _isPrepull: boolean) => false, // Never show this, because its internal only. newValue: () => APLActionCustomRotation.create(), - fields: [ - ], + fields: [], }), // Class/spec specific actions @@ -573,47 +574,50 @@ const actionKindFactories: {[f in NonNullable]: ActionKindConfig< label: 'Optimal Rotation Action', submenu: ['Feral Druid'], shortDescription: 'Executes optimized Feral DPS rotation using hardcoded legacy algorithm.', - includeIf: (player: Player, _isPrepull: boolean) => player.spec.protoID == Spec.SpecFeralDruid, - newValue: () => APLActionCatOptimalRotationAction.create({ - rotationType: FeralDruid_Rotation_AplType.SingleTarget, - manualParams: true, - maxFfDelay: 0.1, - minRoarOffset: 25.0, - ripLeeway: 4, - useRake: true, - useBite: true, - biteTime: 4.0, - flowerWeave: false, - }), + includeIf: (player: Player, _isPrepull: boolean) => player.getSpec() == Spec.SpecFeralDruid, + newValue: () => + APLActionCatOptimalRotationAction.create({ + rotationType: FeralDruid_Rotation_AplType.SingleTarget, + manualParams: true, + maxFfDelay: 0.1, + minRoarOffset: 25.0, + ripLeeway: 4, + useRake: true, + useBite: true, + biteTime: 4.0, + flowerWeave: false, + }), fields: [ AplHelpers.rotationTypeFieldConfig('rotationType'), AplHelpers.booleanFieldConfig('manualParams', 'Manual Advanced Parameters', { - 'labelTooltip': 'Manually specify advanced parameters, otherwise will use preset defaults.', + labelTooltip: 'Manually specify advanced parameters, otherwise will use preset defaults.', }), AplHelpers.numberFieldConfig('maxFfDelay', true, { - 'label': 'Max FF Delay', - 'labelTooltip': 'Max allowed FF delay to fit in damage casts. Ignored if not using manual advanced parameters.', + label: 'Max FF Delay', + labelTooltip: 'Max allowed FF delay to fit in damage casts. Ignored if not using manual advanced parameters.', }), AplHelpers.numberFieldConfig('minRoarOffset', true, { - 'label': 'Roar Offset', - 'labelTooltip': 'Targeted offset in Rip/Roar timings. Ignored for AOE rotation or if not using manual advanced parameters.', + label: 'Roar Offset', + labelTooltip: 'Targeted offset in Rip/Roar timings. Ignored for AOE rotation or if not using manual advanced parameters.', }), AplHelpers.numberFieldConfig('ripLeeway', false, { - 'label': 'Rip Leeway', - 'labelTooltip': 'Rip leeway when optimizing Roar clips. Ignored for AOE rotation or if not using manual advanced parameters.', + label: 'Rip Leeway', + labelTooltip: 'Rip leeway when optimizing Roar clips. Ignored for AOE rotation or if not using manual advanced parameters.', }), AplHelpers.booleanFieldConfig('useRake', 'Use Rake', { - 'labelTooltip': 'Use Rake during rotation. Ignored for AOE rotation or if not using manual advanced parameters.', + labelTooltip: 'Use Rake during rotation. Ignored for AOE rotation or if not using manual advanced parameters.', }), AplHelpers.booleanFieldConfig('useBite', 'Bite during rotation', { - 'labelTooltip': 'Use Bite during rotation rather than exclusively at end of fight. Ignored for AOE rotation or if not using manual advanced parameters.', + labelTooltip: + 'Use Bite during rotation rather than exclusively at end of fight. Ignored for AOE rotation or if not using manual advanced parameters.', }), AplHelpers.numberFieldConfig('biteTime', true, { - 'label': 'Bite Time', - 'labelTooltip': 'Min seconds remaining on Rip/Roar to allow a Bite. Ignored if not Biting during rotation.', + label: 'Bite Time', + labelTooltip: 'Min seconds remaining on Rip/Roar to allow a Bite. Ignored if not Biting during rotation.', }), AplHelpers.booleanFieldConfig('flowerWeave', 'Flower Weave', { - 'labelTooltip': 'Fish for Clearcasting procs during AOE rotation with GotW. Ignored for Single Target rotation or if not using manual advanced parameters.', + labelTooltip: + 'Fish for Clearcasting procs during AOE rotation with GotW. Ignored for Single Target rotation or if not using manual advanced parameters.', }), ], }), diff --git a/ui/core/components/sim_title_dropdown.ts b/ui/core/components/sim_title_dropdown.ts index 6baad1daa9..1519c154d0 100644 --- a/ui/core/components/sim_title_dropdown.ts +++ b/ui/core/components/sim_title_dropdown.ts @@ -12,7 +12,7 @@ interface ClassOptions { interface SpecOptions { type: 'Spec'; - spec: PlayerSpec; + spec: PlayerSpec; } interface RaidOptions { @@ -27,7 +27,7 @@ type SimTitleDropdownConfig = { export class SimTitleDropdown extends Component { private readonly dropdownMenu: HTMLElement | undefined; - constructor(parent: HTMLElement, currentSpec: PlayerSpec | null, config: SimTitleDropdownConfig = {}) { + constructor(parent: HTMLElement, currentSpec: PlayerSpec | null, config: SimTitleDropdownConfig = {}) { super(parent, 'sim-title-dropdown-root'); const rootLinkArgs: SpecOptions | RaidOptions = currentSpec === null ? { type: 'Raid' } : { type: 'Spec', spec: currentSpec }; @@ -172,7 +172,7 @@ export class SimTitleDropdown extends Component { return fragment.children[0] as HTMLElement; } - private buildSpecLink(spec: PlayerSpec): HTMLElement { + private buildSpecLink(spec: PlayerSpec): HTMLElement { const textKlass = this.getContextualKlass({ type: 'Spec', spec: spec }); const iconPath = this.getSimIconPath({ type: 'Spec', spec: spec }); diff --git a/ui/core/components/totem_inputs.ts b/ui/core/components/totem_inputs.ts index db1316d107..76248c9c1b 100644 --- a/ui/core/components/totem_inputs.ts +++ b/ui/core/components/totem_inputs.ts @@ -1,40 +1,32 @@ import { IconEnumPicker } from '../components/icon_enum_picker.js'; import { IconPicker } from '../components/icon_picker.js'; -import { - AirTotem, - EarthTotem, - FireTotem, - WaterTotem, - ShamanTotems, -} from '../proto/shaman.js'; +import * as InputHelpers from '../components/input_helpers.js'; +import { IndividualSimUI } from '../individual_sim_ui.js'; +import { Player } from '../player.js'; import { Spec } from '../proto/common.js'; +import { AirTotem, EarthTotem, FireTotem, ShamanTotems, WaterTotem } from '../proto/shaman.js'; import { ActionId } from '../proto_utils/action_id.js'; -import { Player } from '../player.js'; -import { IndividualSimUI } from '../individual_sim_ui.js'; -import { EventID } from '../typed_event.js'; -import * as InputHelpers from '../components/input_helpers.js'; import { ShamanSpecs } from '../proto_utils/utils.js'; +import { EventID } from '../typed_event.js'; import { ContentBlock } from './content_block.js'; import { Input } from './input.js'; -export function TotemsSection(parentElem: HTMLElement, simUI: IndividualSimUI): ContentBlock { - let contentBlock = new ContentBlock(parentElem, 'totems-settings', { - header: { title: 'Totems' } +export function TotemsSection(parentElem: HTMLElement, simUI: IndividualSimUI): ContentBlock { + const contentBlock = new ContentBlock(parentElem, 'totems-settings', { + header: { title: 'Totems' }, }); - let totemDropdownGroup = Input.newGroupContainer(); + const totemDropdownGroup = Input.newGroupContainer(); totemDropdownGroup.classList.add('totem-dropdowns-container', 'icon-group'); - let fireElementalContainer = document.createElement('div'); + const fireElementalContainer = document.createElement('div'); fireElementalContainer.classList.add('fire-elemental-input-container'); contentBlock.bodyElement.appendChild(totemDropdownGroup); contentBlock.bodyElement.appendChild(fireElementalContainer); - const earthTotemPicker = new IconEnumPicker(totemDropdownGroup, simUI.player, { - extraCssClasses: [ - 'earth-totem-picker', - ], + const _earthTotemPicker = new IconEnumPicker(totemDropdownGroup, simUI.player, { + extraCssClasses: ['earth-totem-picker'], numColumns: 1, values: [ { color: '#ffdfba', value: EarthTotem.NoEarthTotem }, @@ -48,17 +40,14 @@ export function TotemsSection(parentElem: HTMLElement, simUI: IndividualSimUI) => player.getSpecOptions().totems?.earth || EarthTotem.NoEarthTotem, setValue: (eventID: EventID, player: Player, newValue: number) => { const newOptions = player.getSpecOptions(); - if (!newOptions.totems) - newOptions.totems = ShamanTotems.create(); + if (!newOptions.totems) newOptions.totems = ShamanTotems.create(); newOptions.totems!.earth = newValue; player.setSpecOptions(eventID, newOptions); }, }); - const waterTotemPicker = new IconEnumPicker(totemDropdownGroup, simUI.player, { - extraCssClasses: [ - 'water-totem-picker', - ], + const _waterTotemPicker = new IconEnumPicker(totemDropdownGroup, simUI.player, { + extraCssClasses: ['water-totem-picker'], numColumns: 1, values: [ { color: '#bae1ff', value: WaterTotem.NoWaterTotem }, @@ -71,23 +60,24 @@ export function TotemsSection(parentElem: HTMLElement, simUI: IndividualSimUI) => player.getSpecOptions().totems?.water || WaterTotem.NoWaterTotem, setValue: (eventID: EventID, player: Player, newValue: number) => { const newOptions = player.getSpecOptions(); - if (!newOptions.totems) - newOptions.totems = ShamanTotems.create(); + if (!newOptions.totems) newOptions.totems = ShamanTotems.create(); newOptions.totems!.water = newValue; player.setSpecOptions(eventID, newOptions); }, }); - const fireTotemPicker = new IconEnumPicker(totemDropdownGroup, simUI.player, { - extraCssClasses: [ - 'fire-totem-picker', - ], + const _fireTotemPicker = new IconEnumPicker(totemDropdownGroup, simUI.player, { + extraCssClasses: ['fire-totem-picker'], numColumns: 1, values: [ { color: '#ffb3ba', value: FireTotem.NoFireTotem }, { actionId: ActionId.fromSpellId(58734), value: FireTotem.MagmaTotem }, { actionId: ActionId.fromSpellId(58704), value: FireTotem.SearingTotem }, - { actionId: ActionId.fromSpellId(57722), value: FireTotem.TotemOfWrath, showWhen: (player: Player) => player.getTalents().totemOfWrath }, + { + actionId: ActionId.fromSpellId(57722), + value: FireTotem.TotemOfWrath, + showWhen: (player: Player) => player.getTalents().totemOfWrath, + }, { actionId: ActionId.fromSpellId(58656), value: FireTotem.FlametongueTotem }, ], equals: (a: FireTotem, b: FireTotem) => a == b, @@ -96,17 +86,14 @@ export function TotemsSection(parentElem: HTMLElement, simUI: IndividualSimUI) => player.getSpecOptions().totems?.fire || FireTotem.NoFireTotem, setValue: (eventID: EventID, player: Player, newValue: number) => { const newOptions = player.getSpecOptions(); - if (!newOptions.totems) - newOptions.totems = ShamanTotems.create(); + if (!newOptions.totems) newOptions.totems = ShamanTotems.create(); newOptions.totems!.fire = newValue; player.setSpecOptions(eventID, newOptions); }, }); - const airTotemPicker = new IconEnumPicker(totemDropdownGroup, simUI.player, { - extraCssClasses: [ - 'air-totem-picker', - ], + const _airTotemPicker = new IconEnumPicker(totemDropdownGroup, simUI.player, { + extraCssClasses: ['air-totem-picker'], numColumns: 1, values: [ { color: '#baffc9', value: AirTotem.NoAirTotem }, @@ -119,25 +106,28 @@ export function TotemsSection(parentElem: HTMLElement, simUI: IndividualSimUI) => player.getSpecOptions().totems?.air || AirTotem.NoAirTotem, setValue: (eventID: EventID, player: Player, newValue: number) => { const newOptions = player.getSpecOptions(); - if (!newOptions.totems) - newOptions.totems = ShamanTotems.create(); + if (!newOptions.totems) newOptions.totems = ShamanTotems.create(); newOptions.totems!.air = newValue; player.setSpecOptions(eventID, newOptions); }, }); // Enchancement Shaman uses the Fire Elemental Inputs with custom inputs. - if (simUI.player.spec != Spec.SpecEnhancementShaman) { - const fireElementalBooleanIconInput = InputHelpers.makeBooleanIconInput>({ - getModObject: (player: Player) => player, - getValue: (player: Player) => player.getSpecOptions().totems || ShamanTotems.create(), - setValue: (eventID: EventID, player: Player, newVal: ShamanTotems) => { - const newOptions = player.getSpecOptions(); - newOptions.totems = newVal; - player.setSpecOptions(eventID, newOptions); + if (simUI.player.getSpec() != Spec.SpecEnhancementShaman) { + const fireElementalBooleanIconInput = InputHelpers.makeBooleanIconInput>( + { + getModObject: (player: Player) => player, + getValue: (player: Player) => player.getSpecOptions().totems || ShamanTotems.create(), + setValue: (eventID: EventID, player: Player, newVal: ShamanTotems) => { + const newOptions = player.getSpecOptions(); + newOptions.totems = newVal; + player.setSpecOptions(eventID, newOptions); + }, + changeEmitter: (player: Player) => player.specOptionsChangeEmitter, }, - changeEmitter: (player: Player) => player.specOptionsChangeEmitter, - }, ActionId.fromSpellId(2894), "useFireElemental"); + ActionId.fromSpellId(2894), + 'useFireElemental', + ); new IconPicker(fireElementalContainer, simUI.player, fireElementalBooleanIconInput); } diff --git a/ui/core/individual_sim_ui.ts b/ui/core/individual_sim_ui.ts index c7752b2386..19e4a472db 100644 --- a/ui/core/individual_sim_ui.ts +++ b/ui/core/individual_sim_ui.ts @@ -76,13 +76,13 @@ export interface RaidSimPreset { specOptions: SpecOptions; consumes: Consumes; - defaultName: string; + defaultName?: string; defaultFactionRaces: Record; defaultGear: Record>; otherDefaults?: OtherDefaults; - tooltip: string; - iconUrl: string; + tooltip?: string; + iconUrl?: string; } export interface IndividualSimUIConfig extends PlayerConfig { @@ -179,7 +179,7 @@ export abstract class IndividualSimUI extends SimUI { cssScheme: config.cssScheme, spec: player.spec, knownIssues: config.knownIssues, - simStatus: simLaunchStatuses[player.spec.protoID], + simStatus: simLaunchStatuses[player.getSpec()], }); this.rootElem.classList.add('individual-sim-ui'); this.player = player; @@ -188,8 +188,8 @@ export abstract class IndividualSimUI extends SimUI { this.prevEpIterations = 0; this.prevEpSimResult = null; - if ((config.itemSwapSlots || []).length > 0 && !itemSwapEnabledSpecs.includes(player.spec.protoID)) { - itemSwapEnabledSpecs.push(player.spec.protoID); + if ((config.itemSwapSlots || []).length > 0 && !itemSwapEnabledSpecs.includes(player.getSpec())) { + itemSwapEnabledSpecs.push(player.getSpec()); } this.addWarning({ diff --git a/ui/core/player.ts b/ui/core/player.ts index 77dcdcf533..1f7bca4c33 100644 --- a/ui/core/player.ts +++ b/ui/core/player.ts @@ -293,10 +293,10 @@ export class Player { this.spec = spec; this.race = this.spec.playerClass.races[0]; - this.specTypeFunctions = specTypeFunctions[this.spec.protoID] as SpecTypeFunctions; + this.specTypeFunctions = specTypeFunctions[this.getSpec()] as SpecTypeFunctions; this.specOptions = this.specTypeFunctions.optionsCreate(); - const specConfig = SPEC_CONFIGS[this.spec.protoID] as PlayerConfig; + const specConfig = SPEC_CONFIGS[this.getSpec()] as PlayerConfig; if (!specConfig) { throw new Error('Could not find spec config for spec: ' + this.spec); } @@ -341,6 +341,14 @@ export class Player { return this.spec.getIcon('medium'); } + getPlayerSpec(): PlayerSpec { + return this.spec; + } + + getSpec(): SpecType { + return this.getPlayerSpec().protoID; + } + getPlayerClass(): PlayerClass> { return this.spec.playerClass; } @@ -355,7 +363,7 @@ export class Player { // TODO: Cata - Check this isSpec(specId: T): this is Player { - return (this.spec.protoID as unknown) == specId; + return (this.getSpec() as unknown) == specId; } isClass(classId: T): this is Player> { @@ -591,7 +599,7 @@ export class Player { } canDualWield2H(): boolean { - return this.spec.protoID == Spec.SpecFuryWarrior && (this.getTalents() as SpecTalents).titansGrip; + return this.getSpec() == Spec.SpecFuryWarrior && (this.getTalents() as SpecTalents).titansGrip; } equipItem(eventID: EventID, slot: ItemSlot, newItem: EquippedItem | null) { @@ -692,7 +700,7 @@ export class Player { let specSpecificOffset = 0.0; - if (this.spec.protoID === Spec.SpecEnhancementShaman) { + if (this.getSpec() === Spec.SpecEnhancementShaman) { // Elemental Devastation uptime is near 100% // TODO: Cata - Check this const ranks = (this as unknown as Player).getTalents().elementalDevastation; @@ -1345,7 +1353,7 @@ export class Player { distanceFromTarget: this.getDistanceFromTarget(), healingModel: this.getHealingModel(), }); - player = withSpec(this.spec.protoID, player, this.getSpecOptions()); + player = withSpec(this.getSpec(), player, this.getSpecOptions()); } if (exportCategory(SimSettingCategories.External)) { PlayerProto.mergePartial(player, { diff --git a/ui/core/player_specs/index.ts b/ui/core/player_specs/index.ts index 76f4501832..50f8a3d5cb 100644 --- a/ui/core/player_specs/index.ts +++ b/ui/core/player_specs/index.ts @@ -1,6 +1,6 @@ import { LOCAL_STORAGE_PREFIX } from '../constants/other'; import { PlayerSpec } from '../player_spec'; -import { Class, Spec } from '../proto/common'; +import { Spec } from '../proto/common'; import * as DeathKnightSpecs from './death_knight'; import * as DruidSpecs from './druid'; import * as HunterSpecs from './hunter'; @@ -12,7 +12,7 @@ import * as ShamanSpecs from './shaman'; import * as WarlockSpecs from './warlock'; import * as WarriorSpecs from './warrior'; -const protoToPlayerSpec: Record | undefined> = { +const protoToPlayerSpec: Record | undefined> = { [Spec.SpecUnknown]: undefined, // Death Knight [Spec.SpecBloodDeathKnight]: DeathKnightSpecs.BloodDeathKnight, @@ -69,14 +69,16 @@ export const PlayerSpecs = { ...WarriorSpecs, // Prefixes used for storing browser data for each site. Even if a Spec is // renamed, DO NOT change these values or people will lose their saved data. - getLocalStorageKey: (spec: PlayerSpec): string => { - return `${LOCAL_STORAGE_PREFIX}_${spec.friendlyName.toLowerCase().replace(/\s/, '_')}_${spec.class.friendlyName.toLowerCase().replace(/\s/, '_')}`; + getLocalStorageKey: (spec: PlayerSpec): string => { + return `${LOCAL_STORAGE_PREFIX}_${spec.friendlyName.toLowerCase().replace(/\s/, '_')}_${spec.playerClass.friendlyName + .toLowerCase() + .replace(/\s/, '_')}`; }, - fromProto: (protoId: SpecType): PlayerSpec => { + fromProto: (protoId: SpecType): PlayerSpec => { if (protoId == Spec.SpecUnknown) { throw new Error('Invalid Spec'); } - return protoToPlayerSpec[protoId] as PlayerSpec; + return protoToPlayerSpec[protoId] as PlayerSpec; }, }; diff --git a/ui/core/proto_utils/utils.ts b/ui/core/proto_utils/utils.ts index 62d6dbecdf..8c0d7244d9 100644 --- a/ui/core/proto_utils/utils.ts +++ b/ui/core/proto_utils/utils.ts @@ -196,7 +196,6 @@ class UnknownOptions { constructor() {} } -// TODO: Cata - Re-evaluate whether or not these are needed export type DeathKnightSpecs = Spec.SpecBloodDeathKnight | Spec.SpecFrostDeathKnight | Spec.SpecUnholyDeathKnight; export type DruidSpecs = Spec.SpecBalanceDruid | Spec.SpecFeralDruid | Spec.SpecRestorationDruid; export type HunterSpecs = Spec.SpecBeastMasteryHunter | Spec.SpecMarksmanshipHunter | Spec.SpecSurvivalHunter; diff --git a/ui/death_knight/blood/sim.ts b/ui/death_knight/blood/sim.ts index 4568cea76f..6c0ebb96d5 100644 --- a/ui/death_knight/blood/sim.ts +++ b/ui/death_knight/blood/sim.ts @@ -3,8 +3,6 @@ import * as OtherInputs from '../../core/components/other_inputs'; import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui'; import { Player } from '../../core/player'; import { PlayerClasses } from '../../core/player_classes'; -import { DeathKnight } from '../../core/player_classes/death_knight'; -import { PlayerSpecs } from '../../core/player_specs'; import { APLRotation } from '../../core/proto/apl'; import { Debuffs, Faction, IndividualBuffs, PartyBuffs, PseudoStat, Race, RaidBuffs, Spec, Stat, TristateEffect } from '../../core/proto/common'; import { Stats } from '../../core/proto_utils/stats'; @@ -189,10 +187,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecBloodDeathKnight, { raidSimPresets: [ { spec: Spec.SpecBloodDeathKnight, - tooltip: PlayerSpecs.BloodDeathKnight.fullName, - defaultName: PlayerSpecs.BloodDeathKnight.friendlyName, - iconUrl: DeathKnight.getIcon('medium'), - talents: Presets.BloodTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, diff --git a/ui/death_knight/frost/sim.ts b/ui/death_knight/frost/sim.ts index c56185fcbd..34d51563b4 100644 --- a/ui/death_knight/frost/sim.ts +++ b/ui/death_knight/frost/sim.ts @@ -3,7 +3,6 @@ import * as OtherInputs from '../../core/components/other_inputs'; import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui'; import { Player } from '../../core/player'; import { PlayerClasses } from '../../core/player_classes'; -import { PlayerSpecs } from '../../core/player_specs'; import { APLRotation } from '../../core/proto/apl'; import { Debuffs, @@ -199,10 +198,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecFrostDeathKnight, { raidSimPresets: [ { spec: Spec.SpecFrostDeathKnight, - tooltip: PlayerSpecs.FrostDeathKnight.fullName, - defaultName: PlayerSpecs.FrostDeathKnight.friendlyName, - iconUrl: PlayerClasses.DeathKnight.getIcon('medium'), - talents: Presets.FrostTalents.data, specOptions: Presets.DefaultFrostOptions, consumes: Presets.DefaultConsumes, diff --git a/ui/death_knight/unholy/sim.ts b/ui/death_knight/unholy/sim.ts index a6f4b44dd3..bc4518de01 100644 --- a/ui/death_knight/unholy/sim.ts +++ b/ui/death_knight/unholy/sim.ts @@ -3,7 +3,6 @@ import * as OtherInputs from '../../core/components/other_inputs'; import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui'; import { Player } from '../../core/player'; import { PlayerClasses } from '../../core/player_classes'; -import { PlayerSpecs } from '../../core/player_specs'; import { APLRotation } from '../../core/proto/apl'; import { Debuffs, @@ -236,10 +235,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecUnholyDeathKnight, { raidSimPresets: [ { spec: Spec.SpecUnholyDeathKnight, - tooltip: PlayerSpecs.UnholyDeathKnight.fullName, - defaultName: PlayerSpecs.UnholyDeathKnight.friendlyName, - iconUrl: PlayerClasses.DeathKnight.getIcon('medium'), - talents: Presets.UnholyDualWieldTalents.data, specOptions: Presets.DefaultUnholyOptions, consumes: Presets.DefaultConsumes, diff --git a/ui/druid/balance/sim.ts b/ui/druid/balance/sim.ts index eb9daa361f..ef65270b3d 100644 --- a/ui/druid/balance/sim.ts +++ b/ui/druid/balance/sim.ts @@ -3,7 +3,6 @@ import * as OtherInputs from '../../core/components/other_inputs.js'; import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui.js'; import { Player } from '../../core/player.js'; import { PlayerClasses } from '../../core/player_classes'; -import { PlayerSpecs } from '../../core/player_specs'; import { APLRotation } from '../../core/proto/apl.js'; import { Faction, Race, Spec, Stat } from '../../core/proto/common.js'; import { Stats } from '../../core/proto_utils/stats.js'; @@ -104,10 +103,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecBalanceDruid, { raidSimPresets: [ { spec: Spec.SpecBalanceDruid, - tooltip: PlayerSpecs.BalanceDruid.fullName, - defaultName: PlayerSpecs.BalanceDruid.friendlyName, - iconUrl: PlayerClasses.Druid.getIcon('medium'), - talents: Presets.Phase2Talents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, diff --git a/ui/druid/feral/sim.ts b/ui/druid/feral/sim.ts index f07719ff44..25ecf8169e 100644 --- a/ui/druid/feral/sim.ts +++ b/ui/druid/feral/sim.ts @@ -4,7 +4,6 @@ import { PhysicalDPSGemOptimizer } from '../../core/components/suggest_gems_acti import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui.js'; import { Player } from '../../core/player.js'; import { PlayerClasses } from '../../core/player_classes'; -import { PlayerSpecs } from '../../core/player_specs'; import { APLAction, APLListItem, APLPrepullAction, APLRotation } from '../../core/proto/apl.js'; import { Cooldowns, Debuffs, Faction, IndividualBuffs, PartyBuffs, PseudoStat, Race, RaidBuffs, Spec, Stat, TristateEffect } from '../../core/proto/common.js'; import { FeralDruid_Rotation as DruidRotation } from '../../core/proto/druid.js'; @@ -165,10 +164,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecFeralDruid, { raidSimPresets: [ { spec: Spec.SpecFeralDruid, - tooltip: PlayerSpecs.FeralDruid.fullName, - defaultName: PlayerSpecs.FeralDruid.friendlyName, - iconUrl: PlayerSpecs.FeralDruid.getIcon('medium'), - talents: Presets.StandardTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, diff --git a/ui/druid/restoration/sim.ts b/ui/druid/restoration/sim.ts index 0731a569a0..c4cc6b1af0 100644 --- a/ui/druid/restoration/sim.ts +++ b/ui/druid/restoration/sim.ts @@ -2,7 +2,6 @@ import * as OtherInputs from '../../core/components/other_inputs.js'; import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui.js'; import { Player } from '../../core/player.js'; import { PlayerClasses } from '../../core/player_classes'; -import { PlayerSpecs } from '../../core/player_specs'; import { APLRotation } from '../../core/proto/apl.js'; import { Faction, Race, Spec, Stat } from '../../core/proto/common.js'; import { Stats } from '../../core/proto_utils/stats.js'; @@ -91,10 +90,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRestorationDruid, { raidSimPresets: [ { spec: Spec.SpecRestorationDruid, - tooltip: PlayerSpecs.RestorationDruid.fullName, - defaultName: PlayerSpecs.RestorationDruid.friendlyName, - iconUrl: PlayerSpecs.RestorationDruid.getIcon('medium'), - talents: Presets.CelestialFocusTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, diff --git a/ui/hunter/beast_mastery/sim.ts b/ui/hunter/beast_mastery/sim.ts index 07b300d307..a1be587d56 100644 --- a/ui/hunter/beast_mastery/sim.ts +++ b/ui/hunter/beast_mastery/sim.ts @@ -1,10 +1,8 @@ import * as BuffDebuffInputs from '../../core/components/inputs/buffs_debuffs'; -import * as ConsumablesInputs from '../../core/components/inputs/consumables'; import * as OtherInputs from '../../core/components/other_inputs'; import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui'; import { Player } from '../../core/player'; import { PlayerClasses } from '../../core/player_classes'; -import { PlayerSpecs } from '../../core/player_specs'; import { APLListItem, APLRotation } from '../../core/proto/apl'; import { Cooldowns, Debuffs, Faction, IndividualBuffs, PartyBuffs, PseudoStat, Race, RaidBuffs, Spec, Stat, TristateEffect } from '../../core/proto/common'; import { BeastMasteryHunter_Rotation } from '../../core/proto/hunter'; @@ -14,7 +12,7 @@ import * as HunterInputs from './inputs'; import * as Presets from './presets'; const SPEC_CONFIG = registerSpecConfig(Spec.SpecBeastMasteryHunter, { - cssClass: 'hunter-sim-ui', + cssClass: 'beast-mastery-hunter-sim-ui', cssScheme: PlayerClasses.getCssClass(PlayerClasses.Hunter), // List any known bugs / issues here and they'll be shown on the site. knownIssues: [], @@ -109,7 +107,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecBeastMasteryHunter, { playerIconInputs: [HunterInputs.PetTypeInput, HunterInputs.WeaponAmmo, HunterInputs.UseHuntersMark], // Inputs to include in the 'Rotation' section on the settings tab. rotationInputs: HunterInputs.HunterRotationConfig, - petConsumeInputs: [ConsumablesInputs.SpicedMammothTreats], + petConsumeInputs: [], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. includeBuffDebuffInputs: [BuffDebuffInputs.StaminaBuff, BuffDebuffInputs.SpellDamageDebuff], excludeBuffDebuffInputs: [], @@ -188,10 +186,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecBeastMasteryHunter, { raidSimPresets: [ { spec: Spec.SpecBeastMasteryHunter, - tooltip: PlayerSpecs.BeastMasteryHunter.fullName, - defaultName: PlayerSpecs.BeastMasteryHunter.friendlyName, - iconUrl: PlayerSpecs.BeastMasteryHunter.getIcon('medium'), - talents: Presets.BeastMasteryTalents.data, specOptions: Presets.BMDefaultOptions, consumes: Presets.DefaultConsumes, diff --git a/ui/hunter/marksmanship/sim.ts b/ui/hunter/marksmanship/sim.ts index 802cf13d3c..9f409902a2 100644 --- a/ui/hunter/marksmanship/sim.ts +++ b/ui/hunter/marksmanship/sim.ts @@ -1,10 +1,8 @@ import * as BuffDebuffInputs from '../../core/components/inputs/buffs_debuffs'; -import * as ConsumablesInputs from '../../core/components/inputs/consumables'; import * as OtherInputs from '../../core/components/other_inputs'; import * as Mechanics from '../../core/constants/mechanics'; import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui'; import { Player } from '../../core/player'; -import { PlayerSpecs } from '../../core/player_specs'; import { APLListItem, APLRotation } from '../../core/proto/apl'; import { Cooldowns, @@ -28,7 +26,7 @@ import * as HunterInputs from './inputs'; import * as Presets from './presets'; const SPEC_CONFIG = registerSpecConfig(Spec.SpecMarksmanshipHunter, { - cssClass: 'hunter-sim-ui', + cssClass: 'marksmanship-hunter-sim-ui', cssScheme: 'hunter', // List any known bugs / issues here and they'll be shown on the site. knownIssues: [], @@ -142,7 +140,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMarksmanshipHunter, { playerIconInputs: [HunterInputs.PetTypeInput, HunterInputs.WeaponAmmo, HunterInputs.UseHuntersMark], // Inputs to include in the 'Rotation' section on the settings tab. rotationInputs: HunterInputs.HunterRotationConfig, - petConsumeInputs: [ConsumablesInputs.SpicedMammothTreats], + petConsumeInputs: [], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. includeBuffDebuffInputs: [BuffDebuffInputs.StaminaBuff, BuffDebuffInputs.SpellDamageDebuff], excludeBuffDebuffInputs: [], @@ -221,10 +219,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMarksmanshipHunter, { raidSimPresets: [ { spec: Spec.SpecMarksmanshipHunter, - tooltip: PlayerSpecs.MarksmanshipHunter.fullName, - defaultName: PlayerSpecs.MarksmanshipHunter.friendlyName, - iconUrl: PlayerSpecs.MarksmanshipHunter.getIcon('medium'), - talents: Presets.MarksmanTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, diff --git a/ui/hunter/survival/sim.ts b/ui/hunter/survival/sim.ts index 8ef6eb5fb4..7ed92c4615 100644 --- a/ui/hunter/survival/sim.ts +++ b/ui/hunter/survival/sim.ts @@ -4,7 +4,6 @@ import * as Mechanics from '../../core/constants/mechanics'; import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui'; import { Player } from '../../core/player'; import { PlayerClasses } from '../../core/player_classes'; -import { PlayerSpecs } from '../../core/player_specs'; import { APLListItem, APLRotation } from '../../core/proto/apl'; import { Cooldowns, @@ -28,7 +27,7 @@ import * as HunterInputs from './inputs'; import * as Presets from './presets'; const SPEC_CONFIG = registerSpecConfig(Spec.SpecSurvivalHunter, { - cssClass: 'hunter-sim-ui', + cssClass: 'survival-hunter-sim-ui', cssScheme: PlayerClasses.getCssClass(PlayerClasses.Hunter), // List any known bugs / issues here and they'll be shown on the site. knownIssues: [], @@ -221,10 +220,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecSurvivalHunter, { raidSimPresets: [ { spec: Spec.SpecSurvivalHunter, - tooltip: PlayerSpecs.SurvivalHunter.fullName, - defaultName: PlayerSpecs.SurvivalHunter.friendlyName, - iconUrl: PlayerSpecs.SurvivalHunter.getIcon('medium'), - talents: Presets.SurvivalTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, diff --git a/ui/mage/arcane/presets.ts b/ui/mage/arcane/presets.ts index cd01fae013..6ba5aa12e4 100644 --- a/ui/mage/arcane/presets.ts +++ b/ui/mage/arcane/presets.ts @@ -1,17 +1,6 @@ -import { Player } from '../core/player.js'; -import * as PresetUtils from '../core/preset_utils.js'; -import { - Conjured, - Consumes, - Faction, - Flask, - Food, - Glyphs, - Potions, - Profession, - Spec, - UnitReference, -} from '../core/proto/common.js'; +import { Player } from '../../core/player'; +import * as PresetUtils from '../../core/preset_utils'; +import { Conjured, Consumes, Faction, Flask, Food, Glyphs, Potions, Profession, Spec, UnitReference } from '../../core/proto/common'; import { Mage_Options as MageOptions, Mage_Options_ArmorType as ArmorType, @@ -19,12 +8,12 @@ import { Mage_Rotation_PrimaryFireSpell as PrimaryFireSpell, MageMajorGlyph, MageMinorGlyph, -} from '../core/proto/mage.js'; -import { SavedTalents } from '../core/proto/ui.js'; +} from '../../core/proto/mage'; +import { SavedTalents } from '../../core/proto/ui'; +import PreraidArcaneGear from './gear_sets/preraid_arcane.gear.json'; // Preset options for this spec. // Eventually we will import these values for the raid sim too, so its good to // keep them in a separate file. -import PreraidArcaneGear from './gear_sets/preraid_arcane.gear.json'; export const ARCANE_PRERAID_PRESET = PresetUtils.makePresetGear('Arcane Preraid Preset', PreraidArcaneGear, { talentTree: 0 }); import P1ArcaneGear from './gear_sets/p1_arcane.gear.json'; export const ARCANE_P1_PRESET = PresetUtils.makePresetGear('Arcane P1 Preset', P1ArcaneGear, { talentTree: 0 }); @@ -43,25 +32,59 @@ export const FIRE_PRERAID_PRESET = PresetUtils.makePresetGear('Fire Preraid Pres import P1FireGear from './gear_sets/p1_fire.gear.json'; export const FIRE_P1_PRESET = PresetUtils.makePresetGear('Fire P1 Preset', P1FireGear, { talentTree: 1 }); import P2FireGear from './gear_sets/p2_fire.gear.json'; -export const FIRE_P2_PRESET = PresetUtils.makePresetGear('Fire P2 Preset', P2FireGear, { talentTree: 1, customCondition: (player: Player) => !player.getTalents().icyVeins }); +export const FIRE_P2_PRESET = PresetUtils.makePresetGear('Fire P2 Preset', P2FireGear, { + talentTree: 1, + customCondition: (player: Player) => !player.getTalents().icyVeins, +}); import P3FireAllianceGear from './gear_sets/p3_fire_alliance.gear.json'; -export const FIRE_P3_PRESET_ALLIANCE = PresetUtils.makePresetGear('Fire P3 Preset [A]', P3FireAllianceGear, { talentTree: 1, faction: Faction.Alliance, customCondition: (player: Player) => !player.getTalents().icyVeins }); +export const FIRE_P3_PRESET_ALLIANCE = PresetUtils.makePresetGear('Fire P3 Preset [A]', P3FireAllianceGear, { + talentTree: 1, + faction: Faction.Alliance, + customCondition: (player: Player) => !player.getTalents().icyVeins, +}); import P3FireHordeGear from './gear_sets/p3_fire_horde.gear.json'; -export const FIRE_P3_PRESET_HORDE = PresetUtils.makePresetGear('Fire P3 Preset [H]', P3FireHordeGear, { talentTree: 1, faction: Faction.Horde, customCondition: (player: Player) => !player.getTalents().icyVeins }); +export const FIRE_P3_PRESET_HORDE = PresetUtils.makePresetGear('Fire P3 Preset [H]', P3FireHordeGear, { + talentTree: 1, + faction: Faction.Horde, + customCondition: (player: Player) => !player.getTalents().icyVeins, +}); import P4FireAllianceGear from './gear_sets/p4_fire_alliance.gear.json'; -export const FIRE_P4_PRESET_ALLIANCE = PresetUtils.makePresetGear('Fire P4 Preset [A]', P4FireAllianceGear, { talentTree: 1, faction: Faction.Alliance, customCondition: (player: Player) => !player.getTalents().icyVeins }); +export const FIRE_P4_PRESET_ALLIANCE = PresetUtils.makePresetGear('Fire P4 Preset [A]', P4FireAllianceGear, { + talentTree: 1, + faction: Faction.Alliance, + customCondition: (player: Player) => !player.getTalents().icyVeins, +}); import P4FireHordeGear from './gear_sets/p4_fire_horde.gear.json'; -export const FIRE_P4_PRESET_HORDE = PresetUtils.makePresetGear('Fire P4 Preset [H]', P4FireHordeGear, { talentTree: 1, faction: Faction.Horde, customCondition: (player: Player) => !player.getTalents().icyVeins }); +export const FIRE_P4_PRESET_HORDE = PresetUtils.makePresetGear('Fire P4 Preset [H]', P4FireHordeGear, { + talentTree: 1, + faction: Faction.Horde, + customCondition: (player: Player) => !player.getTalents().icyVeins, +}); import P2FfbGear from './gear_sets/p2_ffb.gear.json'; -export const FFB_P2_PRESET = PresetUtils.makePresetGear('FFB P2 Preset', P2FfbGear, { talentTree: 1, customCondition: (player: Player) => player.getTalents().icyVeins }); +export const FFB_P2_PRESET = PresetUtils.makePresetGear('FFB P2 Preset', P2FfbGear, { + talentTree: 1, + customCondition: (player: Player) => player.getTalents().icyVeins, +}); import P3FfbAllianceGear from './gear_sets/p3_ffb_alliance.gear.json'; -export const FFB_P3_PRESET_ALLIANCE = PresetUtils.makePresetGear('FFB P3 Preset [A]', P3FfbAllianceGear, { talentTree: 1, customCondition: (player: Player) => player.getTalents().icyVeins }); +export const FFB_P3_PRESET_ALLIANCE = PresetUtils.makePresetGear('FFB P3 Preset [A]', P3FfbAllianceGear, { + talentTree: 1, + customCondition: (player: Player) => player.getTalents().icyVeins, +}); import P3FfbHordeGear from './gear_sets/p3_ffb_horde.gear.json'; -export const FFB_P3_PRESET_HORDE = PresetUtils.makePresetGear('FFB P3 Preset [H]', P3FfbHordeGear, { talentTree: 1, customCondition: (player: Player) => player.getTalents().icyVeins }); +export const FFB_P3_PRESET_HORDE = PresetUtils.makePresetGear('FFB P3 Preset [H]', P3FfbHordeGear, { + talentTree: 1, + customCondition: (player: Player) => player.getTalents().icyVeins, +}); import P4FfbAllianceGear from './gear_sets/p4_ffb_alliance.gear.json'; -export const FFB_P4_PRESET_ALLIANCE = PresetUtils.makePresetGear('FFB P4 Preset [A]', P4FfbAllianceGear, { talentTree: 1, customCondition: (player: Player) => player.getTalents().icyVeins }); +export const FFB_P4_PRESET_ALLIANCE = PresetUtils.makePresetGear('FFB P4 Preset [A]', P4FfbAllianceGear, { + talentTree: 1, + customCondition: (player: Player) => player.getTalents().icyVeins, +}); import P4FfbHordeGear from './gear_sets/p4_ffb_horde.gear.json'; -export const FFB_P4_PRESET_HORDE = PresetUtils.makePresetGear('FFB P4 Preset [H]', P4FfbHordeGear, { talentTree: 1, customCondition: (player: Player) => player.getTalents().icyVeins }); +export const FFB_P4_PRESET_HORDE = PresetUtils.makePresetGear('FFB P4 Preset [H]', P4FfbHordeGear, { + talentTree: 1, + customCondition: (player: Player) => player.getTalents().icyVeins, +}); import P1FrostGear from './gear_sets/p1_frost.gear.json'; export const FROST_P1_PRESET = PresetUtils.makePresetGear('Frost P1 Preset', P1FrostGear, { talentTree: 2 }); import P2FrostGear from './gear_sets/p2_frost.gear.json'; @@ -157,7 +180,7 @@ export const FrostfireTalents = { minor3: MageMinorGlyph.GlyphOfBlastWave, }), }), -} +}; export const FrostTalents = { name: 'Frost', data: SavedTalents.create({ diff --git a/ui/mage/arcane/sim.ts b/ui/mage/arcane/sim.ts index 058da3195e..e3b8b1094d 100644 --- a/ui/mage/arcane/sim.ts +++ b/ui/mage/arcane/sim.ts @@ -1,36 +1,25 @@ -import * as OtherInputs from '../core/components/other_inputs.js'; -import * as Mechanics from '../core/constants/mechanics.js'; -import {IndividualSimUI, registerSpecConfig} from '../core/individual_sim_ui.js'; -import {Player} from '../core/player.js'; -import {APLAction, APLListItem, APLPrepullAction, APLRotation} from '../core/proto/apl.js'; -import {Class, Cooldowns, Debuffs, Faction, IndividualBuffs, PartyBuffs, Race, RaidBuffs, Spec, Stat, TristateEffect} from '../core/proto/common.js'; -import { - Mage_Rotation as MageRotation, - Mage_Rotation_PrimaryFireSpell as PrimaryFireSpell, -} from '../core/proto/mage.js'; -import * as AplUtils from '../core/proto_utils/apl_utils.js'; -import {Stats} from '../core/proto_utils/stats.js'; -import { getSpecIcon } from '../core/proto_utils/utils.js'; -import * as MageInputs from './inputs.js'; -import * as Presets from './presets.js'; +import * as OtherInputs from '../../core/components/other_inputs'; +import * as Mechanics from '../../core/constants/mechanics'; +import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui'; +import { Player } from '../../core/player'; +import { PlayerClasses } from '../../core/player_classes'; +import { Mage } from '../../core/player_classes/mage'; +import { APLAction, APLListItem, APLPrepullAction, APLRotation } from '../../core/proto/apl'; +import { Cooldowns, Debuffs, Faction, IndividualBuffs, PartyBuffs, Race, RaidBuffs, Spec, Stat, TristateEffect } from '../../core/proto/common'; +import { ArcaneMage_Rotation } from '../../core/proto/mage'; +import * as AplUtils from '../../core/proto_utils/apl_utils'; +import { Stats } from '../../core/proto_utils/stats'; +import * as MageInputs from './inputs'; +import * as Presets from './presets'; -const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { - cssClass: 'mage-sim-ui', - cssScheme: 'mage', +const SPEC_CONFIG = registerSpecConfig(Spec.SpecArcaneMage, { + cssClass: 'arcane-mage-sim-ui', + cssScheme: PlayerClasses.getCssClass(Mage), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], + knownIssues: [], // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], + 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. @@ -46,7 +35,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { Stat.StatSpellHaste, Stat.StatMP5, ], - modifyDisplayStats: (player: Player) => { + modifyDisplayStats: (player: Player) => { let stats = new Stats(); if (player.getTalentTree() === 0) { @@ -111,17 +100,14 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - MageInputs.Armor, - ], + 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: [ - ], + excludeBuffDebuffInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { inputs: [ @@ -150,13 +136,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { Presets.FROST_ROTATION_PRESET_AOE, ], // Preset talents that the user can quickly select. - talents: [ - Presets.ArcaneTalents, - Presets.FireTalents, - Presets.FrostfireTalents, - Presets.FrostTalents, - Presets.Phase3FireTalents, - ], + 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, @@ -185,7 +165,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { ], }, - autoRotation: (player: Player): APLRotation => { + autoRotation: (player: Player): APLRotation => { const talentTree = player.getTalentTree(); const numTargets = player.sim.encounter.targets.length; if (numTargets > 3) { @@ -208,45 +188,51 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { } }, - simpleRotation: (player: Player, simple: MageRotation, cooldowns: Cooldowns): APLRotation => { + simpleRotation: (player: Player, simple: ArcaneMage_Rotation, cooldowns: Cooldowns): APLRotation => { const [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 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 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); - } - const talentTree = player.getTalentTree(); - if (talentTree == 0) { // Arcane - actions.push(...[ + actions.push( + ...([ berserking, hyperspeedAcceleration, combatPot, @@ -257,45 +243,22 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { 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) - } + ].filter(a => a) as Array), + ); return APLRotation.create({ prepullActions: prepullActions, - priorityList: actions.map(action => APLListItem.create({ - action: action, - })) + priorityList: actions.map(action => + APLListItem.create({ + action: action, + }), + ), }); }, raidSimPresets: [ { - spec: Spec.SpecMage, - tooltip: 'Arcane Mage', - defaultName: 'Arcane', - iconUrl: getSpecIcon(Class.ClassMage, 0), - + spec: Spec.SpecArcaneMage, talents: Presets.ArcaneTalents.data, specOptions: Presets.DefaultArcaneOptions, consumes: Presets.DefaultArcaneConsumes, @@ -321,73 +284,11 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { }, }, }, - { - spec: Spec.SpecMage, - tooltip: 'TTW Fire Mage', - defaultName: 'TTW Fire', - iconUrl: getSpecIcon(Class.ClassMage, 1), - - talents: Presets.FireTalents.data, - specOptions: Presets.DefaultFireOptions, - consumes: Presets.DefaultFireConsumes, - otherDefaults: Presets.OtherDefaults, - defaultFactionRaces: { - [Faction.Unknown]: Race.RaceUnknown, - [Faction.Alliance]: Race.RaceGnome, - [Faction.Horde]: Race.RaceTroll, - }, - defaultGear: { - [Faction.Unknown]: {}, - [Faction.Alliance]: { - 1: Presets.FIRE_P1_PRESET.gear, - 2: Presets.FIRE_P2_PRESET.gear, - 3: Presets.FIRE_P3_PRESET_ALLIANCE.gear, - 4: Presets.FIRE_P4_PRESET_ALLIANCE.gear, - }, - [Faction.Horde]: { - 1: Presets.FIRE_P1_PRESET.gear, - 2: Presets.FIRE_P2_PRESET.gear, - 3: Presets.FIRE_P3_PRESET_HORDE.gear, - 4: Presets.FIRE_P4_PRESET_HORDE.gear, - }, - }, - }, - { - spec: Spec.SpecMage, - tooltip: 'FFB Fire Mage', - defaultName: 'FFB Fire', - iconUrl: "https://wow.zamimg.com/images/wow/icons/medium/ability_mage_frostfirebolt.jpg", - - talents: Presets.FrostfireTalents.data, - specOptions: Presets.DefaultFFBOptions, - consumes: Presets.DefaultFireConsumes, - otherDefaults: Presets.OtherDefaults, - defaultFactionRaces: { - [Faction.Unknown]: Race.RaceUnknown, - [Faction.Alliance]: Race.RaceGnome, - [Faction.Horde]: Race.RaceTroll, - }, - defaultGear: { - [Faction.Unknown]: {}, - [Faction.Alliance]: { - 1: Presets.FIRE_P1_PRESET.gear, - 2: Presets.FFB_P2_PRESET.gear, - 3: Presets.FFB_P3_PRESET_ALLIANCE.gear, - 4: Presets.FFB_P4_PRESET_ALLIANCE.gear, - }, - [Faction.Horde]: { - 1: Presets.FIRE_P1_PRESET.gear, - 2: Presets.FFB_P2_PRESET.gear, - 3: Presets.FFB_P3_PRESET_HORDE.gear, - 4: Presets.FFB_P4_PRESET_HORDE.gear, - }, - }, - }, ], }); -export class MageSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { +export class ArcaneMageSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/mage/fire/sim.ts b/ui/mage/fire/sim.ts index 058da3195e..da3f2354e1 100644 --- a/ui/mage/fire/sim.ts +++ b/ui/mage/fire/sim.ts @@ -1,36 +1,24 @@ -import * as OtherInputs from '../core/components/other_inputs.js'; -import * as Mechanics from '../core/constants/mechanics.js'; -import {IndividualSimUI, registerSpecConfig} from '../core/individual_sim_ui.js'; -import {Player} from '../core/player.js'; -import {APLAction, APLListItem, APLPrepullAction, APLRotation} from '../core/proto/apl.js'; -import {Class, Cooldowns, Debuffs, Faction, IndividualBuffs, PartyBuffs, Race, RaidBuffs, Spec, Stat, TristateEffect} from '../core/proto/common.js'; -import { - Mage_Rotation as MageRotation, - Mage_Rotation_PrimaryFireSpell as PrimaryFireSpell, -} from '../core/proto/mage.js'; -import * as AplUtils from '../core/proto_utils/apl_utils.js'; -import {Stats} from '../core/proto_utils/stats.js'; -import { getSpecIcon } from '../core/proto_utils/utils.js'; -import * as MageInputs from './inputs.js'; -import * as Presets from './presets.js'; +import * as OtherInputs from '../../core/components/other_inputs'; +import * as Mechanics from '../../core/constants/mechanics'; +import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui'; +import { Player } from '../../core/player'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLAction, APLListItem, APLPrepullAction, APLRotation } from '../../core/proto/apl'; +import { Cooldowns, Debuffs, Faction, IndividualBuffs, PartyBuffs, Race, RaidBuffs, Spec, Stat, TristateEffect } from '../../core/proto/common'; +import { FireMage_Rotation, FireMage_Rotation_PrimaryFireSpell } from '../../core/proto/mage'; +import * as AplUtils from '../../core/proto_utils/apl_utils'; +import { Stats } from '../../core/proto_utils/stats'; +import * as MageInputs from './inputs'; +import * as Presets from './presets'; -const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { - cssClass: 'mage-sim-ui', - cssScheme: 'mage', +const SPEC_CONFIG = registerSpecConfig(Spec.SpecFireMage, { + cssClass: 'fire-mage-sim-ui', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.Mage), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], + knownIssues: [], // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], + 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. @@ -46,7 +34,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { Stat.StatSpellHaste, Stat.StatMP5, ], - modifyDisplayStats: (player: Player) => { + modifyDisplayStats: (player: Player) => { let stats = new Stats(); if (player.getTalentTree() === 0) { @@ -111,17 +99,14 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - MageInputs.Armor, - ], + 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: [ - ], + excludeBuffDebuffInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { inputs: [ @@ -150,13 +135,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { Presets.FROST_ROTATION_PRESET_AOE, ], // Preset talents that the user can quickly select. - talents: [ - Presets.ArcaneTalents, - Presets.FireTalents, - Presets.FrostfireTalents, - Presets.FrostTalents, - Presets.Phase3FireTalents, - ], + 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, @@ -185,7 +164,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { ], }, - autoRotation: (player: Player): APLRotation => { + autoRotation: (player: Player): APLRotation => { const talentTree = player.getTalentTree(); const numTargets = player.sim.encounter.targets.length; if (numTargets > 3) { @@ -208,125 +187,81 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { } }, - simpleRotation: (player: Player, simple: MageRotation, cooldowns: Cooldowns): APLRotation => { + simpleRotation: (player: Player, simple: FireMage_Rotation, cooldowns: Cooldowns): APLRotation => { const [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 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 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}}}`); - prepullActions.push(prepullMirrorImage); if (player.getTalents().improvedScorch > 0 && simple.maintainImprovedScorch) { actions.push(maintainImpScorch); } - const talentTree = player.getTalentTree(); - if (talentTree == 0) { // Arcane - actions.push(...[ + 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) - } + simple.primaryFireSpell == FireMage_Rotation_PrimaryFireSpell.Fireball + ? fireball + : simple.primaryFireSpell == FireMage_Rotation_PrimaryFireSpell.FrostfireBolt + ? frostfireBolt + : scorch, + ].filter(a => a) as Array), + ); return APLRotation.create({ prepullActions: prepullActions, - priorityList: actions.map(action => APLListItem.create({ - action: action, - })) + priorityList: actions.map(action => + APLListItem.create({ + action: action, + }), + ), }); }, raidSimPresets: [ { - spec: Spec.SpecMage, - tooltip: 'Arcane Mage', - defaultName: 'Arcane', - iconUrl: getSpecIcon(Class.ClassMage, 0), - - talents: Presets.ArcaneTalents.data, - specOptions: Presets.DefaultArcaneOptions, - consumes: Presets.DefaultArcaneConsumes, - otherDefaults: Presets.OtherDefaults, - defaultFactionRaces: { - [Faction.Unknown]: Race.RaceUnknown, - [Faction.Alliance]: Race.RaceGnome, - [Faction.Horde]: Race.RaceTroll, - }, - defaultGear: { - [Faction.Unknown]: {}, - [Faction.Alliance]: { - 1: Presets.ARCANE_P1_PRESET.gear, - 2: Presets.ARCANE_P2_PRESET.gear, - 3: Presets.ARCANE_P3_PRESET_ALLIANCE.gear, - 4: Presets.ARCANE_P4_PRESET_ALLIANCE.gear, - }, - [Faction.Horde]: { - 1: Presets.ARCANE_P1_PRESET.gear, - 2: Presets.ARCANE_P2_PRESET.gear, - 3: Presets.ARCANE_P3_PRESET_HORDE.gear, - 4: Presets.ARCANE_P4_PRESET_HORDE.gear, - }, - }, - }, - { - spec: Spec.SpecMage, - tooltip: 'TTW Fire Mage', - defaultName: 'TTW Fire', - iconUrl: getSpecIcon(Class.ClassMage, 1), - + spec: Spec.SpecFireMage, talents: Presets.FireTalents.data, specOptions: Presets.DefaultFireOptions, consumes: Presets.DefaultFireConsumes, @@ -352,42 +287,11 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { }, }, }, - { - spec: Spec.SpecMage, - tooltip: 'FFB Fire Mage', - defaultName: 'FFB Fire', - iconUrl: "https://wow.zamimg.com/images/wow/icons/medium/ability_mage_frostfirebolt.jpg", - - talents: Presets.FrostfireTalents.data, - specOptions: Presets.DefaultFFBOptions, - consumes: Presets.DefaultFireConsumes, - otherDefaults: Presets.OtherDefaults, - defaultFactionRaces: { - [Faction.Unknown]: Race.RaceUnknown, - [Faction.Alliance]: Race.RaceGnome, - [Faction.Horde]: Race.RaceTroll, - }, - defaultGear: { - [Faction.Unknown]: {}, - [Faction.Alliance]: { - 1: Presets.FIRE_P1_PRESET.gear, - 2: Presets.FFB_P2_PRESET.gear, - 3: Presets.FFB_P3_PRESET_ALLIANCE.gear, - 4: Presets.FFB_P4_PRESET_ALLIANCE.gear, - }, - [Faction.Horde]: { - 1: Presets.FIRE_P1_PRESET.gear, - 2: Presets.FFB_P2_PRESET.gear, - 3: Presets.FFB_P3_PRESET_HORDE.gear, - 4: Presets.FFB_P4_PRESET_HORDE.gear, - }, - }, - }, ], }); -export class MageSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { +export class FireMageSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/mage/frost/sim.ts b/ui/mage/frost/sim.ts index 058da3195e..1be0f94680 100644 --- a/ui/mage/frost/sim.ts +++ b/ui/mage/frost/sim.ts @@ -1,36 +1,24 @@ -import * as OtherInputs from '../core/components/other_inputs.js'; -import * as Mechanics from '../core/constants/mechanics.js'; -import {IndividualSimUI, registerSpecConfig} from '../core/individual_sim_ui.js'; -import {Player} from '../core/player.js'; -import {APLAction, APLListItem, APLPrepullAction, APLRotation} from '../core/proto/apl.js'; -import {Class, Cooldowns, Debuffs, Faction, IndividualBuffs, PartyBuffs, Race, RaidBuffs, Spec, Stat, TristateEffect} from '../core/proto/common.js'; -import { - Mage_Rotation as MageRotation, - Mage_Rotation_PrimaryFireSpell as PrimaryFireSpell, -} from '../core/proto/mage.js'; -import * as AplUtils from '../core/proto_utils/apl_utils.js'; -import {Stats} from '../core/proto_utils/stats.js'; -import { getSpecIcon } from '../core/proto_utils/utils.js'; +import * as OtherInputs from '../../core/components/other_inputs.js'; +import * as Mechanics from '../../core/constants/mechanics.js'; +import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui.js'; +import { Player } from '../../core/player.js'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLAction, APLListItem, APLPrepullAction, APLRotation } from '../../core/proto/apl.js'; +import { Cooldowns, Debuffs, Faction, IndividualBuffs, PartyBuffs, Race, RaidBuffs, Spec, Stat, TristateEffect } from '../../core/proto/common.js'; +import { FrostMage_Rotation } from '../../core/proto/mage'; +import * as AplUtils from '../../core/proto_utils/apl_utils.js'; +import { Stats } from '../../core/proto_utils/stats.js'; import * as MageInputs from './inputs.js'; import * as Presets from './presets.js'; -const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { - cssClass: 'mage-sim-ui', - cssScheme: 'mage', +const SPEC_CONFIG = registerSpecConfig(Spec.SpecFrostMage, { + cssClass: 'frost-mage-sim-ui', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.Mage), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], + knownIssues: [], // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], + 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. @@ -46,7 +34,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { Stat.StatSpellHaste, Stat.StatMP5, ], - modifyDisplayStats: (player: Player) => { + modifyDisplayStats: (player: Player) => { let stats = new Stats(); if (player.getTalentTree() === 0) { @@ -111,17 +99,14 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - MageInputs.Armor, - ], + 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: [ - ], + excludeBuffDebuffInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { inputs: [ @@ -150,13 +135,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { Presets.FROST_ROTATION_PRESET_AOE, ], // Preset talents that the user can quickly select. - talents: [ - Presets.ArcaneTalents, - Presets.FireTalents, - Presets.FrostfireTalents, - Presets.FrostTalents, - Presets.Phase3FireTalents, - ], + 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, @@ -185,7 +164,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { ], }, - autoRotation: (player: Player): APLRotation => { + autoRotation: (player: Player): APLRotation => { const talentTree = player.getTalentTree(); const numTargets = player.sim.encounter.targets.length; if (numTargets > 3) { @@ -208,156 +187,63 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { } }, - simpleRotation: (player: Player, simple: MageRotation, cooldowns: Cooldowns): APLRotation => { + simpleRotation: (player: Player, simple: FrostMage_Rotation, cooldowns: Cooldowns): APLRotation => { const [prepullActions, actions] = AplUtils.standardCooldownDefaults(cooldowns); - 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 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 prepullMirrorImage = APLPrepullAction.fromJsonString( + `{"action":{"castSpell":{"spellId":{"spellId":55342}}},"doAtValue":{"const":{"val":"-2s"}}}`, + ); - 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 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 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 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 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); - } - const talentTree = player.getTalentTree(); - if (talentTree == 0) { // Arcane - actions.push(...[ + 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) - } + ].filter(a => a) as Array), + ); return APLRotation.create({ prepullActions: prepullActions, - priorityList: actions.map(action => APLListItem.create({ - action: action, - })) + priorityList: actions.map(action => + APLListItem.create({ + action: action, + }), + ), }); }, raidSimPresets: [ { - spec: Spec.SpecMage, - tooltip: 'Arcane Mage', - defaultName: 'Arcane', - iconUrl: getSpecIcon(Class.ClassMage, 0), - - talents: Presets.ArcaneTalents.data, - specOptions: Presets.DefaultArcaneOptions, - consumes: Presets.DefaultArcaneConsumes, - otherDefaults: Presets.OtherDefaults, - defaultFactionRaces: { - [Faction.Unknown]: Race.RaceUnknown, - [Faction.Alliance]: Race.RaceGnome, - [Faction.Horde]: Race.RaceTroll, - }, - defaultGear: { - [Faction.Unknown]: {}, - [Faction.Alliance]: { - 1: Presets.ARCANE_P1_PRESET.gear, - 2: Presets.ARCANE_P2_PRESET.gear, - 3: Presets.ARCANE_P3_PRESET_ALLIANCE.gear, - 4: Presets.ARCANE_P4_PRESET_ALLIANCE.gear, - }, - [Faction.Horde]: { - 1: Presets.ARCANE_P1_PRESET.gear, - 2: Presets.ARCANE_P2_PRESET.gear, - 3: Presets.ARCANE_P3_PRESET_HORDE.gear, - 4: Presets.ARCANE_P4_PRESET_HORDE.gear, - }, - }, - }, - { - spec: Spec.SpecMage, - tooltip: 'TTW Fire Mage', - defaultName: 'TTW Fire', - iconUrl: getSpecIcon(Class.ClassMage, 1), - - talents: Presets.FireTalents.data, - specOptions: Presets.DefaultFireOptions, - consumes: Presets.DefaultFireConsumes, - otherDefaults: Presets.OtherDefaults, - defaultFactionRaces: { - [Faction.Unknown]: Race.RaceUnknown, - [Faction.Alliance]: Race.RaceGnome, - [Faction.Horde]: Race.RaceTroll, - }, - defaultGear: { - [Faction.Unknown]: {}, - [Faction.Alliance]: { - 1: Presets.FIRE_P1_PRESET.gear, - 2: Presets.FIRE_P2_PRESET.gear, - 3: Presets.FIRE_P3_PRESET_ALLIANCE.gear, - 4: Presets.FIRE_P4_PRESET_ALLIANCE.gear, - }, - [Faction.Horde]: { - 1: Presets.FIRE_P1_PRESET.gear, - 2: Presets.FIRE_P2_PRESET.gear, - 3: Presets.FIRE_P3_PRESET_HORDE.gear, - 4: Presets.FIRE_P4_PRESET_HORDE.gear, - }, - }, - }, - { - spec: Spec.SpecMage, - tooltip: 'FFB Fire Mage', - defaultName: 'FFB Fire', - iconUrl: "https://wow.zamimg.com/images/wow/icons/medium/ability_mage_frostfirebolt.jpg", - + spec: Spec.SpecFrostMage, talents: Presets.FrostfireTalents.data, specOptions: Presets.DefaultFFBOptions, consumes: Presets.DefaultFireConsumes, @@ -386,8 +272,8 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, { ], }); -export class MageSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { +export class FrostMageSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/paladin/holy/sim.ts b/ui/paladin/holy/sim.ts index f7a465dec7..fbfa1f86bf 100644 --- a/ui/paladin/holy/sim.ts +++ b/ui/paladin/holy/sim.ts @@ -1,42 +1,21 @@ import * as OtherInputs from '../../core/components/other_inputs.js'; import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui.js'; import { Player } from '../../core/player.js'; -import { - APLRotation, -} from '../../core/proto/apl.js'; -import { - Class, - Debuffs, - Faction, - IndividualBuffs, - PartyBuffs, - Race, - RaidBuffs, - Spec, - Stat, - TristateEffect, -} from '../../core/proto/common.js'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLRotation } from '../../core/proto/apl.js'; +import { Debuffs, Faction, IndividualBuffs, PartyBuffs, Race, RaidBuffs, Spec, Stat, TristateEffect } from '../../core/proto/common.js'; import { Stats } from '../../core/proto_utils/stats.js'; -import { getSpecIcon } from '../../core/proto_utils/utils.js'; import * as HolyPaladinInputs from './inputs.js'; import * as Presets from './presets.js'; const SPEC_CONFIG = registerSpecConfig(Spec.SpecHolyPaladin, { cssClass: 'holy-paladin-sim-ui', - cssScheme: 'paladin', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.Paladin), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], + knownIssues: [], // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], + 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. @@ -61,7 +40,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecHolyPaladin, { [Stat.StatSpellPower]: 1, [Stat.StatSpellCrit]: 0.69, [Stat.StatSpellHaste]: 0.77, - [Stat.StatMP5]: 0.00, + [Stat.StatMP5]: 0.0, }), // Default consumes settings. consumes: Presets.DefaultConsumes, @@ -89,8 +68,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecHolyPaladin, { devotionAura: TristateEffect.TristateEffectImproved, shadowProtection: true, }), - partyBuffs: PartyBuffs.create({ - }), + partyBuffs: PartyBuffs.create({}), individualBuffs: IndividualBuffs.create({ blessingOfKings: true, blessingOfSanctuary: true, @@ -116,21 +94,13 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecHolyPaladin, { }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ], + playerIconInputs: [], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - ], - excludeBuffDebuffInputs: [ - ], + includeBuffDebuffInputs: [], + excludeBuffDebuffInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { - inputs: [ - OtherInputs.TankAssignment, - OtherInputs.InspirationUptime, - HolyPaladinInputs.AuraSelection, - HolyPaladinInputs.JudgementSelection, - ], + inputs: [OtherInputs.TankAssignment, OtherInputs.InspirationUptime, HolyPaladinInputs.AuraSelection, HolyPaladinInputs.JudgementSelection], }, encounterPicker: { // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. @@ -139,19 +109,10 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecHolyPaladin, { presets: { // Preset talents that the user can quickly select. - talents: [ - Presets.StandardTalents, - ], - rotations: [ - ], + talents: [Presets.StandardTalents], + rotations: [], // 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, - ], + gear: [Presets.PRERAID_PRESET, Presets.P1_PRESET, Presets.P2_PRESET, Presets.P3_PRESET, Presets.P4_PRESET], }, autoRotation: (_player: Player): APLRotation => { @@ -161,10 +122,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecHolyPaladin, { raidSimPresets: [ { spec: Spec.SpecHolyPaladin, - tooltip: 'Holy Paladin', - defaultName: 'Holy', - iconUrl: getSpecIcon(Class.ClassPaladin, 0), - talents: Presets.StandardTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, diff --git a/ui/paladin/protection/sim.ts b/ui/paladin/protection/sim.ts index 1989690609..333670c097 100644 --- a/ui/paladin/protection/sim.ts +++ b/ui/paladin/protection/sim.ts @@ -1,44 +1,23 @@ -import { - Class, - Cooldowns, - Debuffs, - Faction, - IndividualBuffs, - PartyBuffs, - Race, - RaidBuffs, - Spec, - Stat, PseudoStat, - TristateEffect, -} from '../../core/proto/common.js'; -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 { getSpecIcon } from '../../core/proto_utils/utils.js'; -import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui.js'; -import { TypedEvent } from '../../core/typed_event.js'; - import * as BuffDebuffInputs from '../../core/components/inputs/buffs_debuffs.js'; import * as OtherInputs from '../../core/components/other_inputs.js'; import * as Mechanics from '../../core/constants/mechanics.js'; -import * as AplUtils from '../../core/proto_utils/apl_utils.js'; - +import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui.js'; +import { Player } from '../../core/player.js'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLAction, APLListItem, APLPrepullAction, APLRotation } from '../../core/proto/apl.js'; +import { Cooldowns, Debuffs, Faction, IndividualBuffs, PartyBuffs, PseudoStat, Race, RaidBuffs, Spec, Stat, TristateEffect } from '../../core/proto/common.js'; import { PaladinMajorGlyph, PaladinSeal, ProtectionPaladin_Rotation as ProtectionPaladinRotation } from '../../core/proto/paladin.js'; - +import * as AplUtils from '../../core/proto_utils/apl_utils.js'; +import { Stats } from '../../core/proto_utils/stats.js'; +import { TypedEvent } from '../../core/typed_event.js'; import * as ProtectionPaladinInputs from './inputs.js'; import * as Presets from './presets.js'; const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionPaladin, { cssClass: 'protection-paladin-sim-ui', - cssScheme: 'paladin', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.Paladin), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], + knownIssues: [], // All stats for which EP should be calculated. epStats: [ @@ -65,9 +44,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionPaladin, { Stat.StatShadowResistance, Stat.StatFrostResistance, ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - ], + 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. @@ -100,10 +77,10 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionPaladin, { let stats = new Stats(); TypedEvent.freezeAllAndDo(() => { - if (player.getMajorGlyphs().includes(PaladinMajorGlyph.GlyphOfSealOfVengeance) && (player.getSpecOptions().seal == PaladinSeal.Vengeance)) { + 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, @@ -113,27 +90,30 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionPaladin, { // 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, - }), + epWeights: Stats.fromMap( + { + [Stat.StatArmor]: 0.07, + [Stat.StatBonusArmor]: 0.06, + [Stat.StatStamina]: 1.14, + [Stat.StatStrength]: 1.0, + [Stat.StatAgility]: 0.62, + [Stat.StatAttackPower]: 0.26, + [Stat.StatExpertise]: 0.69, + [Stat.StatMeleeHit]: 0.79, + [Stat.StatMeleeCrit]: 0.3, + [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 talents. @@ -160,8 +140,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionPaladin, { devotionAura: TristateEffect.TristateEffectImproved, shadowProtection: true, }), - partyBuffs: PartyBuffs.create({ - }), + partyBuffs: PartyBuffs.create({}), individualBuffs: IndividualBuffs.create({ blessingOfKings: true, blessingOfSanctuary: true, @@ -187,14 +166,10 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionPaladin, { }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ], + playerIconInputs: [], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - BuffDebuffInputs.HealthBuff, - ], - excludeBuffDebuffInputs: [ - ], + includeBuffDebuffInputs: [BuffDebuffInputs.HealthBuff], + excludeBuffDebuffInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { inputs: [ @@ -219,22 +194,11 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionPaladin, { presets: { // Preset talents that the user can quickly select. - talents: [ - Presets.GenericAoeTalents, - ], + talents: [Presets.GenericAoeTalents], // Preset rotations that the user can quickly select. - rotations: [ - Presets.ROTATION_DEFAULT, - ], + 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, - ], + gear: [Presets.PRERAID_PRESET, Presets.P4_PRERAID_PRESET, Presets.P1_PRESET, Presets.P2_PRESET, Presets.P3_PRESET, Presets.P4_PRESET], }, autoRotation: (_player: Player): APLRotation => { @@ -242,47 +206,48 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionPaladin, { }, simpleRotation: (player: Player, simple: ProtectionPaladinRotation, cooldowns: Cooldowns): APLRotation => { - let [prepullActions, actions] = AplUtils.standardCooldownDefaults(cooldowns); + const [prepullActions, actions] = AplUtils.standardCooldownDefaults(cooldowns); const holyShieldPrepull = APLPrepullAction.fromJsonString(`{"action":{"castSpell":{"spellId":{"spellId":48952}}},"doAtValue":{"const":{"val":"-3s"}}}`); const divinePlea = APLPrepullAction.fromJsonString(`{"action":{"castSpell":{"spellId":{"spellId":54428}}},"doAtValue":{"const":{"val":"-1500ms"}}}`); prepullActions.push(holyShieldPrepull, divinePlea); - const shieldOfRighteousness = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpLe","lhs":{"spellTimeToReady":{"spellId":{"spellId":53595}}},"rhs":{"const":{"val":"3s"}}}},"castSpell":{"spellId":{"spellId":61411}}}`); - const hammerOfRighteousness = APLAction.fromJsonString(`{"condition":{"cmp":{"op":"OpLe","lhs":{"spellTimeToReady":{"spellId":{"spellId":61411}}},"rhs":{"const":{"val":"3s"}}}},"castSpell":{"spellId":{"spellId":53595}}}`); + const shieldOfRighteousness = APLAction.fromJsonString( + `{"condition":{"cmp":{"op":"OpLe","lhs":{"spellTimeToReady":{"spellId":{"spellId":53595}}},"rhs":{"const":{"val":"3s"}}}},"castSpell":{"spellId":{"spellId":61411}}}`, + ); + const hammerOfRighteousness = APLAction.fromJsonString( + `{"condition":{"cmp":{"op":"OpLe","lhs":{"spellTimeToReady":{"spellId":{"spellId":61411}}},"rhs":{"const":{"val":"3s"}}}},"castSpell":{"spellId":{"spellId":53595}}}`, + ); const hammerOfWrath = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":48806}}}`); - const waitPrimary = APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"gcdIsReady":{}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":61411}}}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":53595}}}}},{"cmp":{"op":"OpLe","lhs":{"min":{"vals":[{"spellTimeToReady":{"spellId":{"spellId":61411}}},{"spellTimeToReady":{"spellId":{"spellId":53595}}}]}},"rhs":{"const":{"val":"350ms"}}}}]}},"wait":{"duration":{"min":{"vals":[{"spellTimeToReady":{"spellId":{"spellId":61411}}},{"spellTimeToReady":{"spellId":{"spellId":53595}}}]}}}}`); + const waitPrimary = APLAction.fromJsonString( + `{"condition":{"and":{"vals":[{"gcdIsReady":{}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":61411}}}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":53595}}}}},{"cmp":{"op":"OpLe","lhs":{"min":{"vals":[{"spellTimeToReady":{"spellId":{"spellId":61411}}},{"spellTimeToReady":{"spellId":{"spellId":53595}}}]}},"rhs":{"const":{"val":"350ms"}}}}]}},"wait":{"duration":{"min":{"vals":[{"spellTimeToReady":{"spellId":{"spellId":61411}}},{"spellTimeToReady":{"spellId":{"spellId":53595}}}]}}}}`, + ); const consecration = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":48819}}}`); const holyShield = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":48952}}}`); const judgementOfWisdom = APLAction.fromJsonString(`{"castSpell":{"spellId":{"spellId":53408}}}`); - const waitSecondary = APLAction.fromJsonString(`{"condition":{"and":{"vals":[{"gcdIsReady":{}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":61411}}}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":53595}}}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":48819}}}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":48952}}}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":53408}}}}}]}},"wait":{"duration":{"min":{"vals":[{"spellTimeToReady":{"spellId":{"spellId":61411}}},{"spellTimeToReady":{"spellId":{"spellId":53595}}},{"spellTimeToReady":{"spellId":{"spellId":48819}}},{"spellTimeToReady":{"spellId":{"spellId":48952}}},{"spellTimeToReady":{"spellId":{"spellId":53408}}}]}}}}`); + const waitSecondary = APLAction.fromJsonString( + `{"condition":{"and":{"vals":[{"gcdIsReady":{}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":61411}}}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":53595}}}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":48819}}}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":48952}}}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":53408}}}}}]}},"wait":{"duration":{"min":{"vals":[{"spellTimeToReady":{"spellId":{"spellId":61411}}},{"spellTimeToReady":{"spellId":{"spellId":53595}}},{"spellTimeToReady":{"spellId":{"spellId":48819}}},{"spellTimeToReady":{"spellId":{"spellId":48952}}},{"spellTimeToReady":{"spellId":{"spellId":53408}}}]}}}}`, + ); - actions.push(...[ - shieldOfRighteousness, - hammerOfRighteousness, - hammerOfWrath, - waitPrimary, - consecration, - holyShield, - judgementOfWisdom, - waitSecondary, - ].filter(a => a) as Array) + actions.push( + ...([shieldOfRighteousness, hammerOfRighteousness, hammerOfWrath, waitPrimary, consecration, holyShield, judgementOfWisdom, waitSecondary].filter( + a => a, + ) as Array), + ); return APLRotation.create({ prepullActions: prepullActions, - priorityList: actions.map(action => APLListItem.create({ - action: action, - })) + priorityList: actions.map(action => + APLListItem.create({ + action: action, + }), + ), }); }, raidSimPresets: [ { spec: Spec.SpecProtectionPaladin, - tooltip: 'Protection Paladin', - defaultName: 'Protection', - iconUrl: getSpecIcon(Class.ClassPaladin, 1), - talents: Presets.GenericAoeTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, diff --git a/ui/paladin/retribution/sim.ts b/ui/paladin/retribution/sim.ts index b963c5a28b..2766040d17 100644 --- a/ui/paladin/retribution/sim.ts +++ b/ui/paladin/retribution/sim.ts @@ -3,34 +3,20 @@ import * as OtherInputs from '../../core/components/other_inputs.js'; import * as Mechanics from '../../core/constants/mechanics.js'; import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui.js'; import { Player } from '../../core/player.js'; -import { - APLRotation, -} from '../../core/proto/apl.js'; -import { - Class, - Debuffs, - Faction, - IndividualBuffs, - PartyBuffs, -PseudoStat, - Race, - RaidBuffs, - Spec, - Stat, TristateEffect, -} from '../../core/proto/common.js'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLRotation } from '../../core/proto/apl.js'; +import { Debuffs, Faction, IndividualBuffs, PartyBuffs, PseudoStat, Race, RaidBuffs, Spec, Stat, TristateEffect } from '../../core/proto/common.js'; import { PaladinMajorGlyph, PaladinSeal } from '../../core/proto/paladin.js'; import { Stats } from '../../core/proto_utils/stats.js'; -import { getSpecIcon } from '../../core/proto_utils/utils.js'; import { TypedEvent } from '../../core/typed_event.js'; import * as RetributionPaladinInputs from './inputs.js'; import * as Presets from './presets.js'; const SPEC_CONFIG = registerSpecConfig(Spec.SpecRetributionPaladin, { cssClass: 'retribution-paladin-sim-ui', - cssScheme: 'paladin', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.Paladin), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], + knownIssues: [], // All stats for which EP should be calculated. epStats: [ @@ -49,9 +35,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRetributionPaladin, { Stat.StatSpellHit, Stat.StatSpellHaste, ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - ], + 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. @@ -77,10 +61,10 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRetributionPaladin, { let stats = new Stats(); TypedEvent.freezeAllAndDo(() => { - if (player.getMajorGlyphs().includes(PaladinMajorGlyph.GlyphOfSealOfVengeance) && (player.getSpecOptions().seal == PaladinSeal.Vengeance)) { + 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, @@ -91,24 +75,27 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRetributionPaladin, { // 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, - }), + 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.8, + }, + { + [PseudoStat.PseudoStatMainHandDps]: 7.33, + }, + ), // Default consumes settings. consumes: Presets.DefaultConsumes, // Default talents. @@ -134,8 +121,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRetributionPaladin, { wrathOfAirTotem: true, demonicPactSp: 500, }), - partyBuffs: PartyBuffs.create({ - }), + partyBuffs: PartyBuffs.create({}), individualBuffs: IndividualBuffs.create({ judgementsOfTheWise: true, blessingOfKings: true, @@ -157,23 +143,13 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRetributionPaladin, { }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - RetributionPaladinInputs.AuraSelection, - RetributionPaladinInputs.JudgementSelection, - RetributionPaladinInputs.StartingSealSelection, - ], + playerIconInputs: [RetributionPaladinInputs.AuraSelection, RetributionPaladinInputs.JudgementSelection, RetributionPaladinInputs.StartingSealSelection], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - BuffDebuffInputs.ReplenishmentBuff, - ], - excludeBuffDebuffInputs: [ - ], + includeBuffDebuffInputs: [BuffDebuffInputs.ReplenishmentBuff], + excludeBuffDebuffInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { - inputs: [ - OtherInputs.TankAssignment, - OtherInputs.InFrontOfTarget, - ], + inputs: [OtherInputs.TankAssignment, OtherInputs.InFrontOfTarget], }, encounterPicker: { // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. @@ -181,23 +157,11 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRetributionPaladin, { }, presets: { - rotations: [ - Presets.ROTATION_PRESET_DEFAULT, - ], + rotations: [Presets.ROTATION_PRESET_DEFAULT], // Preset talents that the user can quickly select. - talents: [ - Presets.AuraMasteryTalents, - Presets.DivineSacTalents, - ], + 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, - ], + gear: [Presets.PRERAID_PRESET, Presets.P1_PRESET, Presets.P2_PRESET, Presets.P3_PRESET, Presets.P4_PRESET, Presets.P5_PRESET], }, autoRotation: (_player: Player): APLRotation => { @@ -207,10 +171,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRetributionPaladin, { raidSimPresets: [ { spec: Spec.SpecRetributionPaladin, - tooltip: 'Retribution Paladin', - defaultName: 'Retribution', - iconUrl: getSpecIcon(Class.ClassPaladin, 2), - talents: Presets.AuraMasteryTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, diff --git a/ui/priest/discipline/inputs.ts b/ui/priest/discipline/inputs.ts index 6da1d14b5e..47588684c9 100644 --- a/ui/priest/discipline/inputs.ts +++ b/ui/priest/discipline/inputs.ts @@ -1,18 +1,2 @@ -import { Spec } from '../../core/proto/common.js'; -import { - ShadowPriest_Options_Armor as Armor, -} from '../../core/proto/priest.js'; -import { ActionId } from '../../core/proto_utils/action_id.js'; - -import * as InputHelpers from '../../core/components/input_helpers.js'; - // Configuration for spec-specific UI elements on the settings tab. // These don't need to be in a separate file but it keeps things cleaner. - -export const ArmorInput = InputHelpers.makeSpecOptionsEnumIconInput({ - fieldName: 'armor', - values: [ - { value: Armor.NoArmor, tooltip: 'No Inner Fire' }, - { actionId: ActionId.fromSpellId(48168), value: Armor.InnerFire }, - ], -}); diff --git a/ui/priest/discipline/presets.ts b/ui/priest/discipline/presets.ts index 08459f43ec..c8e7e9ceab 100644 --- a/ui/priest/discipline/presets.ts +++ b/ui/priest/discipline/presets.ts @@ -1,22 +1,6 @@ import * as PresetUtils from '../../core/preset_utils.js'; -import { - Consumes, - Debuffs, - Flask, - Food, - Glyphs, - IndividualBuffs, - Potions, - Profession, - RaidBuffs, - TristateEffect, -} from '../../core/proto/common.js'; -import { - PriestMajorGlyph as MajorGlyph, - PriestMinorGlyph as MinorGlyph, - ShadowPriest_Options as Options, - ShadowPriest_Options_Armor as Armor, -} from '../../core/proto/priest.js'; +import { Consumes, Debuffs, Flask, Food, Glyphs, IndividualBuffs, Potions, Profession, RaidBuffs, TristateEffect } from '../../core/proto/common.js'; +import { DisciplinePriest_Options as Options, PriestMajorGlyph as MajorGlyph, PriestMinorGlyph as MinorGlyph } from '../../core/proto/priest.js'; import { SavedTalents } from '../../core/proto/ui.js'; // Preset options for this spec. // Eventually we will import these values for the raid sim too, so its good to @@ -32,11 +16,11 @@ export const P3_PRESET = PresetUtils.makePresetGear('P3 Preset', P3Gear); import P4Gear from './gear_sets/p4.gear.json'; export const P4_PRESET = PresetUtils.makePresetGear('P4 Preset', P4Gear); -import DefaultApl from './apls/default.apl.json' +import DefaultApl from './apls/default.apl.json'; export const ROTATION_PRESET_DEFAULT = PresetUtils.makePresetAPLRotation('Default', DefaultApl); -import AOE24Apl from './apls/aoe_2_4.apl.json' +import AOE24Apl from './apls/aoe_2_4.apl.json'; export const ROTATION_PRESET_AOE24 = PresetUtils.makePresetAPLRotation('AOE (2 to 4 targets)', AOE24Apl); -import AOE4PlusApl from './apls/aoe_4_plus.apl.json' +import AOE4PlusApl from './apls/aoe_4_plus.apl.json'; export const ROTATION_PRESET_AOE4PLUS = PresetUtils.makePresetAPLRotation('AOE (4+ targets)', AOE4PlusApl); // Default talents. Uses the wowhead calculator format, make the talents on @@ -71,9 +55,7 @@ export const EnlightenmentTalents = { }), }; -export const DefaultOptions = Options.create({ - armor: Armor.InnerFire, -}); +export const DefaultOptions = Options.create({}); export const DefaultConsumes = Consumes.create({ flask: Flask.FlaskOfTheFrostWyrm, diff --git a/ui/priest/discipline/sim.ts b/ui/priest/discipline/sim.ts index 20471a792a..724eb93cfc 100644 --- a/ui/priest/discipline/sim.ts +++ b/ui/priest/discipline/sim.ts @@ -3,39 +3,20 @@ import * as OtherInputs from '../../core/components/other_inputs.js'; import * as Mechanics from '../../core/constants/mechanics.js'; import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui.js'; import { Player } from '../../core/player.js'; -import { - APLRotation, -} from '../../core/proto/apl.js'; -import { - Class, - Faction, - PartyBuffs, - Race, - Spec, - Stat, -} from '../../core/proto/common.js'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLRotation } from '../../core/proto/apl.js'; +import { Faction, PartyBuffs, Race, Spec, Stat } from '../../core/proto/common.js'; import { Stats } from '../../core/proto_utils/stats.js'; -import { getSpecIcon, specNames } from '../../core/proto_utils/utils.js'; -import * as ShadowPriestInputs from './inputs.js'; import * as Presets from './presets.js'; -const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { - cssClass: 'shadow-priest-sim-ui', - cssScheme: 'priest', +const SPEC_CONFIG = registerSpecConfig(Spec.SpecDisciplinePriest, { + cssClass: 'discipline-priest-sim-ui', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.Priest), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], + knownIssues: [], // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], + 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. @@ -51,7 +32,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { Stat.StatSpellHaste, Stat.StatMP5, ], - modifyDisplayStats: (player: Player) => { + modifyDisplayStats: (player: Player) => { let stats = new Stats(); stats = stats.addStat(Stat.StatSpellHit, player.getTalents().shadowFocus * 1 * Mechanics.SPELL_HIT_RATING_PER_HIT_CHANCE); @@ -71,7 +52,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { [Stat.StatSpellHit]: 0.87, [Stat.StatSpellCrit]: 0.74, [Stat.StatSpellHaste]: 1.65, - [Stat.StatMP5]: 0.00, + [Stat.StatMP5]: 0.0, }), // Default consumes settings. consumes: Presets.DefaultConsumes, @@ -92,9 +73,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ShadowPriestInputs.ArmorInput, - ], + playerIconInputs: [], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. includeBuffDebuffInputs: [ BuffDebuffInputs.ReplenishmentBuff, @@ -105,14 +84,10 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { BuffDebuffInputs.AttackPowerBuff, BuffDebuffInputs.StaminaBuff, ], - excludeBuffDebuffInputs: [ - ], + excludeBuffDebuffInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { - inputs: [ - OtherInputs.TankAssignment, - OtherInputs.ChannelClipDelay, - ], + inputs: [OtherInputs.TankAssignment, OtherInputs.ChannelClipDelay], }, encounterPicker: { // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. @@ -121,26 +96,13 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { 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, - ], + 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, - ], + gear: [Presets.PRERAID_PRESET, Presets.P1_PRESET, Presets.P2_PRESET, Presets.P3_PRESET, Presets.P4_PRESET], }, - autoRotation: (player: Player): APLRotation => { + autoRotation: (player: Player): APLRotation => { const numTargets = player.sim.encounter.targets.length; if (numTargets > 4) { return Presets.ROTATION_PRESET_AOE4PLUS.rotation.rotation!; @@ -153,11 +115,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { raidSimPresets: [ { - spec: Spec.SpecShadowPriest, - tooltip: specNames[Spec.SpecShadowPriest], - defaultName: 'Shadow', - iconUrl: getSpecIcon(Class.ClassPriest, 2), - + spec: Spec.SpecDisciplinePriest, talents: Presets.StandardTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, @@ -185,8 +143,8 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { ], }); -export class ShadowPriestSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { +export class DisciplinePriestSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/priest/holy/inputs.ts b/ui/priest/holy/inputs.ts index 6da1d14b5e..47588684c9 100644 --- a/ui/priest/holy/inputs.ts +++ b/ui/priest/holy/inputs.ts @@ -1,18 +1,2 @@ -import { Spec } from '../../core/proto/common.js'; -import { - ShadowPriest_Options_Armor as Armor, -} from '../../core/proto/priest.js'; -import { ActionId } from '../../core/proto_utils/action_id.js'; - -import * as InputHelpers from '../../core/components/input_helpers.js'; - // Configuration for spec-specific UI elements on the settings tab. // These don't need to be in a separate file but it keeps things cleaner. - -export const ArmorInput = InputHelpers.makeSpecOptionsEnumIconInput({ - fieldName: 'armor', - values: [ - { value: Armor.NoArmor, tooltip: 'No Inner Fire' }, - { actionId: ActionId.fromSpellId(48168), value: Armor.InnerFire }, - ], -}); diff --git a/ui/priest/holy/presets.ts b/ui/priest/holy/presets.ts index 2784b58269..87abdb3650 100644 --- a/ui/priest/holy/presets.ts +++ b/ui/priest/holy/presets.ts @@ -1,22 +1,6 @@ import * as PresetUtils from '../../core/preset_utils.js'; -import { - Consumes, - Debuffs, - Flask, - Food, - Glyphs, - IndividualBuffs, - Potions, - Profession, - RaidBuffs, - TristateEffect, -} from '../../core/proto/common.js'; -import { - PriestMajorGlyph as MajorGlyph, - PriestMinorGlyph as MinorGlyph, - ShadowPriest_Options as Options, - ShadowPriest_Options_Armor as Armor, -} from '../../core/proto/priest.js'; +import { Consumes, Debuffs, Flask, Food, Glyphs, IndividualBuffs, Potions, Profession, RaidBuffs, TristateEffect } from '../../core/proto/common.js'; +import { HolyPriest_Options as Options, PriestMajorGlyph as MajorGlyph, PriestMinorGlyph as MinorGlyph } from '../../core/proto/priest.js'; import { SavedTalents } from '../../core/proto/ui.js'; // Preset options for this spec. // Eventually we will import these values for the raid sim too, so its good to @@ -32,11 +16,11 @@ export const P3_PRESET = PresetUtils.makePresetGear('P3 Preset', P3Gear); import P4Gear from './gear_sets/p4.gear.json'; export const P4_PRESET = PresetUtils.makePresetGear('P4 Preset', P4Gear); -import DefaultApl from './apls/default.apl.json' +import DefaultApl from './apls/default.apl.json'; export const ROTATION_PRESET_DEFAULT = PresetUtils.makePresetAPLRotation('Default', DefaultApl); -import AOE24Apl from './apls/aoe_2_4.apl.json' +import AOE24Apl from './apls/aoe_2_4.apl.json'; export const ROTATION_PRESET_AOE24 = PresetUtils.makePresetAPLRotation('AOE (2 to 4 targets)', AOE24Apl); -import AOE4PlusApl from './apls/aoe_4_plus.apl.json' +import AOE4PlusApl from './apls/aoe_4_plus.apl.json'; export const ROTATION_PRESET_AOE4PLUS = PresetUtils.makePresetAPLRotation('AOE (4+ targets)', AOE4PlusApl); // Default talents. Uses the wowhead calculator format, make the talents on @@ -71,9 +55,7 @@ export const EnlightenmentTalents = { }), }; -export const DefaultOptions = Options.create({ - armor: Armor.InnerFire, -}); +export const DefaultOptions = Options.create({}); export const DefaultConsumes = Consumes.create({ flask: Flask.FlaskOfTheFrostWyrm, diff --git a/ui/priest/holy/sim.ts b/ui/priest/holy/sim.ts index 20471a792a..aa8cdcd8ba 100644 --- a/ui/priest/holy/sim.ts +++ b/ui/priest/holy/sim.ts @@ -3,39 +3,20 @@ import * as OtherInputs from '../../core/components/other_inputs.js'; import * as Mechanics from '../../core/constants/mechanics.js'; import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui.js'; import { Player } from '../../core/player.js'; -import { - APLRotation, -} from '../../core/proto/apl.js'; -import { - Class, - Faction, - PartyBuffs, - Race, - Spec, - Stat, -} from '../../core/proto/common.js'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLRotation } from '../../core/proto/apl.js'; +import { Faction, PartyBuffs, Race, Spec, Stat } from '../../core/proto/common.js'; import { Stats } from '../../core/proto_utils/stats.js'; -import { getSpecIcon, specNames } from '../../core/proto_utils/utils.js'; -import * as ShadowPriestInputs from './inputs.js'; import * as Presets from './presets.js'; -const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { - cssClass: 'shadow-priest-sim-ui', - cssScheme: 'priest', +const SPEC_CONFIG = registerSpecConfig(Spec.SpecHolyPriest, { + cssClass: 'holy-priest-sim-ui', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.Priest), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], + knownIssues: [], // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], + 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. @@ -51,7 +32,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { Stat.StatSpellHaste, Stat.StatMP5, ], - modifyDisplayStats: (player: Player) => { + modifyDisplayStats: (player: Player) => { let stats = new Stats(); stats = stats.addStat(Stat.StatSpellHit, player.getTalents().shadowFocus * 1 * Mechanics.SPELL_HIT_RATING_PER_HIT_CHANCE); @@ -71,7 +52,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { [Stat.StatSpellHit]: 0.87, [Stat.StatSpellCrit]: 0.74, [Stat.StatSpellHaste]: 1.65, - [Stat.StatMP5]: 0.00, + [Stat.StatMP5]: 0.0, }), // Default consumes settings. consumes: Presets.DefaultConsumes, @@ -92,9 +73,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ShadowPriestInputs.ArmorInput, - ], + playerIconInputs: [], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. includeBuffDebuffInputs: [ BuffDebuffInputs.ReplenishmentBuff, @@ -105,14 +84,10 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { BuffDebuffInputs.AttackPowerBuff, BuffDebuffInputs.StaminaBuff, ], - excludeBuffDebuffInputs: [ - ], + excludeBuffDebuffInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { - inputs: [ - OtherInputs.TankAssignment, - OtherInputs.ChannelClipDelay, - ], + inputs: [OtherInputs.TankAssignment, OtherInputs.ChannelClipDelay], }, encounterPicker: { // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. @@ -121,26 +96,13 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { 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, - ], + 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, - ], + gear: [Presets.PRERAID_PRESET, Presets.P1_PRESET, Presets.P2_PRESET, Presets.P3_PRESET, Presets.P4_PRESET], }, - autoRotation: (player: Player): APLRotation => { + autoRotation: (player: Player): APLRotation => { const numTargets = player.sim.encounter.targets.length; if (numTargets > 4) { return Presets.ROTATION_PRESET_AOE4PLUS.rotation.rotation!; @@ -153,11 +115,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { raidSimPresets: [ { - spec: Spec.SpecShadowPriest, - tooltip: specNames[Spec.SpecShadowPriest], - defaultName: 'Shadow', - iconUrl: getSpecIcon(Class.ClassPriest, 2), - + spec: Spec.SpecHolyPriest, talents: Presets.StandardTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, @@ -185,8 +143,8 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { ], }); -export class ShadowPriestSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { +export class HolyPriestSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/priest/shadow/sim.ts b/ui/priest/shadow/sim.ts index 20471a792a..94777c1b3c 100644 --- a/ui/priest/shadow/sim.ts +++ b/ui/priest/shadow/sim.ts @@ -3,39 +3,21 @@ import * as OtherInputs from '../../core/components/other_inputs.js'; import * as Mechanics from '../../core/constants/mechanics.js'; import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui.js'; import { Player } from '../../core/player.js'; -import { - APLRotation, -} from '../../core/proto/apl.js'; -import { - Class, - Faction, - PartyBuffs, - Race, - Spec, - Stat, -} from '../../core/proto/common.js'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLRotation } from '../../core/proto/apl.js'; +import { Faction, PartyBuffs, Race, Spec, Stat } from '../../core/proto/common.js'; import { Stats } from '../../core/proto_utils/stats.js'; -import { getSpecIcon, specNames } from '../../core/proto_utils/utils.js'; import * as ShadowPriestInputs from './inputs.js'; import * as Presets from './presets.js'; const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { cssClass: 'shadow-priest-sim-ui', - cssScheme: 'priest', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.Priest), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], + knownIssues: [], // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], + 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. @@ -71,7 +53,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { [Stat.StatSpellHit]: 0.87, [Stat.StatSpellCrit]: 0.74, [Stat.StatSpellHaste]: 1.65, - [Stat.StatMP5]: 0.00, + [Stat.StatMP5]: 0.0, }), // Default consumes settings. consumes: Presets.DefaultConsumes, @@ -92,9 +74,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ShadowPriestInputs.ArmorInput, - ], + playerIconInputs: [ShadowPriestInputs.ArmorInput], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. includeBuffDebuffInputs: [ BuffDebuffInputs.ReplenishmentBuff, @@ -105,14 +85,10 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { BuffDebuffInputs.AttackPowerBuff, BuffDebuffInputs.StaminaBuff, ], - excludeBuffDebuffInputs: [ - ], + excludeBuffDebuffInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { - inputs: [ - OtherInputs.TankAssignment, - OtherInputs.ChannelClipDelay, - ], + inputs: [OtherInputs.TankAssignment, OtherInputs.ChannelClipDelay], }, encounterPicker: { // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. @@ -121,23 +97,10 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { 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, - ], + 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, - ], + gear: [Presets.PRERAID_PRESET, Presets.P1_PRESET, Presets.P2_PRESET, Presets.P3_PRESET, Presets.P4_PRESET], }, autoRotation: (player: Player): APLRotation => { @@ -154,10 +117,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, { raidSimPresets: [ { spec: Spec.SpecShadowPriest, - tooltip: specNames[Spec.SpecShadowPriest], - defaultName: 'Shadow', - iconUrl: getSpecIcon(Class.ClassPriest, 2), - talents: Presets.StandardTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, diff --git a/ui/raid/import_export.ts b/ui/raid/import_export.ts index 62b7453770..ffe7804ff5 100644 --- a/ui/raid/import_export.ts +++ b/ui/raid/import_export.ts @@ -3,6 +3,7 @@ import { Importer } from '../core/components/importers'; import { Encounter } from '../core/encounter'; import { RaidSimPreset } from '../core/individual_sim_ui'; import { Player } from '../core/player'; +import { PlayerSpecs } from '../core/player_specs'; import { Party as PartyProto, Player as PlayerProto, Raid as RaidProto } from '../core/proto/api'; import { Class, @@ -20,13 +21,10 @@ import { import { RaidSimSettings } from '../core/proto/ui'; import { professionNames, raceNames } from '../core/proto_utils/names'; import { - DeathknightSpecs, DruidSpecs, + getPlayerSpecFromPlayer, getTalentTreePoints, - isTankSpec, makeDefaultBlessings, - playerToSpec, - PriestSpecs, raceToFaction, RogueSpecs, SpecOptions, @@ -64,7 +62,7 @@ export class RaidJsonExporter extends Exporter { private readonly simUI: RaidSimUI; constructor(parent: HTMLElement, simUI: RaidSimUI) { - super(parent, simUI, {title: 'JSON Export', allowDownload: true}); + super(parent, simUI, { title: 'JSON Export', allowDownload: true }); this.simUI = simUI; this.init(); } @@ -75,7 +73,6 @@ export class RaidJsonExporter extends Exporter { } export class RaidWCLImporter extends Importer { - private queryCounter = 0; private readonly simUI: RaidSimUI; @@ -124,14 +121,14 @@ export class RaidWCLImporter extends Importer { private async getWCLBearerToken(): Promise { if (this.token == '') { const response = await fetch('https://classic.warcraftlogs.com/oauth/token', { - 'method': 'POST', - 'headers': { - 'Authorization': 'Basic ' + btoa('963d31c8-7efa-4dde-87cf-1b254a8a2f8c:lRJVhujEEnF96xfUoxVHSpnqKN9v8bTqGEjutsO3'), + method: 'POST', + headers: { + Authorization: 'Basic ' + btoa('963d31c8-7efa-4dde-87cf-1b254a8a2f8c:lRJVhujEEnF96xfUoxVHSpnqKN9v8bTqGEjutsO3'), }, body: new URLSearchParams({ - 'grant_type': 'client_credentials', + grant_type: 'client_credentials', }), - }) + }); const json = await response.json(); this.token = json.access_token; } @@ -142,8 +139,8 @@ export class RaidWCLImporter extends Importer { const token = await this.getWCLBearerToken(); const headers = { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}`, - 'Accept': 'application/json', + Authorization: `Bearer ${token}`, + Accept: 'application/json', }; const queryURL = `https://classic.warcraftlogs.com/api/v2/client?query=${query}`; @@ -151,8 +148,8 @@ export class RaidWCLImporter extends Importer { // Query WCL const res = await fetch(encodeURI(queryURL), { - 'method': 'GET', - 'headers': headers, + method: 'GET', + headers: headers, }); const result = await res.json(); @@ -174,7 +171,7 @@ export class RaidWCLImporter extends Importer { const urlData = { reportID: match[1], fightID: '', - } + }; // If the URL has a Fight ID in it, use it if (match[2] && match[3] && match[3] != 'last') { @@ -195,7 +192,7 @@ export class RaidWCLImporter extends Importer { const fights = fightData.data.reportData.report.fights; if (match[3] == 'last') { - urlData.fightID = String(fights[fights.length - 1].id) + urlData.fightID = String(fights[fights.length - 1].id); } else { // Default to using the first Fight urlData.fightID = String(fights[0].id); @@ -226,7 +223,7 @@ export class RaidWCLImporter extends Importer { console.error(error); alert('Failed import from WCL: ' + error); } - this.importButton.disabled = false + this.importButton.disabled = false; this.rootElem.style.removeProperty('cursor'); } @@ -254,14 +251,23 @@ export class RaidWCLImporter extends Importer { startTime, endTime, id, name } - reportCastEvents: events(dataType:Casts, endTime: 99999999, filterExpression: "${[racialSpells, professionSpells].flat().map(spell => spell.id).map(id => `ability.id = ${id}`).join(' OR ') - }", limit: 10000) { data } + reportCastEvents: events(dataType:Casts, endTime: 99999999, filterExpression: "${[racialSpells, professionSpells] + .flat() + .map(spell => spell.id) + .map(id => `ability.id = ${id}`) + .join(' OR ')}", limit: 10000) { data } - fightCastEvents: events(fightIDs: [${urlData.fightID}], dataType:Casts, filterExpression: "${[externalCDSpells].flat().map(spell => spell.id).map(id => `ability.id = ${id}`).join(' OR ') - }", limit: 10000) { data } + fightCastEvents: events(fightIDs: [${urlData.fightID}], dataType:Casts, filterExpression: "${[externalCDSpells] + .flat() + .map(spell => spell.id) + .map(id => `ability.id = ${id}`) + .join(' OR ')}", limit: 10000) { data } - fightHealEvents: events(fightIDs: [${urlData.fightID}], dataType:Healing, filterExpression: "${[samePartyHealingSpells, otherPartyHealingSpells].flat().map(spell => spell.id).map(id => `ability.id = ${id}`).join(' OR ') - }", limit: 10000) { data } + fightHealEvents: events(fightIDs: [${urlData.fightID}], dataType:Healing, filterExpression: "${[samePartyHealingSpells, otherPartyHealingSpells] + .flat() + .map(spell => spell.id) + .map(id => `ability.id = ${id}`) + .join(' OR ')}", limit: 10000) { data } manaTideTotem: events(fightIDs: [${urlData.fightID}], dataType:Resources, filterExpression: "ability.id = 39609", limit: 100) { data } } @@ -465,7 +471,7 @@ export class RaidWCLImporter extends Importer { } private getEncounterProto(wclData: any): EncounterProto { - const fight: { startTime: number, endTime: number, id: number, name: string } = wclData.fights[0]; + const fight: { startTime: number; endTime: number; id: number; name: string } = wclData.fights[0]; const encounter = EncounterProto.create({ duration: (fight.endTime - fight.startTime) / 1000, @@ -491,22 +497,23 @@ export class RaidWCLImporter extends Importer { private getRaidProto(wclPlayers: WCLSimPlayer[]): RaidProto { const raid = RaidProto.create({ - parties: [...new Array(MAX_NUM_PARTIES).keys()].map(_party => PartyProto.create({ - players: [...new Array(5).keys()].map(_player => PlayerProto.create()), - })), + parties: [...new Array(MAX_NUM_PARTIES).keys()].map(_party => + PartyProto.create({ + players: [...new Array(5).keys()].map(_player => PlayerProto.create()), + }), + ), }); - wclPlayers - .forEach(player => { - const positionInParty = player.raidIndex % 5; - const partyIdx = (player.raidIndex - positionInParty) / 5; - const playerProto = player.player.toProto(); - raid.parties[partyIdx].players[positionInParty] = playerProto; + wclPlayers.forEach(player => { + const positionInParty = player.raidIndex % 5; + const partyIdx = (player.raidIndex - positionInParty) / 5; + const playerProto = player.player.toProto(); + raid.parties[partyIdx].players[positionInParty] = playerProto; - if (playerToSpec(playerProto).isTankSpec) { - raid.tanks.push(player.toUnitReference()); - } - }); + if (getPlayerSpecFromPlayer(playerProto).isTankSpec) { + raid.tanks.push(player.toUnitReference()); + } + }); return raid; } @@ -547,15 +554,18 @@ class WCLSimPlayer { throw new Error('Player type not implemented: ' + this.fullType); } this.spec = foundSpec; - this.player = new Player(this.spec, simUI.sim); + this.player = new Player(PlayerSpecs.fromProto(this.spec), simUI.sim); this.preset = WCLSimPlayer.getMatchingPreset(foundSpec, data.talents); if (this.preset === undefined) { - throw new Error('Could not find matching preset: ' + JSON.stringify({ - 'name': this.name, - 'type': this.fullType, - 'talents': data.talents, - }).toString()); + throw new Error( + 'Could not find matching preset: ' + + JSON.stringify({ + name: this.name, + type: this.fullType, + talents: data.talents, + }).toString(), + ); } // Apply preset defaults. @@ -568,13 +578,20 @@ class WCLSimPlayer { // Apply settings from report data. this.player.setName(eventID, data.name); - this.player.setGear(eventID, simUI.sim.db.lookupEquipmentSpec(EquipmentSpec.create({ - items: data.gear.map(gear => ItemSpec.create({ - id: gear.id, - enchant: gear.permanentEnchant, - gems: gear.gems ? gear.gems.map(gemInfo => gemInfo.id) : [], - })), - }))); + this.player.setGear( + eventID, + simUI.sim.db.lookupEquipmentSpec( + EquipmentSpec.create({ + items: data.gear.map(gear => + ItemSpec.create({ + id: gear.id, + enchant: gear.permanentEnchant, + gems: gear.gems ? gear.gems.map(gemInfo => gemInfo.id) : [], + }), + ), + }), + ), + ); } private static getMatchingPreset(spec: Spec, talents: wclTalents[]): RaidSimPreset { @@ -587,7 +604,7 @@ class WCLSimPlayer { matchingPresets.forEach((preset, i) => { const presetTalents = getTalentTreePoints(preset.talents.talentsString); // Diff the distance to the preset. - const newDistance = presetTalents.reduce((acc, v, i) => acc += Math.abs(talents[i]?.guid - presetTalents[i]), 0); + const newDistance = presetTalents.reduce((acc, v, i) => (acc += Math.abs(talents[i]?.guid - presetTalents[i])), 0); // If this is the best distance, assign this preset. if (newDistance < distance) { @@ -616,48 +633,48 @@ class WCLSimPlayer { } const fullTypeToSpec: Record = { - 'DeathKnightBlood': Spec.SpecBloodDeathKnight, - 'DeathKnightFrost': Spec.SpecFrostDeathKnight, - 'DeathKnightUnholy': Spec.SpecUnholyDeathKnight, - 'DruidBalance': Spec.SpecBalanceDruid, - 'DruidFeral': Spec.SpecFeralDruid, + DeathKnightBlood: Spec.SpecBloodDeathKnight, + DeathKnightFrost: Spec.SpecFrostDeathKnight, + DeathKnightUnholy: Spec.SpecUnholyDeathKnight, + DruidBalance: Spec.SpecBalanceDruid, + DruidFeral: Spec.SpecFeralDruid, // TOCO: Cata - Verify tank druid // 'DruidWarden': Spec.SpecFeralDruid, // 'DruidGuardian': Spec.SpecFeralDruid, - 'DruidRestoration': Spec.SpecRestorationDruid, - 'HunterBeastMastery': Spec.SpecBeastMasteryHunter, - 'HunterMarksmanship': Spec.SpecMarksmanshipHunter, - 'HunterSurvival': Spec.SpecSurvivalHunter, - 'MageArcane': Spec.SpecArcaneMage, - 'MageFire': Spec.SpecFireMage, - 'MageFrost': Spec.SpecFrostMage, - 'PaladinHoly': Spec.SpecHolyPaladin, - 'PaladinJusticar': Spec.SpecProtectionPaladin, - 'PaladinProtection': Spec.SpecProtectionPaladin, - 'PaladinRetribution': Spec.SpecRetributionPaladin, - 'PriestHoly': Spec.SpecHolyPriest, - 'PriestDiscipline': Spec.SpecDisciplinePriest, - 'PriestShadow': Spec.SpecShadowPriest, + DruidRestoration: Spec.SpecRestorationDruid, + HunterBeastMastery: Spec.SpecBeastMasteryHunter, + HunterMarksmanship: Spec.SpecMarksmanshipHunter, + HunterSurvival: Spec.SpecSurvivalHunter, + MageArcane: Spec.SpecArcaneMage, + MageFire: Spec.SpecFireMage, + MageFrost: Spec.SpecFrostMage, + PaladinHoly: Spec.SpecHolyPaladin, + PaladinJusticar: Spec.SpecProtectionPaladin, + PaladinProtection: Spec.SpecProtectionPaladin, + PaladinRetribution: Spec.SpecRetributionPaladin, + PriestHoly: Spec.SpecHolyPriest, + PriestDiscipline: Spec.SpecDisciplinePriest, + PriestShadow: Spec.SpecShadowPriest, // 'PriestSmite': Spec.SpecSmitePriest, - 'RogueAssassination': Spec.SpecAssassinationRogue, - 'RogueCombat': Spec.SpecCombatRogue, - 'RogueSubtlety': Spec.SpecSubtletyRogue, - 'ShamanElemental': Spec.SpecElementalShaman, - 'ShamanEnhancement': Spec.SpecEnhancementShaman, - 'ShamanRestoration': Spec.SpecRestorationShaman, - 'WarlockDestruction': Spec.SpecDestructionWarlock, - 'WarlockAffliction': Spec.SpecAfflictionWarlock, - 'WarlockDemonology': Spec.SpecDemonologyWarlock, - 'WarriorArms': Spec.SpecArmsWarrior, - 'WarriorFury': Spec.SpecFuryWarrior, + RogueAssassination: Spec.SpecAssassinationRogue, + RogueCombat: Spec.SpecCombatRogue, + RogueSubtlety: Spec.SpecSubtletyRogue, + ShamanElemental: Spec.SpecElementalShaman, + ShamanEnhancement: Spec.SpecEnhancementShaman, + ShamanRestoration: Spec.SpecRestorationShaman, + WarlockDestruction: Spec.SpecDestructionWarlock, + WarlockAffliction: Spec.SpecAfflictionWarlock, + WarlockDemonology: Spec.SpecDemonologyWarlock, + WarriorArms: Spec.SpecArmsWarrior, + WarriorFury: Spec.SpecFuryWarrior, // 'WarriorChampion': Spec.SpecWarrior, // 'WarriorWarrior': Spec.SpecWarrior, // 'WarriorGladiator': Spec.SpecWarrior, - 'WarriorProtection': Spec.SpecProtectionWarrior, + WarriorProtection: Spec.SpecProtectionWarrior, }; // Spells which imply a specific Race. -const racialSpells: Array<{ id: number, name: string, race: Race }> = [ +const racialSpells: Array<{ id: number; name: string; race: Race }> = [ { id: 25046, name: 'Arcane Torrent (Energy)', race: Race.RaceBloodElf }, { id: 28730, name: 'Arcane Torrent (Mana)', race: Race.RaceBloodElf }, { id: 50613, name: 'Arcane Torrent (Runic Power)', race: Race.RaceBloodElf }, @@ -673,44 +690,56 @@ const racialSpells: Array<{ id: number, name: string, race: Race }> = [ ]; // Spells which imply a specific Profession. -const professionSpells: Array<{ id: number, name: string, profession: Profession }> = [ +const professionSpells: Array<{ id: number; name: string; profession: Profession }> = [ { id: 55503, name: 'Lifeblood', profession: Profession.Herbalism }, { id: 50305, name: 'Skinning', profession: Profession.Skinning }, ]; -const externalCDSpells: Array<{ id: number, name: string, class: Class, applyFunc: (player: Player, raidTarget: UnitReference) => SpecOptions }> = [ +const externalCDSpells: Array<{ id: number; name: string; class: Class; applyFunc: (player: Player, raidTarget: UnitReference) => SpecOptions }> = [ { - id: 29166, name: 'Innervate', class: Class.ClassDruid, applyFunc: (player: Player, raidTarget: UnitReference) => { + id: 29166, + name: 'Innervate', + class: Class.ClassDruid, + applyFunc: (player: Player, raidTarget: UnitReference) => { const options = player.getSpecOptions() as SpecOptions; options.innervateTarget = raidTarget; return options; - } + }, }, { - id: 10060, name: 'Power Infusion', class: Class.ClassPriest, applyFunc: (player: Player, raidTarget: UnitReference) => { - const options = player.getSpecOptions() as SpecOptions; + id: 10060, + name: 'Power Infusion', + class: Class.ClassPriest, + applyFunc: (player: Player, raidTarget: UnitReference) => { + const options = player.getSpecOptions() as SpecOptions; options.powerInfusionTarget = raidTarget; return options; - } + }, }, { - id: 57933, name: 'Tricks of the Trade', class: Class.ClassRogue, applyFunc: (player: Player, raidTarget: UnitReference) => { + id: 57933, + name: 'Tricks of the Trade', + class: Class.ClassRogue, + applyFunc: (player: Player, raidTarget: UnitReference) => { const options = player.getSpecOptions() as SpecOptions; - options.tricksOfTheTradeTarget = raidTarget; + options.rogueOptions!.tricksOfTheTradeTarget = raidTarget; return options; - } + }, }, { - id: 49016, name: 'Unholy Frenzy', class: Class.ClassDeathknight, applyFunc: (player: Player, raidTarget: UnitReference) => { - const options = player.getSpecOptions() as SpecOptions; + id: 49016, + name: 'Unholy Frenzy', + class: Class.ClassDeathKnight, + applyFunc: (player: Player, raidTarget: UnitReference) => { + const options = player.getSpecOptions() as SpecOptions; options.unholyFrenzyTarget = raidTarget; return options; - } + }, }, ]; // Healing spells which only affect the caster's party. -const samePartyHealingSpells: Array<{ id: number, name: string }> = [ +const samePartyHealingSpells: Array<{ id: number; name: string }> = [ { id: 52042, name: 'Healing Stream Totem' }, { id: 48076, name: 'Holy Nova' }, { id: 48445, name: 'Tranquility' }, @@ -718,17 +747,15 @@ const samePartyHealingSpells: Array<{ id: number, name: string }> = [ ]; // Healing spells which only affect a single party, but not necessarily the caster's party. -const otherPartyHealingSpells: Array<{ id: number, name: string }> = [ - { id: 48072, name: 'Prayer of Healing' }, -]; +const otherPartyHealingSpells: Array<{ id: number; name: string }> = [{ id: 48072, name: 'Prayer of Healing' }]; interface wclUrlData { - reportID: string, - fightID: string, + reportID: string; + fightID: string; } interface wclCastEvent { - type: 'cast', + type: 'cast'; timestamp: number; sourceID: number; targetID: number; @@ -737,7 +764,7 @@ interface wclCastEvent { } interface wclHealEvent { - type: 'heal', + type: 'heal'; timestamp: number; sourceID: number; targetID: number; @@ -757,9 +784,9 @@ interface wclCombatantInfoEvent { } interface wclRateLimitData { - limitPerHour: number, - pointsSpentThisHour: number, - pointsResetIn: number + limitPerHour: number; + pointsSpentThisHour: number; + pointsResetIn: number; } // Typed interface for WCL talents @@ -818,7 +845,7 @@ interface _wclAura { totalUptime: number; totalUses: number; bands: { - startTime: number, - endTime: number, + startTime: number; + endTime: number; }[]; } diff --git a/ui/raid/presets.ts b/ui/raid/presets.ts index 54ccaf4cc2..748ccab53b 100644 --- a/ui/raid/presets.ts +++ b/ui/raid/presets.ts @@ -11,15 +11,29 @@ import { RestorationDruidSimUI } from '../druid/restoration/sim.js'; import { BeastMasteryHunterSimUI } from '../hunter/beast_mastery/sim'; import { MarksmanshipHunterSimUI } from '../hunter/marksmanship/sim'; import { SurvivalHunterSimUI } from '../hunter/survival/sim'; +import { ArcaneMageSimUI } from '../mage/arcane/sim'; +import { FireMageSimUI } from '../mage/fire/sim'; +import { FrostMageSimUI } from '../mage/frost/sim'; import { HolyPaladinSimUI } from '../paladin/holy/sim.js'; import { ProtectionPaladinSimUI } from '../paladin/protection/sim.js'; import { RetributionPaladinSimUI } from '../paladin/retribution/sim.js'; +import { DisciplinePriestSimUI } from '../priest/discipline/sim'; +import { HolyPriestSimUI } from '../priest/holy/sim'; import { ShadowPriestSimUI } from '../priest/shadow/sim.js'; +import { AssassinationRogueSimUI } from '../rogue/assassination/sim'; +import { CombatRogueSimUI } from '../rogue/combat/sim'; +import { SubtletyRogueSimUI } from '../rogue/subtlety/sim'; import { ElementalShamanSimUI } from '../shaman/elemental/sim.js'; import { EnhancementShamanSimUI } from '../shaman/enhancement/sim.js'; import { RestorationShamanSimUI } from '../shaman/restoration/sim.js'; +import { AfflictionWarlockSimUI } from '../warlock/affliction/sim'; +import { DemonologyWarlockSimUI } from '../warlock/demonology/sim'; +import { DestructionWarlockSimUI } from '../warlock/destruction/sim'; +import { ArmsWarriorSimUI } from '../warrior/arms/sim'; +import { FuryWarriorSimUI } from '../warrior/fury/sim'; +import { ProtectionWarriorSimUI } from '../warrior/protection/sim'; -export const specSimFactories: Record) => IndividualSimUI> = { +export const specSimFactories: Partial) => IndividualSimUI>> = { // Death Knight [Spec.SpecBloodDeathKnight]: (parentElem: HTMLElement, player: Player) => new BloodDeathknightSimUI(parentElem, player), [Spec.SpecFrostDeathKnight]: (parentElem: HTMLElement, player: Player) => new FrostDeathKnightSimUI(parentElem, player), @@ -33,19 +47,33 @@ export const specSimFactories: Record) => new MarksmanshipHunterSimUI(parentElem, player), [Spec.SpecSurvivalHunter]: (parentElem: HTMLElement, player: Player) => new SurvivalHunterSimUI(parentElem, player), // Mage + [Spec.SpecArcaneMage]: (parentElem: HTMLElement, player: Player) => new ArcaneMageSimUI(parentElem, player), + [Spec.SpecFireMage]: (parentElem: HTMLElement, player: Player) => new FireMageSimUI(parentElem, player), + [Spec.SpecFrostMage]: (parentElem: HTMLElement, player: Player) => new FrostMageSimUI(parentElem, player), // Paladin [Spec.SpecHolyPaladin]: (parentElem: HTMLElement, player: Player) => new HolyPaladinSimUI(parentElem, player), [Spec.SpecProtectionPaladin]: (parentElem: HTMLElement, player: Player) => new ProtectionPaladinSimUI(parentElem, player), [Spec.SpecRetributionPaladin]: (parentElem: HTMLElement, player: Player) => new RetributionPaladinSimUI(parentElem, player), // Priest + [Spec.SpecDisciplinePriest]: (parentElem: HTMLElement, player: Player) => new DisciplinePriestSimUI(parentElem, player), + [Spec.SpecHolyPriest]: (parentElem: HTMLElement, player: Player) => new HolyPriestSimUI(parentElem, player), [Spec.SpecShadowPriest]: (parentElem: HTMLElement, player: Player) => new ShadowPriestSimUI(parentElem, player), // Rogue + [Spec.SpecAssassinationRogue]: (parentElem: HTMLElement, player: Player) => new AssassinationRogueSimUI(parentElem, player), + [Spec.SpecCombatRogue]: (parentElem: HTMLElement, player: Player) => new CombatRogueSimUI(parentElem, player), + [Spec.SpecSubtletyRogue]: (parentElem: HTMLElement, player: Player) => new SubtletyRogueSimUI(parentElem, player), // Shaman [Spec.SpecElementalShaman]: (parentElem: HTMLElement, player: Player) => new ElementalShamanSimUI(parentElem, player), [Spec.SpecEnhancementShaman]: (parentElem: HTMLElement, player: Player) => new EnhancementShamanSimUI(parentElem, player), [Spec.SpecRestorationShaman]: (parentElem: HTMLElement, player: Player) => new RestorationShamanSimUI(parentElem, player), // Warlock + [Spec.SpecAfflictionWarlock]: (parentElem: HTMLElement, player: Player) => new AfflictionWarlockSimUI(parentElem, player), + [Spec.SpecDemonologyWarlock]: (parentElem: HTMLElement, player: Player) => new DemonologyWarlockSimUI(parentElem, player), + [Spec.SpecDestructionWarlock]: (parentElem: HTMLElement, player: Player) => new DestructionWarlockSimUI(parentElem, player), // Warrior + [Spec.SpecArmsWarrior]: (parentElem: HTMLElement, player: Player) => new ArmsWarriorSimUI(parentElem, player), + [Spec.SpecFuryWarrior]: (parentElem: HTMLElement, player: Player) => new FuryWarriorSimUI(parentElem, player), + [Spec.SpecProtectionWarrior]: (parentElem: HTMLElement, player: Player) => new ProtectionWarriorSimUI(parentElem, player), }; export const playerPresets: Array> = naturalPlayerClassOrder diff --git a/ui/raid/raid_picker.ts b/ui/raid/raid_picker.ts index 8ad5dbd27a..b0167a1ca3 100644 --- a/ui/raid/raid_picker.ts +++ b/ui/raid/raid_picker.ts @@ -6,10 +6,12 @@ import { EnumPicker } from '../core/components/enum_picker.js'; import { MAX_PARTY_SIZE, Party } from '../core/party.js'; import { Player } from '../core/player.js'; import { PlayerClasses } from '../core/player_classes'; +import { PlayerSpecs } from '../core/player_specs'; import { Player as PlayerProto } from '../core/proto/api.js'; import { Class, Faction, Glyphs, Profession, Spec } from '../core/proto/common.js'; import { BalanceDruid_Options as BalanceDruidOptions } from '../core/proto/druid.js'; -import { newUnitReference, playerToSpec, specToClass } from '../core/proto_utils/utils.js'; +import { ArcaneMage_Options } from '../core/proto/mage'; +import { getPlayerSpecFromPlayer, newUnitReference } from '../core/proto_utils/utils.js'; import { Raid } from '../core/raid.js'; import { EventID, TypedEvent } from '../core/typed_event.js'; import { formatDeltaTextElem, getEnumValues } from '../core/utils.js'; @@ -410,7 +412,7 @@ export class PlayerPicker extends Component { } const playerProto = PlayerProto.fromBinary(bytes); - const localPlayer = new Player(playerToSpec(playerProto), this.raidPicker.raidSimUI.sim); + const localPlayer = new Player(getPlayerSpecFromPlayer(playerProto), this.raidPicker.raidSimUI.sim); localPlayer.fromProto(eventID, playerProto); this.raidPicker.currentDragPlayer = localPlayer; } @@ -462,7 +464,7 @@ export class PlayerPicker extends Component { this.dpsResultElem = null; this.referenceDeltaElem = null; } else { - const classCssClass = PlayerClasses.getCssClass(this.player.getClass()); + const classCssClass = PlayerClasses.getCssClass(this.player.getPlayerClass()); this.rootElem.className = `player-picker-root player bg-${classCssClass}-dampened`; this.rootElem.innerHTML = ` @@ -605,7 +607,7 @@ class PlayerEditorModal extends BaseModal { ); const editorRoot = this.rootElem.getElementsByClassName('player-editor')[0] as HTMLElement; - const _individualSim = specSimFactories[player.spec.protoID]!(editorRoot, player); + const _individualSim = specSimFactories[player.getSpec()]!(editorRoot, player); } } @@ -621,7 +623,7 @@ class NewPlayerPicker extends Component { return; } - const matchingPresets = playerPresets.filter(preset => specToClass[preset.spec] == wowClass); + const matchingPresets = playerPresets.filter(preset => PlayerSpecs.fromProto(preset.spec).playerClass.protoID == wowClass); if (matchingPresets.length == 0) { return; } @@ -634,6 +636,7 @@ class NewPlayerPicker extends Component { this.rootElem.appendChild(classPresetsContainer); matchingPresets.forEach(matchingPreset => { + const playerSpec = PlayerSpecs.fromProto(matchingPreset.spec); const presetElemFragment = document.createElement('fragment'); presetElemFragment.innerHTML = ` - + `; const presetElem = presetElemFragment.children[0] as HTMLElement; @@ -656,19 +659,20 @@ class NewPlayerPicker extends Component { const eventID = TypedEvent.nextEventID(); TypedEvent.freezeAllAndDo(() => { const dragImage = new Image(); - dragImage.src = matchingPreset.iconUrl; + dragImage.src = matchingPreset.iconUrl ?? playerSpec.getIcon('medium'); event.dataTransfer!.setDragImage(dragImage, 30, 30); event.dataTransfer!.setData('text/plain', ''); event.dataTransfer!.dropEffect = 'copy'; - const newPlayer = new Player(matchingPreset.spec, this.raidPicker.raid.sim); + const newPlayer = new Player(playerSpec, this.raidPicker.raid.sim); + newPlayer.applySharedDefaults(eventID); newPlayer.setRace(eventID, matchingPreset.defaultFactionRaces[this.raidPicker.getCurrentFaction()]); newPlayer.setTalentsString(eventID, matchingPreset.talents.talentsString); newPlayer.setGlyphs(eventID, matchingPreset.talents.glyphs || Glyphs.create()); newPlayer.setSpecOptions(eventID, matchingPreset.specOptions); newPlayer.setConsumes(eventID, matchingPreset.consumes); - newPlayer.setName(eventID, matchingPreset.defaultName); + newPlayer.setName(eventID, matchingPreset.defaultName ?? playerSpec.friendlyName); newPlayer.setProfession1(eventID, matchingPreset.otherDefaults?.profession1 || Profession.Engineering); newPlayer.setProfession2(eventID, matchingPreset.otherDefaults?.profession2 || Profession.Jewelcrafting); newPlayer.setDistanceFromTarget(eventID, matchingPreset.otherDefaults?.distanceFromTarget || 0); @@ -703,17 +707,13 @@ function applyNewPlayerAssignments(eventID: EventID, newPlayer: Player, rai } // Spec-specific assignments. For most cases, default to buffing self. - if (newPlayer.spec == Spec.SpecBalanceDruid) { + if (newPlayer.getSpec() == Spec.SpecBalanceDruid) { const newOptions = newPlayer.getSpecOptions() as BalanceDruidOptions; newOptions.innervateTarget = newUnitReference(newPlayer.getRaidIndex()); newPlayer.setSpecOptions(eventID, newOptions); - } else if (newPlayer.spec == Spec.SpecSmitePriest) { - const newOptions = newPlayer.getSpecOptions() as SmitePriestOptions; - newOptions.powerInfusionTarget = newUnitReference(newPlayer.getRaidIndex()); - newPlayer.setSpecOptions(eventID, newOptions); - } else if (newPlayer.spec == Spec.SpecMage) { - const newOptions = newPlayer.getSpecOptions() as MageOptions; - newOptions.focusMagicTarget = newUnitReference(newPlayer.getRaidIndex()); + } else if (newPlayer.getSpec() == Spec.SpecArcaneMage) { + const newOptions = newPlayer.getSpecOptions() as ArcaneMage_Options; + newOptions.mageOptions!.focusMagicTarget = newUnitReference(newPlayer.getRaidIndex()); newPlayer.setSpecOptions(eventID, newOptions); } } diff --git a/ui/rogue/assassination/sim.ts b/ui/rogue/assassination/sim.ts index 171a1f95b1..99ad557694 100644 --- a/ui/rogue/assassination/sim.ts +++ b/ui/rogue/assassination/sim.ts @@ -1,5 +1,10 @@ +import * as BuffDebuffInputs from '../../core/components/inputs/buffs_debuffs'; +import * as OtherInputs from '../../core/components/other_inputs'; +import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui'; +import { Player } from '../../core/player'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLRotation } from '../../core/proto/apl'; import { - Class, Debuffs, Faction, IndividualBuffs, @@ -11,43 +16,28 @@ import { Spec, Stat, TristateEffect, - WeaponType -} from '../core/proto/common.js'; -import { - APLRotation, -} from '../core/proto/apl.js'; -import { Player } from '../core/player.js'; -import { Stats } from '../core/proto_utils/stats.js'; -import { getSpecIcon } from '../core/proto_utils/utils.js'; -import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; - -import { - Rogue_Options_PoisonImbue, -} from '../core/proto/rogue.js'; - -import * as BuffDebuffInputs from '../core/components/inputs/buffs_debuffs.js'; -import * as OtherInputs from '../core/components/other_inputs.js'; - -import * as RogueInputs from './inputs.js'; -import * as Presets from './presets.js'; + WeaponType, +} from '../../core/proto/common'; +import { RogueOptions_PoisonImbue } from '../../core/proto/rogue'; +import { Stats } from '../../core/proto_utils/stats'; +import * as RogueInputs from './inputs'; +import * as Presets from './presets'; -const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { - cssClass: 'rogue-sim-ui', - cssScheme: 'rogue', +const SPEC_CONFIG = registerSpecConfig(Spec.SpecAssassinationRogue, { + cssClass: 'assassination-rogue-sim-ui', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.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.', - ], + knownIssues: ['Rotations are not fully optimized, especially for non-standard setups.'], warnings: [ - (simUI: IndividualSimUI) => { + (simUI: IndividualSimUI) => { return { updateOn: simUI.sim.encounter.changeEmitter, getContent: () => { - let hasNoArmor = false + let hasNoArmor = false; for (const target of simUI.sim.encounter.targets) { if (new Stats(target.stats).getStat(Stat.StatArmor) <= 0) { - hasNoArmor = true - break + hasNoArmor = true; + break; } } if (hasNoArmor) { @@ -58,7 +48,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, }; }, - (simUI: IndividualSimUI) => { + (simUI: IndividualSimUI) => { return { updateOn: simUI.player.changeEmitter, getContent: () => { @@ -74,15 +64,17 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, }; }, - (simUI: IndividualSimUI) => { + (simUI: IndividualSimUI) => { return { updateOn: simUI.player.changeEmitter, getContent: () => { if (simUI.player.getTalents().hackAndSlash) { - if (simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType == WeaponType.WeaponTypeSword || + 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) { + simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType == WeaponType.WeaponTypeAxe + ) { return ''; } else { return '"Hack and Slash" talent selected, but swords or axes not equipped.'; @@ -93,15 +85,17 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, }; }, - (simUI: IndividualSimUI) => { + (simUI: IndividualSimUI) => { return { updateOn: simUI.player.changeEmitter, getContent: () => { if (simUI.player.getTalents().closeQuartersCombat) { - if (simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType == WeaponType.WeaponTypeFist || + 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) { + simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType == WeaponType.WeaponTypeDagger + ) { return ''; } else { return '"Close Quarters Combat" talent selected, but fists or daggers not equipped.'; @@ -112,13 +106,15 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, }; }, - (simUI: IndividualSimUI) => { + (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) { + 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.'; @@ -129,21 +125,25 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, }; }, - (simUI: IndividualSimUI) => { + (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) { + const mhImbue = simUI.player.getSpecOptions().rogueOptions!.mhImbue; + const ohImbue = simUI.player.getSpecOptions().rogueOptions!.ohImbue; + if ( + typeof mhWeaponSpeed == 'undefined' || + typeof ohWeaponSpeed == 'undefined' || + !simUI.player.getSpecOptions().rogueOptions!.applyPoisonsManually + ) { return ''; } - if (mhWeaponSpeed < ohWeaponSpeed && ohImbue == Rogue_Options_PoisonImbue.DeadlyPoison) { + if (mhWeaponSpeed < ohWeaponSpeed && ohImbue == RogueOptions_PoisonImbue.DeadlyPoison) { return 'Deadly poison applied to slower (off hand) weapon.'; } - if (ohWeaponSpeed < mhWeaponSpeed && mhImbue == Rogue_Options_PoisonImbue.DeadlyPoison) { + if (ohWeaponSpeed < mhWeaponSpeed && mhImbue == RogueOptions_PoisonImbue.DeadlyPoison) { return 'Deadly poison applied to slower (main hand) weapon.'; } return ''; @@ -165,10 +165,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { Stat.StatArmorPenetration, Stat.StatExpertise, ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - PseudoStat.PseudoStatOffHandDps, - ], + 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. @@ -191,21 +188,24 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { // 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, - }), + 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 talents. @@ -224,8 +224,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { elementalOath: true, sanctifiedRetribution: true, }), - partyBuffs: PartyBuffs.create({ - }), + partyBuffs: PartyBuffs.create({}), individualBuffs: IndividualBuffs.create({ blessingOfKings: true, blessingOfMight: TristateEffect.TristateEffectImproved, @@ -242,24 +241,18 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, playerInputs: { - inputs: [ - RogueInputs.ApplyPoisonsManually - ] + inputs: [RogueInputs.ApplyPoisonsManually], }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - RogueInputs.MainHandImbue, - RogueInputs.OffHandImbue, - ], + playerIconInputs: [RogueInputs.MainHandImbue, RogueInputs.OffHandImbue], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. includeBuffDebuffInputs: [ BuffDebuffInputs.SpellCritBuff, BuffDebuffInputs.SpellCritDebuff, BuffDebuffInputs.SpellHitDebuff, - BuffDebuffInputs.SpellDamageDebuff - ], - excludeBuffDebuffInputs: [ + BuffDebuffInputs.SpellDamageDebuff, ], + excludeBuffDebuffInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { inputs: [ @@ -320,7 +313,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { ], }, - autoRotation: (player: Player): APLRotation => { + autoRotation: (player: Player): APLRotation => { const talentTree = player.getTalentTree(); const numTargets = player.sim.encounter.targets.length; if (numTargets >= 5) { @@ -337,11 +330,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { raidSimPresets: [ { - spec: Spec.SpecRogue, - tooltip: 'Assassination Rogue', - defaultName: 'Assassination', - iconUrl: getSpecIcon(Class.ClassRogue, 0), - + spec: Spec.SpecAssassinationRogue, talents: Presets.AssassinationTalents137.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, @@ -366,89 +355,59 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, }, }, - { - spec: Spec.SpecRogue, - tooltip: 'Combat Rogue', - defaultName: 'Combat', - iconUrl: getSpecIcon(Class.ClassRogue, 1), - - talents: Presets.CombatCQCTalents.data, - specOptions: Presets.DefaultOptions, - consumes: Presets.DefaultConsumes, - defaultFactionRaces: { - [Faction.Unknown]: Race.RaceUnknown, - [Faction.Alliance]: Race.RaceHuman, - [Faction.Horde]: Race.RaceOrc, - }, - defaultGear: { - [Faction.Unknown]: {}, - [Faction.Alliance]: { - 1: Presets.P1_PRESET_COMBAT.gear, - 2: Presets.P2_PRESET_COMBAT.gear, - 3: Presets.P3_PRESET_COMBAT.gear, - 4: Presets.P4_PRESET_COMBAT.gear, - }, - [Faction.Horde]: { - 1: Presets.P1_PRESET_COMBAT.gear, - 2: Presets.P2_PRESET_COMBAT.gear, - 3: Presets.P3_PRESET_COMBAT.gear, - 4: Presets.P4_PRESET_COMBAT.gear, - }, - }, - }, ], }); -export class RogueSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { +export class AssassinationRogueSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { super(parentElem, player, SPEC_CONFIG); - this.player.changeEmitter.on((c) => { - const options = this.player.getSpecOptions() - const encounter = this.sim.encounter - if (!options.applyPoisonsManually) { + this.player.changeEmitter.on(c => { + const options = this.player.getSpecOptions(); + const encounter = this.sim.encounter; + if (!options.rogueOptions!.applyPoisonsManually) { const mhWeaponSpeed = this.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponSpeed; const ohWeaponSpeed = this.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponSpeed; if (typeof mhWeaponSpeed == 'undefined' || typeof ohWeaponSpeed == 'undefined') { - return + return; } if (encounter.targets.length > 3) { - options.mhImbue = Rogue_Options_PoisonImbue.InstantPoison - options.ohImbue = Rogue_Options_PoisonImbue.InstantPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.InstantPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.InstantPoison; } else { if (mhWeaponSpeed <= ohWeaponSpeed) { - options.mhImbue = Rogue_Options_PoisonImbue.DeadlyPoison - options.ohImbue = Rogue_Options_PoisonImbue.InstantPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.DeadlyPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.InstantPoison; } else { - options.mhImbue = Rogue_Options_PoisonImbue.InstantPoison - options.ohImbue = Rogue_Options_PoisonImbue.DeadlyPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.InstantPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.DeadlyPoison; } } } - this.player.setSpecOptions(c, options) + this.player.setSpecOptions(c, options); }); - this.sim.encounter.changeEmitter.on((c) => { - const options = this.player.getSpecOptions() - const encounter = this.sim.encounter - if (!options.applyPoisonsManually) { + this.sim.encounter.changeEmitter.on(c => { + const options = this.player.getSpecOptions(); + const encounter = this.sim.encounter; + if (!options.rogueOptions!.applyPoisonsManually) { const mhWeaponSpeed = this.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponSpeed; const ohWeaponSpeed = this.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponSpeed; if (typeof mhWeaponSpeed == 'undefined' || typeof ohWeaponSpeed == 'undefined') { - return + return; } if (encounter.targets.length > 3) { - options.mhImbue = Rogue_Options_PoisonImbue.InstantPoison - options.ohImbue = Rogue_Options_PoisonImbue.InstantPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.InstantPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.InstantPoison; } else { if (mhWeaponSpeed <= ohWeaponSpeed) { - options.mhImbue = Rogue_Options_PoisonImbue.DeadlyPoison - options.ohImbue = Rogue_Options_PoisonImbue.InstantPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.DeadlyPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.InstantPoison; } else { - options.mhImbue = Rogue_Options_PoisonImbue.InstantPoison - options.ohImbue = Rogue_Options_PoisonImbue.DeadlyPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.InstantPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.DeadlyPoison; } } } - this.player.setSpecOptions(c, options) + this.player.setSpecOptions(c, options); }); } } diff --git a/ui/rogue/combat/sim.ts b/ui/rogue/combat/sim.ts index 171a1f95b1..bad75aece6 100644 --- a/ui/rogue/combat/sim.ts +++ b/ui/rogue/combat/sim.ts @@ -1,5 +1,10 @@ +import * as BuffDebuffInputs from '../../core/components/inputs/buffs_debuffs'; +import * as OtherInputs from '../../core/components/other_inputs'; +import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui'; +import { Player } from '../../core/player'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLRotation } from '../../core/proto/apl'; import { - Class, Debuffs, Faction, IndividualBuffs, @@ -11,43 +16,28 @@ import { Spec, Stat, TristateEffect, - WeaponType -} from '../core/proto/common.js'; -import { - APLRotation, -} from '../core/proto/apl.js'; -import { Player } from '../core/player.js'; -import { Stats } from '../core/proto_utils/stats.js'; -import { getSpecIcon } from '../core/proto_utils/utils.js'; -import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; - -import { - Rogue_Options_PoisonImbue, -} from '../core/proto/rogue.js'; - -import * as BuffDebuffInputs from '../core/components/inputs/buffs_debuffs.js'; -import * as OtherInputs from '../core/components/other_inputs.js'; - -import * as RogueInputs from './inputs.js'; -import * as Presets from './presets.js'; + WeaponType, +} from '../../core/proto/common'; +import { RogueOptions_PoisonImbue } from '../../core/proto/rogue'; +import { Stats } from '../../core/proto_utils/stats'; +import * as RogueInputs from './inputs'; +import * as Presets from './presets'; -const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { - cssClass: 'rogue-sim-ui', - cssScheme: 'rogue', +const SPEC_CONFIG = registerSpecConfig(Spec.SpecCombatRogue, { + cssClass: 'combat-rogue-sim-ui', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.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.', - ], + knownIssues: ['Rotations are not fully optimized, especially for non-standard setups.'], warnings: [ - (simUI: IndividualSimUI) => { + (simUI: IndividualSimUI) => { return { updateOn: simUI.sim.encounter.changeEmitter, getContent: () => { - let hasNoArmor = false + let hasNoArmor = false; for (const target of simUI.sim.encounter.targets) { if (new Stats(target.stats).getStat(Stat.StatArmor) <= 0) { - hasNoArmor = true - break + hasNoArmor = true; + break; } } if (hasNoArmor) { @@ -58,7 +48,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, }; }, - (simUI: IndividualSimUI) => { + (simUI: IndividualSimUI) => { return { updateOn: simUI.player.changeEmitter, getContent: () => { @@ -74,15 +64,17 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, }; }, - (simUI: IndividualSimUI) => { + (simUI: IndividualSimUI) => { return { updateOn: simUI.player.changeEmitter, getContent: () => { if (simUI.player.getTalents().hackAndSlash) { - if (simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType == WeaponType.WeaponTypeSword || + 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) { + simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType == WeaponType.WeaponTypeAxe + ) { return ''; } else { return '"Hack and Slash" talent selected, but swords or axes not equipped.'; @@ -93,15 +85,17 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, }; }, - (simUI: IndividualSimUI) => { + (simUI: IndividualSimUI) => { return { updateOn: simUI.player.changeEmitter, getContent: () => { if (simUI.player.getTalents().closeQuartersCombat) { - if (simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType == WeaponType.WeaponTypeFist || + 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) { + simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType == WeaponType.WeaponTypeDagger + ) { return ''; } else { return '"Close Quarters Combat" talent selected, but fists or daggers not equipped.'; @@ -112,13 +106,15 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, }; }, - (simUI: IndividualSimUI) => { + (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) { + 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.'; @@ -129,21 +125,25 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, }; }, - (simUI: IndividualSimUI) => { + (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) { + const mhImbue = simUI.player.getSpecOptions().rogueOptions!.mhImbue; + const ohImbue = simUI.player.getSpecOptions().rogueOptions!.ohImbue; + if ( + typeof mhWeaponSpeed == 'undefined' || + typeof ohWeaponSpeed == 'undefined' || + !simUI.player.getSpecOptions().rogueOptions!.applyPoisonsManually + ) { return ''; } - if (mhWeaponSpeed < ohWeaponSpeed && ohImbue == Rogue_Options_PoisonImbue.DeadlyPoison) { + if (mhWeaponSpeed < ohWeaponSpeed && ohImbue == RogueOptions_PoisonImbue.DeadlyPoison) { return 'Deadly poison applied to slower (off hand) weapon.'; } - if (ohWeaponSpeed < mhWeaponSpeed && mhImbue == Rogue_Options_PoisonImbue.DeadlyPoison) { + if (ohWeaponSpeed < mhWeaponSpeed && mhImbue == RogueOptions_PoisonImbue.DeadlyPoison) { return 'Deadly poison applied to slower (main hand) weapon.'; } return ''; @@ -165,10 +165,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { Stat.StatArmorPenetration, Stat.StatExpertise, ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - PseudoStat.PseudoStatOffHandDps, - ], + 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. @@ -191,21 +188,24 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { // 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, - }), + 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 talents. @@ -224,8 +224,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { elementalOath: true, sanctifiedRetribution: true, }), - partyBuffs: PartyBuffs.create({ - }), + partyBuffs: PartyBuffs.create({}), individualBuffs: IndividualBuffs.create({ blessingOfKings: true, blessingOfMight: TristateEffect.TristateEffectImproved, @@ -242,24 +241,18 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, playerInputs: { - inputs: [ - RogueInputs.ApplyPoisonsManually - ] + inputs: [RogueInputs.ApplyPoisonsManually], }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - RogueInputs.MainHandImbue, - RogueInputs.OffHandImbue, - ], + playerIconInputs: [RogueInputs.MainHandImbue, RogueInputs.OffHandImbue], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. includeBuffDebuffInputs: [ BuffDebuffInputs.SpellCritBuff, BuffDebuffInputs.SpellCritDebuff, BuffDebuffInputs.SpellHitDebuff, - BuffDebuffInputs.SpellDamageDebuff - ], - excludeBuffDebuffInputs: [ + BuffDebuffInputs.SpellDamageDebuff, ], + excludeBuffDebuffInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { inputs: [ @@ -320,7 +313,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { ], }, - autoRotation: (player: Player): APLRotation => { + autoRotation: (player: Player): APLRotation => { const talentTree = player.getTalentTree(); const numTargets = player.sim.encounter.targets.length; if (numTargets >= 5) { @@ -337,41 +330,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { raidSimPresets: [ { - spec: Spec.SpecRogue, - tooltip: 'Assassination Rogue', - defaultName: 'Assassination', - iconUrl: getSpecIcon(Class.ClassRogue, 0), - - talents: Presets.AssassinationTalents137.data, - specOptions: Presets.DefaultOptions, - consumes: Presets.DefaultConsumes, - defaultFactionRaces: { - [Faction.Unknown]: Race.RaceUnknown, - [Faction.Alliance]: Race.RaceHuman, - [Faction.Horde]: Race.RaceOrc, - }, - defaultGear: { - [Faction.Unknown]: {}, - [Faction.Alliance]: { - 1: Presets.P1_PRESET_ASSASSINATION.gear, - 2: Presets.P2_PRESET_ASSASSINATION.gear, - 3: Presets.P3_PRESET_ASSASSINATION.gear, - 4: Presets.P4_PRESET_ASSASSINATION.gear, - }, - [Faction.Horde]: { - 1: Presets.P1_PRESET_ASSASSINATION.gear, - 2: Presets.P2_PRESET_ASSASSINATION.gear, - 3: Presets.P3_PRESET_ASSASSINATION.gear, - 4: Presets.P4_PRESET_ASSASSINATION.gear, - }, - }, - }, - { - spec: Spec.SpecRogue, - tooltip: 'Combat Rogue', - defaultName: 'Combat', - iconUrl: getSpecIcon(Class.ClassRogue, 1), - + spec: Spec.SpecCombatRogue, talents: Presets.CombatCQCTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, @@ -399,56 +358,56 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { ], }); -export class RogueSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { +export class CombatRogueSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { super(parentElem, player, SPEC_CONFIG); - this.player.changeEmitter.on((c) => { - const options = this.player.getSpecOptions() - const encounter = this.sim.encounter - if (!options.applyPoisonsManually) { + this.player.changeEmitter.on(c => { + const options = this.player.getSpecOptions(); + const encounter = this.sim.encounter; + if (!options.rogueOptions!.applyPoisonsManually) { const mhWeaponSpeed = this.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponSpeed; const ohWeaponSpeed = this.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponSpeed; if (typeof mhWeaponSpeed == 'undefined' || typeof ohWeaponSpeed == 'undefined') { - return + return; } if (encounter.targets.length > 3) { - options.mhImbue = Rogue_Options_PoisonImbue.InstantPoison - options.ohImbue = Rogue_Options_PoisonImbue.InstantPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.InstantPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.InstantPoison; } else { if (mhWeaponSpeed <= ohWeaponSpeed) { - options.mhImbue = Rogue_Options_PoisonImbue.DeadlyPoison - options.ohImbue = Rogue_Options_PoisonImbue.InstantPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.DeadlyPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.InstantPoison; } else { - options.mhImbue = Rogue_Options_PoisonImbue.InstantPoison - options.ohImbue = Rogue_Options_PoisonImbue.DeadlyPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.InstantPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.DeadlyPoison; } } } - this.player.setSpecOptions(c, options) + this.player.setSpecOptions(c, options); }); - this.sim.encounter.changeEmitter.on((c) => { - const options = this.player.getSpecOptions() - const encounter = this.sim.encounter - if (!options.applyPoisonsManually) { + this.sim.encounter.changeEmitter.on(c => { + const options = this.player.getSpecOptions(); + const encounter = this.sim.encounter; + if (!options.rogueOptions!.applyPoisonsManually) { const mhWeaponSpeed = this.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponSpeed; const ohWeaponSpeed = this.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponSpeed; if (typeof mhWeaponSpeed == 'undefined' || typeof ohWeaponSpeed == 'undefined') { - return + return; } if (encounter.targets.length > 3) { - options.mhImbue = Rogue_Options_PoisonImbue.InstantPoison - options.ohImbue = Rogue_Options_PoisonImbue.InstantPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.InstantPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.InstantPoison; } else { if (mhWeaponSpeed <= ohWeaponSpeed) { - options.mhImbue = Rogue_Options_PoisonImbue.DeadlyPoison - options.ohImbue = Rogue_Options_PoisonImbue.InstantPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.DeadlyPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.InstantPoison; } else { - options.mhImbue = Rogue_Options_PoisonImbue.InstantPoison - options.ohImbue = Rogue_Options_PoisonImbue.DeadlyPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.InstantPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.DeadlyPoison; } } } - this.player.setSpecOptions(c, options) + this.player.setSpecOptions(c, options); }); } } diff --git a/ui/rogue/subtlety/sim.ts b/ui/rogue/subtlety/sim.ts index 171a1f95b1..b6f8544ade 100644 --- a/ui/rogue/subtlety/sim.ts +++ b/ui/rogue/subtlety/sim.ts @@ -1,5 +1,10 @@ +import * as BuffDebuffInputs from '../../core/components/inputs/buffs_debuffs'; +import * as OtherInputs from '../../core/components/other_inputs'; +import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui'; +import { Player } from '../../core/player'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLRotation } from '../../core/proto/apl'; import { - Class, Debuffs, Faction, IndividualBuffs, @@ -11,43 +16,28 @@ import { Spec, Stat, TristateEffect, - WeaponType -} from '../core/proto/common.js'; -import { - APLRotation, -} from '../core/proto/apl.js'; -import { Player } from '../core/player.js'; -import { Stats } from '../core/proto_utils/stats.js'; -import { getSpecIcon } from '../core/proto_utils/utils.js'; -import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; - -import { - Rogue_Options_PoisonImbue, -} from '../core/proto/rogue.js'; - -import * as BuffDebuffInputs from '../core/components/inputs/buffs_debuffs.js'; -import * as OtherInputs from '../core/components/other_inputs.js'; - -import * as RogueInputs from './inputs.js'; -import * as Presets from './presets.js'; + WeaponType, +} from '../../core/proto/common'; +import { RogueOptions_PoisonImbue } from '../../core/proto/rogue'; +import { Stats } from '../../core/proto_utils/stats'; +import * as RogueInputs from './inputs'; +import * as Presets from './presets'; -const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { - cssClass: 'rogue-sim-ui', - cssScheme: 'rogue', +const SPEC_CONFIG = registerSpecConfig(Spec.SpecSubtletyRogue, { + cssClass: 'subtlety-rogue-sim-ui', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.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.', - ], + knownIssues: ['Rotations are not fully optimized, especially for non-standard setups.'], warnings: [ - (simUI: IndividualSimUI) => { + (simUI: IndividualSimUI) => { return { updateOn: simUI.sim.encounter.changeEmitter, getContent: () => { - let hasNoArmor = false + let hasNoArmor = false; for (const target of simUI.sim.encounter.targets) { if (new Stats(target.stats).getStat(Stat.StatArmor) <= 0) { - hasNoArmor = true - break + hasNoArmor = true; + break; } } if (hasNoArmor) { @@ -58,7 +48,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, }; }, - (simUI: IndividualSimUI) => { + (simUI: IndividualSimUI) => { return { updateOn: simUI.player.changeEmitter, getContent: () => { @@ -74,15 +64,17 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, }; }, - (simUI: IndividualSimUI) => { + (simUI: IndividualSimUI) => { return { updateOn: simUI.player.changeEmitter, getContent: () => { if (simUI.player.getTalents().hackAndSlash) { - if (simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType == WeaponType.WeaponTypeSword || + 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) { + simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType == WeaponType.WeaponTypeAxe + ) { return ''; } else { return '"Hack and Slash" talent selected, but swords or axes not equipped.'; @@ -93,15 +85,17 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, }; }, - (simUI: IndividualSimUI) => { + (simUI: IndividualSimUI) => { return { updateOn: simUI.player.changeEmitter, getContent: () => { if (simUI.player.getTalents().closeQuartersCombat) { - if (simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType == WeaponType.WeaponTypeFist || + 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) { + simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType == WeaponType.WeaponTypeDagger + ) { return ''; } else { return '"Close Quarters Combat" talent selected, but fists or daggers not equipped.'; @@ -112,13 +106,15 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, }; }, - (simUI: IndividualSimUI) => { + (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) { + 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.'; @@ -129,21 +125,25 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, }; }, - (simUI: IndividualSimUI) => { + (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) { + const mhImbue = simUI.player.getSpecOptions().rogueOptions!.mhImbue; + const ohImbue = simUI.player.getSpecOptions().rogueOptions!.ohImbue; + if ( + typeof mhWeaponSpeed == 'undefined' || + typeof ohWeaponSpeed == 'undefined' || + !simUI.player.getSpecOptions().rogueOptions!.applyPoisonsManually + ) { return ''; } - if (mhWeaponSpeed < ohWeaponSpeed && ohImbue == Rogue_Options_PoisonImbue.DeadlyPoison) { + if (mhWeaponSpeed < ohWeaponSpeed && ohImbue == RogueOptions_PoisonImbue.DeadlyPoison) { return 'Deadly poison applied to slower (off hand) weapon.'; } - if (ohWeaponSpeed < mhWeaponSpeed && mhImbue == Rogue_Options_PoisonImbue.DeadlyPoison) { + if (ohWeaponSpeed < mhWeaponSpeed && mhImbue == RogueOptions_PoisonImbue.DeadlyPoison) { return 'Deadly poison applied to slower (main hand) weapon.'; } return ''; @@ -165,10 +165,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { Stat.StatArmorPenetration, Stat.StatExpertise, ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - PseudoStat.PseudoStatOffHandDps, - ], + 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. @@ -191,21 +188,24 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { // 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, - }), + 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 talents. @@ -224,8 +224,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { elementalOath: true, sanctifiedRetribution: true, }), - partyBuffs: PartyBuffs.create({ - }), + partyBuffs: PartyBuffs.create({}), individualBuffs: IndividualBuffs.create({ blessingOfKings: true, blessingOfMight: TristateEffect.TristateEffectImproved, @@ -242,24 +241,18 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, playerInputs: { - inputs: [ - RogueInputs.ApplyPoisonsManually - ] + inputs: [RogueInputs.ApplyPoisonsManually], }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - RogueInputs.MainHandImbue, - RogueInputs.OffHandImbue, - ], + playerIconInputs: [RogueInputs.MainHandImbue, RogueInputs.OffHandImbue], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. includeBuffDebuffInputs: [ BuffDebuffInputs.SpellCritBuff, BuffDebuffInputs.SpellCritDebuff, BuffDebuffInputs.SpellHitDebuff, - BuffDebuffInputs.SpellDamageDebuff - ], - excludeBuffDebuffInputs: [ + BuffDebuffInputs.SpellDamageDebuff, ], + excludeBuffDebuffInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { inputs: [ @@ -320,7 +313,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { ], }, - autoRotation: (player: Player): APLRotation => { + autoRotation: (player: Player): APLRotation => { const talentTree = player.getTalentTree(); const numTargets = player.sim.encounter.targets.length; if (numTargets >= 5) { @@ -337,11 +330,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { raidSimPresets: [ { - spec: Spec.SpecRogue, - tooltip: 'Assassination Rogue', - defaultName: 'Assassination', - iconUrl: getSpecIcon(Class.ClassRogue, 0), - + spec: Spec.SpecSubtletyRogue, talents: Presets.AssassinationTalents137.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, @@ -366,89 +355,59 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, { }, }, }, - { - spec: Spec.SpecRogue, - tooltip: 'Combat Rogue', - defaultName: 'Combat', - iconUrl: getSpecIcon(Class.ClassRogue, 1), - - talents: Presets.CombatCQCTalents.data, - specOptions: Presets.DefaultOptions, - consumes: Presets.DefaultConsumes, - defaultFactionRaces: { - [Faction.Unknown]: Race.RaceUnknown, - [Faction.Alliance]: Race.RaceHuman, - [Faction.Horde]: Race.RaceOrc, - }, - defaultGear: { - [Faction.Unknown]: {}, - [Faction.Alliance]: { - 1: Presets.P1_PRESET_COMBAT.gear, - 2: Presets.P2_PRESET_COMBAT.gear, - 3: Presets.P3_PRESET_COMBAT.gear, - 4: Presets.P4_PRESET_COMBAT.gear, - }, - [Faction.Horde]: { - 1: Presets.P1_PRESET_COMBAT.gear, - 2: Presets.P2_PRESET_COMBAT.gear, - 3: Presets.P3_PRESET_COMBAT.gear, - 4: Presets.P4_PRESET_COMBAT.gear, - }, - }, - }, ], }); -export class RogueSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { +export class SubtletyRogueSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { super(parentElem, player, SPEC_CONFIG); - this.player.changeEmitter.on((c) => { - const options = this.player.getSpecOptions() - const encounter = this.sim.encounter - if (!options.applyPoisonsManually) { + this.player.changeEmitter.on(c => { + const options = this.player.getSpecOptions(); + const encounter = this.sim.encounter; + if (!options.rogueOptions!.applyPoisonsManually) { const mhWeaponSpeed = this.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponSpeed; const ohWeaponSpeed = this.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponSpeed; if (typeof mhWeaponSpeed == 'undefined' || typeof ohWeaponSpeed == 'undefined') { - return + return; } if (encounter.targets.length > 3) { - options.mhImbue = Rogue_Options_PoisonImbue.InstantPoison - options.ohImbue = Rogue_Options_PoisonImbue.InstantPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.InstantPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.InstantPoison; } else { if (mhWeaponSpeed <= ohWeaponSpeed) { - options.mhImbue = Rogue_Options_PoisonImbue.DeadlyPoison - options.ohImbue = Rogue_Options_PoisonImbue.InstantPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.DeadlyPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.InstantPoison; } else { - options.mhImbue = Rogue_Options_PoisonImbue.InstantPoison - options.ohImbue = Rogue_Options_PoisonImbue.DeadlyPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.InstantPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.DeadlyPoison; } } } - this.player.setSpecOptions(c, options) + this.player.setSpecOptions(c, options); }); - this.sim.encounter.changeEmitter.on((c) => { - const options = this.player.getSpecOptions() - const encounter = this.sim.encounter - if (!options.applyPoisonsManually) { + this.sim.encounter.changeEmitter.on(c => { + const options = this.player.getSpecOptions(); + const encounter = this.sim.encounter; + if (!options.rogueOptions!.applyPoisonsManually) { const mhWeaponSpeed = this.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponSpeed; const ohWeaponSpeed = this.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponSpeed; if (typeof mhWeaponSpeed == 'undefined' || typeof ohWeaponSpeed == 'undefined') { - return + return; } if (encounter.targets.length > 3) { - options.mhImbue = Rogue_Options_PoisonImbue.InstantPoison - options.ohImbue = Rogue_Options_PoisonImbue.InstantPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.InstantPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.InstantPoison; } else { if (mhWeaponSpeed <= ohWeaponSpeed) { - options.mhImbue = Rogue_Options_PoisonImbue.DeadlyPoison - options.ohImbue = Rogue_Options_PoisonImbue.InstantPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.DeadlyPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.InstantPoison; } else { - options.mhImbue = Rogue_Options_PoisonImbue.InstantPoison - options.ohImbue = Rogue_Options_PoisonImbue.DeadlyPoison + options.rogueOptions!.mhImbue = RogueOptions_PoisonImbue.InstantPoison; + options.rogueOptions!.ohImbue = RogueOptions_PoisonImbue.DeadlyPoison; } } } - this.player.setSpecOptions(c, options) + this.player.setSpecOptions(c, options); }); } } diff --git a/ui/shaman/elemental/sim.ts b/ui/shaman/elemental/sim.ts index cb456b3b3d..93c2cecf4c 100644 --- a/ui/shaman/elemental/sim.ts +++ b/ui/shaman/elemental/sim.ts @@ -3,33 +3,19 @@ import { TotemsSection } from '../../core/components/totem_inputs.js'; import * as Mechanics from '../../core/constants/mechanics.js'; import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui.js'; import { Player } from '../../core/player.js'; -import { - APLRotation, -} from '../../core/proto/apl.js'; -import { - Class, - Debuffs, - Faction, - IndividualBuffs, - PartyBuffs, - Race, - RaidBuffs, - Spec, - Stat, - TristateEffect, -} from '../../core/proto/common.js'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLRotation } from '../../core/proto/apl.js'; +import { Debuffs, Faction, IndividualBuffs, PartyBuffs, Race, RaidBuffs, Spec, Stat, TristateEffect } from '../../core/proto/common.js'; import { Stats } from '../../core/proto_utils/stats.js'; -import { getSpecIcon, specNames } from '../../core/proto_utils/utils.js'; import { TypedEvent } from '../../core/typed_event.js'; import * as ShamanInputs from './inputs.js'; import * as Presets from './presets.js'; const SPEC_CONFIG = registerSpecConfig(Spec.SpecElementalShaman, { cssClass: 'elemental-shaman-sim-ui', - cssScheme: 'shaman', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.Shaman), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], + knownIssues: [], warnings: [ // Warning to use all 4 totems if T6 2pc bonus is active. (simUI: IndividualSimUI) => { @@ -50,14 +36,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecElementalShaman, { ], // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpellPower, - Stat.StatSpellHit, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], + 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. @@ -75,8 +54,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecElementalShaman, { 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); + stats = stats.addStat(Stat.StatSpellCrit, player.getTalents().tidalMastery * 1 * Mechanics.SPELL_CRIT_RATING_PER_CRIT_CHANCE); return { talents: stats, }; @@ -110,8 +88,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecElementalShaman, { demonicPactSp: 500, wrathOfAirTotem: true, }), - partyBuffs: PartyBuffs.create({ - }), + partyBuffs: PartyBuffs.create({}), individualBuffs: IndividualBuffs.create({ blessingOfKings: true, blessingOfWisdom: 2, @@ -127,24 +104,15 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecElementalShaman, { }), }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ShamanInputs.ShamanShieldInput, - ], + playerIconInputs: [ShamanInputs.ShamanShieldInput], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - ], - excludeBuffDebuffInputs: [ - ], + includeBuffDebuffInputs: [], + excludeBuffDebuffInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { - inputs: [ - ShamanInputs.InThunderstormRange, - OtherInputs.TankAssignment, - ], + inputs: [ShamanInputs.InThunderstormRange, OtherInputs.TankAssignment], }, - customSections: [ - TotemsSection, - ], + customSections: [TotemsSection], encounterPicker: { // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. showExecuteProportion: false, @@ -152,23 +120,11 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecElementalShaman, { presets: { // Preset talents that the user can quickly select. - talents: [ - Presets.StandardTalents, - ], + talents: [Presets.StandardTalents], // Preset rotations that the user can quickly select. - rotations: [ - Presets.ROTATION_PRESET_DEFAULT, - Presets.ROTATION_PRESET_ADVANCED, - ], + rotations: [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, - ], + 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 => { @@ -178,10 +134,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecElementalShaman, { raidSimPresets: [ { spec: Spec.SpecElementalShaman, - tooltip: specNames[Spec.SpecElementalShaman], - defaultName: 'Elemental', - iconUrl: getSpecIcon(Class.ClassShaman, 0), - talents: Presets.StandardTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, diff --git a/ui/shaman/enhancement/sim.ts b/ui/shaman/enhancement/sim.ts index f25e7539d7..60fc72233c 100644 --- a/ui/shaman/enhancement/sim.ts +++ b/ui/shaman/enhancement/sim.ts @@ -4,33 +4,19 @@ import * as OtherInputs from '../../core/components/other_inputs.js'; import { TotemsSection } from '../../core/components/totem_inputs.js'; import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui.js'; import { Player } from '../../core/player.js'; -import { - APLRotation, -} from '../../core/proto/apl.js'; -import { - Class, - Faction, - IndividualBuffs, - ItemSlot, - PartyBuffs, - PseudoStat, - Race, - Spec, - Stat, - TristateEffect, -} from '../../core/proto/common.js'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLRotation } from '../../core/proto/apl.js'; +import { Faction, IndividualBuffs, ItemSlot, PartyBuffs, PseudoStat, Race, Spec, Stat, TristateEffect } from '../../core/proto/common.js'; import { ShamanImbue } from '../../core/proto/shaman.js'; import { Stats } from '../../core/proto_utils/stats.js'; -import { getSpecIcon, specNames } from '../../core/proto_utils/utils.js'; import * as ShamanInputs from './inputs.js'; import * as Presets from './presets.js'; const SPEC_CONFIG = registerSpecConfig(Spec.SpecEnhancementShaman, { cssClass: 'enhancement-shaman-sim-ui', - cssScheme: 'shaman', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.Shaman), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], + knownIssues: [], // All stats for which EP should be calculated. epStats: [ @@ -48,10 +34,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecEnhancementShaman, { Stat.StatSpellHit, Stat.StatSpellHaste, ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - PseudoStat.PseudoStatOffHandDps, - ], + 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. @@ -77,24 +60,27 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecEnhancementShaman, { // 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, - }), + 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 talents. @@ -103,8 +89,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecEnhancementShaman, { specOptions: Presets.DefaultOptions, // Default raid/party buffs settings. raidBuffs: Presets.DefaultRaidBuffs, - partyBuffs: PartyBuffs.create({ - }), + partyBuffs: PartyBuffs.create({}), individualBuffs: IndividualBuffs.create({ blessingOfKings: true, blessingOfWisdom: TristateEffect.TristateEffectImproved, @@ -115,34 +100,16 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecEnhancementShaman, { }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ShamanInputs.ShamanShieldInput, - ShamanInputs.ShamanImbueMH, - ShamanInputs.ShamanImbueOH, - ], + playerIconInputs: [ShamanInputs.ShamanShieldInput, ShamanInputs.ShamanImbueMH, ShamanInputs.ShamanImbueOH], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - BuffDebuffInputs.ReplenishmentBuff, - BuffDebuffInputs.MP5Buff, - BuffDebuffInputs.SpellHasteBuff, - BuffDebuffInputs.SpiritBuff, - ], - excludeBuffDebuffInputs: [ - BuffDebuffInputs.BleedDebuff, - ], + includeBuffDebuffInputs: [BuffDebuffInputs.ReplenishmentBuff, BuffDebuffInputs.MP5Buff, BuffDebuffInputs.SpellHasteBuff, BuffDebuffInputs.SpiritBuff], + excludeBuffDebuffInputs: [BuffDebuffInputs.BleedDebuff], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { - inputs: [ - ShamanInputs.SyncTypeInput, - OtherInputs.TankAssignment, - OtherInputs.InFrontOfTarget, - ], + inputs: [ShamanInputs.SyncTypeInput, OtherInputs.TankAssignment, OtherInputs.InFrontOfTarget], }, itemSwapSlots: [ItemSlot.ItemSlotMainHand, ItemSlot.ItemSlotOffHand], - customSections: [ - TotemsSection, - FireElementalSection - ], + customSections: [TotemsSection, FireElementalSection], encounterPicker: { // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. showExecuteProportion: false, @@ -150,16 +117,9 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecEnhancementShaman, { presets: { // Preset talents that the user can quickly select. - talents: [ - Presets.StandardTalents, - Presets.Phase3Talents, - ], + 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, - ], + 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, @@ -174,14 +134,15 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecEnhancementShaman, { }, 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 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"); + console.log('has set'); return Presets.ROTATION_PHASE_3.rotation.rotation!; } else if (options.imbueMh == ShamanImbue.FlametongueWeapon) { return Presets.ROTATION_FT_DEFAULT.rotation.rotation!; @@ -193,10 +154,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecEnhancementShaman, { raidSimPresets: [ { spec: Spec.SpecEnhancementShaman, - tooltip: specNames[Spec.SpecEnhancementShaman], - defaultName: 'Enhancement', - iconUrl: getSpecIcon(Class.ClassShaman, 1), - talents: Presets.StandardTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, diff --git a/ui/shaman/restoration/sim.ts b/ui/shaman/restoration/sim.ts index 6189a8698f..766536f089 100644 --- a/ui/shaman/restoration/sim.ts +++ b/ui/shaman/restoration/sim.ts @@ -3,44 +3,22 @@ import { TotemsSection } from '../../core/components/totem_inputs.js'; import * as Mechanics from '../../core/constants/mechanics.js'; import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui.js'; import { Player } from '../../core/player.js'; -import { - APLRotation, -} from '../../core/proto/apl.js'; -import { - Class, - Debuffs, - Faction, - IndividualBuffs, - PartyBuffs, - Race, - RaidBuffs, - Spec, - Stat, - TristateEffect, -} from '../../core/proto/common.js'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLRotation } from '../../core/proto/apl.js'; +import { Debuffs, Faction, IndividualBuffs, PartyBuffs, Race, RaidBuffs, Spec, Stat, TristateEffect } from '../../core/proto/common.js'; import { Stats } from '../../core/proto_utils/stats.js'; -import { getSpecIcon, specNames } from '../../core/proto_utils/utils.js'; import * as ShamanInputs from './inputs.js'; import * as Presets from './presets.js'; const SPEC_CONFIG = registerSpecConfig(Spec.SpecRestorationShaman, { cssClass: 'restoration-shaman-sim-ui', - cssScheme: 'shaman', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.Shaman), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], - warnings: [ - ], + knownIssues: [], + warnings: [], // All stats for which EP should be calculated. - epStats: [ - Stat.StatIntellect, - Stat.StatSpirit, - Stat.StatSpellPower, - Stat.StatSpellCrit, - Stat.StatSpellHaste, - Stat.StatMP5, - ], + 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. @@ -89,8 +67,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRestorationShaman, { moonkinAura: TristateEffect.TristateEffectImproved, sanctifiedRetribution: true, }), - partyBuffs: PartyBuffs.create({ - }), + partyBuffs: PartyBuffs.create({}), individualBuffs: IndividualBuffs.create({ blessingOfKings: true, blessingOfWisdom: 2, @@ -105,24 +82,15 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRestorationShaman, { }), }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ShamanInputs.ShamanShieldInput, - ], + playerIconInputs: [ShamanInputs.ShamanShieldInput], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - ], - excludeBuffDebuffInputs: [ - ], + includeBuffDebuffInputs: [], + excludeBuffDebuffInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { - inputs: [ - ShamanInputs.TriggerEarthShield, - OtherInputs.TankAssignment - ], + inputs: [ShamanInputs.TriggerEarthShield, OtherInputs.TankAssignment], }, - customSections: [ - TotemsSection, - ], + customSections: [TotemsSection], encounterPicker: { // Whether to include 'Execute Duration (%)' in the 'Encounter' section of the settings tab. showExecuteProportion: false, @@ -130,20 +98,10 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRestorationShaman, { presets: { // Preset talents that the user can quickly select. - talents: [ - Presets.RaidHealingTalents, - Presets.TankHealingTalents, - ], - rotations: [ - ], + 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.P3_PRESET, - Presets.P4_PRESET, - ], + gear: [Presets.PRERAID_PRESET, Presets.P1_PRESET, Presets.P2_PRESET, Presets.P3_PRESET, Presets.P4_PRESET], }, autoRotation: (_player: Player): APLRotation => { @@ -153,10 +111,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRestorationShaman, { raidSimPresets: [ { spec: Spec.SpecRestorationShaman, - tooltip: specNames[Spec.SpecRestorationShaman], - defaultName: 'Restoration', - iconUrl: getSpecIcon(Class.ClassShaman, 2), - talents: Presets.RaidHealingTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, diff --git a/ui/warlock/affliction/sim.ts b/ui/warlock/affliction/sim.ts index 0230cc36b8..69800eb6e4 100644 --- a/ui/warlock/affliction/sim.ts +++ b/ui/warlock/affliction/sim.ts @@ -1,43 +1,22 @@ -import * as BuffDebuffInputs from '../core/components/inputs/buffs_debuffs.js'; -import * as ConsumablesInputs from '../core/components/inputs/consumables.js'; -import * as OtherInputs from '../core/components/other_inputs.js'; -import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; -import { Player } from '../core/player.js'; -import { - APLRotation, -} from '../core/proto/apl.js'; -import { - Class, - Faction, - ItemSlot, - PartyBuffs, - Race, - Spec, - Stat, -} from '../core/proto/common.js'; -import { Stats } from '../core/proto_utils/stats.js'; -import { getSpecIcon } from '../core/proto_utils/utils.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', +import * as BuffDebuffInputs from '../../core/components/inputs/buffs_debuffs'; +import * as OtherInputs from '../../core/components/other_inputs'; +import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui'; +import { Player } from '../../core/player'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLRotation } from '../../core/proto/apl'; +import { Faction, ItemSlot, PartyBuffs, Race, Spec, Stat } from '../../core/proto/common'; +import { Stats } from '../../core/proto_utils/stats'; +import * as WarlockInputs from './inputs'; +import * as Presets from './presets'; + +const SPEC_CONFIG = registerSpecConfig(Spec.SpecAfflictionWarlock, { + cssClass: 'affliction-warlock-sim-ui', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.Warlock), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - "Drain Soul is currently disabled for APL rotations" - ], + 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, - ], + 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. @@ -88,11 +67,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - WarlockInputs.PetInput, - WarlockInputs.ArmorInput, - WarlockInputs.WeaponImbueInput, - ], + playerIconInputs: [WarlockInputs.PetInput, WarlockInputs.ArmorInput, WarlockInputs.WeaponImbueInput], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. includeBuffDebuffInputs: [ @@ -108,19 +83,11 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { BuffDebuffInputs.StrengthAndAgilityBuff, BuffDebuffInputs.StaminaBuff, ], - excludeBuffDebuffInputs: [ - ], - petConsumeInputs: [ - ConsumablesInputs.SpicedMammothTreats, - ], + excludeBuffDebuffInputs: [], + petConsumeInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { - inputs: [ - WarlockInputs.DetonateSeed, - OtherInputs.DistanceFromTarget, - OtherInputs.TankAssignment, - OtherInputs.ChannelClipDelay, - ], + inputs: [WarlockInputs.DetonateSeed, OtherInputs.DistanceFromTarget, OtherInputs.TankAssignment, OtherInputs.ChannelClipDelay], }, itemSwapSlots: [ItemSlot.ItemSlotMainHand, ItemSlot.ItemSlotOffHand, ItemSlot.ItemSlotRanged], encounterPicker: { @@ -130,17 +97,9 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { presets: { // Preset talents that the user can quickly select. - talents: [ - Presets.AfflictionTalents, - Presets.DemonologyTalents, - Presets.DestructionTalents, - ], + talents: [Presets.AfflictionTalents, Presets.DemonologyTalents, Presets.DestructionTalents], // Preset rotations that the user can quickly select. - rotations: [ - Presets.APL_Affliction_Default, - Presets.APL_Demo_Default, - Presets.APL_Destro_Default, - ], + rotations: [Presets.APL_Affliction_Default, Presets.APL_Demo_Default, Presets.APL_Destro_Default], // Preset gear configurations that the user can quickly select. gear: [ @@ -163,7 +122,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { ], }, - autoRotation: (player: Player): APLRotation => { + autoRotation: (player: Player): APLRotation => { const talentTree = player.getTalentTree(); if (talentTree == 0) { return Presets.APL_Affliction_Default.rotation.rotation!; @@ -176,11 +135,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { raidSimPresets: [ { - spec: Spec.SpecWarlock, - tooltip: 'Affliction Warlock', - defaultName: 'Affliction', - iconUrl: getSpecIcon(Class.ClassWarlock, 0), - + spec: Spec.SpecAfflictionWarlock, talents: Presets.AfflictionTalents.data, specOptions: Presets.AfflictionOptions, consumes: Presets.DefaultConsumes, @@ -206,73 +161,11 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { }, otherDefaults: Presets.OtherDefaults, }, - { - spec: Spec.SpecWarlock, - tooltip: 'Demonology Warlock', - defaultName: 'Demonology', - iconUrl: getSpecIcon(Class.ClassWarlock, 1), - - talents: Presets.DemonologyTalents.data, - specOptions: Presets.DemonologyOptions, - consumes: Presets.DefaultConsumes, - defaultFactionRaces: { - [Faction.Unknown]: Race.RaceUnknown, - [Faction.Alliance]: Race.RaceHuman, - [Faction.Horde]: Race.RaceOrc, - }, - defaultGear: { - [Faction.Unknown]: {}, - [Faction.Alliance]: { - 1: Presets.P1_DEMODESTRO_PRESET.gear, - 2: Presets.P2_DEMODESTRO_PRESET.gear, - 3: Presets.P3_DEMO_ALLIANCE_PRESET.gear, - 4: Presets.P4_DEMO_PRESET.gear, - }, - [Faction.Horde]: { - 1: Presets.P1_DEMODESTRO_PRESET.gear, - 2: Presets.P2_DEMODESTRO_PRESET.gear, - 3: Presets.P3_DEMO_HORDE_PRESET.gear, - 4: Presets.P4_DEMO_PRESET.gear, - }, - }, - otherDefaults: Presets.OtherDefaults, - }, - { - spec: Spec.SpecWarlock, - tooltip: 'Destruction Warlock', - defaultName: 'Destruction', - iconUrl: getSpecIcon(Class.ClassWarlock, 2), - - talents: Presets.DestructionTalents.data, - specOptions: Presets.DestructionOptions, - consumes: Presets.DefaultConsumes, - defaultFactionRaces: { - [Faction.Unknown]: Race.RaceUnknown, - [Faction.Alliance]: Race.RaceHuman, - [Faction.Horde]: Race.RaceOrc, - }, - defaultGear: { - [Faction.Unknown]: {}, - [Faction.Alliance]: { - 1: Presets.P1_DEMODESTRO_PRESET.gear, - 2: Presets.P2_DEMODESTRO_PRESET.gear, - 3: Presets.P3_DESTRO_ALLIANCE_PRESET.gear, - 4: Presets.P4_DESTRO_PRESET.gear, - }, - [Faction.Horde]: { - 1: Presets.P1_DEMODESTRO_PRESET.gear, - 2: Presets.P2_DEMODESTRO_PRESET.gear, - 3: Presets.P3_DESTRO_HORDE_PRESET.gear, - 4: Presets.P4_DESTRO_PRESET.gear, - }, - }, - otherDefaults: Presets.OtherDefaults, - }, ], }); -export class WarlockSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { +export class AfflictionWarlockSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/warlock/demonology/sim.ts b/ui/warlock/demonology/sim.ts index 0230cc36b8..220c9ab8cb 100644 --- a/ui/warlock/demonology/sim.ts +++ b/ui/warlock/demonology/sim.ts @@ -1,43 +1,22 @@ -import * as BuffDebuffInputs from '../core/components/inputs/buffs_debuffs.js'; -import * as ConsumablesInputs from '../core/components/inputs/consumables.js'; -import * as OtherInputs from '../core/components/other_inputs.js'; -import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; -import { Player } from '../core/player.js'; -import { - APLRotation, -} from '../core/proto/apl.js'; -import { - Class, - Faction, - ItemSlot, - PartyBuffs, - Race, - Spec, - Stat, -} from '../core/proto/common.js'; -import { Stats } from '../core/proto_utils/stats.js'; -import { getSpecIcon } from '../core/proto_utils/utils.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', +import * as BuffDebuffInputs from '../../core/components/inputs/buffs_debuffs'; +import * as OtherInputs from '../../core/components/other_inputs'; +import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui'; +import { Player } from '../../core/player'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLRotation } from '../../core/proto/apl'; +import { Faction, ItemSlot, PartyBuffs, Race, Spec, Stat } from '../../core/proto/common'; +import { Stats } from '../../core/proto_utils/stats'; +import * as WarlockInputs from './inputs'; +import * as Presets from './presets'; + +const SPEC_CONFIG = registerSpecConfig(Spec.SpecDemonologyWarlock, { + cssClass: 'demonology-warlock-sim-ui', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.Warlock), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - "Drain Soul is currently disabled for APL rotations" - ], + 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, - ], + 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. @@ -88,11 +67,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - WarlockInputs.PetInput, - WarlockInputs.ArmorInput, - WarlockInputs.WeaponImbueInput, - ], + playerIconInputs: [WarlockInputs.PetInput, WarlockInputs.ArmorInput, WarlockInputs.WeaponImbueInput], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. includeBuffDebuffInputs: [ @@ -108,19 +83,11 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { BuffDebuffInputs.StrengthAndAgilityBuff, BuffDebuffInputs.StaminaBuff, ], - excludeBuffDebuffInputs: [ - ], - petConsumeInputs: [ - ConsumablesInputs.SpicedMammothTreats, - ], + excludeBuffDebuffInputs: [], + petConsumeInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { - inputs: [ - WarlockInputs.DetonateSeed, - OtherInputs.DistanceFromTarget, - OtherInputs.TankAssignment, - OtherInputs.ChannelClipDelay, - ], + inputs: [WarlockInputs.DetonateSeed, OtherInputs.DistanceFromTarget, OtherInputs.TankAssignment, OtherInputs.ChannelClipDelay], }, itemSwapSlots: [ItemSlot.ItemSlotMainHand, ItemSlot.ItemSlotOffHand, ItemSlot.ItemSlotRanged], encounterPicker: { @@ -130,17 +97,9 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { presets: { // Preset talents that the user can quickly select. - talents: [ - Presets.AfflictionTalents, - Presets.DemonologyTalents, - Presets.DestructionTalents, - ], + talents: [Presets.AfflictionTalents, Presets.DemonologyTalents, Presets.DestructionTalents], // Preset rotations that the user can quickly select. - rotations: [ - Presets.APL_Affliction_Default, - Presets.APL_Demo_Default, - Presets.APL_Destro_Default, - ], + rotations: [Presets.APL_Affliction_Default, Presets.APL_Demo_Default, Presets.APL_Destro_Default], // Preset gear configurations that the user can quickly select. gear: [ @@ -163,7 +122,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { ], }, - autoRotation: (player: Player): APLRotation => { + autoRotation: (player: Player): APLRotation => { const talentTree = player.getTalentTree(); if (talentTree == 0) { return Presets.APL_Affliction_Default.rotation.rotation!; @@ -176,42 +135,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { raidSimPresets: [ { - spec: Spec.SpecWarlock, - tooltip: 'Affliction Warlock', - defaultName: 'Affliction', - iconUrl: getSpecIcon(Class.ClassWarlock, 0), - - talents: Presets.AfflictionTalents.data, - specOptions: Presets.AfflictionOptions, - consumes: Presets.DefaultConsumes, - defaultFactionRaces: { - [Faction.Unknown]: Race.RaceUnknown, - [Faction.Alliance]: Race.RaceHuman, - [Faction.Horde]: Race.RaceOrc, - }, - defaultGear: { - [Faction.Unknown]: {}, - [Faction.Alliance]: { - 1: Presets.P1_AFFLICTION_PRESET.gear, - 2: Presets.P2_AFFLICTION_PRESET.gear, - 3: Presets.P3_AFFLICTION_ALLIANCE_PRESET.gear, - 4: Presets.P4_AFFLICTION_PRESET.gear, - }, - [Faction.Horde]: { - 1: Presets.P1_AFFLICTION_PRESET.gear, - 2: Presets.P2_AFFLICTION_PRESET.gear, - 3: Presets.P3_AFFLICTION_HORDE_PRESET.gear, - 4: Presets.P4_AFFLICTION_PRESET.gear, - }, - }, - otherDefaults: Presets.OtherDefaults, - }, - { - spec: Spec.SpecWarlock, - tooltip: 'Demonology Warlock', - defaultName: 'Demonology', - iconUrl: getSpecIcon(Class.ClassWarlock, 1), - + spec: Spec.SpecDemonologyWarlock, talents: Presets.DemonologyTalents.data, specOptions: Presets.DemonologyOptions, consumes: Presets.DefaultConsumes, @@ -237,42 +161,11 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { }, otherDefaults: Presets.OtherDefaults, }, - { - spec: Spec.SpecWarlock, - tooltip: 'Destruction Warlock', - defaultName: 'Destruction', - iconUrl: getSpecIcon(Class.ClassWarlock, 2), - - talents: Presets.DestructionTalents.data, - specOptions: Presets.DestructionOptions, - consumes: Presets.DefaultConsumes, - defaultFactionRaces: { - [Faction.Unknown]: Race.RaceUnknown, - [Faction.Alliance]: Race.RaceHuman, - [Faction.Horde]: Race.RaceOrc, - }, - defaultGear: { - [Faction.Unknown]: {}, - [Faction.Alliance]: { - 1: Presets.P1_DEMODESTRO_PRESET.gear, - 2: Presets.P2_DEMODESTRO_PRESET.gear, - 3: Presets.P3_DESTRO_ALLIANCE_PRESET.gear, - 4: Presets.P4_DESTRO_PRESET.gear, - }, - [Faction.Horde]: { - 1: Presets.P1_DEMODESTRO_PRESET.gear, - 2: Presets.P2_DEMODESTRO_PRESET.gear, - 3: Presets.P3_DESTRO_HORDE_PRESET.gear, - 4: Presets.P4_DESTRO_PRESET.gear, - }, - }, - otherDefaults: Presets.OtherDefaults, - }, ], }); -export class WarlockSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { +export class DemonologyWarlockSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/warlock/destruction/sim.ts b/ui/warlock/destruction/sim.ts index 0230cc36b8..a30a028ce3 100644 --- a/ui/warlock/destruction/sim.ts +++ b/ui/warlock/destruction/sim.ts @@ -1,43 +1,22 @@ -import * as BuffDebuffInputs from '../core/components/inputs/buffs_debuffs.js'; -import * as ConsumablesInputs from '../core/components/inputs/consumables.js'; -import * as OtherInputs from '../core/components/other_inputs.js'; -import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; -import { Player } from '../core/player.js'; -import { - APLRotation, -} from '../core/proto/apl.js'; -import { - Class, - Faction, - ItemSlot, - PartyBuffs, - Race, - Spec, - Stat, -} from '../core/proto/common.js'; -import { Stats } from '../core/proto_utils/stats.js'; -import { getSpecIcon } from '../core/proto_utils/utils.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', +import * as BuffDebuffInputs from '../../core/components/inputs/buffs_debuffs'; +import * as OtherInputs from '../../core/components/other_inputs'; +import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui'; +import { Player } from '../../core/player'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLRotation } from '../../core/proto/apl'; +import { Faction, ItemSlot, PartyBuffs, Race, Spec, Stat } from '../../core/proto/common'; +import { Stats } from '../../core/proto_utils/stats'; +import * as WarlockInputs from './inputs'; +import * as Presets from './presets'; + +const SPEC_CONFIG = registerSpecConfig(Spec.SpecDestructionWarlock, { + cssClass: 'destruction-warlock-sim-ui', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.Warlock), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - "Drain Soul is currently disabled for APL rotations" - ], + 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, - ], + 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. @@ -88,11 +67,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - WarlockInputs.PetInput, - WarlockInputs.ArmorInput, - WarlockInputs.WeaponImbueInput, - ], + playerIconInputs: [WarlockInputs.PetInput, WarlockInputs.ArmorInput, WarlockInputs.WeaponImbueInput], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. includeBuffDebuffInputs: [ @@ -108,19 +83,11 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { BuffDebuffInputs.StrengthAndAgilityBuff, BuffDebuffInputs.StaminaBuff, ], - excludeBuffDebuffInputs: [ - ], - petConsumeInputs: [ - ConsumablesInputs.SpicedMammothTreats, - ], + excludeBuffDebuffInputs: [], + petConsumeInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { - inputs: [ - WarlockInputs.DetonateSeed, - OtherInputs.DistanceFromTarget, - OtherInputs.TankAssignment, - OtherInputs.ChannelClipDelay, - ], + inputs: [WarlockInputs.DetonateSeed, OtherInputs.DistanceFromTarget, OtherInputs.TankAssignment, OtherInputs.ChannelClipDelay], }, itemSwapSlots: [ItemSlot.ItemSlotMainHand, ItemSlot.ItemSlotOffHand, ItemSlot.ItemSlotRanged], encounterPicker: { @@ -130,17 +97,9 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { presets: { // Preset talents that the user can quickly select. - talents: [ - Presets.AfflictionTalents, - Presets.DemonologyTalents, - Presets.DestructionTalents, - ], + talents: [Presets.AfflictionTalents, Presets.DemonologyTalents, Presets.DestructionTalents], // Preset rotations that the user can quickly select. - rotations: [ - Presets.APL_Affliction_Default, - Presets.APL_Demo_Default, - Presets.APL_Destro_Default, - ], + rotations: [Presets.APL_Affliction_Default, Presets.APL_Demo_Default, Presets.APL_Destro_Default], // Preset gear configurations that the user can quickly select. gear: [ @@ -163,7 +122,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { ], }, - autoRotation: (player: Player): APLRotation => { + autoRotation: (player: Player): APLRotation => { const talentTree = player.getTalentTree(); if (talentTree == 0) { return Presets.APL_Affliction_Default.rotation.rotation!; @@ -176,73 +135,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { raidSimPresets: [ { - spec: Spec.SpecWarlock, - tooltip: 'Affliction Warlock', - defaultName: 'Affliction', - iconUrl: getSpecIcon(Class.ClassWarlock, 0), - - talents: Presets.AfflictionTalents.data, - specOptions: Presets.AfflictionOptions, - consumes: Presets.DefaultConsumes, - defaultFactionRaces: { - [Faction.Unknown]: Race.RaceUnknown, - [Faction.Alliance]: Race.RaceHuman, - [Faction.Horde]: Race.RaceOrc, - }, - defaultGear: { - [Faction.Unknown]: {}, - [Faction.Alliance]: { - 1: Presets.P1_AFFLICTION_PRESET.gear, - 2: Presets.P2_AFFLICTION_PRESET.gear, - 3: Presets.P3_AFFLICTION_ALLIANCE_PRESET.gear, - 4: Presets.P4_AFFLICTION_PRESET.gear, - }, - [Faction.Horde]: { - 1: Presets.P1_AFFLICTION_PRESET.gear, - 2: Presets.P2_AFFLICTION_PRESET.gear, - 3: Presets.P3_AFFLICTION_HORDE_PRESET.gear, - 4: Presets.P4_AFFLICTION_PRESET.gear, - }, - }, - otherDefaults: Presets.OtherDefaults, - }, - { - spec: Spec.SpecWarlock, - tooltip: 'Demonology Warlock', - defaultName: 'Demonology', - iconUrl: getSpecIcon(Class.ClassWarlock, 1), - - talents: Presets.DemonologyTalents.data, - specOptions: Presets.DemonologyOptions, - consumes: Presets.DefaultConsumes, - defaultFactionRaces: { - [Faction.Unknown]: Race.RaceUnknown, - [Faction.Alliance]: Race.RaceHuman, - [Faction.Horde]: Race.RaceOrc, - }, - defaultGear: { - [Faction.Unknown]: {}, - [Faction.Alliance]: { - 1: Presets.P1_DEMODESTRO_PRESET.gear, - 2: Presets.P2_DEMODESTRO_PRESET.gear, - 3: Presets.P3_DEMO_ALLIANCE_PRESET.gear, - 4: Presets.P4_DEMO_PRESET.gear, - }, - [Faction.Horde]: { - 1: Presets.P1_DEMODESTRO_PRESET.gear, - 2: Presets.P2_DEMODESTRO_PRESET.gear, - 3: Presets.P3_DEMO_HORDE_PRESET.gear, - 4: Presets.P4_DEMO_PRESET.gear, - }, - }, - otherDefaults: Presets.OtherDefaults, - }, - { - spec: Spec.SpecWarlock, - tooltip: 'Destruction Warlock', - defaultName: 'Destruction', - iconUrl: getSpecIcon(Class.ClassWarlock, 2), - + spec: Spec.SpecDestructionWarlock, talents: Presets.DestructionTalents.data, specOptions: Presets.DestructionOptions, consumes: Presets.DefaultConsumes, @@ -271,8 +164,8 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, { ], }); -export class WarlockSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { +export class DestructionWarlockSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { super(parentElem, player, SPEC_CONFIG); } } diff --git a/ui/warrior/arms/sim.ts b/ui/warrior/arms/sim.ts index f005c8d56f..88d790cfa9 100644 --- a/ui/warrior/arms/sim.ts +++ b/ui/warrior/arms/sim.ts @@ -1,41 +1,20 @@ -import { - Class, - Debuffs, - Faction, - GemColor, - IndividualBuffs, - ItemSlot, - PartyBuffs, - Race, - RaidBuffs, - Spec, - Stat, PseudoStat, - Profession, - 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 { getSpecIcon } from '../core/proto_utils/utils.js'; -import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; -import { Gear } from '../core/proto_utils/gear.js'; -import { PhysicalDPSGemOptimizer } from '../core/components/suggest_gems_action.js'; - -import * as BuffDebuffInputs from '../core/components/inputs/buffs_debuffs.js'; -import * as OtherInputs from '../core/components/other_inputs.js'; -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', +import * as BuffDebuffInputs from '../../core/components/inputs/buffs_debuffs'; +import * as OtherInputs from '../../core/components/other_inputs'; +import * as Mechanics from '../../core/constants/mechanics'; +import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui'; +import { Player } from '../../core/player'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLRotation } from '../../core/proto/apl'; +import { Debuffs, Faction, IndividualBuffs, PartyBuffs, PseudoStat, Race, RaidBuffs, Spec, Stat, TristateEffect } from '../../core/proto/common'; +import { Stats } from '../../core/proto_utils/stats'; +import * as WarriorInputs from './inputs'; +import * as Presets from './presets'; + +const SPEC_CONFIG = registerSpecConfig(Spec.SpecArmsWarrior, { + cssClass: 'arms-warrior-sim-ui', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.Warrior), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], + knownIssues: [], // All stats for which EP should be calculated. epStats: [ @@ -49,10 +28,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, { Stat.StatArmorPenetration, Stat.StatArmor, ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - PseudoStat.PseudoStatOffHandDps, - ], + 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. @@ -69,7 +45,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, { Stat.StatArmorPenetration, Stat.StatArmor, ], - modifyDisplayStats: (player: Player) => { + modifyDisplayStats: (player: Player) => { let stats = new Stats(); if (!player.getInFrontOfTarget()) { // When behind target, dodge is the only outcome affected by Expertise. @@ -84,20 +60,23 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, { // 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, - }), + 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 talents. @@ -136,19 +115,14 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, { }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - WarriorInputs.ShoutPicker, - WarriorInputs.Recklessness, - WarriorInputs.ShatteringThrow, - ], + playerIconInputs: [WarriorInputs.ShoutPicker, WarriorInputs.Recklessness, WarriorInputs.ShatteringThrow], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. includeBuffDebuffInputs: [ // just for Bryntroll BuffDebuffInputs.SpellDamageDebuff, BuffDebuffInputs.SpellHitDebuff, ], - excludeBuffDebuffInputs: [ - ], + excludeBuffDebuffInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { inputs: [ @@ -166,17 +140,9 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, { presets: { // Preset talents that the user can quickly select. - talents: [ - Presets.ArmsTalents, - Presets.FuryTalents, - ], + 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, - ], + 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, @@ -198,7 +164,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, { ], }, - autoRotation: (player: Player): APLRotation => { + autoRotation: (player: Player): APLRotation => { const talentTree = player.getTalentTree(); if (talentTree == 0) { return Presets.ROTATION_ARMS_SUNDER.rotation.rotation!; @@ -209,11 +175,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, { raidSimPresets: [ { - spec: Spec.SpecWarrior, - tooltip: 'Arms Warrior', - defaultName: 'Arms', - iconUrl: getSpecIcon(Class.ClassWarrior, 0), - + spec: Spec.SpecArmsWarrior, talents: Presets.ArmsTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, @@ -238,68 +200,11 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, { }, }, }, - { - spec: Spec.SpecWarrior, - tooltip: 'Fury Warrior', - defaultName: 'Fury', - iconUrl: getSpecIcon(Class.ClassWarrior, 1), - - talents: Presets.FuryTalents.data, - specOptions: Presets.DefaultOptions, - consumes: Presets.DefaultConsumes, - defaultFactionRaces: { - [Faction.Unknown]: Race.RaceUnknown, - [Faction.Alliance]: Race.RaceHuman, - [Faction.Horde]: Race.RaceOrc, - }, - defaultGear: { - [Faction.Unknown]: {}, - [Faction.Alliance]: { - 1: Presets.P1_FURY_PRESET.gear, - 2: Presets.P2_FURY_PRESET.gear, - 3: Presets.P3_FURY_PRESET_ALLIANCE.gear, - 4: Presets.P4_FURY_PRESET_ALLIANCE.gear, - }, - [Faction.Horde]: { - 1: Presets.P1_FURY_PRESET.gear, - 2: Presets.P2_FURY_PRESET.gear, - 3: Presets.P3_FURY_PRESET_HORDE.gear, - 4: Presets.P4_FURY_PRESET_HORDE.gear, - }, - }, - }, ], }); -export class WarriorSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { +export class ArmsWarriorSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { super(parentElem, player, SPEC_CONFIG); - const gemOptimizer = new WarriorGemOptimizer(this); - } -} - -class WarriorGemOptimizer extends PhysicalDPSGemOptimizer { - readonly player: Player; - - constructor(simUI: IndividualSimUI) { - super(simUI, true, true, false, true); - this.player = simUI.player; - } - - updateGemPriority(ungemmedGear: Gear, passiveStats: Stats) { - this.useExpGems = !this.player.getSpecOptions().disableExpertiseGemming; - super.updateGemPriority(ungemmedGear, passiveStats); - } - - calcExpTarget(): number { - let expTarget = super.calcExpTarget(); - const weaponMastery = this.player.getTalents().weaponMastery; - const hasWeaponMasteryTalent = !!weaponMastery; - - if (hasWeaponMasteryTalent) { - expTarget -= weaponMastery * 4 * Mechanics.EXPERTISE_PER_QUARTER_PERCENT_REDUCTION; - } - - return expTarget; } } diff --git a/ui/warrior/fury/sim.ts b/ui/warrior/fury/sim.ts index f005c8d56f..687c464f97 100644 --- a/ui/warrior/fury/sim.ts +++ b/ui/warrior/fury/sim.ts @@ -1,41 +1,20 @@ -import { - Class, - Debuffs, - Faction, - GemColor, - IndividualBuffs, - ItemSlot, - PartyBuffs, - Race, - RaidBuffs, - Spec, - Stat, PseudoStat, - Profession, - 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 { getSpecIcon } from '../core/proto_utils/utils.js'; -import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; -import { Gear } from '../core/proto_utils/gear.js'; -import { PhysicalDPSGemOptimizer } from '../core/components/suggest_gems_action.js'; - -import * as BuffDebuffInputs from '../core/components/inputs/buffs_debuffs.js'; -import * as OtherInputs from '../core/components/other_inputs.js'; -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', +import * as BuffDebuffInputs from '../../core/components/inputs/buffs_debuffs'; +import * as OtherInputs from '../../core/components/other_inputs'; +import * as Mechanics from '../../core/constants/mechanics'; +import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui'; +import { Player } from '../../core/player'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLRotation } from '../../core/proto/apl'; +import { Debuffs, Faction, IndividualBuffs, PartyBuffs, PseudoStat, Race, RaidBuffs, Spec, Stat, TristateEffect } from '../../core/proto/common'; +import { Stats } from '../../core/proto_utils/stats'; +import * as WarriorInputs from './inputs'; +import * as Presets from './presets'; + +const SPEC_CONFIG = registerSpecConfig(Spec.SpecFuryWarrior, { + cssClass: 'fury-warrior-sim-ui', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.Warrior), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], + knownIssues: [], // All stats for which EP should be calculated. epStats: [ @@ -49,10 +28,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, { Stat.StatArmorPenetration, Stat.StatArmor, ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - PseudoStat.PseudoStatOffHandDps, - ], + 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. @@ -69,7 +45,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, { Stat.StatArmorPenetration, Stat.StatArmor, ], - modifyDisplayStats: (player: Player) => { + modifyDisplayStats: (player: Player) => { let stats = new Stats(); if (!player.getInFrontOfTarget()) { // When behind target, dodge is the only outcome affected by Expertise. @@ -84,20 +60,23 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, { // 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, - }), + 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 talents. @@ -136,19 +115,14 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, { }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - WarriorInputs.ShoutPicker, - WarriorInputs.Recklessness, - WarriorInputs.ShatteringThrow, - ], + playerIconInputs: [WarriorInputs.ShoutPicker, WarriorInputs.Recklessness, WarriorInputs.ShatteringThrow], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. includeBuffDebuffInputs: [ // just for Bryntroll BuffDebuffInputs.SpellDamageDebuff, BuffDebuffInputs.SpellHitDebuff, ], - excludeBuffDebuffInputs: [ - ], + excludeBuffDebuffInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { inputs: [ @@ -166,17 +140,9 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, { presets: { // Preset talents that the user can quickly select. - talents: [ - Presets.ArmsTalents, - Presets.FuryTalents, - ], + 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, - ], + 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, @@ -198,7 +164,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, { ], }, - autoRotation: (player: Player): APLRotation => { + autoRotation: (player: Player): APLRotation => { const talentTree = player.getTalentTree(); if (talentTree == 0) { return Presets.ROTATION_ARMS_SUNDER.rotation.rotation!; @@ -209,41 +175,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, { raidSimPresets: [ { - spec: Spec.SpecWarrior, - tooltip: 'Arms Warrior', - defaultName: 'Arms', - iconUrl: getSpecIcon(Class.ClassWarrior, 0), - - talents: Presets.ArmsTalents.data, - specOptions: Presets.DefaultOptions, - consumes: Presets.DefaultConsumes, - defaultFactionRaces: { - [Faction.Unknown]: Race.RaceUnknown, - [Faction.Alliance]: Race.RaceHuman, - [Faction.Horde]: Race.RaceOrc, - }, - defaultGear: { - [Faction.Unknown]: {}, - [Faction.Alliance]: { - 1: Presets.P1_ARMS_PRESET.gear, - 2: Presets.P2_ARMS_PRESET.gear, - 3: Presets.P3_ARMS_4P_PRESET_ALLIANCE.gear, - 4: Presets.P4_ARMS_PRESET_ALLIANCE.gear, - }, - [Faction.Horde]: { - 1: Presets.P1_ARMS_PRESET.gear, - 2: Presets.P2_ARMS_PRESET.gear, - 3: Presets.P3_ARMS_4P_PRESET_HORDE.gear, - 4: Presets.P4_ARMS_PRESET_HORDE.gear, - }, - }, - }, - { - spec: Spec.SpecWarrior, - tooltip: 'Fury Warrior', - defaultName: 'Fury', - iconUrl: getSpecIcon(Class.ClassWarrior, 1), - + spec: Spec.SpecFuryWarrior, talents: Presets.FuryTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes, @@ -271,35 +203,8 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, { ], }); -export class WarriorSimUI extends IndividualSimUI { - constructor(parentElem: HTMLElement, player: Player) { +export class FuryWarriorSimUI extends IndividualSimUI { + constructor(parentElem: HTMLElement, player: Player) { super(parentElem, player, SPEC_CONFIG); - const gemOptimizer = new WarriorGemOptimizer(this); - } -} - -class WarriorGemOptimizer extends PhysicalDPSGemOptimizer { - readonly player: Player; - - constructor(simUI: IndividualSimUI) { - super(simUI, true, true, false, true); - this.player = simUI.player; - } - - updateGemPriority(ungemmedGear: Gear, passiveStats: Stats) { - this.useExpGems = !this.player.getSpecOptions().disableExpertiseGemming; - super.updateGemPriority(ungemmedGear, passiveStats); - } - - calcExpTarget(): number { - let expTarget = super.calcExpTarget(); - const weaponMastery = this.player.getTalents().weaponMastery; - const hasWeaponMasteryTalent = !!weaponMastery; - - if (hasWeaponMasteryTalent) { - expTarget -= weaponMastery * 4 * Mechanics.EXPERTISE_PER_QUARTER_PERCENT_REDUCTION; - } - - return expTarget; } } diff --git a/ui/warrior/protection/sim.ts b/ui/warrior/protection/sim.ts index 810fc64f3f..ca3a6421f0 100644 --- a/ui/warrior/protection/sim.ts +++ b/ui/warrior/protection/sim.ts @@ -2,39 +2,20 @@ import * as BuffDebuffInputs from '../../core/components/inputs/buffs_debuffs.js import * as OtherInputs from '../../core/components/other_inputs.js'; import { IndividualSimUI, registerSpecConfig } from '../../core/individual_sim_ui.js'; import { Player } from '../../core/player.js'; -import { - APLAction, - APLListItem, - APLPrepullAction, - APLRotation, -} from '../../core/proto/apl.js'; -import { - Class, - Cooldowns, - Debuffs, - Faction, - IndividualBuffs, - PartyBuffs, - PseudoStat, - Race, - RaidBuffs, - Spec, - Stat, - TristateEffect -} from '../../core/proto/common.js'; +import { PlayerClasses } from '../../core/player_classes'; +import { APLAction, APLListItem, APLPrepullAction, APLRotation } from '../../core/proto/apl.js'; +import { Cooldowns, Debuffs, Faction, IndividualBuffs, PartyBuffs, PseudoStat, Race, RaidBuffs, Spec, Stat, TristateEffect } from '../../core/proto/common.js'; import { ProtectionWarrior_Rotation as ProtectionWarriorRotation } from '../../core/proto/warrior.js'; import * as AplUtils from '../../core/proto_utils/apl_utils.js'; import { Stats } from '../../core/proto_utils/stats.js'; -import { getSpecIcon } from '../../core/proto_utils/utils.js'; import * as ProtectionWarriorInputs from './inputs.js'; import * as Presets from './presets.js'; const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionWarrior, { cssClass: 'protection-warrior-sim-ui', - cssScheme: 'warrior', + cssScheme: PlayerClasses.getCssClass(PlayerClasses.Warrior), // List any known bugs / issues here and they'll be shown on the site. - knownIssues: [ - ], + knownIssues: [], // All stats for which EP should be calculated. epStats: [ @@ -59,9 +40,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionWarrior, { Stat.StatShadowResistance, Stat.StatFrostResistance, ], - epPseudoStats: [ - PseudoStat.PseudoStatMainHandDps, - ], + 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. @@ -93,26 +72,29 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionWarrior, { // 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, - }), + 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.32, + [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 talents. @@ -136,8 +118,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionWarrior, { thorns: TristateEffect.TristateEffectImproved, shadowProtection: true, }), - partyBuffs: PartyBuffs.create({ - }), + partyBuffs: PartyBuffs.create({}), individualBuffs: IndividualBuffs.create({ blessingOfKings: true, blessingOfMight: TristateEffect.TristateEffectImproved, @@ -157,16 +138,10 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionWarrior, { }, // IconInputs to include in the 'Player' section on the settings tab. - playerIconInputs: [ - ProtectionWarriorInputs.ShoutPicker, - ProtectionWarriorInputs.ShatteringThrow, - ], + playerIconInputs: [ProtectionWarriorInputs.ShoutPicker, ProtectionWarriorInputs.ShatteringThrow], // Buff and Debuff inputs to include/exclude, overriding the EP-based defaults. - includeBuffDebuffInputs: [ - BuffDebuffInputs.HealthBuff, - ], - excludeBuffDebuffInputs: [ - ], + includeBuffDebuffInputs: [BuffDebuffInputs.HealthBuff], + excludeBuffDebuffInputs: [], // Inputs to include in the 'Other' section on the settings tab. otherInputs: { inputs: [ @@ -188,15 +163,9 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionWarrior, { presets: { // Preset talents that the user can quickly select. - talents: [ - Presets.StandardTalents, - Presets.UATalents, - ], + talents: [Presets.StandardTalents, Presets.UATalents], // Preset rotations that the user can quickly select. - rotations: [ - Presets.ROTATION_DEFAULT, - Presets.ROTATION_PRESET_SIMPLE, - ], + rotations: [Presets.ROTATION_DEFAULT, Presets.ROTATION_PRESET_SIMPLE], // Preset gear configurations that the user can quickly select. gear: [ Presets.PRERAID_BALANCED_PRESET, @@ -217,41 +186,39 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionWarrior, { 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 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 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); - 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, - })) + priorityList: actions.map(action => + APLListItem.create({ + action: action, + }), + ), }); }, raidSimPresets: [ { spec: Spec.SpecProtectionWarrior, - tooltip: 'Protection Warrior', - defaultName: 'Protection', - iconUrl: getSpecIcon(Class.ClassWarrior, 2), - talents: Presets.StandardTalents.data, specOptions: Presets.DefaultOptions, consumes: Presets.DefaultConsumes,