diff --git a/proto/mage.proto b/proto/mage.proto index 74a5c86795..752c8638b1 100644 --- a/proto/mage.proto +++ b/proto/mage.proto @@ -155,8 +155,8 @@ message Mage { Type type = 1; // Arcane Options. - int32 extra_blasts_during_first_ap = 10; - int32 missile_barrage_below_arcane_blast_stacks = 14; + int32 extra_blasts_during_first_ap = 10 [deprecated = true]; + int32 missile_barrage_below_arcane_blast_stacks = 14 [deprecated = true]; double missile_barrage_below_mana_percent = 15; double blast_without_missile_barrage_above_mana_percent = 16; double only_3_arcane_blast_stacks_below_mana_percent = 17; @@ -182,7 +182,7 @@ message Mage { Flamestrike = 1; Blizzard = 2; } - AoeRotation aoe = 7; + AoeRotation aoe = 7 [deprecated = true]; } Rotation rotation = 1; diff --git a/ui/core/launched_sims.ts b/ui/core/launched_sims.ts index d66223bb18..f07b317afc 100644 --- a/ui/core/launched_sims.ts +++ b/ui/core/launched_sims.ts @@ -48,7 +48,7 @@ export const aplLaunchStatuses: Record = { [Spec.SpecEnhancementShaman]: LaunchStatus.Beta, [Spec.SpecRestorationShaman]: LaunchStatus.Launched, [Spec.SpecHunter]: LaunchStatus.Launched, - [Spec.SpecMage]: LaunchStatus.Beta, + [Spec.SpecMage]: LaunchStatus.Launched, [Spec.SpecRogue]: LaunchStatus.Beta, [Spec.SpecHolyPaladin]: LaunchStatus.Launched, [Spec.SpecProtectionPaladin]: LaunchStatus.Launched, diff --git a/ui/mage/apls/arcane.apl.json b/ui/mage/apls/arcane.apl.json index 2e5fbb341d..aa2375c625 100644 --- a/ui/mage/apls/arcane.apl.json +++ b/ui/mage/apls/arcane.apl.json @@ -8,7 +8,7 @@ {"action":{"autocastOtherCooldowns":{}}}, {"action":{"condition":{"not":{"val":{"auraIsActive":{"auraId":{"spellId":12472}}}}},"castSpell":{"spellId":{"spellId":26297}}}}, {"action":{"condition":{"not":{"val":{"auraIsActive":{"auraId":{"spellId":12472}}}}},"castSpell":{"spellId":{"spellId":54758}}}}, - {"action":{"condition":{"not":{"val":{"auraIsActive":{"auraId":{"spellId":12472}}}}},"castSpell":{"spellId":{"itemId":40211}}}}, + {"action":{"condition":{"not":{"val":{"auraIsActive":{"auraId":{"spellId":12472}}}}},"castSpell":{"spellId":{"otherId":"OtherActionPotion"}}}}, {"action":{"condition":{"cmp":{"op":"OpLt","lhs":{"auraNumStacks":{"auraId":{"spellId":36032}}},"rhs":{"const":{"val":"4"}}}},"castSpell":{"spellId":{"spellId":42897}}}}, {"action":{"condition":{"auraIsActiveWithReactionTime":{"auraId":{"spellId":44401}}},"castSpell":{"spellId":{"spellId":42846}}}}, {"action":{"condition":{"cmp":{"op":"OpLe","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"25%"}}}},"castSpell":{"spellId":{"spellId":12051}}}}, diff --git a/ui/mage/inputs.ts b/ui/mage/inputs.ts index 58954d73b9..4319e0cb52 100644 --- a/ui/mage/inputs.ts +++ b/ui/mage/inputs.ts @@ -94,18 +94,6 @@ export const MageRotationConfig = { labelTooltip: 'When above this mana %, spam AB until a Missile Barrage proc occurs.', showWhen: (player: Player) => player.getRotation().type == RotationType.Arcane, }), - InputHelpers.makeRotationNumberInput({ - fieldName: 'extraBlastsDuringFirstAp', - label: 'Extra ABs during first AP', - labelTooltip: 'Extend AB streak by this mana casts, during the first Arcane Power CD duration.', - showWhen: (player: Player) => player.getRotation().type == RotationType.Arcane, - }), - InputHelpers.makeRotationNumberInput({ - fieldName: 'missileBarrageBelowArcaneBlastStacks', - label: 'Use Missile Barrage below n AB stacks', - labelTooltip: 'Setting this to 1 or 2 can potentially be a DPS increase with Arcane Barrage rotation or T8 4pc set bonus.', - showWhen: (player: Player) => player.getRotation().type == RotationType.Arcane, - }), InputHelpers.makeRotationNumberInput({ fieldName: 'missileBarrageBelowManaPercent', percent: true, diff --git a/ui/mage/presets.ts b/ui/mage/presets.ts index 7cac6d3215..434a1baa63 100644 --- a/ui/mage/presets.ts +++ b/ui/mage/presets.ts @@ -22,7 +22,7 @@ import { } from '../core/proto/mage.js'; import * as Tooltips from '../core/constants/tooltips.js'; -import { APLRotation } from '../core/proto/apl.js'; +import { APLRotation, APLRotation_Type } from '../core/proto/apl.js'; import ArcaneApl from './apls/arcane.apl.json'; import FireApl from './apls/fire.apl.json'; @@ -161,8 +161,6 @@ export const DefaultArcaneRotation = MageRotation.create({ type: RotationType.Arcane, only3ArcaneBlastStacksBelowManaPercent: 0.15, blastWithoutMissileBarrageAboveManaPercent: 0.2, - extraBlastsDuringFirstAp: 0, - missileBarrageBelowArcaneBlastStacks: 0, missileBarrageBelowManaPercent: 0, }); @@ -185,6 +183,19 @@ export const OtherDefaults = { profession2: Profession.Tailoring, }; +export const ARCANE_ROTATION_PRESET_SIMPLE = { + name: 'Simple Arcane', + enableWhen: (player: Player) => player.getTalentTree() == 0, + rotation: SavedRotation.create({ + rotation: { + type: APLRotation_Type.TypeSimple, + simple: { + specRotationJson: MageRotation.toJsonString(DefaultArcaneRotation), + }, + }, + }), +} + export const ARCANE_ROTATION_PRESET_DEFAULT = { name: 'Arcane', enableWhen: (player: Player) => player.getTalentTree() == 0, @@ -194,6 +205,19 @@ export const ARCANE_ROTATION_PRESET_DEFAULT = { }), } +export const FIRE_ROTATION_PRESET_SIMPLE = { + name: 'Simple Fire', + enableWhen: (player: Player) => player.getTalentTree() == 1, + rotation: SavedRotation.create({ + rotation: { + type: APLRotation_Type.TypeSimple, + simple: { + specRotationJson: MageRotation.toJsonString(DefaultFireRotation), + }, + }, + }), +} + export const FIRE_ROTATION_PRESET_DEFAULT = { name: 'Fire', enableWhen: (player: Player) => player.getTalentTree() == 1, @@ -212,6 +236,19 @@ export const FROSTFIRE_ROTATION_PRESET_DEFAULT = { }), } +export const FROST_ROTATION_PRESET_SIMPLE = { + name: 'Simple Frost', + enableWhen: (player: Player) => player.getTalentTree() == 2, + rotation: SavedRotation.create({ + rotation: { + type: APLRotation_Type.TypeSimple, + simple: { + specRotationJson: MageRotation.toJsonString(DefaultFrostRotation), + }, + }, + }), +} + export const FROST_ROTATION_PRESET_DEFAULT = { name: 'Frost', enableWhen: (player: Player) => player.getTalentTree() == 2, diff --git a/ui/mage/sim.ts b/ui/mage/sim.ts index 0120f0f74d..03875d74b3 100644 --- a/ui/mage/sim.ts +++ b/ui/mage/sim.ts @@ -1,12 +1,17 @@ -import {Debuffs, IndividualBuffs, PartyBuffs, RaidBuffs, Spec, Stat, TristateEffect} from '../core/proto/common.js'; -import {APLRotation, APLRotation_Type,} from '../core/proto/apl.js'; +import {Cooldowns, Debuffs, IndividualBuffs, PartyBuffs, RaidBuffs, Spec, Stat, 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 {IndividualSimUI} from '../core/individual_sim_ui.js'; +import { + Mage_Rotation as MageRotation, + Mage_Rotation_PrimaryFireSpell as PrimaryFireSpell, + Mage_Rotation_Type as RotationType, +} from '../core/proto/mage.js'; -import {Mage_Rotation_Type as RotationType,} from '../core/proto/mage.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 * as MageInputs from './inputs.js'; import * as Presets from './presets.js'; @@ -141,6 +146,9 @@ export class MageSimUI extends IndividualSimUI { presets: { // Preset rotations that the user can quickly select. rotations: [ + Presets.ARCANE_ROTATION_PRESET_SIMPLE, + Presets.FIRE_ROTATION_PRESET_SIMPLE, + Presets.FROST_ROTATION_PRESET_SIMPLE, Presets.ARCANE_ROTATION_PRESET_DEFAULT, Presets.FIRE_ROTATION_PRESET_DEFAULT, Presets.FROSTFIRE_ROTATION_PRESET_DEFAULT, @@ -204,6 +212,87 @@ export class MageSimUI extends IndividualSimUI { return Presets.FROST_ROTATION_PRESET_DEFAULT.rotation.rotation!; } }, + + simpleRotation: (player: Player, simple: MageRotation, cooldowns: Cooldowns): APLRotation => { + let [prepullActions, actions] = AplUtils.standardCooldownDefaults(cooldowns); + + const prepullMirrorImage = APLPrepullAction.fromJsonString(`{"action":{"castSpell":{"spellId":{"spellId":55342}}},"doAtValue":{"const":{"val":"-2s"}}}`); + + const 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 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 (simple.type == RotationType.Arcane) { + actions.push(...[ + berserking, + hyperspeedAcceleration, + combatPot, + simple.missileBarrageBelowManaPercent > 0 ? arcaneMissilesWithMissileBarrageBelowMana : null, + arcaneBlastBelowStacks, + arcaneMisslesWithMissileBarrage, + evocation, + arcaneBlastAboveMana, + simple.useArcaneBarrage ? arcaneBarrage : null, + arcaneMissiles, + ].filter(a => a) as Array) + } else if (simple.type == RotationType.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 (simple.type == RotationType.Frost) { + actions.push(...[ + berserking, + hyperspeedAcceleration, + evocation, + deepFreeze, + frostfireBoltWithBrainFreeze, + simple.useIceLance ? iceLance : null, + frostbolt, + ].filter(a => a) as Array) + } + + return APLRotation.create({ + prepullActions: prepullActions, + priorityList: actions.map(action => APLListItem.create({ + action: action, + })) + }); + }, }); } }