From e96990bad502e84555bb368585492b819c4a1d32 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Fri, 29 Dec 2023 11:18:02 +0900
Subject: [PATCH 01/28] Remove legacy rotation code from UI

---
 proto/priest.proto                            |   6 +-
 proto/ui.proto                                |   8 +-
 ui/balance_druid/inputs.ts                    | 137 +------
 ui/balance_druid/presets.ts                   |  17 -
 ui/balance_druid/sim.ts                       |   4 -
 .../custom_rotation_picker.ts                 | 135 -------
 .../individual_sim_ui/rotation_tab.ts         |  41 +-
 .../individual_sim_ui/settings_tab.ts         |  68 +---
 ui/core/components/input_helpers.ts           |  33 --
 ui/core/individual_sim_ui.ts                  |  19 +-
 ui/core/launched_sims.ts                      |  25 --
 ui/core/player.ts                             | 119 ++----
 ui/core/proto_utils/utils.ts                  |  22 --
 ui/deathknight/inputs.ts                      | 360 +-----------------
 ui/deathknight/presets.ts                     |  79 ----
 ui/deathknight/sim.ts                         |   7 -
 ui/elemental_shaman/inputs.ts                 |  95 +----
 ui/elemental_shaman/presets.ts                |  20 -
 ui/elemental_shaman/sim.ts                    |   5 -
 ui/enhancement_shaman/inputs.ts               | 215 -----------
 ui/enhancement_shaman/presets.ts              |  44 +--
 ui/enhancement_shaman/sim.ts                  |   4 -
 ui/feral_druid/inputs.ts                      |   5 -
 ui/feral_druid/sim.ts                         |   2 -
 ui/feral_tank_druid/inputs.ts                 |   6 -
 ui/feral_tank_druid/sim.ts                    |   4 +-
 ui/healing_priest/inputs.ts                   |  37 --
 ui/healing_priest/presets.ts                  |  29 --
 ui/healing_priest/sim.ts                      |   4 -
 ui/holy_paladin/inputs.ts                     |   4 -
 ui/holy_paladin/presets.ts                    |   4 -
 ui/holy_paladin/sim.ts                        |   4 -
 ui/hunter/inputs.ts                           |   7 +-
 ui/hunter/presets.ts                          |   4 +-
 ui/hunter/sim.ts                              |   4 +-
 ui/mage/sim.ts                                |   2 -
 ui/protection_paladin/inputs.ts               |   4 -
 ui/protection_paladin/presets.ts              |  23 --
 ui/protection_paladin/sim.ts                  |   4 -
 ui/protection_warrior/inputs.ts               |  59 ---
 ui/protection_warrior/presets.ts              |  27 +-
 ui/protection_warrior/sim.ts                  |   4 -
 ui/restoration_druid/inputs.ts                |  11 +-
 ui/restoration_druid/presets.ts               |   4 -
 ui/restoration_druid/sim.ts                   |   4 -
 ui/restoration_shaman/inputs.ts               |  74 +---
 ui/restoration_shaman/presets.ts              |  20 -
 ui/restoration_shaman/sim.ts                  |   5 +-
 ui/retribution_paladin/inputs.ts              | 146 +------
 ui/retribution_paladin/presets.ts             |  46 ---
 ui/retribution_paladin/sim.ts                 |  21 -
 ui/rogue/inputs.ts                            | 138 -------
 ui/rogue/presets.ts                           |  20 -
 ui/rogue/sim.ts                               | 123 ------
 .../_custom_rotation_picker.scss              |  33 --
 .../individual_sim_ui/_settings_tab.scss      |   1 -
 ui/shadow_priest/inputs.ts                    |  43 ---
 ui/shadow_priest/presets.ts                   |   7 -
 ui/shadow_priest/sim.ts                       |   9 -
 ui/smite_priest/inputs.ts                     |  33 +-
 ui/smite_priest/presets.ts                    |   8 -
 ui/smite_priest/sim.ts                        |   5 -
 ui/tank_deathknight/inputs.ts                 |   3 -
 ui/tank_deathknight/presets.ts                |  15 -
 ui/tank_deathknight/sim.ts                    |   3 -
 ui/warlock/inputs.ts                          | 218 +----------
 ui/warlock/presets.ts                         |  40 +-
 ui/warlock/sim.ts                             |   6 -
 ui/warrior/inputs.ts                          | 197 ----------
 ui/warrior/presets.ts                         |  41 --
 ui/warrior/sim.ts                             |   4 -
 71 files changed, 91 insertions(+), 2887 deletions(-)
 delete mode 100644 ui/core/components/individual_sim_ui/custom_rotation_picker.ts
 delete mode 100644 ui/scss/core/components/individual_sim_ui/_custom_rotation_picker.scss

diff --git a/proto/priest.proto b/proto/priest.proto
index 9e04269f47..d275e525bd 100644
--- a/proto/priest.proto
+++ b/proto/priest.proto
@@ -162,10 +162,10 @@ message ShadowPriest {
 			InnerFire = 1;
 		}
 
-		bool use_shadowfiend = 1;
+		bool use_shadowfiend = 1 [deprecated = true];
 		Armor armor = 2;
-		bool use_mind_blast = 4;
-		bool use_shadow_word_death = 5;
+		bool use_mind_blast = 4 [deprecated = true];
+		bool use_shadow_word_death = 5 [deprecated = true];
 		UnitReference power_infusion_target = 6;
 		double latency = 7 [deprecated = true]; // Latency between actions
 	}
diff --git a/proto/ui.proto b/proto/ui.proto
index 1d6b7cfe6a..ae1b928691 100644
--- a/proto/ui.proto
+++ b/proto/ui.proto
@@ -290,8 +290,8 @@ message SavedSettings {
 	IndividualBuffs player_buffs = 3;
 	Consumes consumes = 4;
 	Race race = 5;
-	Cooldowns cooldowns = 6;
-	string rotation_json = 8;
+	Cooldowns cooldowns = 6 [deprecated = true];
+	string rotation_json = 8 [deprecated = true];
 	repeated Profession professions = 9;
 	bool enable_item_swap = 18;
 	ItemSwap item_swap = 17;
@@ -313,8 +313,8 @@ message SavedTalents {
 
 message SavedRotation {
 	APLRotation rotation = 1;
-	string spec_rotation_options_json = 2;
-	Cooldowns cooldowns = 3;
+	string spec_rotation_options_json = 2 [deprecated = true];
+	Cooldowns cooldowns = 3 [deprecated = true];
 }
 
 message BlessingsAssignment {
diff --git a/ui/balance_druid/inputs.ts b/ui/balance_druid/inputs.ts
index 71ee7a5896..5bcab559e6 100644
--- a/ui/balance_druid/inputs.ts
+++ b/ui/balance_druid/inputs.ts
@@ -2,21 +2,10 @@ import { UnitReference, UnitReference_Type as UnitType } from '../core/proto/com
 import { Spec } from '../core/proto/common.js';
 import { ActionId } from '../core/proto_utils/action_id.js';
 import { Player } from '../core/player.js';
-import { EventID, TypedEvent } from '../core/typed_event.js';
+import { EventID } from '../core/typed_event.js';
 
 import * as InputHelpers from '../core/components/input_helpers.js';
 
-import {
-	BalanceDruid_Options as DruidOptions,
-	BalanceDruid_Rotation_Type as RotationType,
-	BalanceDruid_Rotation_MfUsage as MfUsage,
-	BalanceDruid_Rotation_MfExtension as MfExtension,
-	BalanceDruid_Rotation_IsUsage as IsUsage,
-	BalanceDruid_Rotation_WrathUsage as WrathUsage,
-	BalanceDruid_Rotation_EclipsePrio as EclipsePrio,
-} from '../core/proto/druid.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.
 
@@ -43,127 +32,3 @@ export const OkfUptime = InputHelpers.makeSpecOptionsNumberInput<Spec.SpecBalanc
 	labelTooltip: 'Percentage of fight uptime for Owlkin Frenzy',
 	percent: true,
 });
-
-export const BalanceDruidRotationConfig = {
-	inputs: [
-		InputHelpers.makeRotationEnumInput<Spec.SpecBalanceDruid, RotationType>({
-			fieldName: 'type',
-			label: 'Type',
-			labelTooltip: 'Set to \'Manual\', to manage eclipses, spells, CDs and DoTs usage.',
-			values: [
-				{
-					name: 'Default', value: RotationType.Default,
-					tooltip: 'The default rotation.',
-				},
-				{
-					name: 'Manual', value: RotationType.Manual,
-					tooltip: 'Allows custom selection of which spells to use, dot management and CD usage.',
-				},
-			],
-		}),
-		InputHelpers.makeRotationEnumInput<Spec.SpecBalanceDruid, EclipsePrio>({
-			fieldName: 'eclipsePrio',
-			label: 'Eclipse priority',
-			labelTooltip: 'Defines which eclipse will get prioritized in the rotation.',
-			values: [
-				{ name: 'Lunar', value: EclipsePrio.Lunar },
-				{ name: 'Solar', value: EclipsePrio.Solar },
-			],
-			showWhen: (player: Player<Spec.SpecBalanceDruid>) => player.getRotation().type == RotationType.Manual,
-		}),
-		InputHelpers.makeRotationEnumInput<Spec.SpecBalanceDruid, MfUsage>({
-			fieldName: 'mfUsage',
-			label: 'Moonfire Usage',
-			labelTooltip: 'Defines how Moonfire will be used in the rotation.',
-			values: [
-				{ name: 'Unused', value: MfUsage.NoMf },
-				{ name: 'Before lunar', value: MfUsage.BeforeLunar },
-				{ name: 'Maximize', value: MfUsage.MaximizeMf },
-				{ name: 'Multidot', value: MfUsage.MultidotMf },
-			],
-			showWhen: (player: Player<Spec.SpecBalanceDruid>) => player.getRotation().type == RotationType.Manual,
-		}),
-		InputHelpers.makeRotationEnumInput<Spec.SpecBalanceDruid, MfExtension>({
-			fieldName: 'mfExtension',
-			label: 'Moonfire Extension',
-			labelTooltip: 'When should the rotation try to extend Moonfire on the main target.',
-			values: [
-				{ name: 'Extend always', value: MfExtension.ExtendAlways },
-				{ name: 'Extend outside solar', value: MfExtension.ExtendOutsideSolar },
-				{ name: 'Do not extend', value: MfExtension.DontExtend },
-			],
-			showWhen: (player: Player<Spec.SpecBalanceDruid>) => player.getRotation().type == RotationType.Manual,
-		}),
-		InputHelpers.makeRotationEnumInput<Spec.SpecBalanceDruid, IsUsage>({
-			fieldName: 'isUsage',
-			label: 'Insect Swarm Usage',
-			labelTooltip: 'Defines how Insect Swarm will be used in the rotation.',
-			values: [
-				{ name: 'Unused', value: IsUsage.NoIs },
-				{ name: 'Before solar', value: IsUsage.BeforeSolar },
-				{ name: 'Optimize', value: IsUsage.OptimizeIs },
-				{ name: 'Multidot', value: IsUsage.MultidotIs },
-			],
-			showWhen: (player: Player<Spec.SpecBalanceDruid>) => player.getRotation().type == RotationType.Manual,
-		}),
-		InputHelpers.makeRotationEnumInput<Spec.SpecBalanceDruid, WrathUsage>({
-			fieldName: 'wrathUsage',
-			label: 'Wrath usage',
-			labelTooltip: 'Defines how Wrath will be used in the rotation.',
-			values: [
-				{ name: 'Unused', value: WrathUsage.NoWrath },
-				{ name: 'Fishing for Lunar', value: WrathUsage.FishingForLunar },
-				{ name: 'Regular', value: WrathUsage.RegularWrath },
-			],
-			showWhen: (player: Player<Spec.SpecBalanceDruid>) => player.getRotation().type == RotationType.Manual,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecBalanceDruid>({
-			fieldName: 'useStarfire',
-			label: 'Use Starfire',
-			labelTooltip: 'Should the rotation use Starfire.',
-			showWhen: (player: Player<Spec.SpecBalanceDruid>) => player.getRotation().type == RotationType.Manual,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecBalanceDruid>({
-			fieldName: 'useSmartCooldowns',
-			label: 'Smart Cooldowns usage',
-			labelTooltip: 'The rotation will use cooldowns during eclipses, avoiding Haste CDs in solar and Crit CDs in lunar',
-			showWhen: (player: Player<Spec.SpecBalanceDruid>) => player.getRotation().type == RotationType.Manual,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecBalanceDruid>({
-			fieldName: 'snapshotMf',
-			label: 'Snapshot Moonfire',
-			labelTooltip: 'The rotation will try to snapshot Moonfire with SP procs',
-			showWhen: (player: Player<Spec.SpecBalanceDruid>) => player.getRotation().type == RotationType.Manual,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecBalanceDruid>({
-			fieldName: 'eclipseShuffling',
-			label: 'Eclipse Shuffling',
-			labelTooltip: 'Should the rotation alternate Starfire and Wrath when both eclipses are available.',
-			showWhen: (player: Player<Spec.SpecBalanceDruid>) => player.getRotation().type == RotationType.Manual,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecBalanceDruid>({
-			fieldName: 'useTyphoon',
-			label: 'Use Typhoon',
-			labelTooltip: 'Should the rotation use Typhoon.',
-			showWhen: (player: Player<Spec.SpecBalanceDruid>) => player.getRotation().type == RotationType.Manual,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecBalanceDruid>({
-			fieldName: 'useHurricane',
-			label: 'Use Hurricane',
-			labelTooltip: 'Should the rotation use Hurricane.',
-			showWhen: (player: Player<Spec.SpecBalanceDruid>) => player.getRotation().type == RotationType.Manual,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecBalanceDruid>({
-			fieldName: 'useBattleRes',
-			label: 'Use Battle Res',
-			labelTooltip: 'Cast Battle Res on an ally sometime during the encounter.',
-			showWhen: (player: Player<Spec.SpecBalanceDruid>) => player.getRotation().type == RotationType.Manual,
-		}),
-		InputHelpers.makeRotationNumberInput<Spec.SpecBalanceDruid>({
-			fieldName: 'playerLatency',
-			label: 'Player latency',
-			labelTooltip: 'Time before the player reacts to an eclipse proc, in milliseconds.',
-			showWhen: (player: Player<Spec.SpecBalanceDruid>) => player.getRotation().type == RotationType.Manual,
-		}),
-	],
-};
diff --git a/ui/balance_druid/presets.ts b/ui/balance_druid/presets.ts
index 794cebb306..b11e50dd42 100644
--- a/ui/balance_druid/presets.ts
+++ b/ui/balance_druid/presets.ts
@@ -18,11 +18,6 @@ import { SavedTalents } from '../core/proto/ui.js';
 
 import {
 	BalanceDruid_Options as BalanceDruidOptions,
-	BalanceDruid_Rotation as BalanceDruidRotation,
-	BalanceDruid_Rotation_IsUsage,
-	BalanceDruid_Rotation_MfUsage,
-	BalanceDruid_Rotation_Type as RotationType,
-	BalanceDruid_Rotation_WrathUsage,
 	DruidMajorGlyph,
 	DruidMinorGlyph,
 } from '../core/proto/druid.js';
@@ -117,18 +112,6 @@ export const Phase4Talents = {
 	}),
 };
 
-export const DefaultRotation = BalanceDruidRotation.create({
-	type: RotationType.Default,
-	maintainFaerieFire: true,
-	useSmartCooldowns: true,
-	mfUsage: BalanceDruid_Rotation_MfUsage.BeforeLunar,
-	isUsage: BalanceDruid_Rotation_IsUsage.OptimizeIs,
-	wrathUsage: BalanceDruid_Rotation_WrathUsage.RegularWrath,
-	useStarfire: true,
-	useBattleRes: false,
-	playerLatency: 200,
-});
-
 export const DefaultOptions = BalanceDruidOptions.create({
 	innervateTarget: UnitReference.create(),
 });
diff --git a/ui/balance_druid/sim.ts b/ui/balance_druid/sim.ts
index 768be09c72..1190eb4579 100644
--- a/ui/balance_druid/sim.ts
+++ b/ui/balance_druid/sim.ts
@@ -65,8 +65,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecBalanceDruid, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultRotation,
 		// Default talents.
 		talents: Presets.Phase3Talents.data,
 		// Default spec-specific settings.
@@ -83,8 +81,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecBalanceDruid, {
 	playerIconInputs: [
 		DruidInputs.SelfInnervate,
 	],
-	// Inputs to include in the 'Rotation' section on the settings tab.
-	rotationInputs: DruidInputs.BalanceDruidRotationConfig,
 	// Buff and Debuff inputs to include/exclude, overriding the EP-based defaults.
 	includeBuffDebuffInputs: [
 		IconInputs.MeleeHasteBuff,
diff --git a/ui/core/components/individual_sim_ui/custom_rotation_picker.ts b/ui/core/components/individual_sim_ui/custom_rotation_picker.ts
deleted file mode 100644
index 7014d6edfa..0000000000
--- a/ui/core/components/individual_sim_ui/custom_rotation_picker.ts
+++ /dev/null
@@ -1,135 +0,0 @@
-import { Spec } from '../../proto/common.js';
-import { CustomRotation, CustomSpell } from '../../proto/common.js';
-import { EventID } from '../../typed_event.js';
-import { Player } from '../../player.js';
-import { IconEnumPicker, IconEnumValueConfig } from '../icon_enum_picker.js';
-import { ListItemPickerConfig, ListPicker } from '../list_picker.js';
-import { NumberPicker } from '../number_picker.js';
-
-import { Component } from '../component.js';
-import { Input } from '../input.js';
-import { SimUI } from 'ui/core/sim_ui.js';
-
-export interface CustomRotationPickerConfig<SpecType extends Spec, T> {
-	getValue: (player: Player<SpecType>) => CustomRotation,
-	setValue: (eventID: EventID, player: Player<SpecType>, newValue: CustomRotation) => void,
-
-	numColumns: number,
-	extraCssClasses?: string[];
-	showCastsPerMinute?: boolean,
-	values: Array<IconEnumValueConfig<Player<SpecType>, T>>;
-
-	showWhen?: (player: Player<SpecType>) => boolean,
-}
-
-export class CustomRotationPicker<SpecType extends Spec, T> extends Component {
-	constructor(parent: HTMLElement, simUI: SimUI, modPlayer: Player<SpecType>, config: CustomRotationPickerConfig<SpecType, T>) {
-		super(parent, 'custom-rotation-picker-root');
-
-		if (config.extraCssClasses)
-			this.rootElem.classList.add(...config.extraCssClasses);
-
-		new ListPicker<Player<SpecType>, CustomSpell>(this.rootElem, modPlayer, {
-			extraCssClasses: ['custom-spells-picker'],
-			title: 'Spell Priority',
-			titleTooltip: 'Spells at the top of the list are prioritized first. Safely ignores untalented options.',
-			itemLabel: 'Spell',
-			changedEvent: (player: Player<SpecType>) => player.changeEmitter,
-			getValue: (player: Player<SpecType>) => config.getValue(player).spells,
-			setValue: (eventID: EventID, player: Player<SpecType>, newValue: Array<CustomSpell>) => {
-				config.setValue(eventID, player, CustomRotation.create({
-					spells: newValue,
-				}));
-			},
-			newItem: () => CustomSpell.create(),
-			copyItem: (oldItem: CustomSpell) => CustomSpell.clone(oldItem),
-			newItemPicker: (parent: HTMLElement, listPicker: ListPicker<Player<SpecType>, CustomSpell>, index: number, itemConfig: ListItemPickerConfig<Player<SpecType>, CustomSpell>) => new CustomSpellPicker(parent, modPlayer, index, itemConfig, listPicker, config),
-			inlineMenuBar: true,
-			showWhen: config.showWhen,
-		});
-	}
-}
-
-class CustomSpellPicker<SpecType extends Spec, T> extends Input<Player<SpecType>, CustomSpell> {
-	private readonly player: Player<SpecType>;
-	private readonly listPicker: ListPicker<Player<SpecType>, CustomSpell>;
-	private readonly spellIndex: number;
-
-	private readonly spellPicker: Input<null, number>;
-	private readonly cpmPicker: Input<null, number> | null;
-
-	getSpell(): CustomSpell {
-		return this.listPicker.config.getValue(this.player)[this.spellIndex] || CustomSpell.create();
-	}
-
-	constructor(parent: HTMLElement, player: Player<SpecType>, spellIndex: number, config: ListItemPickerConfig<Player<SpecType>, CustomSpell>, listPicker: ListPicker<Player<SpecType>, CustomSpell>, crConfig: CustomRotationPickerConfig<SpecType, T>) {
-		super(parent, 'custom-spell-picker-root', player, config);
-		this.player = player;
-		this.listPicker = listPicker;
-		this.spellIndex = spellIndex;
-
-		this.spellPicker = new IconEnumPicker<null, number>(this.rootElem, null, {
-			numColumns: crConfig.numColumns,
-			values: crConfig.values.map(value => {
-				if (value.showWhen) {
-					const oldShowWhen = value.showWhen;
-					value.showWhen = (() => oldShowWhen(player)) as unknown as ((player: Player<SpecType>) => boolean);
-				}
-				return value;
-			}) as unknown as Array<IconEnumValueConfig<null, number>>,
-			equals: (a: number, b: number) => a == b,
-			zeroValue: 0,
-			changedEvent: () => player.changeEmitter,
-			getValue: () => this.getSpell().spell,
-			setValue: (eventID: EventID, _: null, newValue: number) => {
-				const spell = this.getSpell();
-				spell.spell = newValue;
-				this.setSpell(eventID, spell);
-			},
-		});
-
-		this.cpmPicker = null;
-		if (crConfig.showCastsPerMinute) {
-			this.cpmPicker = new NumberPicker<null>(this.rootElem, null, {
-				label: 'CPM',
-				labelTooltip: 'Desired Casts-Per-Minute for this spell.',
-				float: true,
-				positive: true,
-				changedEvent: () => player.changeEmitter,
-				getValue: () => this.getSpell().castsPerMinute,
-				setValue: (eventID: EventID, _: null, newValue: number) => {
-					const spell = this.getSpell();
-					spell.castsPerMinute = newValue;
-					this.setSpell(eventID, spell);
-				},
-			});
-		}
-	}
-
-	getInputElem(): HTMLElement | null {
-		return this.rootElem;
-	}
-
-	getInputValue(): CustomSpell {
-		return CustomSpell.create({
-			spell: this.spellPicker.getInputValue(),
-			castsPerMinute: this.cpmPicker?.getInputValue(),
-		});
-	}
-
-	setInputValue(newValue: CustomSpell) {
-		if (!newValue) {
-			return;
-		}
-		this.spellPicker.setInputValue(newValue.spell);
-		if (this.cpmPicker) {
-			this.cpmPicker.setInputValue(newValue.castsPerMinute);
-		}
-	}
-
-	private setSpell(eventID: EventID, spell: CustomSpell) {
-		const customSpells = this.listPicker.config.getValue(this.player);
-		customSpells[this.spellIndex] = CustomSpell.clone(spell);
-		this.listPicker.config.setValue(eventID, this.player, customSpells);
-	}
-}
diff --git a/ui/core/components/individual_sim_ui/rotation_tab.ts b/ui/core/components/individual_sim_ui/rotation_tab.ts
index 74c6ff3ce6..e962848ee1 100644
--- a/ui/core/components/individual_sim_ui/rotation_tab.ts
+++ b/ui/core/components/individual_sim_ui/rotation_tab.ts
@@ -12,7 +12,6 @@ import {
 } from "../../proto/ui";
 import { EventID, TypedEvent } from "../../typed_event";
 import { Player } from "../../player";
-import { aplLaunchStatuses, LaunchStatus } from '../../launched_sims';
 
 import { ContentBlock } from "../content_block";
 import { SimTab } from "../sim_tab";
@@ -21,7 +20,6 @@ import { BooleanPicker } from "../boolean_picker";
 import { EnumPicker } from "../enum_picker";
 import { Input } from "../input";
 import { CooldownsPicker } from "./cooldowns_picker";
-import { CustomRotationPicker } from "./custom_rotation_picker";
 import { SavedDataManager } from "../saved_data_manager";
 
 import * as IconInputs from '../icon_inputs.js';
@@ -59,7 +57,7 @@ export class RotationTab extends SimTab {
 
 		this.buildAutoContent();
 		this.buildAplContent();
-		this.buildSimpleOrLegacyContent(this.simUI.player.hasSimpleRotationGenerator());
+		this.buildSimpleContent();
 
 		this.buildSavedDataPickers();
 	}
@@ -87,20 +85,11 @@ export class RotationTab extends SimTab {
 		header.classList.add('rotation-tab-header');
 		this.leftPanel.appendChild(header);
 
-		const aplLaunchStatus = aplLaunchStatuses[this.simUI.player.spec];
-
 		new EnumPicker(header, this.simUI.player, {
 			label: 'Rotation Type',
 			labelTooltip: 'Which set of options to use for specifying the rotation.',
 			inline: true,
-			values: aplLaunchStatus == LaunchStatus.Alpha ? [
-				{ value: APLRotationType.TypeLegacy, name: 'Legacy' },
-				{ value: APLRotationType.TypeAPL, name: 'APL' },
-			] : aplLaunchStatus == LaunchStatus.Beta ? [
-				{ value: APLRotationType.TypeAuto, name: 'Auto' },
-				{ value: APLRotationType.TypeAPL, name: 'APL' },
-				{ value: APLRotationType.TypeLegacy, name: 'Legacy' },
-			] : this.simUI.player.hasSimpleRotationGenerator() ? [
+			values: this.simUI.player.hasSimpleRotationGenerator() ? [
 				{ value: APLRotationType.TypeAuto, name: 'Auto' },
 				{ value: APLRotationType.TypeSimple, name: 'Simple' },
 				{ value: APLRotationType.TypeAPL, name: 'APL' },
@@ -132,11 +121,11 @@ export class RotationTab extends SimTab {
 		new APLRotationPicker(content, this.simUI, this.simUI.player);
 	}
 
-	private buildSimpleOrLegacyContent(isSimple: boolean) {
-		if (!isSimple && aplLaunchStatuses[this.simUI.player.spec] == LaunchStatus.Launched) {
+	private buildSimpleContent() {
+		if (!this.simUI.player.hasSimpleRotationGenerator() || !this.simUI.individualConfig.rotationInputs) {
 			return;
 		}
-		const cssClass = isSimple ? 'rotation-tab-simple' : 'rotation-tab-legacy';
+		const cssClass = 'rotation-tab-simple';
 
 		const contentBlock = new ContentBlock(this.leftPanel, 'rotation-settings', {
 			header: { title: 'Rotation' }
@@ -177,8 +166,6 @@ export class RotationTab extends SimTab {
 				new BooleanPicker(sectionElem, this.simUI.player, { ...inputConfig, ...{ cssScheme: this.simUI.cssScheme } });
 			} else if (inputConfig.type == 'enum') {
 				new EnumPicker(sectionElem, this.simUI.player, inputConfig);
-			} else if (inputConfig.type == 'customRotation') {
-				new CustomRotationPicker(sectionElem, this.simUI, this.simUI.player, inputConfig);
 			}
 		});
 	}
@@ -196,32 +183,18 @@ export class RotationTab extends SimTab {
 	}
 
 	private buildSavedDataPickers() {
-		const aplIsLaunched = aplLaunchStatuses[this.simUI.player.spec] == LaunchStatus.Launched;
-
 		const savedRotationsManager = new SavedDataManager<Player<any>, SavedRotation>(this.rightPanel, this.simUI.player, {
 			label: 'Rotation',
 			header: { title: 'Saved Rotations' },
 			storageKey: this.simUI.getSavedRotationStorageKey(),
 			getData: (player: Player<any>) => SavedRotation.create({
 				rotation: APLRotation.clone(player.aplRotation),
-				specRotationOptionsJson: aplIsLaunched ? '{}' : JSON.stringify(player.specTypeFunctions.rotationToJson(player.getRotation())),
-				cooldowns: aplIsLaunched ? Cooldowns.create() : player.getCooldowns(),
+				specRotationOptionsJson: '{}',
+				cooldowns: Cooldowns.create(),
 			}),
 			setData: (eventID: EventID, player: Player<any>, newRotation: SavedRotation) => {
 				TypedEvent.freezeAllAndDo(() => {
 					player.setAplRotation(eventID, newRotation.rotation || APLRotation.create());
-					if (!aplIsLaunched) {
-						if (newRotation.specRotationOptionsJson) {
-							try {
-								const json = JSON.parse(newRotation.specRotationOptionsJson);
-								const specRot = player.specTypeFunctions.rotationFromJson(json);
-								player.setRotation(eventID, specRot);
-							} catch (e) {
-								console.warn('Error parsing rotation spec options: ' + e);
-							}
-						}
-						player.setCooldowns(eventID, newRotation.cooldowns || Cooldowns.create());
-					}
 				});
 			},
 			changeEmitters: [this.simUI.player.rotationChangeEmitter, this.simUI.player.cooldownsChangeEmitter, this.simUI.player.talentsChangeEmitter],
diff --git a/ui/core/components/individual_sim_ui/settings_tab.ts b/ui/core/components/individual_sim_ui/settings_tab.ts
index c5e59348de..f313fbdf81 100644
--- a/ui/core/components/individual_sim_ui/settings_tab.ts
+++ b/ui/core/components/individual_sim_ui/settings_tab.ts
@@ -1,7 +1,6 @@
 import { IndividualSimUI, InputSection, StatOption } from "../../individual_sim_ui";
 import {
 	Consumes,
-	Cooldowns,
 	Debuffs,
 	HealingModel,
 	IndividualBuffs,
@@ -20,7 +19,6 @@ import { SavedEncounter, SavedSettings } from "../../proto/ui";
 import { EventID, TypedEvent } from "../../typed_event";
 import { getEnumValues } from "../../utils";
 import { Player } from "../../player";
-import { aplLaunchStatuses, LaunchStatus } from '../../launched_sims';
 
 import { ContentBlock } from "../content_block";
 import { EncounterPicker } from '../encounter_picker.js';
@@ -34,8 +32,6 @@ import { MultiIconPicker } from "../multi_icon_picker";
 import { IconPickerConfig } from "../icon_picker";
 import { TypedIconPickerConfig } from "../input_helpers";
 
-import { CustomRotationPicker } from "./custom_rotation_picker";
-import { CooldownsPicker } from "./cooldowns_picker";
 import { ConsumesPicker } from "./consumes_picker";
 
 import * as IconInputs from '../icon_inputs.js';
@@ -84,16 +80,9 @@ export class SettingsTab extends SimTab {
 			this.buildEncounterSettings();
 		}
 
-		if (aplLaunchStatuses[this.simUI.player.spec] == LaunchStatus.Unlaunched) {
-			this.buildRotationSettings();
-		}
-
 		this.buildPlayerSettings();
 		this.buildCustomSettingsSections();
 		this.buildConsumesSection();
-		if (aplLaunchStatuses[this.simUI.player.spec] == LaunchStatus.Unlaunched) {
-			this.buildCooldownSettings();
-		}
 		this.buildOtherSettings();
 
 		if (!this.simUI.isWithinRaidSim) {
@@ -114,32 +103,8 @@ export class SettingsTab extends SimTab {
 		new EncounterPicker(contentBlock.bodyElement, this.simUI.sim.encounter, this.simUI.individualConfig.encounterPicker, this.simUI);
 	}
 
-	private buildRotationSettings() {
-		const contentBlock = new ContentBlock(this.column1, 'rotation-settings', {
-			header: { title: 'Rotation' }
-		});
-
-		const rotationIconGroup = Input.newGroupContainer();
-		rotationIconGroup.classList.add('rotation-icon-group', 'icon-group');
-		contentBlock.bodyElement.appendChild(rotationIconGroup);
-
-		if (this.simUI.individualConfig.rotationIconInputs?.length) {
-			this.configureIconSection(
-				rotationIconGroup,
-				this.simUI.individualConfig.rotationIconInputs.map(iconInput => IconInputs.buildIconInput(rotationIconGroup, this.simUI.player, iconInput)),
-				true
-			);
-		}
-
-		this.configureInputSection(contentBlock.bodyElement, this.simUI.individualConfig.rotationInputs);
-
-		contentBlock.bodyElement.querySelectorAll('.input-root').forEach(elem => {
-			elem.classList.add('input-inline');
-		})
-	}
-
 	private buildPlayerSettings() {
-		const column = aplLaunchStatuses[this.simUI.player.spec] == LaunchStatus.Unlaunched ? this.column2 : this.column1;
+		const column = this.column1;
 		const contentBlock = new ContentBlock(column, 'player-settings', {
 			header: { title: 'Player' }
 		});
@@ -155,7 +120,7 @@ export class SettingsTab extends SimTab {
 		);
 
 		const races = specToEligibleRaces[this.simUI.player.spec];
-		const racePicker = new EnumPicker(contentBlock.bodyElement, this.simUI.player, {
+		const _racePicker = new EnumPicker(contentBlock.bodyElement, this.simUI.player, {
 			label: 'Race',
 			values: races.map(race => {
 				return {
@@ -176,7 +141,7 @@ export class SettingsTab extends SimTab {
 		contentBlock.bodyElement.appendChild(professionGroup);
 
 		const professions = getEnumValues(Profession) as Array<Profession>;
-		const profession1Picker = new EnumPicker(professionGroup, this.simUI.player, {
+		const _profession1Picker = new EnumPicker(professionGroup, this.simUI.player, {
 			label: 'Profession 1',
 			values: professions.map(p => {
 				return {
@@ -189,7 +154,7 @@ export class SettingsTab extends SimTab {
 			setValue: (eventID, sim, newValue) => sim.setProfession1(eventID, newValue),
 		});
 
-		const profession2Picker = new EnumPicker(professionGroup, this.simUI.player, {
+		const _profession2Picker = new EnumPicker(professionGroup, this.simUI.player, {
 			label: 'Profession 2',
 			values: professions.map(p => {
 				return {
@@ -219,17 +184,7 @@ export class SettingsTab extends SimTab {
 		new ConsumesPicker(contentBlock.bodyElement, this, this.simUI);
 	}
 
-	private buildCooldownSettings() {
-		const column = (this.simUI.isWithinRaidSim ? this.column4 : this.column2) as HTMLElement;
-		const contentBlock = new ContentBlock(column, 'cooldown-settings', {
-			header: { title: 'Cooldowns', tooltip: Tooltips.COOLDOWNS_SECTION }
-		});
-
-		new CooldownsPicker(contentBlock.bodyElement, this.simUI.player);
-	}
-
 	private buildOtherSettings() {
-		const column = this.simUI.isWithinRaidSim ? this.column4 : this.column2;
 		const settings = this.simUI.individualConfig.otherInputs?.inputs.filter(inputs =>
 			!inputs.extraCssClasses?.includes('within-raid-sim-hide') || true
 		)
@@ -412,8 +367,6 @@ export class SettingsTab extends SimTab {
 					distanceFromTarget: player.getDistanceFromTarget(),
 					healingModel: player.getHealingModel(),
 					nibelungAverageCasts: player.getNibelungAverageCasts(),
-					cooldowns: aplLaunchStatuses[simUI.player.spec] == LaunchStatus.Unlaunched ? player.getCooldowns() : undefined,
-					rotationJson: aplLaunchStatuses[simUI.player.spec] == LaunchStatus.Unlaunched ? JSON.stringify(player.specTypeFunctions.rotationToJson(player.getRotation())) : undefined,
 				});
 			},
 			setData: (eventID: EventID, simUI: IndividualSimUI<any>, newSettings: SavedSettings) => {
@@ -436,12 +389,6 @@ export class SettingsTab extends SimTab {
 					simUI.player.setDistanceFromTarget(eventID, newSettings.distanceFromTarget);
 					simUI.player.setHealingModel(eventID, newSettings.healingModel || HealingModel.create());
 					simUI.player.setNibelungAverageCasts(eventID, newSettings.nibelungAverageCasts)
-					if (aplLaunchStatuses[simUI.player.spec] == LaunchStatus.Unlaunched) {
-						simUI.player.setCooldowns(eventID, newSettings.cooldowns || Cooldowns.create());
-						if (newSettings.rotationJson) {
-							simUI.player.setRotation(eventID, simUI.player.specTypeFunctions.rotationFromJson(JSON.parse(newSettings.rotationJson)));
-						}
-					}
 				});
 			},
 			changeEmitters: [
@@ -457,10 +404,7 @@ export class SettingsTab extends SimTab {
 				this.simUI.player.inFrontOfTargetChangeEmitter,
 				this.simUI.player.distanceFromTargetChangeEmitter,
 				this.simUI.player.healingModelChangeEmitter,
-			].concat(aplLaunchStatuses[this.simUI.player.spec] == LaunchStatus.Unlaunched ? [
-				this.simUI.player.cooldownsChangeEmitter,
-				this.simUI.player.rotationChangeEmitter,
-			] : []),
+			],
 			equals: (a: SavedSettings, b: SavedSettings) => SavedSettings.equals(a, b),
 			toJson: (a: SavedSettings) => SavedSettings.toJson(a),
 			fromJson: (obj: any) => SavedSettings.fromJson(obj),
@@ -480,8 +424,6 @@ export class SettingsTab extends SimTab {
 				new BooleanPicker(sectionElem, this.simUI.player, { ...inputConfig, ...{ cssScheme: this.simUI.cssScheme } });
 			} else if (inputConfig.type == 'enum') {
 				new EnumPicker(sectionElem, this.simUI.player, inputConfig);
-			} else if (inputConfig.type == 'customRotation') {
-				new CustomRotationPicker(sectionElem, this.simUI, this.simUI.player, inputConfig);
 			}
 		});
 	};
diff --git a/ui/core/components/input_helpers.ts b/ui/core/components/input_helpers.ts
index 6feced79b3..4058200c79 100644
--- a/ui/core/components/input_helpers.ts
+++ b/ui/core/components/input_helpers.ts
@@ -1,10 +1,8 @@
 import { ActionId } from '../proto_utils/action_id.js';
-import { CustomRotation } from '../proto/common.js';
 import { Spec } from '../proto/common.js';
 import { Player } from '../player.js';
 import { EventID, TypedEvent } from '../typed_event.js';
 import { SpecOptions, SpecRotation } from '../proto_utils/utils.js';
-import { CustomRotationPickerConfig } from './individual_sim_ui/custom_rotation_picker.js';
 import { IconPickerConfig } from './icon_picker.js';
 import { IconEnumPickerConfig, IconEnumValueConfig } from './icon_enum_picker.js';
 import { EnumPickerConfig, EnumValueConfig } from './enum_picker.js';
@@ -446,34 +444,3 @@ export function makeRotationEnumIconInput<SpecType extends Spec, T>(config: Play
 		extraCssClasses: config.extraCssClasses,
 	});
 }
-
-export interface TypedCustomRotationPickerConfig<SpecType extends Spec, T> extends CustomRotationPickerConfig<SpecType, T> {
-	type: 'customRotation',
-}
-
-interface WrappedCustomRotationInputConfig<SpecType extends Spec, T> {
-	fieldName: keyof SpecRotation<SpecType>,
-	getValue?: (player: Player<SpecType>) => CustomRotation,
-	setValue?: (eventID: EventID, player: Player<SpecType>, newValue: CustomRotation) => void,
-
-	numColumns: number,
-	showCastsPerMinute?: boolean,
-	values: Array<IconEnumValueConfig<Player<SpecType>, T>>;
-
-	showWhen?: (player: Player<SpecType>) => boolean,
-}
-export function makeCustomRotationInput<SpecType extends Spec, T>(config: WrappedCustomRotationInputConfig<SpecType, T>): TypedCustomRotationPickerConfig<SpecType, T> {
-	return {
-		type: 'customRotation',
-		getValue: config.getValue || ((player: Player<SpecType>) => (player.getRotation()[config.fieldName] as unknown as CustomRotation) || CustomRotation.create()),
-		setValue: config.setValue || ((eventID: EventID, player: Player<SpecType>, newValue: CustomRotation) => {
-			const rotation = player.getRotation();
-			(rotation[config.fieldName] as unknown as CustomRotation) = newValue;
-			player.setRotation(eventID, rotation);
-		}),
-		showWhen: config.showWhen,
-		numColumns: config.numColumns,
-		showCastsPerMinute: config.showCastsPerMinute || false,
-		values: config.values,
-	}
-}
diff --git a/ui/core/individual_sim_ui.ts b/ui/core/individual_sim_ui.ts
index b1a26e633c..08502d6d36 100644
--- a/ui/core/individual_sim_ui.ts
+++ b/ui/core/individual_sim_ui.ts
@@ -1,4 +1,4 @@
-import { aplLaunchStatuses, LaunchStatus, simLaunchStatuses } from './launched_sims';
+import { simLaunchStatuses } from './launched_sims';
 import { Player, PlayerConfig, registerSpecConfig as registerPlayerConfig } from './player';
 import { SimUI, SimWarning } from './sim_ui';
 import { EventID, TypedEvent } from './typed_event';
@@ -47,7 +47,6 @@ import {
 	isHealingSpec,
 	isTankSpec,
 	SpecOptions,
-	SpecRotation,
 	specToEligibleRaces,
 	specToLocalStorageKey,
 } from './proto_utils/utils';
@@ -70,8 +69,7 @@ const SAVED_TALENTS_STORAGE_KEY = '__savedTalents__';
 export type InputConfig<ModObject> = (
 	InputHelpers.TypedBooleanPickerConfig<ModObject> |
 	InputHelpers.TypedNumberPickerConfig<ModObject> |
-	InputHelpers.TypedEnumPickerConfig<ModObject> |
-	InputHelpers.TypedCustomRotationPickerConfig<any, any>
+	InputHelpers.TypedEnumPickerConfig<ModObject>
 );
 
 export interface InputSection {
@@ -121,7 +119,6 @@ export interface IndividualSimUIConfig<SpecType extends Spec> extends PlayerConf
 		gear: EquipmentSpec,
 		epWeights: Stats,
 		consumes: Consumes,
-		rotation: SpecRotation<SpecType>,
 		talents: SavedTalents,
 		specOptions: SpecOptions<SpecType>,
 
@@ -137,7 +134,7 @@ export interface IndividualSimUIConfig<SpecType extends Spec> extends PlayerConf
 	playerInputs?: InputSection,
 	playerIconInputs: Array<IconInputs.IconInputConfig<Player<SpecType>, any>>,
 	petConsumeInputs?: Array<IconInputs.IconInputConfig<Player<SpecType>, any>>,
-	rotationInputs: InputSection;
+	rotationInputs?: InputSection;
 	rotationIconInputs?: Array<IconInputs.IconInputConfig<Player<any>, any>>;
 	includeBuffDebuffInputs: Array<any>,
 	excludeBuffDebuffInputs: Array<any>,
@@ -198,7 +195,6 @@ export abstract class IndividualSimUI<SpecType extends Spec> extends SimUI {
 			spec: player.spec,
 			knownIssues: config.knownIssues,
 			launchStatus: simLaunchStatuses[player.spec],
-			noticeText: aplLaunchStatuses[player.spec] == LaunchStatus.Alpha ? 'Rotation settings have been moved to the \'Rotation\' tab, where experimental APL options are also available. Try them out!' : undefined,
 		});
 		this.rootElem.classList.add('individual-sim-ui');
 		this.player = player;
@@ -293,9 +289,7 @@ export abstract class IndividualSimUI<SpecType extends Spec> extends SimUI {
 		this.bt = this.addBulkTab();
 		this.addSettingsTab();
 		this.addTalentsTab();
-		if (aplLaunchStatuses[this.player.spec] != LaunchStatus.Unlaunched) {
-			this.addRotationTab();
-		}
+		this.addRotationTab();
 
 		if (!this.isWithinRaidSim) {
 			this.addDetailedResultsTab();
@@ -415,11 +409,6 @@ export abstract class IndividualSimUI<SpecType extends Spec> extends SimUI {
 			this.player.setRace(eventID, specToEligibleRaces[this.player.spec][0]);
 			this.player.setGear(eventID, this.sim.db.lookupEquipmentSpec(this.individualConfig.defaults.gear));
 			this.player.setConsumes(eventID, this.individualConfig.defaults.consumes);
-			if (aplLaunchStatuses[this.player.spec] < LaunchStatus.Beta) {
-				this.player.setRotation(eventID, this.individualConfig.defaults.rotation);
-			} else if (aplLaunchStatuses[this.player.spec] == LaunchStatus.Beta) {
-				this.player.setRotation(eventID, this.player.specTypeFunctions.rotationCreate());
-			}
 			this.player.setTalentsString(eventID, this.individualConfig.defaults.talents.talentsString);
 			this.player.setGlyphs(eventID, this.individualConfig.defaults.talents.glyphs || Glyphs.create());
 			this.player.setSpecOptions(eventID, this.individualConfig.defaults.specOptions);
diff --git a/ui/core/launched_sims.ts b/ui/core/launched_sims.ts
index 47d45e1755..61ed8e65c6 100644
--- a/ui/core/launched_sims.ts
+++ b/ui/core/launched_sims.ts
@@ -38,31 +38,6 @@ export const simLaunchStatuses: Record<Spec, LaunchStatus> = {
 	[Spec.SpecTankDeathknight]: LaunchStatus.Launched,
 };
 
-// Alpha and Beta show an info notice at the top of the page.
-export const aplLaunchStatuses: Record<Spec, LaunchStatus> = {
-	[Spec.SpecBalanceDruid]: LaunchStatus.Beta,
-	[Spec.SpecFeralDruid]: LaunchStatus.Launched,
-	[Spec.SpecFeralTankDruid]: LaunchStatus.Launched,
-	[Spec.SpecRestorationDruid]: LaunchStatus.Launched,
-	[Spec.SpecElementalShaman]: LaunchStatus.Beta,
-	[Spec.SpecEnhancementShaman]: LaunchStatus.Beta,
-	[Spec.SpecRestorationShaman]: LaunchStatus.Launched,
-	[Spec.SpecHunter]: LaunchStatus.Launched,
-	[Spec.SpecMage]: LaunchStatus.Launched,
-	[Spec.SpecRogue]: LaunchStatus.Beta,
-	[Spec.SpecHolyPaladin]: LaunchStatus.Launched,
-	[Spec.SpecProtectionPaladin]: LaunchStatus.Launched,
-	[Spec.SpecRetributionPaladin]: LaunchStatus.Beta,
-	[Spec.SpecHealingPriest]: LaunchStatus.Launched,
-	[Spec.SpecShadowPriest]: LaunchStatus.Beta,
-	[Spec.SpecSmitePriest]: LaunchStatus.Launched,
-	[Spec.SpecWarlock]: LaunchStatus.Beta,
-	[Spec.SpecWarrior]: LaunchStatus.Launched,
-	[Spec.SpecProtectionWarrior]: LaunchStatus.Launched,
-	[Spec.SpecDeathknight]: LaunchStatus.Beta,
-	[Spec.SpecTankDeathknight]: LaunchStatus.Beta,
-};
-
 // Meme specs are excluded from title drop-down menu.
 export const memeSpecs: Array<Spec> = [
 	Spec.SpecSmitePriest,
diff --git a/ui/core/player.ts b/ui/core/player.ts
index 0c64a544c5..3a5e80b252 100644
--- a/ui/core/player.ts
+++ b/ui/core/player.ts
@@ -46,7 +46,6 @@ import { Player as PlayerProto } from './proto/api.js';
 import { StatWeightsResult } from './proto/api.js';
 import { ActionId } from './proto_utils/action_id.js';
 import { EquippedItem, getWeaponDPS } from './proto_utils/equipped_item.js';
-import { aplLaunchStatuses, LaunchStatus } from './launched_sims';
 
 import { playerTalentStringToProto } from './talents/factory.js';
 import { Gear, ItemSwapGear } from './proto_utils/gear.js';
@@ -321,7 +320,7 @@ export class Player<SpecType extends Spec> {
 			throw new Error('Could not find spec config for spec: ' + this.spec);
 		}
 		this.autoRotationGenerator = specConfig.autoRotation;
-		if (aplLaunchStatuses[this.spec] == LaunchStatus.Launched && specConfig.simpleRotation) {
+		if (specConfig.simpleRotation) {
 			this.simpleRotationGenerator = specConfig.simpleRotation;
 		} else {
 			this.simpleRotationGenerator = null;
@@ -600,27 +599,18 @@ export class Player<SpecType extends Spec> {
 
 	getCooldowns(): Cooldowns {
 		// Make a defensive copy
-		if (aplLaunchStatuses[this.spec] == LaunchStatus.Launched) {
-			return Cooldowns.clone(this.aplRotation.simple?.cooldowns || Cooldowns.create());
-		} else {
-			return Cooldowns.clone(this.cooldowns);
-		}
+		return Cooldowns.clone(this.aplRotation.simple?.cooldowns || Cooldowns.create());
 	}
 
 	setCooldowns(eventID: EventID, newCooldowns: Cooldowns) {
 		if (Cooldowns.equals(this.getCooldowns(), newCooldowns))
 			return;
 
-		if (aplLaunchStatuses[this.spec] == LaunchStatus.Launched) {
-			if (!this.aplRotation.simple) {
-				this.aplRotation.simple = SimpleRotation.create();
-			}
-			this.aplRotation.simple.cooldowns = newCooldowns;
-			this.rotationChangeEmitter.emit(eventID);
-		} else {
-			// Make a defensive copy
-			this.cooldowns = Cooldowns.clone(newCooldowns);
+		if (!this.aplRotation.simple) {
+			this.aplRotation.simple = SimpleRotation.create();
 		}
+		this.aplRotation.simple.cooldowns = newCooldowns;
+		this.rotationChangeEmitter.emit(eventID);
 
 		this.cooldownsChangeEmitter.emit(eventID);
 	}
@@ -762,21 +752,17 @@ export class Player<SpecType extends Spec> {
 	}
 
 	getRotation(): SpecRotation<SpecType> {
-		if (aplLaunchStatuses[this.spec] == LaunchStatus.Launched) {
-			const jsonStr = this.aplRotation.simple?.specRotationJson || '';
-			if (!jsonStr) {
-				return this.specTypeFunctions.rotationCreate();
-			}
+		const jsonStr = this.aplRotation.simple?.specRotationJson || '';
+		if (!jsonStr) {
+			return this.specTypeFunctions.rotationCreate();
+		}
 
-			try {
-				const json = JSON.parse(jsonStr);
-				return this.specTypeFunctions.rotationFromJson(json);
-			} catch (e) {
-				console.warn(`Error parsing rotation spec options: ${e}\n\nSpec options: '${jsonStr}'`);
-				return this.specTypeFunctions.rotationCreate();
-			}
-		} else {
-			return this.specTypeFunctions.rotationCopy(this.rotation);
+		try {
+			const json = JSON.parse(jsonStr);
+			return this.specTypeFunctions.rotationFromJson(json);
+		} catch (e) {
+			console.warn(`Error parsing rotation spec options: ${e}\n\nSpec options: '${jsonStr}'`);
+			return this.specTypeFunctions.rotationCreate();
 		}
 	}
 
@@ -784,14 +770,10 @@ export class Player<SpecType extends Spec> {
 		if (this.specTypeFunctions.rotationEquals(newRotation, this.getRotation()))
 			return;
 
-		if (aplLaunchStatuses[this.spec] == LaunchStatus.Launched) {
-			if (!this.aplRotation.simple) {
-				this.aplRotation.simple = SimpleRotation.create();
-			}
-			this.aplRotation.simple.specRotationJson = JSON.stringify(this.specTypeFunctions.rotationToJson(newRotation));
-		} else {
-			this.rotation = this.specTypeFunctions.rotationCopy(newRotation);
+		if (!this.aplRotation.simple) {
+			this.aplRotation.simple = SimpleRotation.create();
 		}
+		this.aplRotation.simple.specRotationJson = JSON.stringify(this.specTypeFunctions.rotationToJson(newRotation));
 
 		this.rotationChangeEmitter.emit(eventID);
 	}
@@ -1337,7 +1319,6 @@ export class Player<SpecType extends Spec> {
 				|| exportCategories.length == 0
 				|| exportCategories.includes(cat);
 
-		const aplIsLaunched = aplLaunchStatuses[this.spec] == LaunchStatus.Launched;
 		const gear = this.getGear();
 		const aplRotation = forSimming ? this.getResolvedAplRotation() : this.aplRotation;
 
@@ -1361,9 +1342,7 @@ export class Player<SpecType extends Spec> {
 		}
 		if (exportCategory(SimSettingCategories.Rotation)) {
 			PlayerProto.mergePartial(player, {
-				cooldowns: (aplIsLaunched || (forSimming && aplRotation.type == APLRotationType.TypeAPL))
-					? Cooldowns.create({ hpPercentForDefensives: this.getCooldowns().hpPercentForDefensives })
-					: this.getCooldowns(),
+				cooldowns: Cooldowns.create({ hpPercentForDefensives: this.getCooldowns().hpPercentForDefensives }),
 				rotation: aplRotation,
 			});
 		}
@@ -1386,21 +1365,14 @@ export class Player<SpecType extends Spec> {
 				nibelungAverageCasts: this.getNibelungAverageCasts(),
 				nibelungAverageCastsSet: this.nibelungAverageCastsSet,
 			});
+			player = withSpecProto(this.spec, player, this.getSpecOptions());
 		}
 		if (exportCategory(SimSettingCategories.External)) {
 			PlayerProto.mergePartial(player, {
 				buffs: this.getBuffs(),
 			});
 		}
-		return withSpecProto(
-			this.spec,
-			player,
-			(aplIsLaunched || (forSimming && aplRotation.type == APLRotationType.TypeAPL) || !exportCategory(SimSettingCategories.Rotation))
-				? this.specTypeFunctions.rotationCreate()
-				: this.getRotation(),
-			exportCategory(SimSettingCategories.Miscellaneous)
-				? this.getSpecOptions()
-				: this.specTypeFunctions.optionsCreate());
+		return player;
 	}
 
 	fromProto(eventID: EventID, proto: PlayerProto, includeCategories?: Array<SimSettingCategories>) {
@@ -1424,24 +1396,22 @@ export class Player<SpecType extends Spec> {
 			}
 		}
 
-		if (aplLaunchStatuses[this.spec] == LaunchStatus.Launched) {
-			const rot = this.specTypeFunctions.rotationFromPlayer(proto);
-			if (rot && !this.specTypeFunctions.rotationEquals(rot, this.specTypeFunctions.rotationCreate())) {
-				if (proto.rotation?.type == APLRotationType.TypeAPL) {
-					// Do nothing
-				} else if (this.simpleRotationGenerator) {
-					proto.rotation = APLRotation.create({
-						type: APLRotationType.TypeSimple,
-						simple: {
-							specRotationJson: JSON.stringify(this.specTypeFunctions.rotationToJson(rot)),
-							cooldowns: proto.cooldowns,
-						},
-					});
-				} else {
-					proto.rotation = APLRotation.create({
-						type: APLRotationType.TypeAuto,
-					});
-				}
+		const rot = this.specTypeFunctions.rotationFromPlayer(proto);
+		if (rot && !this.specTypeFunctions.rotationEquals(rot, this.specTypeFunctions.rotationCreate())) {
+			if (proto.rotation?.type == APLRotationType.TypeAPL) {
+				// Do nothing
+			} else if (this.simpleRotationGenerator) {
+				proto.rotation = APLRotation.create({
+					type: APLRotationType.TypeSimple,
+					simple: {
+						specRotationJson: JSON.stringify(this.specTypeFunctions.rotationToJson(rot)),
+						cooldowns: proto.cooldowns,
+					},
+				});
+			} else {
+				proto.rotation = APLRotation.create({
+					type: APLRotationType.TypeAuto,
+				});
 			}
 		}
 
@@ -1458,16 +1428,11 @@ export class Player<SpecType extends Spec> {
 				this.setGlyphs(eventID, proto.glyphs || Glyphs.create());
 			}
 			if (loadCategory(SimSettingCategories.Rotation)) {
-				if (aplLaunchStatuses[this.spec] == LaunchStatus.Launched) {
-					if (proto.rotation?.type == APLRotationType.TypeUnknown || proto.rotation?.type == APLRotationType.TypeLegacy) {
-						if (!proto.rotation) {
-							proto.rotation = APLRotation.create();
-						}
-						proto.rotation.type = APLRotationType.TypeAuto;
+				if (proto.rotation?.type == APLRotationType.TypeUnknown || proto.rotation?.type == APLRotationType.TypeLegacy) {
+					if (!proto.rotation) {
+						proto.rotation = APLRotation.create();
 					}
-				} else {
-					this.setCooldowns(eventID, proto.cooldowns || Cooldowns.create());
-					this.setRotation(eventID, this.specTypeFunctions.rotationFromPlayer(proto));
+					proto.rotation.type = APLRotationType.TypeAuto;
 				}
 				this.setAplRotation(eventID, proto.rotation || APLRotation.create())
 			}
diff --git a/ui/core/proto_utils/utils.ts b/ui/core/proto_utils/utils.ts
index 650c413a41..8a6be3a96a 100644
--- a/ui/core/proto_utils/utils.ts
+++ b/ui/core/proto_utils/utils.ts
@@ -1318,7 +1318,6 @@ export const specToLocalStorageKey: Record<Spec, string> = {
 export function withSpecProto<SpecType extends Spec>(
 	spec: Spec,
 	player: Player,
-	rotation: SpecRotation<SpecType>,
 	specOptions: SpecOptions<SpecType>): Player {
 	const copy = Player.clone(player);
 
@@ -1327,7 +1326,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'balanceDruid',
 				balanceDruid: BalanceDruid.create({
-					rotation: rotation as BalanceDruidRotation,
 					options: specOptions as BalanceDruidOptions,
 				}),
 			};
@@ -1336,7 +1334,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'feralDruid',
 				feralDruid: FeralDruid.create({
-					rotation: rotation as FeralDruidRotation,
 					options: specOptions as FeralDruidOptions,
 				}),
 			};
@@ -1345,7 +1342,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'feralTankDruid',
 				feralTankDruid: FeralTankDruid.create({
-					rotation: rotation as FeralTankDruidRotation,
 					options: specOptions as FeralTankDruidOptions,
 				}),
 			};
@@ -1354,7 +1350,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'restorationDruid',
 				restorationDruid: RestorationDruid.create({
-					rotation: rotation as RestorationDruidRotation,
 					options: specOptions as RestorationDruidOptions,
 				}),
 			};
@@ -1363,7 +1358,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'elementalShaman',
 				elementalShaman: ElementalShaman.create({
-					rotation: rotation as ElementalShamanRotation,
 					options: specOptions as ElementalShamanOptions,
 				}),
 			};
@@ -1372,7 +1366,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'enhancementShaman',
 				enhancementShaman: EnhancementShaman.create({
-					rotation: rotation as EnhancementShamanRotation,
 					options: specOptions as ElementalShamanOptions,
 				}),
 			};
@@ -1381,7 +1374,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'restorationShaman',
 				restorationShaman: RestorationShaman.create({
-					rotation: rotation as RestorationShamanRotation,
 					options: specOptions as RestorationShamanOptions,
 				}),
 			};
@@ -1390,7 +1382,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'hunter',
 				hunter: Hunter.create({
-					rotation: rotation as HunterRotation,
 					options: specOptions as HunterOptions,
 				}),
 			};
@@ -1399,7 +1390,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'mage',
 				mage: Mage.create({
-					rotation: rotation as MageRotation,
 					options: specOptions as MageOptions,
 				}),
 			};
@@ -1408,7 +1398,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'holyPaladin',
 				holyPaladin: HolyPaladin.create({
-					rotation: rotation as HolyPaladinRotation,
 					options: specOptions as HolyPaladinOptions,
 				}),
 			};
@@ -1417,7 +1406,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'protectionPaladin',
 				protectionPaladin: ProtectionPaladin.create({
-					rotation: rotation as ProtectionPaladinRotation,
 					options: specOptions as ProtectionPaladinOptions,
 				}),
 			};
@@ -1426,7 +1414,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'retributionPaladin',
 				retributionPaladin: RetributionPaladin.create({
-					rotation: rotation as RetributionPaladinRotation,
 					options: specOptions as RetributionPaladinOptions,
 				}),
 			};
@@ -1435,7 +1422,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'rogue',
 				rogue: Rogue.create({
-					rotation: rotation as RogueRotation,
 					options: specOptions as RogueOptions,
 				}),
 			};
@@ -1444,7 +1430,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'healingPriest',
 				healingPriest: HealingPriest.create({
-					rotation: rotation as HealingPriestRotation,
 					options: specOptions as HealingPriestOptions,
 				}),
 			};
@@ -1453,7 +1438,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'shadowPriest',
 				shadowPriest: ShadowPriest.create({
-					rotation: rotation as ShadowPriestRotation,
 					options: specOptions as ShadowPriestOptions,
 				}),
 			};
@@ -1462,7 +1446,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'smitePriest',
 				smitePriest: SmitePriest.create({
-					rotation: rotation as SmitePriestRotation,
 					options: specOptions as SmitePriestOptions,
 				}),
 			};
@@ -1471,7 +1454,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'warlock',
 				warlock: Warlock.create({
-					rotation: rotation as WarlockRotation,
 					options: specOptions as WarlockOptions,
 				}),
 			};
@@ -1480,7 +1462,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'warrior',
 				warrior: Warrior.create({
-					rotation: rotation as WarriorRotation,
 					options: specOptions as WarriorOptions,
 				}),
 			};
@@ -1489,7 +1470,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'protectionWarrior',
 				protectionWarrior: ProtectionWarrior.create({
-					rotation: rotation as ProtectionWarriorRotation,
 					options: specOptions as ProtectionWarriorOptions,
 				}),
 			};
@@ -1498,7 +1478,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'deathknight',
 				deathknight: Deathknight.create({
-					rotation: rotation as DeathknightRotation,
 					options: specOptions as DeathknightOptions,
 				}),
 			};
@@ -1507,7 +1486,6 @@ export function withSpecProto<SpecType extends Spec>(
 			copy.spec = {
 				oneofKind: 'tankDeathknight',
 				tankDeathknight: TankDeathknight.create({
-					rotation: rotation as TankDeathknightRotation,
 					options: specOptions as TankDeathknightOptions,
 				}),
 			};
diff --git a/ui/deathknight/inputs.ts b/ui/deathknight/inputs.ts
index 84e520a551..71df139b96 100644
--- a/ui/deathknight/inputs.ts
+++ b/ui/deathknight/inputs.ts
@@ -1,22 +1,7 @@
-import { ItemSlot, Spec } from '../core/proto/common.js';
+import { Spec } from '../core/proto/common.js';
 import { UnitReference, UnitReference_Type as UnitType } from '../core/proto/common.js';
-import { ActionId } from '../core/proto_utils/action_id.js';
 
 import {
-	DeathknightTalents as DeathKnightTalents,
-	Deathknight_Rotation_ArmyOfTheDead as ArmyOfTheDead,
-	Deathknight_Rotation_DrwDiseases as DrwDiseases,
-	Deathknight_Rotation_BloodOpener as BloodOpener,
-	Deathknight_Rotation_BloodSpell as BloodSpell,
-	Deathknight_Rotation_FirstDisease as FirstDisease,
-	Deathknight_Rotation_DeathAndDecayPrio as DeathAndDecayPrio,
-	Deathknight_Rotation_Presence as StartingPresence,
-	Deathknight_Rotation_BloodRuneFiller as BloodRuneFiller,
-	Deathknight_Rotation_BloodTap as BloodTap,
-	Deathknight_Rotation_FrostRotationType as FrostRotationType,
-	Deathknight_Rotation_CustomSpellOption as CustomSpellOption,
-	Deathknight_Rotation as DeathKnightRotation,
-	Deathknight_Options as DeathKnightOptions,
 	DeathknightMajorGlyph,
 } from '../core/proto/deathknight.js';
 
@@ -79,301 +64,14 @@ export const DrwPestiApply = InputHelpers.makeSpecOptionsBooleanInput<Spec.SpecD
 	fieldName: 'drwPestiApply',
 	label: 'DRW Pestilence Add',
 	labelTooltip: 'There is currently an interaction with DRW and pestilence where you can use pestilence to force DRW to apply diseases if they are already applied by the DK. It only works with Glyph of Disease and if there is an off target. This toggle forces the sim to assume there is an off target.',
-	showWhen: (player: Player<Spec.SpecDeathknight>) => !player.getRotation().autoRotation && player.getTalentTree() == 0 && (player.getGlyphs().major1 == DeathknightMajorGlyph.GlyphOfDisease || player.getGlyphs().major2 == DeathknightMajorGlyph.GlyphOfDisease || player.getGlyphs().major3 == DeathknightMajorGlyph.GlyphOfDisease),
+	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalentTree() == 0 && (player.getGlyphs().major1 == DeathknightMajorGlyph.GlyphOfDisease || player.getGlyphs().major2 == DeathknightMajorGlyph.GlyphOfDisease || player.getGlyphs().major3 == DeathknightMajorGlyph.GlyphOfDisease),
 	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.specOptionsChangeEmitter, player.rotationChangeEmitter, player.talentsChangeEmitter]),
 });
 
-export const DiseaseRefreshDuration = InputHelpers.makeRotationNumberInput<Spec.SpecDeathknight>({
-	fieldName: 'diseaseRefreshDuration',
-	label: 'Disease Refresh Duration',
-	labelTooltip: 'Minimum duration for refreshing a disease.',
-});
-
-export const UseDeathAndDecay = InputHelpers.makeRotationBooleanInput<Spec.SpecDeathknight>({
-	fieldName: 'useDeathAndDecay',
-	label: 'Death and Decay',
-	labelTooltip: 'Use Death and Decay based rotation.',
-	showWhen: (player: Player<Spec.SpecDeathknight>) => (player.getTalents().summonGargoyle && player.getTalents().scourgeStrike && !player.getRotation().autoRotation) || (!player.getTalents().epidemic && !player.getRotation().desyncRotation && player.getTalentTree() == 1 && player.sim.encounter.targets.length >= 3),
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter, player.sim.encounter.targetsChangeEmitter]),
-});
-
-export const SetDeathAndDecayPrio = InputHelpers.makeRotationEnumInput<Spec.SpecDeathknight, DeathAndDecayPrio>({
-	fieldName: 'deathAndDecayPrio',
-	label: 'Death and Decay Prio',
-	labelTooltip: '<p>Chose how to prioritize death and decay usage:</p>\
-		<p><b>Max Rune Downtime</b>: Prioritizes spending runes over holding them for death and decay</p>\
-		<p><b>Max Dnd Uptime</b>: Prioritizes dnd uptime and can hold runes for longer then rune grace</p>',
-	values: [
-		{ name: 'Max Rune Downtime', value: DeathAndDecayPrio.MaxRuneDowntime },
-		{ name: 'Max Dnd Uptime', value: DeathAndDecayPrio.MaxDndUptime },
-	],
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().summonGargoyle && (player.getRotation().useDeathAndDecay || !player.getTalents().scourgeStrike) && !player.getRotation().autoRotation,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-})
-
-export const UseEmpowerRuneWeapon = InputHelpers.makeRotationBooleanInput<Spec.SpecDeathknight>({
-	fieldName: 'useEmpowerRuneWeapon',
-	label: 'Empower Rune Weapon',
-	labelTooltip: 'Use Empower Rune Weapon in rotation.',
-	showWhen: (player: Player<Spec.SpecDeathknight>) => !player.getRotation().autoRotation && player.getTalentTree() != 0 && player.getRotation().frostRotationType != FrostRotationType.Custom,
-});
-
-export const UseGargoyle = InputHelpers.makeRotationBooleanInput<Spec.SpecDeathknight>({
-	fieldName: 'useGargoyle',
-	label: 'Summon Gargoyle',
-	labelTooltip: 'Use Summon Gargoyle in rotation.',
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().summonGargoyle && !player.getRotation().autoRotation,
-});
-
-export const HoldErwArmy = InputHelpers.makeRotationBooleanInput<Spec.SpecDeathknight>({
-	fieldName: 'holdErwArmy',
-	label: 'Hold ERW for AotD',
-	labelTooltip: 'Hold Empower Rune Weapon for after Summon Gargoyle to guarantee maximized snapshot for Army of the Dead.',
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-	showWhen: (player: Player<Spec.SpecDeathknight>) => !player.getRotation().autoRotation && player.getRotation().useEmpowerRuneWeapon && player.getRotation().armyOfTheDead == ArmyOfTheDead.AsMajorCd && player.getTalentTree() != 0,
-});
-
-export const BloodlustPresence = InputHelpers.makeRotationEnumInput<Spec.SpecDeathknight, StartingPresence>({
-	fieldName: 'blPresence',
-	label: 'Bloodlust Presence',
-	labelTooltip: 'Presence during bloodlust.',
-	values: [
-		{ name: 'Blood', value: StartingPresence.Blood },
-		{ name: 'Unholy', value: StartingPresence.Unholy },
-	],
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().summonGargoyle && !player.getRotation().autoRotation,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
-export const GargoylePresence = InputHelpers.makeRotationEnumInput<Spec.SpecDeathknight, StartingPresence>({
-	fieldName: 'gargoylePresence',
-	label: 'Gargoyle Presence',
-	labelTooltip: 'Presence during Gargoyle.',
-	values: [
-		{ name: 'Blood', value: StartingPresence.Blood },
-		{ name: 'Unholy', value: StartingPresence.Unholy },
-	],
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().summonGargoyle && !player.getRotation().autoRotation && !player.getRotation().preNerfedGargoyle,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
-export const BloodTapGhoulFrenzy = InputHelpers.makeRotationBooleanInput<Spec.SpecDeathknight>({
-	fieldName: 'btGhoulFrenzy',
-	label: 'BT Ghoul Frenzy',
-	labelTooltip: 'Use Ghoul Frenzy only with Blood Tap.',
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().ghoulFrenzy && !player.getRotation().autoRotation,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
-export const FirstDiseaseInput = InputHelpers.makeRotationEnumInput<Spec.SpecDeathknight, FirstDisease>({
-	fieldName: 'firstDisease',
-	label: 'First Disease',
-	labelTooltip: 'Chose which disease to apply first.',
-	values: [
-		{ name: 'Frost Fever', value: FirstDisease.FrostFever },
-		{ name: 'Blood Plague', value: FirstDisease.BloodPlague },
-	],
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().summonGargoyle && !player.getRotation().autoRotation,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-})
-
-export const ArmyOfTheDeadInput = InputHelpers.makeRotationEnumInput<Spec.SpecDeathknight, ArmyOfTheDead>({
-	fieldName: 'armyOfTheDead',
-	label: 'Army of the Dead',
-	labelTooltip: 'Chose how to use Army of the Dead.',
-	values: [
-		{ name: 'Do not use', value: ArmyOfTheDead.DoNotUse },
-		{ name: 'Pre pull', value: ArmyOfTheDead.PreCast },
-		{ name: 'As Major CD', value: ArmyOfTheDead.AsMajorCd },
-	],
-	showWhen: (player: Player<Spec.SpecDeathknight>) => !player.getRotation().autoRotation,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
-export const StartingPresenceInput = InputHelpers.makeRotationEnumInput<Spec.SpecDeathknight, StartingPresence>({
-	fieldName: 'startingPresence',
-	label: 'Starting Presence',
-	labelTooltip: 'Chose the presence you start combat in.',
-	values: [
-		{ name: 'Blood', value: StartingPresence.Blood },
-		{ name: 'Unholy', value: StartingPresence.Unholy },
-	],
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().summonGargoyle && !player.getRotation().autoRotation,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-})
-
-export const FightPresence = InputHelpers.makeRotationEnumInput<Spec.SpecDeathknight, StartingPresence>({
-	fieldName: 'presence',
-	label: 'Fight Presence',
-	labelTooltip: 'Presence to be in during the encounter.',
-	values: [
-		{ name: 'Blood', value: StartingPresence.Blood },
-		{ name: 'Unholy', value: StartingPresence.Unholy },
-	],
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().summonGargoyle && !player.getRotation().autoRotation,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
-export const BloodRuneFillerInput = InputHelpers.makeRotationEnumInput<Spec.SpecDeathknight, BloodRuneFiller>({
-	fieldName: 'bloodRuneFiller',
-	label: 'Blood Rune Filler',
-	labelTooltip: 'Chose what to spend your free blood runes on.',
-	values: [
-		{ name: 'Blood Strike', value: BloodRuneFiller.BloodStrike },
-		{ name: 'Blood Boil', value: BloodRuneFiller.BloodBoil },
-	],
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().summonGargoyle && !player.getRotation().autoRotation,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-})
-
-export const PreNerfedGargoyleInput = InputHelpers.makeRotationBooleanInput<Spec.SpecDeathknight>({
-	fieldName: 'preNerfedGargoyle',
-	label: 'Pre-Nerfed Gargoyle (haste snapshot)',
-	labelTooltip: "Use old Gargoyle that snapshots haste",
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().summonGargoyle && !player.getRotation().autoRotation && player.sim.getShowExperimental(),
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-})
-
-export const BloodTapInput = InputHelpers.makeRotationEnumInput<Spec.SpecDeathknight, BloodTap>({
-	fieldName: 'bloodTap',
-	label: 'Blood Tap',
-	labelTooltip: 'Chose what to spend your Blood Tap on.',
-	values: [
-		{ name: 'Ghoul Frenzy', value: BloodTap.GhoulFrenzy },
-		{ name: 'Icy Touch', value: BloodTap.IcyTouch },
-		{ name: 'Blood Strike', value: BloodTap.BloodStrikeBT },
-		{ name: 'Blood Boil', value: BloodTap.BloodBoilBT },
-	],
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().summonGargoyle && !player.getRotation().autoRotation,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-})
-
-export const UseAMSInput = InputHelpers.makeRotationBooleanInput<Spec.SpecDeathknight>({
-	fieldName: 'useAms',
-	label: 'Use AMS',
-	labelTooltip: 'Use AMS around predicted damage for a RP gain.',
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().howlingBlast && !player.getRotation().autoRotation && player.getRotation().frostRotationType != FrostRotationType.Custom,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
-export const AvgAMSSuccessRateInput = InputHelpers.makeRotationNumberInput<Spec.SpecDeathknight>({
-	fieldName: 'avgAmsSuccessRate',
-	label: 'Avg AMS Success %',
-	labelTooltip: 'Chance for damage to be taken during the 5 second window of AMS.',
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getRotation().useAms == true && !player.getRotation().autoRotation && player.getTalents().howlingBlast && player.getRotation().frostRotationType != FrostRotationType.Custom,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
-export const AvgAMSHitInput = InputHelpers.makeRotationNumberInput<Spec.SpecDeathknight>({
-	fieldName: 'avgAmsHit',
-	label: 'Avg AMS Hit',
-	labelTooltip: 'How much on average (+-10%) the character is hit for when AMS is successful.',
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getRotation().useAms == true && !player.getRotation().autoRotation && player.getTalents().howlingBlast && player.getRotation().frostRotationType != FrostRotationType.Custom,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
-export const UseAutoRotation = InputHelpers.makeRotationBooleanInput<Spec.SpecDeathknight>({
-	fieldName: 'autoRotation',
-	label: 'Automatic Rotation',
-	labelTooltip: 'Have sim automatically adjust rotation based on the scenario. This is still in development and currently only works for Unholy.',
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-	showWhen: (player: Player<Spec.SpecDeathknight>) => !player.getTalents().howlingBlast,
-});
-
-export const DesyncRotation = InputHelpers.makeRotationBooleanInput<Spec.SpecDeathknight>({
-	fieldName: 'desyncRotation',
-	label: 'Use Desync Rotation',
-	labelTooltip: 'Use the Desync Rotation.',
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().howlingBlast && !player.getTalents().epidemic && !player.getRotation().autoRotation,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
-export const Presence = InputHelpers.makeRotationEnumInput<Spec.SpecDeathknight, StartingPresence>({
-	fieldName: 'presence',
-	label: 'Presence',
-	labelTooltip: 'Presence to be in during the encounter.',
-	values: [
-		{ name: 'Blood', value: StartingPresence.Blood },
-		{ name: 'Frost', value: StartingPresence.Frost },
-		{ name: 'Unholy', value: StartingPresence.Unholy },
-	],
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().howlingBlast && !player.getRotation().autoRotation,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
-export const BloodSpenderInput = InputHelpers.makeRotationEnumInput<Spec.SpecDeathknight, BloodSpell>({
-	fieldName: 'bloodSpender',
-	label: 'Blood Spender',
-	labelTooltip: 'Chose how to use Blood Runes.',
-	values: [
-		{ name: 'Heart Strike', value: BloodSpell.HS },
-		{ name: 'Blood Boil', value: BloodSpell.BB },
-		{ name: 'Blood Strike', value: BloodSpell.BS },
-	],
-	showWhen: (player: Player<Spec.SpecDeathknight>) => !player.getRotation().autoRotation && player.getTalentTree() == 0,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
-export const UseDancingRuneWeapon = InputHelpers.makeRotationBooleanInput<Spec.SpecDeathknight>({
-	fieldName: 'useDancingRuneWeapon',
-	label: 'Dancing Rune Weapon',
-	labelTooltip: 'Use Dancing Rune Weapon.',
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().dancingRuneWeapon && !player.getRotation().autoRotation,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
-export const DrwDiseasesInput = InputHelpers.makeRotationEnumInput<Spec.SpecDeathknight, DrwDiseases>({
-	fieldName: 'drwDiseases',
-	label: 'DRW Disease',
-	labelTooltip: 'Chose how to apply diseases for Dancing Rune Weapon.',
-	values: [
-		{ name: 'Do not apply', value: DrwDiseases.DoNotApply },
-		{ name: 'IT + PS', value: DrwDiseases.Normal },
-		{ name: 'Pestilence', value: DrwDiseases.Pestilence },
-	],
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getRotation().useDancingRuneWeapon && !player.getRotation().autoRotation && player.getTalents().dancingRuneWeapon && player.getRotation().bloodOpener == BloodOpener.Standard,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
-export const BloodOpenerInput = InputHelpers.makeRotationEnumInput<Spec.SpecDeathknight, BloodOpener>({
-	fieldName: 'bloodOpener',
-	label: 'Opener',
-	labelTooltip: 'Chose which opener to use.',
-	values: [
-		{ name: 'Standard', value: BloodOpener.Standard },
-		{ name: 'Incan', value: BloodOpener.Experimental_1 },
-	],
-	showWhen: (player: Player<Spec.SpecDeathknight>) => !player.getRotation().autoRotation && player.getTalentTree() == 0,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
-export const FrostCustomRotation = InputHelpers.makeCustomRotationInput<Spec.SpecDeathknight, CustomSpellOption>({
-	fieldName: 'frostCustomRotation',
-	numColumns: 4,
-	values: [
-		{ actionId: ActionId.fromSpellId(49909), value: CustomSpellOption.CustomIcyTouch },
-		{ actionId: ActionId.fromSpellId(49921), value: CustomSpellOption.CustomPlagueStrike },
-		{ actionId: ActionId.fromSpellId(50842), value: CustomSpellOption.CustomPestilence },
-		{ actionId: ActionId.fromSpellId(51425), value: CustomSpellOption.CustomObliterate },
-		{ actionId: ActionId.fromSpellId(51411), value: CustomSpellOption.CustomHowlingBlast },
-		{ actionId: ActionId.fromSpellId(59052), value: CustomSpellOption.CustomHowlingBlastRime },
-		{ actionId: ActionId.fromSpellId(49941), value: CustomSpellOption.CustomBloodBoil },
-		{ actionId: ActionId.fromSpellId(49930), value: CustomSpellOption.CustomBloodStrike },
-		{ actionId: ActionId.fromSpellId(49938), value: CustomSpellOption.CustomDeathAndDecay },
-		{ actionId: ActionId.fromSpellId(57623), value: CustomSpellOption.CustomHornOfWinter },
-		{ actionId: ActionId.fromSpellId(51271), value: CustomSpellOption.CustomUnbreakableArmor },
-		{ actionId: ActionId.fromSpellId(45529), value: CustomSpellOption.CustomBloodTap },
-		{ actionId: ActionId.fromSpellId(47568), value: CustomSpellOption.CustomEmpoweredRuneWeapon },
-		{ actionId: ActionId.fromSpellId(55268), value: CustomSpellOption.CustomFrostStrike },
-	],
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getRotation().frostRotationType == FrostRotationType.Custom,
-});
-
 export const NewDrwInput = InputHelpers.makeSpecOptionsBooleanInput<Spec.SpecDeathknight>({
 	fieldName: 'newDrw',
 	label: 'PTR DRW Scaling',
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().dancingRuneWeapon && player.getRotation().useDancingRuneWeapon,
+	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().dancingRuneWeapon,
 	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
 })
 
@@ -381,56 +79,6 @@ export const DiseaseDowntime = InputHelpers.makeSpecOptionsNumberInput<Spec.Spec
 	fieldName: 'diseaseDowntime',
 	label: 'Disease Downtime',
 	labelTooltip: 'Maximum allowed downtime between disease applications.',
-	showWhen: (player: Player<Spec.SpecDeathknight>) => !player.getRotation().autoRotation && player.getTalentTree() == 2,
+	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalentTree() == 2,
 	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
 });
-
-export const VirulenceRefreshTime = InputHelpers.makeRotationNumberInput<Spec.SpecDeathknight>({
-	fieldName: 'virulenceRefresh',
-	label: 'Virulence Refresh',
-	labelTooltip: 'How long to wait after ICD is ready before trying to refresh buff with strike (0-10 seconds range).',
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.sim.getShowExperimental() && !player.getRotation().autoRotation && player.getTalentTree() == 2 && player.getEquippedItem(ItemSlot.ItemSlotRanged)?._item.id == 47673,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter, player.gearChangeEmitter]),
-});
-
-export const DeathKnightRotationConfig = {
-	inputs: [
-		InputHelpers.makeRotationEnumInput<Spec.SpecDeathknight, FrostRotationType>({
-			fieldName: 'frostRotationType',
-			label: 'Rotation Type',
-			values: [
-				{ name: 'Single Target', value: FrostRotationType.SingleTarget },
-				{ name: 'Custom', value: FrostRotationType.Custom },
-			],
-			changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-			showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().howlingBlast && !player.getRotation().autoRotation,
-		}),
-		Presence,
-		UseAutoRotation,
-		BloodTapGhoulFrenzy,
-		UseGargoyle,
-		UseEmpowerRuneWeapon,
-		UseDancingRuneWeapon,
-		//NewDrwInput,
-		HoldErwArmy,
-		BloodTapInput,
-		BloodSpenderInput,
-		ArmyOfTheDeadInput,
-		//BloodOpenerInput,
-		DrwDiseasesInput,
-		FirstDiseaseInput,
-		StartingPresenceInput,
-		GargoylePresence,
-		BloodlustPresence,
-		FightPresence,
-		BloodRuneFillerInput,
-		UseDeathAndDecay,
-		UseAMSInput,
-		AvgAMSSuccessRateInput,
-		AvgAMSHitInput,
-		DesyncRotation,
-		FrostCustomRotation,
-		PreNerfedGargoyleInput,
-		VirulenceRefreshTime,
-	],
-};
diff --git a/ui/deathknight/presets.ts b/ui/deathknight/presets.ts
index f29f1149a8..d73bd8b94d 100644
--- a/ui/deathknight/presets.ts
+++ b/ui/deathknight/presets.ts
@@ -1,7 +1,5 @@
 import {
 	Consumes,
-	CustomRotation,
-	CustomSpell,
 	Explosive,
 	Flask,
 	Food,
@@ -9,22 +7,13 @@ import {
 	PetFood,
 	Potions,
 	UnitReference,
-	Spec
 } from '../core/proto/common.js';
 import { SavedTalents } from '../core/proto/ui.js';
 
 import {
 	Deathknight_Options as DeathKnightOptions,
-	Deathknight_Rotation as DeathKnightRotation,
-	Deathknight_Rotation_ArmyOfTheDead,
-	Deathknight_Rotation_BloodRuneFiller,
-	Deathknight_Rotation_CustomSpellOption as CustomSpellOption,
-	Deathknight_Rotation_FrostRotationType,
-	Deathknight_Rotation_Presence,
 	DeathknightMajorGlyph,
 	DeathknightMinorGlyph,
-	Deathknight_Rotation_DrwDiseases,
-	Deathknight_Rotation_BloodSpell,
 } from '../core/proto/deathknight.js';
 
 import * as PresetUtils from '../core/preset_utils.js';
@@ -70,74 +59,6 @@ export const P3_UNHOLY_DW_PRESET = PresetUtils.makePresetGear('P3 DW Unholy', P3
 import P4UhDwGear from './gear_sets/p4_uh_dw.gear.json';
 export const P4_UNHOLY_DW_PRESET = PresetUtils.makePresetGear('P4 DW Unholy', P4UhDwGear, { talentTree: 2 });
 
-export const DefaultUnholyRotation = DeathKnightRotation.create({
-	useDeathAndDecay: true,
-	btGhoulFrenzy: true,
-	refreshHornOfWinter: false,
-	useGargoyle: true,
-	useEmpowerRuneWeapon: true,
-	holdErwArmy: false,
-	preNerfedGargoyle: false,
-	armyOfTheDead: Deathknight_Rotation_ArmyOfTheDead.AsMajorCd,
-	startingPresence: Deathknight_Rotation_Presence.Unholy,
-	blPresence: Deathknight_Rotation_Presence.Blood,
-	presence: Deathknight_Rotation_Presence.Blood,
-	gargoylePresence: Deathknight_Rotation_Presence.Unholy,
-	bloodRuneFiller: Deathknight_Rotation_BloodRuneFiller.BloodBoil,
-	useAms: false,
-	drwDiseases: Deathknight_Rotation_DrwDiseases.Pestilence,
-	bloodSpender: Deathknight_Rotation_BloodSpell.HS,
-	useDancingRuneWeapon: true
-});
-
-export const DefaultFrostRotation = DeathKnightRotation.create({
-	useDeathAndDecay: false,
-	btGhoulFrenzy: false,
-	refreshHornOfWinter: false,
-	useEmpowerRuneWeapon: true,
-	preNerfedGargoyle: false,
-	startingPresence: Deathknight_Rotation_Presence.Blood,
-	presence: Deathknight_Rotation_Presence.Blood,
-	bloodRuneFiller: Deathknight_Rotation_BloodRuneFiller.BloodBoil,
-	useAms: false,
-	avgAmsSuccessRate: 1.0,
-	avgAmsHit: 10000.0,
-	drwDiseases: Deathknight_Rotation_DrwDiseases.Pestilence,
-  	frostRotationType: Deathknight_Rotation_FrostRotationType.SingleTarget,
-	armyOfTheDead: Deathknight_Rotation_ArmyOfTheDead.PreCast,
-  	frostCustomRotation: CustomRotation.create({
-		spells: [
-			CustomSpell.create({ spell: CustomSpellOption.CustomDeathAndDecay }),
-			CustomSpell.create({ spell: CustomSpellOption.CustomIcyTouch }),
-			CustomSpell.create({ spell: CustomSpellOption.CustomPlagueStrike }),
-			CustomSpell.create({ spell: CustomSpellOption.CustomPestilence }),
-			CustomSpell.create({ spell: CustomSpellOption.CustomHowlingBlastRime }),
-			CustomSpell.create({ spell: CustomSpellOption.CustomHowlingBlast }),
-			CustomSpell.create({ spell: CustomSpellOption.CustomBloodBoil }),
-			CustomSpell.create({ spell: CustomSpellOption.CustomObliterate }),
-			CustomSpell.create({ spell: CustomSpellOption.CustomFrostStrike }),
-		],
-	}),
-});
-
-export const DefaultBloodRotation = DeathKnightRotation.create({
-	refreshHornOfWinter: false,
-	useEmpowerRuneWeapon: true,
-	preNerfedGargoyle: false,
-	startingPresence: Deathknight_Rotation_Presence.Blood,
-	bloodRuneFiller: Deathknight_Rotation_BloodRuneFiller.BloodStrike,
-	armyOfTheDead: Deathknight_Rotation_ArmyOfTheDead.PreCast,
-	holdErwArmy: false,
-	useAms: false,
-	drwDiseases: Deathknight_Rotation_DrwDiseases.Pestilence,
-	bloodSpender: Deathknight_Rotation_BloodSpell.HS,
-	useDancingRuneWeapon: true,
-});
-
-export const BLOOD_ROTATION_PRESET_LEGACY_DEFAULT = PresetUtils.makePresetLegacyRotation('Blood Legacy', Spec.SpecDeathknight, DefaultBloodRotation, { talentTree: 0 });
-export const FROST_ROTATION_PRESET_LEGACY_DEFAULT = PresetUtils.makePresetLegacyRotation('Frost Legacy', Spec.SpecDeathknight, DefaultFrostRotation, { talentTree: 1 });
-export const UNHOLY_DW_ROTATION_PRESET_LEGACY_DEFAULT = PresetUtils.makePresetLegacyRotation('Unholy DW Legacy', Spec.SpecDeathknight, DefaultUnholyRotation, { talentTree: 2 });
-
 import BloodDPSApl from './apls/blood_dps.apl.json';
 export const BLOOD_DPS_ROTATION_PRESET_DEFAULT = PresetUtils.makePresetAPLRotation('Blood DPS', BloodDPSApl, { talentTree: 0 });
 import BloodPestiAoeApl from './apls/blood_pesti_aoe.apl.json';
diff --git a/ui/deathknight/sim.ts b/ui/deathknight/sim.ts
index 0aebcc9f37..afd9a0f956 100644
--- a/ui/deathknight/sim.ts
+++ b/ui/deathknight/sim.ts
@@ -90,8 +90,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecDeathknight, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultUnholyRotation,
 		// Default talents.
 		talents: Presets.UnholyDualWieldTalents.data,
 		// Default spec-specific settings.
@@ -168,8 +166,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecDeathknight, {
 	// IconInputs to include in the 'Player' section on the settings tab.
 	playerIconInputs: [
 	],
-	// Inputs to include in the 'Rotation' section on the settings tab.
-	rotationInputs: DeathKnightInputs.DeathKnightRotationConfig,
 	petConsumeInputs: [
 		IconInputs.SpicedMammothTreats,
 	],
@@ -218,9 +214,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecDeathknight, {
 		],
 		// Preset rotations that the user can quickly select.
 		rotations: [
-			Presets.BLOOD_ROTATION_PRESET_LEGACY_DEFAULT,
-			Presets.FROST_ROTATION_PRESET_LEGACY_DEFAULT,
-			Presets.UNHOLY_DW_ROTATION_PRESET_LEGACY_DEFAULT,
 			Presets.BLOOD_DPS_ROTATION_PRESET_DEFAULT,
 			Presets.BLOOD_PESTI_AOE_ROTATION_PRESET_DEFAULT,
 			Presets.FROST_BL_PESTI_ROTATION_PRESET_DEFAULT,
diff --git a/ui/elemental_shaman/inputs.ts b/ui/elemental_shaman/inputs.ts
index d3963843fd..4fe31b5180 100644
--- a/ui/elemental_shaman/inputs.ts
+++ b/ui/elemental_shaman/inputs.ts
@@ -1,13 +1,10 @@
-import { IconPickerConfig } from '../core/components/icon_picker.js';
-import { ElementalShaman_Options_ThunderstormRange, ElementalShaman_Rotation_BloodlustUse, ElementalShaman_Rotation_RotationType as RotationType, ShamanShield, ShamanTotems } from '../core/proto/shaman.js';
-import { ElementalShaman_Options as ShamanOptions } from '../core/proto/shaman.js';
-import { AirTotem } from '../core/proto/shaman.js';
+import { ElementalShaman_Options_ThunderstormRange, ShamanShield } from '../core/proto/shaman.js';
 import { Spec } from '../core/proto/common.js';
 import { ActionId } from '../core/proto_utils/action_id.js';
 import { Player } from '../core/player.js';
 
 import * as InputHelpers from '../core/components/input_helpers.js';
-import { EventID, TypedEvent } from 'ui/core/typed_event.js';
+import { EventID } from 'ui/core/typed_event.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.
@@ -37,91 +34,3 @@ export const ShamanShieldInput = InputHelpers.makeSpecOptionsEnumIconInput<Spec.
 		{ actionId: ActionId.fromSpellId(49281), value: ShamanShield.LightningShield },
 	],
 });
-
-export const ElementalShamanRotationConfig = {
-	inputs: [
-		InputHelpers.makeRotationEnumInput<Spec.SpecElementalShaman, RotationType>({
-			fieldName: 'type',
-			label: 'Type',
-			values: [
-				{
-					name: 'Adaptive', value: RotationType.Adaptive,
-					tooltip: 'Dynamically adapts based on available mana to maximize CL casts without going OOM.',
-				},
-				{
-					name: 'Manual', value: RotationType.Manual,
-					tooltip: 'Allows custom selection of which spells to use and to modify cast conditions.',
-				},
-			],
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecElementalShaman>({
-			fieldName: 'bloodlust',
-			label: 'Use Bloodlust',
-			labelTooltip: 'Player will cast bloodlust',
-			getValue: (player: Player<Spec.SpecElementalShaman>) => player.getRotation().bloodlust == ElementalShaman_Rotation_BloodlustUse.UseBloodlust,
-			setValue: (eventID: EventID, player: Player<Spec.SpecElementalShaman>, newValue: boolean) => {
-				const newRotation = player.getRotation();
-				if (newValue) {
-					newRotation.bloodlust = ElementalShaman_Rotation_BloodlustUse.UseBloodlust;
-				} else {
-					newRotation.bloodlust = ElementalShaman_Rotation_BloodlustUse.NoBloodlust;
-				}
-				player.setRotation(eventID, newRotation);
-			},
-		}),
-		InputHelpers.makeRotationNumberInput<Spec.SpecElementalShaman>({
-			fieldName: 'lvbFsWaitMs',
-			label: 'Max wait for LvB/FS (ms)',
-			labelTooltip: 'Amount of time the sim will wait if FS is about to fall off or LvB CD is about to come up. Setting to 0 will default to 175ms',
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecElementalShaman>({
-			fieldName: 'useChainLightning',
-			label: 'Use Chain Lightning in Rotation',
-			labelTooltip: 'Use Chain Lightning in rotation',
-			enableWhen: (player: Player<Spec.SpecElementalShaman>) => player.getRotation().type == RotationType.Manual,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecElementalShaman>({
-			fieldName: 'useClOnlyGap',
-			label: 'Use CL only as gap filler',
-			labelTooltip: 'Use CL to fill short gaps in LvB CD instead of on CD.',
-			enableWhen: (player: Player<Spec.SpecElementalShaman>) => player.getRotation().type == RotationType.Manual && player.getRotation().useChainLightning,
-		}),
-		InputHelpers.makeRotationNumberInput<Spec.SpecElementalShaman>({
-			fieldName: 'clMinManaPer',
-			label: 'Min mana percent to use Chain Lightning',
-			labelTooltip: 'Customize minimum mana level to cast Chain Lightning. 0 will spam until OOM.',
-			enableWhen: (player: Player<Spec.SpecElementalShaman>) => player.getRotation().type == RotationType.Manual && player.getRotation().useChainLightning,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecElementalShaman>({
-			fieldName: 'useFireNova',
-			label: 'Use Fire Nova in Rotation',
-			labelTooltip: 'Fire Nova will hit all targets when cast.',
-			enableWhen: (player: Player<Spec.SpecElementalShaman>) => player.getRotation().type == RotationType.Manual,
-		}),
-		InputHelpers.makeRotationNumberInput<Spec.SpecElementalShaman>({
-			fieldName: 'fnMinManaPer',
-			label: 'Min mana percent to use FireNova',
-			labelTooltip: 'Customize minimum mana level to cast Fire Nova. 0 will spam until OOM.',
-			enableWhen: (player: Player<Spec.SpecElementalShaman>) => player.getRotation().type == RotationType.Manual && player.getRotation().useFireNova,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecElementalShaman>({
-			fieldName: 'overwriteFlameshock',
-			label: 'Allow Flameshock to be overwritten',
-			labelTooltip: 'Will use flameshock at the end of the duration even if its still ticking if there isn\'t enough time to cast lavaburst before expiring.',
-			enableWhen: (player: Player<Spec.SpecElementalShaman>) => player.getRotation().type == RotationType.Manual,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecElementalShaman>({
-			fieldName: 'alwaysCritLvb',
-			label: 'Only cast Lavaburst with FS',
-			labelTooltip: 'Will only cast Lavaburst if Flameshock will be active when the cast finishes.',
-			enableWhen: (player: Player<Spec.SpecElementalShaman>) => player.getRotation().type == RotationType.Manual,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecElementalShaman>({
-			fieldName: 'useThunderstorm',
-			label: 'Allow Thunderstorm to be cast.',
-			labelTooltip: 'Disabling this will stop thunderstorm from being cast entirely.',
-			enableWhen: (player: Player<Spec.SpecElementalShaman>) => player.getRotation().type == RotationType.Manual,
-			showWhen: (player: Player<Spec.SpecElementalShaman>) => player.getTalents().thunderstorm,
-		}),
-	],
-};
diff --git a/ui/elemental_shaman/presets.ts b/ui/elemental_shaman/presets.ts
index fba358df48..d41b815709 100644
--- a/ui/elemental_shaman/presets.ts
+++ b/ui/elemental_shaman/presets.ts
@@ -6,13 +6,10 @@ import {
 	Glyphs,
 	Potions,
   Profession,
-	Spec,
 } from '../core/proto/common.js';
 import { SavedTalents } from '../core/proto/ui.js';
 
 import {
-  ElementalShaman_Rotation_RotationType as RotationType,
-  ElementalShaman_Rotation as ElementalShamanRotation,
   ElementalShaman_Options as ElementalShamanOptions,
   ShamanShield,
   ShamanMajorGlyph,
@@ -43,23 +40,6 @@ export const P3_PRESET_HORDE = PresetUtils.makePresetGear('P3 Preset [H]', P3Hor
 import P4Gear from './gear_sets/p4.gear.json';
 export const P4_PRESET = PresetUtils.makePresetGear('P4 Preset', P4Gear);
 
-export const DefaultRotation = ElementalShamanRotation.create({
-  totems: ShamanTotems.create({
-    earth: EarthTotem.StrengthOfEarthTotem,
-    air: AirTotem.WrathOfAirTotem,
-    fire: FireTotem.TotemOfWrath,
-    water: WaterTotem.ManaSpringTotem,
-    useFireElemental: true,
-  }),
-  type: RotationType.Adaptive,
-  fnMinManaPer: 66,
-  clMinManaPer: 33,
-  useChainLightning: false,
-  useFireNova: false,
-  useThunderstorm: true,
-});
-
-export const ROTATION_PRESET_LEGACY = PresetUtils.makePresetLegacyRotation('Legacy', Spec.SpecElementalShaman, DefaultRotation);
 import DefaultApl from './apls/default.apl.json';
 export const ROTATION_PRESET_DEFAULT = PresetUtils.makePresetAPLRotation('Default', DefaultApl);
 import AdvancedApl from './apls/advanced.apl.json';
diff --git a/ui/elemental_shaman/sim.ts b/ui/elemental_shaman/sim.ts
index 5162287ceb..6bbaf3bef6 100644
--- a/ui/elemental_shaman/sim.ts
+++ b/ui/elemental_shaman/sim.ts
@@ -95,8 +95,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecElementalShaman, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultRotation,
 		// Default talents.
 		talents: Presets.StandardTalents.data,
 		// Default spec-specific settings.
@@ -132,8 +130,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecElementalShaman, {
 	playerIconInputs: [
 		ShamanInputs.ShamanShieldInput,
 	],
-	// Inputs to include in the 'Rotation' section on the settings tab.
-	rotationInputs: ShamanInputs.ElementalShamanRotationConfig,
 	// Buff and Debuff inputs to include/exclude, overriding the EP-based defaults.
 	includeBuffDebuffInputs: [
 	],
@@ -162,7 +158,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecElementalShaman, {
 		],
 		// Preset rotations that the user can quickly select.
 		rotations: [
-			Presets.ROTATION_PRESET_LEGACY,
 			Presets.ROTATION_PRESET_DEFAULT,
 			Presets.ROTATION_PRESET_ADVANCED,
 		],
diff --git a/ui/enhancement_shaman/inputs.ts b/ui/enhancement_shaman/inputs.ts
index 63b07fa739..891084ebbf 100644
--- a/ui/enhancement_shaman/inputs.ts
+++ b/ui/enhancement_shaman/inputs.ts
@@ -1,25 +1,12 @@
 import {
-	AirTotem,
-	EarthTotem,
-	FireTotem,
-	WaterTotem,
-	EnhancementShaman_Options as ShamanOptions,
-	ShamanTotems,
 	ShamanShield,
 	ShamanImbue,
 	ShamanSyncType,
-	EnhancementShaman_Rotation_PrimaryShock as PrimaryShock,
-	EnhancementShaman_Rotation_RotationType as RotationType,
-	EnhancementShaman_Rotation_CustomRotationSpell as CustomRotationSpell,
-	EnhancementShaman_Rotation,
-	EnhancementShaman_Rotation_BloodlustUse
 } from '../core/proto/shaman.js';
 import { Spec } from '../core/proto/common.js';
 import { ActionId } from '../core/proto_utils/action_id.js';
-import { Player } from '../core/player.js';
 
 import * as InputHelpers from '../core/components/input_helpers.js';
-import { EventID } from 'ui/core/typed_event.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.
@@ -73,205 +60,3 @@ export const SyncTypeInput = InputHelpers.makeSpecOptionsEnumInput<Spec.SpecEnha
 		{ name: 'Delayed Offhand', value: ShamanSyncType.DelayOffhandSwings },
 	],
 });
-
-export const EnhancementShamanRotationConfig = {
-	inputs:
-		[
-			InputHelpers.makeRotationEnumInput<Spec.SpecEnhancementShaman, RotationType>({
-				fieldName: 'rotationType',
-				label: 'Type',
-				labelTooltip:
-					`<ul>
-					<li>
-						<div>Standard: Priority Rotation</div>
-					</li>
-					<li>
-						<div>Custom: Highest spell that is ready will be cast.</div>
-					</li>
-				</ul>`,
-				values: [
-					{ name: 'Standard', value: RotationType.Priority },
-					{ name: 'Custom', value: RotationType.Custom },
-				],
-			}),
-			InputHelpers.makeCustomRotationInput<Spec.SpecEnhancementShaman, CustomRotationSpell>({
-				fieldName: 'customRotation',
-				numColumns: 2,
-				values: [
-					{ actionId: ActionId.fromSpellId(49238), value: CustomRotationSpell.LightningBolt },
-					{ actionId: ActionId.fromSpellId(49238), value: CustomRotationSpell.LightningBoltWeave, text: "Weave" },
-					{ actionId: ActionId.fromSpellId(49238), value: CustomRotationSpell.LightningBoltDelayedWeave, text: "Delay" },
-					{ actionId: ActionId.fromSpellId(49271), value: CustomRotationSpell.ChainLightning },
-					{ actionId: ActionId.fromSpellId(17364), value: CustomRotationSpell.StormstrikeDebuffMissing, text: "Debuff" },
-					{ actionId: ActionId.fromSpellId(17364), value: CustomRotationSpell.Stormstrike },
-					{ actionId: ActionId.fromSpellId(49233), value: CustomRotationSpell.FlameShock },
-					{ actionId: ActionId.fromSpellId(49231), value: CustomRotationSpell.EarthShock },
-					{ actionId: ActionId.fromSpellId(58734), value: CustomRotationSpell.MagmaTotem },
-					{ actionId: ActionId.fromSpellId(61657), value: CustomRotationSpell.FireNova },
-					{ actionId: ActionId.fromSpellId(60103), value: CustomRotationSpell.LavaLash },
-					{ actionId: ActionId.fromSpellId(49281), value: CustomRotationSpell.LightningShield },
-					{ actionId: ActionId.fromSpellId(60043), value: CustomRotationSpell.LavaBurst, text: "Weave" },
-					{ actionId: ActionId.fromSpellId(49236), value: CustomRotationSpell.FrostShock },
-				],
-				showWhen: (player: Player<Spec.SpecEnhancementShaman>) => player.getRotation().rotationType == RotationType.Custom,
-			}),
-			InputHelpers.makeRotationEnumInput<Spec.SpecEnhancementShaman, PrimaryShock>({
-				fieldName: 'primaryShock',
-				label: 'Primary Shock',
-				values: [
-					{ name: 'None', value: PrimaryShock.None },
-					{ name: 'Earth Shock', value: PrimaryShock.Earth },
-					{ name: 'Frost Shock', value: PrimaryShock.Frost },
-				],
-				showWhen: (player: Player<Spec.SpecEnhancementShaman>) => player.getRotation().rotationType != RotationType.Custom
-			}),
-			InputHelpers.makeRotationBooleanInput<Spec.SpecEnhancementShaman>({
-				fieldName: 'weaveFlameShock',
-				label: 'Weave Flame Shock',
-				labelTooltip: 'Use Flame Shock whenever the target does not already have the DoT.',
-				showWhen: (player: Player<Spec.SpecEnhancementShaman>) => player.getRotation().rotationType != RotationType.Custom
-			}),
-			InputHelpers.makeRotationNumberInput<Spec.SpecEnhancementShaman>({
-				fieldName: 'flameShockClipTicks',
-				label: 'Refresh Flame Shock at ticks remaining',
-				labelTooltip: 'Set to 0 to require the debuff be missing. A tick is 3s, affected by spell haste',
-				enableWhen: (player: Player<Spec.SpecEnhancementShaman>) => player.getRotation().weaveFlameShock,
-				showWhen: (player: Player<Spec.SpecEnhancementShaman>) => {
-					if (player.getRotation().rotationType == RotationType.Custom) {
-						return player.getRotation().customRotation?.spells.find(customSpell => customSpell.spell == CustomRotationSpell.FlameShock) != undefined
-					}
-
-					return player.getRotation().weaveFlameShock
-				}
-			}),
-			InputHelpers.makeRotationBooleanInput<Spec.SpecEnhancementShaman>({
-				fieldName: 'lightningboltWeave',
-				label: 'Enable Weaving Lightning Bolt',
-				labelTooltip: 'Will provide a DPS increase, but is harder to execute',
-				enableWhen: (player: Player<Spec.SpecEnhancementShaman>) => player.getTalents().maelstromWeapon > 0,
-				showWhen: (player: Player<Spec.SpecEnhancementShaman>) => player.getRotation().rotationType != RotationType.Custom
-			}),
-			InputHelpers.makeRotationEnumInput<Spec.SpecEnhancementShaman, number>({
-				fieldName: 'maelstromweaponMinStack',
-				label: 'Minimum Maelstrom Stacks to Weave',
-				labelTooltip: '3 stacks is the most realistic, however there are cases where lower might be possible, just much harder to do in practice',
-				values: [
-					{ name: '1', value: 1 },
-					{ name: '2', value: 2 },
-					{ name: '3', value: 3 },
-					{ name: '4', value: 4 },
-				],
-				enableWhen: (player: Player<Spec.SpecEnhancementShaman>) => {
-					if (player.getRotation().rotationType == RotationType.Custom) {
-						return player.getRotation().customRotation?.spells.find(customSpell => customSpell.spell == CustomRotationSpell.LightningBoltWeave) != undefined
-					}
-
-					return player.getRotation().lightningboltWeave
-				},
-				showWhen: (player: Player<Spec.SpecEnhancementShaman>) => {
-					if (player.getRotation().rotationType == RotationType.Custom) {
-						return player.getRotation().customRotation?.spells.find(customSpell => customSpell.spell == CustomRotationSpell.LightningBoltWeave) != undefined
-					}
-
-					return true
-				}
-			}),
-			InputHelpers.makeRotationNumberInput<Spec.SpecEnhancementShaman>({
-				fieldName: 'autoWeaveDelay',
-				label: 'Weaving Delay After Auto Attack',
-				labelTooltip: 'The amount of time to wait after an auto attack before weaveing, in milliseconds',
-				enableWhen: (player: Player<Spec.SpecEnhancementShaman>) => {
-					if (player.getRotation().rotationType == RotationType.Custom) {
-						return player.getRotation().customRotation?.spells.find(customSpell => customSpell.spell == CustomRotationSpell.LightningBoltWeave) != undefined
-					}
-
-					return player.getRotation().lightningboltWeave
-				},
-				showWhen: (player: Player<Spec.SpecEnhancementShaman>) => {
-					if (player.getRotation().rotationType == RotationType.Custom) {
-						return player.getRotation().customRotation?.spells.find(customSpell => customSpell.spell == CustomRotationSpell.LightningBoltWeave) != undefined
-					}
-
-					return true
-				},
-			}), InputHelpers.makeRotationNumberInput<Spec.SpecEnhancementShaman>({
-				fieldName: 'delayGcdWeave',
-				label: 'Delay LL to Weave',
-				labelTooltip: 'The amount of time to hold Lava Lash to weave in milliseconds. Setting to 0 will disable delaying',
-				enableWhen: (player: Player<Spec.SpecEnhancementShaman>) => {
-					if (player.getRotation().rotationType == RotationType.Custom) {
-						return false
-					}
-
-					return player.getRotation().lightningboltWeave
-				},
-				showWhen: (player: Player<Spec.SpecEnhancementShaman>) => {
-					if (player.getRotation().rotationType == RotationType.Custom) {
-						return false
-					}
-
-					return true
-				},
-			}),
-			InputHelpers.makeRotationNumberInput<Spec.SpecEnhancementShaman>({
-				fieldName: 'delayGcdWeave',
-				label: 'Delay Weave Time',
-				labelTooltip: 'The amount of time to hold a GCD to weave in milliseconds. Setting to 0 will disable delaying',
-				enableWhen: (player: Player<Spec.SpecEnhancementShaman>) => {
-					if (player.getRotation().rotationType == RotationType.Custom) {
-						return player.getRotation().customRotation?.spells.find(customSpell => customSpell.spell == CustomRotationSpell.LightningBoltDelayedWeave) != undefined
-					}
-
-					return false
-				},
-				showWhen: (player: Player<Spec.SpecEnhancementShaman>) => {
-					if (player.getRotation().rotationType == RotationType.Custom) {
-						return player.getRotation().customRotation?.spells.find(customSpell => customSpell.spell == CustomRotationSpell.LightningBoltDelayedWeave) != undefined
-					}
-
-					return false
-				},
-			}),
-			InputHelpers.makeRotationBooleanInput<Spec.SpecEnhancementShaman>({
-				fieldName: 'lavaburstWeave',
-				label: 'Enable Weaving Lava Burst',
-				labelTooltip: 'Not particularily useful for dual wield, mostly a 2h option',
-				enableWhen: (player: Player<Spec.SpecEnhancementShaman>) => player.getRotation().lightningboltWeave,
-				showWhen: (player: Player<Spec.SpecEnhancementShaman>) => player.getRotation().rotationType != RotationType.Custom
-			}),
-			InputHelpers.makeRotationNumberInput<Spec.SpecEnhancementShaman>({
-				fieldName: 'firenovaManaThreshold',
-				label: 'Minimum mana to cast Fire Nova',
-				labelTooltip: 'Fire Nova will not be cast when mana is below this value. Set this medium-low, it has a bad mana-to-damage ratio',
-				showWhen: (player: Player<Spec.SpecEnhancementShaman>) => {
-					if (player.getRotation().rotationType == RotationType.Custom) {
-						return player.getRotation().customRotation?.spells.find(customSpell => customSpell.spell == CustomRotationSpell.FireNova) != undefined
-					}
-
-					return true
-				},
-			}),
-			InputHelpers.makeRotationNumberInput<Spec.SpecEnhancementShaman>({
-				fieldName: 'shamanisticRageManaThreshold',
-				label: 'Mana % to use Shamanistic Rage',
-				enableWhen: (player: Player<Spec.SpecEnhancementShaman>) => player.getTalents().shamanisticRage,
-			}),
-			InputHelpers.makeRotationBooleanInput<Spec.SpecEnhancementShaman>({
-				fieldName: 'bloodlust',
-				label: 'Use Bloodlust',
-				labelTooltip: 'Player will cast bloodlust',
-				getValue: (player: Player<Spec.SpecEnhancementShaman>) => player.getRotation().bloodlust == EnhancementShaman_Rotation_BloodlustUse.UseBloodlust,
-				setValue: (eventID: EventID, player: Player<Spec.SpecEnhancementShaman>, newValue: boolean) => {
-					const newRotation = player.getRotation();
-					if (newValue) {
-						newRotation.bloodlust = EnhancementShaman_Rotation_BloodlustUse.UseBloodlust;
-					} else {
-						newRotation.bloodlust = EnhancementShaman_Rotation_BloodlustUse.NoBloodlust;
-					}
-					player.setRotation(eventID, newRotation);
-				},
-			}),
-		],
-};
-
-
diff --git a/ui/enhancement_shaman/presets.ts b/ui/enhancement_shaman/presets.ts
index 43421f6e1b..1d4174bec1 100644
--- a/ui/enhancement_shaman/presets.ts
+++ b/ui/enhancement_shaman/presets.ts
@@ -7,13 +7,10 @@ import {
 	RaidBuffs,
 	TristateEffect,
 	Debuffs,
-	CustomRotation,
-	CustomSpell,
 	Faction,
 } from '../core/proto/common.js';
 import { SavedTalents } from '../core/proto/ui.js';
 
-import { EnhancementShaman_Rotation as EnhancementShamanRotation, EnhancementShaman_Options as EnhancementShamanOptions, ShamanShield } from '../core/proto/shaman.js';
 import {
 	AirTotem,
 	EarthTotem,
@@ -21,11 +18,10 @@ import {
 	WaterTotem,
 	ShamanTotems,
 	ShamanImbue,
+	ShamanShield,
 	ShamanSyncType,
 	ShamanMajorGlyph,
-	EnhancementShaman_Rotation_PrimaryShock as PrimaryShock,
-	EnhancementShaman_Rotation_RotationType as RotationType,
-	EnhancementShaman_Rotation_CustomRotationSpell as CustomRotationSpell
+	EnhancementShaman_Options as EnhancementShamanOptions,
 } from '../core/proto/shaman.js';
 
 import * as PresetUtils from '../core/preset_utils.js';
@@ -51,42 +47,6 @@ export const P4_PRESET_FT = PresetUtils.makePresetGear('P4 Preset FT', P4FtGear)
 import P4WfGear from './gear_sets/p4_wf.gear.json';
 export const P4_PRESET_WF = PresetUtils.makePresetGear('P4 Preset WF', P4WfGear);
 
-export const DefaultRotation = EnhancementShamanRotation.create({
-	totems: ShamanTotems.create({
-		earth: EarthTotem.StrengthOfEarthTotem,
-		air: AirTotem.WindfuryTotem,
-		fire: FireTotem.MagmaTotem,
-		water: WaterTotem.ManaSpringTotem,
-		useFireElemental: true,
-	}),
-	maelstromweaponMinStack: 3,
-	lightningboltWeave: true,
-	autoWeaveDelay: 500,
-	delayGcdWeave: 750,
-	lavaburstWeave: false,
-	firenovaManaThreshold: 3000,
-	shamanisticRageManaThreshold: 25,
-	primaryShock: PrimaryShock.Earth,
-	weaveFlameShock: true,
-	rotationType: RotationType.Priority,
-	customRotation: CustomRotation.create({
-		spells: [
-			CustomSpell.create({ spell: CustomRotationSpell.LightningBolt }),
-			CustomSpell.create({ spell: CustomRotationSpell.StormstrikeDebuffMissing }),
-			CustomSpell.create({ spell: CustomRotationSpell.LightningBoltWeave }),
-			CustomSpell.create({ spell: CustomRotationSpell.Stormstrike }),
-			CustomSpell.create({ spell: CustomRotationSpell.FlameShock }),
-			CustomSpell.create({ spell: CustomRotationSpell.EarthShock }),
-			CustomSpell.create({ spell: CustomRotationSpell.MagmaTotem }),
-			CustomSpell.create({ spell: CustomRotationSpell.LightningShield }),
-			CustomSpell.create({ spell: CustomRotationSpell.FireNova }),
-			CustomSpell.create({ spell: CustomRotationSpell.LightningBoltDelayedWeave }),
-			CustomSpell.create({ spell: CustomRotationSpell.LavaLash }),
-		],
-	}),
-});
-
-
 import DefaultFt from './apls/default_ft.apl.json';
 export const ROTATION_FT_DEFAULT = PresetUtils.makePresetAPLRotation('Default FT', DefaultFt);
 import DefaultWf from './apls/default_wf.apl.json';
diff --git a/ui/enhancement_shaman/sim.ts b/ui/enhancement_shaman/sim.ts
index 92e3cd8466..f0451ec3bc 100644
--- a/ui/enhancement_shaman/sim.ts
+++ b/ui/enhancement_shaman/sim.ts
@@ -98,8 +98,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecEnhancementShaman, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultRotation,
 		// Default talents.
 		talents: Presets.StandardTalents.data,
 		// Default spec-specific settings.
@@ -123,8 +121,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecEnhancementShaman, {
 		ShamanInputs.ShamanImbueMH,
 		ShamanInputs.ShamanImbueOH,
 	],
-	// Inputs to include in the 'Rotation' section on the settings tab.
-	rotationInputs: ShamanInputs.EnhancementShamanRotationConfig,
 	// Buff and Debuff inputs to include/exclude, overriding the EP-based defaults.
 	includeBuffDebuffInputs: [
 		IconInputs.ReplenishmentBuff,
diff --git a/ui/feral_druid/inputs.ts b/ui/feral_druid/inputs.ts
index 9f2c94e515..03e89d8fa6 100644
--- a/ui/feral_druid/inputs.ts
+++ b/ui/feral_druid/inputs.ts
@@ -7,13 +7,8 @@ import { EventID, TypedEvent } from '../core/typed_event.js';
 import * as InputHelpers from '../core/components/input_helpers.js';
 
 import {
-	FeralDruid,
-	FeralDruid_Rotation as DruidRotation,
 	FeralDruid_Rotation_AplType as AplType,
-	FeralDruid_Rotation_BearweaveType as BearweaveType,
 	FeralDruid_Rotation_BiteModeType as BiteModeType,
-	FeralDruid_Options as DruidOptions,
-	FeralDruid_Rotation_BiteModeType
 } from '../core/proto/druid.js';
 
 // Configuration for spec-specific UI elements on the settings tab.
diff --git a/ui/feral_druid/sim.ts b/ui/feral_druid/sim.ts
index 8d0b45bc50..b6643b34a0 100644
--- a/ui/feral_druid/sim.ts
+++ b/ui/feral_druid/sim.ts
@@ -93,8 +93,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecFeralDruid, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultRotation,
 		// Default talents.
 		talents: Presets.StandardTalents.data,
 		// Default spec-specific settings.
diff --git a/ui/feral_tank_druid/inputs.ts b/ui/feral_tank_druid/inputs.ts
index 86a3bd3869..02ab793660 100644
--- a/ui/feral_tank_druid/inputs.ts
+++ b/ui/feral_tank_druid/inputs.ts
@@ -2,12 +2,6 @@ import { Spec } from '../core/proto/common.js';
 
 import * as InputHelpers from '../core/components/input_helpers.js';
 
-import {
-	FeralTankDruid,
-	FeralTankDruid_Rotation as DruidRotation,
-	FeralTankDruid_Options as DruidOptions
-} from '../core/proto/druid.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.
 
diff --git a/ui/feral_tank_druid/sim.ts b/ui/feral_tank_druid/sim.ts
index a42a25c005..8197bfee6d 100644
--- a/ui/feral_tank_druid/sim.ts
+++ b/ui/feral_tank_druid/sim.ts
@@ -112,8 +112,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecFeralTankDruid, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultSimpleRotation,
 		// Default talents.
 		talents: Presets.StandardTalents.data,
 		// Default spec-specific settings.
@@ -280,6 +278,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecFeralTankDruid, {
 export class FeralTankDruidSimUI extends IndividualSimUI<Spec.SpecFeralTankDruid> {
 	constructor(parentElem: HTMLElement, player: Player<Spec.SpecFeralTankDruid>) {
 		super(parentElem, player, SPEC_CONFIG);
-		const gemOptimizer = new TankGemOptimizer(this);
+		const _gemOptimizer = new TankGemOptimizer(this);
 	}
 }
diff --git a/ui/healing_priest/inputs.ts b/ui/healing_priest/inputs.ts
index 4632ca8ad7..2288765e74 100644
--- a/ui/healing_priest/inputs.ts
+++ b/ui/healing_priest/inputs.ts
@@ -4,13 +4,6 @@ import { ActionId } from '../core/proto_utils/action_id.js';
 import { Player } from '../core/player.js';
 import { EventID, TypedEvent } from '../core/typed_event.js';
 
-import {
-	HealingPriest,
-	HealingPriest_Rotation as PriestRotation,
-	HealingPriest_Rotation_RotationType as RotationType,
-	HealingPriest_Rotation_SpellOption as SpellOption,
-} from '../core/proto/priest.js';
-
 import * as InputHelpers from '../core/components/input_helpers.js';
 
 // Configuration for spec-specific UI elements on the settings tab.
@@ -50,33 +43,3 @@ export const RapturesPerMinute = InputHelpers.makeSpecOptionsNumberInput<Spec.Sp
 	showWhen: (player: Player<Spec.SpecHealingPriest>) => player.getTalents().rapture > 0,
 	changeEmitter: (player: Player<Spec.SpecHealingPriest>) => TypedEvent.onAny([player.specOptionsChangeEmitter, player.talentsChangeEmitter]),
 });
-
-export const HealingPriestRotationConfig = {
-	inputs: [
-		InputHelpers.makeRotationEnumInput<Spec.SpecHealingPriest, RotationType>({
-			fieldName: 'type',
-			label: 'Type',
-			values: [
-				{ name: 'Cycle', value: RotationType.Cycle },
-				{ name: 'Custom', value: RotationType.Custom },
-			],
-		}),
-		InputHelpers.makeCustomRotationInput<Spec.SpecHealingPriest, SpellOption>({
-			fieldName: 'customRotation',
-			numColumns: 2,
-			showCastsPerMinute: true,
-			values: [
-				{ actionId: ActionId.fromSpellId(48063), value: SpellOption.GreaterHeal },
-				{ actionId: ActionId.fromSpellId(48071), value: SpellOption.FlashHeal },
-				{ actionId: ActionId.fromSpellId(48068), value: SpellOption.Renew },
-				{ actionId: ActionId.fromSpellId(48066), value: SpellOption.PowerWordShield },
-				{ actionId: ActionId.fromSpellId(48089), value: SpellOption.CircleOfHealing },
-				{ actionId: ActionId.fromSpellId(48072), value: SpellOption.PrayerOfHealing },
-				{ actionId: ActionId.fromSpellId(48113), value: SpellOption.PrayerOfMending },
-				{ actionId: ActionId.fromSpellId(53007), value: SpellOption.Penance },
-				{ actionId: ActionId.fromSpellId(48120), value: SpellOption.BindingHeal },
-			],
-			showWhen: (player: Player<Spec.SpecHealingPriest>) => player.getRotation().type == RotationType.Custom,
-		}),
-	],
-};
diff --git a/ui/healing_priest/presets.ts b/ui/healing_priest/presets.ts
index 14e144e116..8bb3ab74bc 100644
--- a/ui/healing_priest/presets.ts
+++ b/ui/healing_priest/presets.ts
@@ -1,7 +1,5 @@
 import {
 	Consumes,
-	CustomRotation,
-	CustomSpell,
 	Debuffs,
 	IndividualBuffs,
 	Flask,
@@ -15,9 +13,6 @@ import {
 import { SavedTalents } from '../core/proto/ui.js';
 
 import {
-	HealingPriest_Rotation as Rotation,
-	HealingPriest_Rotation_RotationType as RotationType,
-	HealingPriest_Rotation_SpellOption as SpellOption,
 	HealingPriest_Options as Options,
 	PriestMajorGlyph as MajorGlyph,
 	PriestMinorGlyph as MinorGlyph,
@@ -50,30 +45,6 @@ import P4HolyGear from './gear_sets/p4_holy.gear.json';
 export const DISC_P4_PRESET = PresetUtils.makePresetGear('Disc P4 Preset', P4DiscGear, { talentTree: 0 });
 export const HOLY_P4_PRESET = PresetUtils.makePresetGear('Holy P4 Preset', P4HolyGear, { talentTree: 1 });
 
-export const DiscDefaultRotation = Rotation.create({
-	type: RotationType.Cycle,
-	customRotation: CustomRotation.create({
-		spells: [
-			CustomSpell.create({ spell: SpellOption.PowerWordShield, castsPerMinute: 18 }),
-			CustomSpell.create({ spell: SpellOption.Penance, castsPerMinute: 4 }),
-			CustomSpell.create({ spell: SpellOption.PrayerOfMending, castsPerMinute: 2 }),
-			CustomSpell.create({ spell: SpellOption.GreaterHeal, castsPerMinute: 1 }),
-		],
-	}),
-});
-
-export const HolyDefaultRotation = Rotation.create({
-	type: RotationType.Cycle,
-	customRotation: CustomRotation.create({
-		spells: [
-			CustomSpell.create({ spell: SpellOption.GreaterHeal, castsPerMinute: 10 }),
-			CustomSpell.create({ spell: SpellOption.CircleOfHealing, castsPerMinute: 5 }),
-			CustomSpell.create({ spell: SpellOption.Renew, castsPerMinute: 10 }),
-			CustomSpell.create({ spell: SpellOption.PrayerOfMending, castsPerMinute: 2 }),
-		],
-	}),
-});
-
 import DiscApl from './apls/disc.apl.json';
 export const ROTATION_PRESET_DISC = PresetUtils.makePresetAPLRotation('Disc', DiscApl);
 import HolyApl from './apls/holy.apl.json';
diff --git a/ui/healing_priest/sim.ts b/ui/healing_priest/sim.ts
index ec5c3f207d..07219c342e 100644
--- a/ui/healing_priest/sim.ts
+++ b/ui/healing_priest/sim.ts
@@ -64,8 +64,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecHealingPriest, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DiscDefaultRotation,
 		// Default talents.
 		talents: Presets.DiscTalents.data,
 		// Default spec-specific settings.
@@ -83,8 +81,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecHealingPriest, {
 		HealingPriestInputs.InnerFire,
 		HealingPriestInputs.Shadowfiend,
 	],
-	// Inputs to include in the 'Rotation' section on the settings tab.
-	rotationInputs: HealingPriestInputs.HealingPriestRotationConfig,
 	// Buff and Debuff inputs to include/exclude, overriding the EP-based defaults.
 	includeBuffDebuffInputs: [
 	],
diff --git a/ui/holy_paladin/inputs.ts b/ui/holy_paladin/inputs.ts
index d900336243..df39fb9664 100644
--- a/ui/holy_paladin/inputs.ts
+++ b/ui/holy_paladin/inputs.ts
@@ -9,10 +9,6 @@ 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 HolyPaladinRotationConfig = {
-	inputs: [
-	],
-}
 
 export const AuraSelection = InputHelpers.makeSpecOptionsEnumInput<Spec.SpecHolyPaladin, PaladinAura>({
 	fieldName: 'aura',
diff --git a/ui/holy_paladin/presets.ts b/ui/holy_paladin/presets.ts
index 05ab8e31ab..40f8a11b4c 100644
--- a/ui/holy_paladin/presets.ts
+++ b/ui/holy_paladin/presets.ts
@@ -11,7 +11,6 @@ import {
 	PaladinMajorGlyph,
 	PaladinMinorGlyph,
 	PaladinJudgement,
-	HolyPaladin_Rotation as HolyPaladinRotation,
 	HolyPaladin_Options as HolyPaladinOptions,
 } from '../core/proto/paladin.js';
 
@@ -50,9 +49,6 @@ export const StandardTalents = {
 	}),
 };
 
-export const DefaultRotation = HolyPaladinRotation.create({
-});
-
 export const DefaultOptions = HolyPaladinOptions.create({
 	aura: PaladinAura.DevotionAura,
 	judgement: PaladinJudgement.NoJudgement,
diff --git a/ui/holy_paladin/sim.ts b/ui/holy_paladin/sim.ts
index f2eaa896bb..726cf95491 100644
--- a/ui/holy_paladin/sim.ts
+++ b/ui/holy_paladin/sim.ts
@@ -67,8 +67,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecHolyPaladin, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultRotation,
 		// Default talents.
 		talents: Presets.StandardTalents.data,
 		// Default spec-specific settings.
@@ -122,8 +120,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecHolyPaladin, {
 	// IconInputs to include in the 'Player' section on the settings tab.
 	playerIconInputs: [
 	],
-	// Inputs to include in the 'Rotation' section on the settings tab.
-	rotationInputs: HolyPaladinInputs.HolyPaladinRotationConfig,
 	// Buff and Debuff inputs to include/exclude, overriding the EP-based defaults.
 	includeBuffDebuffInputs: [
 	],
diff --git a/ui/hunter/inputs.ts b/ui/hunter/inputs.ts
index 377348b303..e28f2091da 100644
--- a/ui/hunter/inputs.ts
+++ b/ui/hunter/inputs.ts
@@ -1,20 +1,15 @@
 import { Spec } from '../core/proto/common.js';
 import { ActionId } from '../core/proto_utils/action_id.js';
 import { Player } from '../core/player.js';
-import { EventID, TypedEvent } from '../core/typed_event.js';
+import { TypedEvent } from '../core/typed_event.js';
 import { makePetTypeInputConfig } from '../core/talents/hunter_pet.js';
 
 import * as InputHelpers from '../core/components/input_helpers.js';
 
 import {
-	Hunter,
-	Hunter_Rotation as HunterRotation,
 	Hunter_Rotation_RotationType as RotationType,
 	Hunter_Rotation_StingType as StingType,
-	Hunter_Rotation_SpellOption as SpellOption,
-	Hunter_Options as HunterOptions,
 	Hunter_Options_Ammo as Ammo,
-	Hunter_Options_PetType as PetType,
 } from '../core/proto/hunter.js';
 
 // Configuration for spec-specific UI elements on the settings tab.
diff --git a/ui/hunter/presets.ts b/ui/hunter/presets.ts
index 2f4772a3a8..274b062756 100644
--- a/ui/hunter/presets.ts
+++ b/ui/hunter/presets.ts
@@ -52,7 +52,7 @@ export const SV_P4_PRESET = PresetUtils.makePresetGear('SV P4 Preset', P4SVGear,
 import P5SVGear from './gear_sets/p5_sv.gear.json';
 export const SV_P5_PRESET = PresetUtils.makePresetGear('SV P5 Preset', P5SVGear, { talentTree: 2 });
 
-export const DefaultRotation = HunterRotation.create({
+export const DefaultSimpleRotation = HunterRotation.create({
 	type: RotationType.SingleTarget,
 	sting: StingType.SerpentSting,
 	trapWeave: true,
@@ -62,7 +62,7 @@ export const DefaultRotation = HunterRotation.create({
 	allowExplosiveShotDownrank: true,
 });
 
-export const ROTATION_PRESET_SIMPLE_DEFAULT = PresetUtils.makePresetSimpleRotation('Simple Default', Spec.SpecHunter, DefaultRotation);
+export const ROTATION_PRESET_SIMPLE_DEFAULT = PresetUtils.makePresetSimpleRotation('Simple Default', Spec.SpecHunter, DefaultSimpleRotation);
 import BmApl from './apls/bm.apl.json';
 export const ROTATION_PRESET_BM = PresetUtils.makePresetAPLRotation('BM', BmApl, { talentTree: 0 });
 import MmApl from './apls/mm.apl.json';
diff --git a/ui/hunter/sim.ts b/ui/hunter/sim.ts
index 67c8b34dd0..5d68f55091 100644
--- a/ui/hunter/sim.ts
+++ b/ui/hunter/sim.ts
@@ -171,8 +171,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecHunter, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultRotation,
 		// Default talents.
 		talents: Presets.SurvivalTalents.data,
 		// Default spec-specific settings.
@@ -465,7 +463,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecHunter, {
 export class HunterSimUI extends IndividualSimUI<Spec.SpecHunter> {
 	constructor(parentElem: HTMLElement, player: Player<Spec.SpecHunter>) {
 		super(parentElem, player, SPEC_CONFIG);
-		const gemOptimizer = new HunterGemOptimizer(this);
+		const _gemOptimizer = new HunterGemOptimizer(this);
 	}
 }
 
diff --git a/ui/mage/sim.ts b/ui/mage/sim.ts
index 964ada82ba..ff702df22d 100644
--- a/ui/mage/sim.ts
+++ b/ui/mage/sim.ts
@@ -75,8 +75,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultFireConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultSimpleRotation,
 		// Default talents.
 		talents: Presets.Phase3FireTalents.data,
 		// Default spec-specific settings.
diff --git a/ui/protection_paladin/inputs.ts b/ui/protection_paladin/inputs.ts
index a7da711739..dfa50143fc 100644
--- a/ui/protection_paladin/inputs.ts
+++ b/ui/protection_paladin/inputs.ts
@@ -10,10 +10,6 @@ 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 ProtectionPaladinRotationConfig = {
-	inputs: [
-	],
-}
 
 export const AuraSelection = InputHelpers.makeSpecOptionsEnumInput<Spec.SpecProtectionPaladin, PaladinAura>({
 	fieldName: 'aura',
diff --git a/ui/protection_paladin/presets.ts b/ui/protection_paladin/presets.ts
index 6e02f8b2d6..2403c329fa 100644
--- a/ui/protection_paladin/presets.ts
+++ b/ui/protection_paladin/presets.ts
@@ -1,7 +1,5 @@
 import {
 	Consumes,
-	CustomRotation,
-	CustomSpell,
 	Flask,
 	Food,
 	Potions,
@@ -13,8 +11,6 @@ import {
 	PaladinMajorGlyph,
 	PaladinMinorGlyph,
 	PaladinJudgement as PaladinJudgement,
-	ProtectionPaladin_Rotation_SpellOption as SpellOption,
-	ProtectionPaladin_Rotation as ProtectionPaladinRotation,
 	ProtectionPaladin_Options as ProtectionPaladinOptions,
 } from '../core/proto/paladin.js';
 
@@ -37,25 +33,6 @@ 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);
 
-export const DefaultRotation = ProtectionPaladinRotation.create({
-	hammerFirst: false,
-	squeezeHolyWrath: true,
-	waitSlack: 300,
-	useCustomPrio: false,
-	customRotation: CustomRotation.create({
-		spells: [
-			CustomSpell.create({ spell: SpellOption.ShieldOfRighteousness }),
-			CustomSpell.create({ spell: SpellOption.HammerOfTheRighteous }),
-			CustomSpell.create({ spell: SpellOption.HolyShield }),
-			CustomSpell.create({ spell: SpellOption.HammerOfWrath }),
-			CustomSpell.create({ spell: SpellOption.Consecration }),
-			CustomSpell.create({ spell: SpellOption.AvengersShield }),
-			CustomSpell.create({ spell: SpellOption.JudgementOfWisdom }),
-			CustomSpell.create({ spell: SpellOption.Exorcism })
-		],
-	}),
-});
-
 import DefaultApl from './apls/default.apl.json';
 export const ROTATION_DEFAULT = PresetUtils.makePresetAPLRotation('Default (969)', DefaultApl);
 
diff --git a/ui/protection_paladin/sim.ts b/ui/protection_paladin/sim.ts
index 9ffd66c852..89161b19f3 100644
--- a/ui/protection_paladin/sim.ts
+++ b/ui/protection_paladin/sim.ts
@@ -136,8 +136,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionPaladin, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultRotation,
 		// Default talents.
 		talents: Presets.GenericAoeTalents.data,
 		// Default spec-specific settings.
@@ -191,8 +189,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionPaladin, {
 	// IconInputs to include in the 'Player' section on the settings tab.
 	playerIconInputs: [
 	],
-	// Inputs to include in the 'Rotation' section on the settings tab.
-	rotationInputs: ProtectionPaladinInputs.ProtectionPaladinRotationConfig,
 	// Buff and Debuff inputs to include/exclude, overriding the EP-based defaults.
 	includeBuffDebuffInputs: [
 		IconInputs.HealthBuff,
diff --git a/ui/protection_warrior/inputs.ts b/ui/protection_warrior/inputs.ts
index e4699dc043..3e9a79b798 100644
--- a/ui/protection_warrior/inputs.ts
+++ b/ui/protection_warrior/inputs.ts
@@ -3,18 +3,9 @@ import { ActionId } from '../core/proto_utils/action_id.js';
 
 import {
 	WarriorShout,
-	WarriorTalents as WarriorTalents,
-	ProtectionWarrior,
-	ProtectionWarrior_Rotation as ProtectionWarriorRotation,
-	ProtectionWarrior_Rotation_DemoShoutChoice as DemoShoutChoice,
-	ProtectionWarrior_Rotation_ThunderClapChoice as ThunderClapChoice,
-	ProtectionWarrior_Options as ProtectionWarriorOptions,
-	ProtectionWarrior_Rotation_SpellOption as SpellOption,
 } from '../core/proto/warrior.js';
 
 import * as InputHelpers from '../core/components/input_helpers.js';
-import * as Presets from './presets.js';
-import { SimUI } from '../core/sim_ui.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.
@@ -38,53 +29,3 @@ export const ShatteringThrow = InputHelpers.makeSpecOptionsBooleanIconInput<Spec
 	fieldName: 'useShatteringThrow',
 	id: ActionId.fromSpellId(64382),
 });
-
-export const ProtectionWarriorRotationConfig = {
-	inputs: [
-		// TODO: Currently these are unhooked and not configurable in Simple mode.
-		// InputHelpers.makeCustomRotationInput<Spec.SpecProtectionWarrior, SpellOption>({
-			// fieldName: 'customRotation',
-			// numColumns: 3,
-			// values: [
-				// { actionId: ActionId.fromSpellId(57823), value: SpellOption.Revenge },
-				// { actionId: ActionId.fromSpellId(47488), value: SpellOption.ShieldSlam },
-				// { actionId: ActionId.fromSpellId(47440), value: SpellOption.Shout },
-				// { actionId: ActionId.fromSpellId(47502), value: SpellOption.ThunderClap },
-				// { actionId: ActionId.fromSpellId(25203), value: SpellOption.DemoralizingShout },
-				// { actionId: ActionId.fromSpellId(47486), value: SpellOption.MortalStrike },
-				// { actionId: ActionId.fromSpellId(47498), value: SpellOption.Devastate },
-				// { actionId: ActionId.fromSpellId(47467), value: SpellOption.SunderArmor },
-				// { actionId: ActionId.fromSpellId(12809), value: SpellOption.ConcussionBlow },
-				// { actionId: ActionId.fromSpellId(46968), value: SpellOption.Shockwave },
-			// ],
-		// }),
-		// InputHelpers.makeRotationNumberInput<Spec.SpecProtectionWarrior>({
-			// fieldName: 'hsRageThreshold',
-			// label: 'HS rage threshold',
-			// labelTooltip: 'Heroic Strike when rage is above:',
-		// }),
-		// InputHelpers.makeRotationBooleanInput<Spec.SpecProtectionWarrior>({
-			// fieldName: 'prioSslamOnShieldBlock',
-			// label: 'Prio SSlam on Shield Block',
-			// labelTooltip: 'The rotation code will prio SSlam over Revenge during active shield block windows.',
-		// }),
-		// InputHelpers.makeRotationEnumInput<Spec.SpecProtectionWarrior, DemoShoutChoice>({
-			// fieldName: 'demoShoutChoice',
-			// label: 'Demo Shout',
-			// values: [
-				// { name: 'Never', value: DemoShoutChoice.DemoShoutChoiceNone },
-				// { name: 'Maintain Debuff', value: DemoShoutChoice.DemoShoutChoiceMaintain },
-				// { name: 'Filler', value: DemoShoutChoice.DemoShoutChoiceFiller },
-			// ],
-		// }),
-		// InputHelpers.makeRotationEnumInput<Spec.SpecProtectionWarrior, ThunderClapChoice>({
-			// fieldName: 'thunderClapChoice',
-			// label: 'Thunder Clap',
-			// values: [
-				// { name: 'Never', value: ThunderClapChoice.ThunderClapChoiceNone },
-				// { name: 'Maintain Debuff', value: ThunderClapChoice.ThunderClapChoiceMaintain },
-				// { name: 'On CD', value: ThunderClapChoice.ThunderClapChoiceOnCD },
-			// ],
-		// }),
-	],
-};
diff --git a/ui/protection_warrior/presets.ts b/ui/protection_warrior/presets.ts
index 0ebe1f039f..65bd5d80d8 100644
--- a/ui/protection_warrior/presets.ts
+++ b/ui/protection_warrior/presets.ts
@@ -1,8 +1,6 @@
 import {
 	BattleElixir,
 	Consumes,
-	CustomRotation,
-	CustomSpell,
 	Explosive,
 	Food,
 	Glyphs,
@@ -15,10 +13,7 @@ import { SavedTalents } from '../core/proto/ui.js';
 import {
 	WarriorShout,
 	ProtectionWarrior_Rotation as ProtectionWarriorRotation,
-	ProtectionWarrior_Rotation_DemoShoutChoice as DemoShoutChoice,
-	ProtectionWarrior_Rotation_ThunderClapChoice as ThunderClapChoice,
 	ProtectionWarrior_Options as ProtectionWarriorOptions,
-	ProtectionWarrior_Rotation_SpellOption as SpellOption,
 	WarriorMajorGlyph,
 	WarriorMinorGlyph,
 } from '../core/proto/warrior.js';
@@ -42,29 +37,9 @@ 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);
 
-export const DefaultRotation = ProtectionWarriorRotation.create({
-	customRotation: CustomRotation.create({
-		spells: [
-			CustomSpell.create({ spell: SpellOption.ShieldSlam }),
-			CustomSpell.create({ spell: SpellOption.Revenge }),
-			CustomSpell.create({ spell: SpellOption.Shout }),
-			CustomSpell.create({ spell: SpellOption.ThunderClap }),
-			CustomSpell.create({ spell: SpellOption.DemoralizingShout }),
-			CustomSpell.create({ spell: SpellOption.MortalStrike }),
-			CustomSpell.create({ spell: SpellOption.Devastate }),
-			CustomSpell.create({ spell: SpellOption.SunderArmor }),
-			CustomSpell.create({ spell: SpellOption.ConcussionBlow }),
-			CustomSpell.create({ spell: SpellOption.Shockwave }),
-		],
-	}),
-	demoShoutChoice: DemoShoutChoice.DemoShoutChoiceNone,
-	thunderClapChoice: ThunderClapChoice.ThunderClapChoiceNone,
-	hsRageThreshold: 30,
-});
-
 import DefaultApl from './apls/default.apl.json';
 export const ROTATION_DEFAULT = PresetUtils.makePresetAPLRotation('Default APL', DefaultApl);
-export const ROTATION_PRESET_SIMPLE = PresetUtils.makePresetSimpleRotation('Simple Cooldowns', Spec.SpecProtectionWarrior, DefaultRotation);
+export const ROTATION_PRESET_SIMPLE = PresetUtils.makePresetSimpleRotation('Simple Cooldowns', Spec.SpecProtectionWarrior, ProtectionWarriorRotation.create());
 
 
 // Default talents. Uses the wowhead calculator format, make the talents on
diff --git a/ui/protection_warrior/sim.ts b/ui/protection_warrior/sim.ts
index e63f82bd7a..bd0251091f 100644
--- a/ui/protection_warrior/sim.ts
+++ b/ui/protection_warrior/sim.ts
@@ -119,8 +119,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionWarrior, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultRotation,
 		// Default talents.
 		talents: Presets.StandardTalents.data,
 		// Default spec-specific settings.
@@ -167,8 +165,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionWarrior, {
 		ProtectionWarriorInputs.ShoutPicker,
 		ProtectionWarriorInputs.ShatteringThrow,
 	],
-	// Inputs to include in the 'Rotation' section on the settings tab.
-	rotationInputs: ProtectionWarriorInputs.ProtectionWarriorRotationConfig,
 	// Buff and Debuff inputs to include/exclude, overriding the EP-based defaults.
 	includeBuffDebuffInputs: [
 		IconInputs.HealthBuff,
diff --git a/ui/restoration_druid/inputs.ts b/ui/restoration_druid/inputs.ts
index e44dea933d..f965272dc7 100644
--- a/ui/restoration_druid/inputs.ts
+++ b/ui/restoration_druid/inputs.ts
@@ -2,14 +2,10 @@ import { UnitReference, UnitReference_Type as UnitType } from '../core/proto/com
 import { Spec } from '../core/proto/common.js';
 import { ActionId } from '../core/proto_utils/action_id.js';
 import { Player } from '../core/player.js';
-import { EventID, TypedEvent } from '../core/typed_event.js';
+import { EventID } from '../core/typed_event.js';
 
 import * as InputHelpers from '../core/components/input_helpers.js';
 
-import {
-	RestorationDruid_Options as DruidOptions,
-} from '../core/proto/druid.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.
@@ -30,8 +26,3 @@ export const SelfInnervate = InputHelpers.makeSpecOptionsBooleanIconInput<Spec.S
 		player.setSpecOptions(eventID, newOptions);
 	},
 });
-
-export const RestorationDruidRotationConfig = {
-	inputs: [
-	],
-};
diff --git a/ui/restoration_druid/presets.ts b/ui/restoration_druid/presets.ts
index 5ddba487fe..a028591935 100644
--- a/ui/restoration_druid/presets.ts
+++ b/ui/restoration_druid/presets.ts
@@ -15,7 +15,6 @@ import { SavedTalents } from '../core/proto/ui.js';
 
 import {
 	RestorationDruid_Options as RestorationDruidOptions,
-	RestorationDruid_Rotation as RestorationDruidRotation,
 	DruidMajorGlyph,
 	DruidMinorGlyph,
 } from '../core/proto/druid.js';
@@ -37,9 +36,6 @@ 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);
 
-export const DefaultRotation = RestorationDruidRotation.create({
-});
-
 // Default talents. Uses the wowhead calculator format, make the talents on
 // https://wowhead.com/wotlk/talent-calc and copy the numbers in the url.
 export const CelestialFocusTalents = {
diff --git a/ui/restoration_druid/sim.ts b/ui/restoration_druid/sim.ts
index e9f140ab81..3572c34ea9 100644
--- a/ui/restoration_druid/sim.ts
+++ b/ui/restoration_druid/sim.ts
@@ -62,8 +62,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRestorationDruid, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultRotation,
 		// Default talents.
 		talents: Presets.CelestialFocusTalents.data,
 		// Default spec-specific settings.
@@ -84,8 +82,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRestorationDruid, {
 	playerIconInputs: [
 		DruidInputs.SelfInnervate,
 	],
-	// Inputs to include in the 'Rotation' section on the settings tab.
-	rotationInputs: DruidInputs.RestorationDruidRotationConfig,
 	// Buff and Debuff inputs to include/exclude, overriding the EP-based defaults.
 	includeBuffDebuffInputs: [
 	],
diff --git a/ui/restoration_shaman/inputs.ts b/ui/restoration_shaman/inputs.ts
index 750ed23e14..1143a463e3 100644
--- a/ui/restoration_shaman/inputs.ts
+++ b/ui/restoration_shaman/inputs.ts
@@ -1,14 +1,10 @@
 import { Spec } from '../core/proto/common.js';
 import { Player } from '../core/player.js';
 import { ActionId } from '../core/proto_utils/action_id.js';
-import { EventID, TypedEvent } from '../core/typed_event.js';
+import { TypedEvent } from '../core/typed_event.js';
 
 import {
-	AirTotem,
-	RestorationShaman_Options as ShamanOptions,
-	ShamanHealSpell,
 	ShamanShield,
-	RestorationShaman_Rotation_BloodlustUse,
 } from '../core/proto/shaman.js';
 
 import * as InputHelpers from '../core/components/input_helpers.js';
@@ -25,77 +21,11 @@ export const ShamanShieldInput = InputHelpers.makeSpecOptionsEnumIconInput<Spec.
 	],
 });
 
-
-export const PrimaryHealInput = InputHelpers.makeRotationEnumInput<Spec.SpecRestorationShaman, ShamanHealSpell>({
-	fieldName: 'primaryHeal',
-	label: 'Primary Heal',
-	labelTooltip: 'Set to \'AutoHeal\', to automatically swap based on best heal.',
-	values: [
-		{
-			name: "Auto Heal",
-			value: ShamanHealSpell.AutoHeal
-		},
-		{
-			name: "Lesser Healing Wave",
-			value: ShamanHealSpell.LesserHealingWave // actionId: ActionId.fromSpellId(49276),
-		},
-		{
-			name: "Healing Wave",
-			value: ShamanHealSpell.HealingWave // actionId: ActionId.fromSpellId(49273),
-		},
-		{
-			name: "Chain Heal",
-			value: ShamanHealSpell.ChainHeal // actionId: ActionId.fromSpellId(55459),
-		},
-	]
-});
-
-
-export const UseRiptide = InputHelpers.makeRotationBooleanInput<Spec.SpecRestorationShaman>({
-	fieldName: 'useRiptide',
-	label: 'Use Riptide',
-	labelTooltip: 'Causes riptide to be cast on primary target when CD is available and not already on.',
-	showWhen: (player: Player<Spec.SpecRestorationShaman>) => player.getTalents().riptide,
-	changeEmitter: (player: Player<Spec.SpecRestorationShaman>) => TypedEvent.onAny([player.specOptionsChangeEmitter, player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
-export const UseEarthShield = InputHelpers.makeRotationBooleanInput<Spec.SpecRestorationShaman>({
-	fieldName: 'useEarthShield',
-	label: 'Use Earth Shield',
-	labelTooltip: 'Causes earth shield to be cast on healing target.',
-	showWhen: (player: Player<Spec.SpecRestorationShaman>) => player.getTalents().earthShield,
-	changeEmitter: (player: Player<Spec.SpecRestorationShaman>) => TypedEvent.onAny([player.specOptionsChangeEmitter, player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
 export const TriggerEarthShield = InputHelpers.makeSpecOptionsNumberInput<Spec.SpecRestorationShaman>({
 	fieldName: 'earthShieldPPM',
 	label: 'Earth Shield PPM',
 	labelTooltip: 'How many times Earth Shield should be triggered per minute.',
-	showWhen: (player: Player<Spec.SpecRestorationShaman>) => player.getTalents().earthShield && player.getRotation().useEarthShield,
+	showWhen: (player: Player<Spec.SpecRestorationShaman>) => player.getTalents().earthShield,
 	changeEmitter: (player: Player<Spec.SpecRestorationShaman>) => TypedEvent.onAny([player.specOptionsChangeEmitter, player.rotationChangeEmitter, player.talentsChangeEmitter]),
 });
 
-export const RestorationShamanRotationConfig = {
-	inputs: [
-		PrimaryHealInput,
-		UseRiptide,
-		UseEarthShield,
-		TriggerEarthShield,
-		InputHelpers.makeRotationBooleanInput<Spec.SpecRestorationShaman>({
-			fieldName: 'bloodlust',
-			label: 'Use Bloodlust',
-			labelTooltip: 'Player will cast bloodlust',
-			getValue: (player: Player<Spec.SpecRestorationShaman>) => player.getRotation().bloodlust == RestorationShaman_Rotation_BloodlustUse.UseBloodlust,
-			setValue: (eventID: EventID, player: Player<Spec.SpecRestorationShaman>, newValue: boolean) => {
-				const newRotation = player.getRotation();
-				if (newValue) {
-					newRotation.bloodlust = RestorationShaman_Rotation_BloodlustUse.UseBloodlust;
-				} else {
-					newRotation.bloodlust = RestorationShaman_Rotation_BloodlustUse.NoBloodlust;
-				}
-				player.setRotation(eventID, newRotation);
-			},
-		}),
-	],
-};
-
diff --git a/ui/restoration_shaman/presets.ts b/ui/restoration_shaman/presets.ts
index 35632299c5..c1b8631e04 100644
--- a/ui/restoration_shaman/presets.ts
+++ b/ui/restoration_shaman/presets.ts
@@ -8,21 +8,12 @@ import {
 import { SavedTalents } from '../core/proto/ui.js';
 
 import {
-	RestorationShaman_Rotation as RestorationShamanRotation,
 	RestorationShaman_Options as RestorationShamanOptions,
 	ShamanShield,
 	ShamanMajorGlyph,
 	ShamanMinorGlyph,
 } from '../core/proto/shaman.js';
 
-import {
-	AirTotem,
-	EarthTotem,
-	FireTotem,
-	WaterTotem,
-	ShamanTotems,
-} from '../core/proto/shaman.js';
-
 import * as PresetUtils from '../core/preset_utils.js';
 
 // Preset options for this spec.
@@ -40,17 +31,6 @@ 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);
 
-export const DefaultRotation = RestorationShamanRotation.create({
-	totems: ShamanTotems.create({
-		earth: EarthTotem.StrengthOfEarthTotem,
-		air: AirTotem.WrathOfAirTotem,
-		fire: FireTotem.FlametongueTotem,
-		water: WaterTotem.HealingStreamTotem,
-	}),
-	useEarthShield: true,
-	useRiptide: true,
-});
-
 // Default talents. Uses the wowhead calculator format, make the talents on
 // https://wowhead.com/wotlk/talent-calc and copy the numbers in the url.
 export const TankHealingTalents = {
diff --git a/ui/restoration_shaman/sim.ts b/ui/restoration_shaman/sim.ts
index 19304a0084..daa42081f7 100644
--- a/ui/restoration_shaman/sim.ts
+++ b/ui/restoration_shaman/sim.ts
@@ -79,8 +79,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRestorationShaman, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultRotation,
 		// Default talents.
 		talents: Presets.RaidHealingTalents.data,
 		// Default spec-specific settings.
@@ -112,8 +110,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRestorationShaman, {
 	playerIconInputs: [
 		ShamanInputs.ShamanShieldInput,
 	],
-	// Inputs to include in the 'Rotation' section on the settings tab.
-	rotationInputs: ShamanInputs.RestorationShamanRotationConfig,
 	// Buff and Debuff inputs to include/exclude, overriding the EP-based defaults.
 	includeBuffDebuffInputs: [
 	],
@@ -122,6 +118,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRestorationShaman, {
 	// Inputs to include in the 'Other' section on the settings tab.
 	otherInputs: {
 		inputs: [
+			ShamanInputs.TriggerEarthShield,
 			OtherInputs.TankAssignment
 		],
 	},
diff --git a/ui/retribution_paladin/inputs.ts b/ui/retribution_paladin/inputs.ts
index 945b907ba0..7faecd52a0 100644
--- a/ui/retribution_paladin/inputs.ts
+++ b/ui/retribution_paladin/inputs.ts
@@ -1,22 +1,18 @@
-import { Spec, ItemSlot } from '../core/proto/common.js';
+import { Spec } from '../core/proto/common.js';
 import { Player } from '../core/player.js';
-import { EventID } from '../core/typed_event.js';
-import { IndividualSimUI } from '../core/individual_sim_ui.js';
 import { ActionId } from '../core/proto_utils/action_id.js';
-import { CustomRotation } from '../core/proto/common.js';
 
 import {
 	PaladinAura as PaladinAura,
-	RetributionPaladin_Rotation as RetributionPaladinRotation,
-	RetributionPaladin_Options as RetributionPaladinOptions,
-	RetributionPaladin_Rotation_SpellOption as SpellOption,
-	RetributionPaladin_Rotation_RotationType as RotationType,
 	PaladinJudgement as PaladinJudgement,
 	PaladinSeal,
 } from '../core/proto/paladin.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 AuraSelection = InputHelpers.makeSpecOptionsEnumIconInput<Spec.SpecRetributionPaladin, PaladinAura>({
 	fieldName: 'aura',
 	values: [
@@ -44,138 +40,4 @@ export const JudgementSelection = InputHelpers.makeSpecOptionsEnumIconInput<Spec
 		{ actionId: ActionId.fromSpellId(53408), value: PaladinJudgement.JudgementOfWisdom },
 		{ actionId: ActionId.fromSpellId(20271), value: PaladinJudgement.JudgementOfLight },
 	],
-});
-
-// 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 RetributionPaladinRotationExoSlackConfig = InputHelpers.makeRotationNumberInput<Spec.SpecRetributionPaladin>({
-	fieldName: "exoSlack",
-	label: "Exo Slack (MS)",
-	labelTooltip: "Amount of extra time in MS to give main abilities to come off cooldown before using Exorcism on single target",
-	positive: true,
-	showWhen: (player: Player<Spec.SpecRetributionPaladin>) => player.getRotation().type == RotationType.Standard,
-})
-
-export const RetributionPaladinRotationConsSlackConfig = InputHelpers.makeRotationNumberInput<Spec.SpecRetributionPaladin>({
-	fieldName: "consSlack",
-	label: "Cons Slack (MS)",
-	labelTooltip: "Amount of extra time in MS to give main abilities to come off cooldown before using Consecration on single target",
-	positive: true,
-	showWhen: (player: Player<Spec.SpecRetributionPaladin>) => player.getRotation().type == RotationType.Standard,
-})
-
-
-export const RetributionPaladinRotationDivinePleaSelection = InputHelpers.makeRotationBooleanInput<Spec.SpecRetributionPaladin>({
-	fieldName: 'useDivinePlea',
-	label: 'Use Divine Plea',
-	labelTooltip: 'Whether or not to maintain Divine Plea',
-	showWhen: (player: Player<Spec.SpecRetributionPaladin>) => player.getRotation().type == RotationType.Standard,
-});
-
-// Reuse field name, but different tooltip.
-export const RetributionPaladinRotationDivinePleaSelectionAlternate = InputHelpers.makeRotationBooleanInput<Spec.SpecRetributionPaladin>({
-	fieldName: 'useDivinePlea',
-	label: 'Use Divine Plea - Out of Sequence',
-	labelTooltip: 'Whether or not to maintain Divine Plea, this happens OUTSIDE of the cast sequence',
-	showWhen: (player: Player<Spec.SpecRetributionPaladin>) => player.getRotation().type == RotationType.CastSequence
-});
-
-export const RetributionPaladinRotationDivinePleaPercentageConfig = InputHelpers.makeRotationNumberInput<Spec.SpecRetributionPaladin>({
-	fieldName: "divinePleaPercentage",
-	label: "Divine Plea Mana Threshold %",
-	labelTooltip: "% of max mana left before beginning to use Divine Plea",
-	percent: true,
-	positive: true,
-})
-
-export const RetributionPaladinRotationHolyWrathConfig = InputHelpers.makeRotationNumberInput<Spec.SpecRetributionPaladin>({
-	fieldName: "holyWrathThreshold",
-	label: "Holy Wrath Threshold",
-	labelTooltip: "Minimum number of Demon and Undead units before Holy Wrath is considered viable to add to an AOE rotation.",
-	positive: true,
-	showWhen: (player: Player<Spec.SpecRetributionPaladin>) => player.getRotation().type == RotationType.Standard,
-})
-
-export const RetributionPaladinSoVTargets = InputHelpers.makeRotationNumberInput<Spec.SpecRetributionPaladin>({
-	fieldName: "sovTargets",
-	label: "Max SoV Targets",
-	labelTooltip: "The maximum number of targets to keep the SoV debuff on.",
-	positive: true,
-	showWhen: (player: Player<Spec.SpecRetributionPaladin>) => player.getSpecOptions().seal == PaladinSeal.Vengeance,
-	changeEmitter: (player: Player<Spec.SpecRetributionPaladin>) => player.changeEmitter,
-})
-
-export const RetributionPaladinRotationAvoidClippingConsecration = InputHelpers.makeRotationBooleanInput<Spec.SpecRetributionPaladin>({
-	fieldName: 'avoidClippingConsecration',
-	label: 'Avoid Clipping Consecration',
-	labelTooltip: 'Avoid clipping consecration at the end of a fight.',
-	showWhen: (player: Player<Spec.SpecRetributionPaladin>) => (player.getRotation().type == RotationType.Standard) || (player.getRotation().type == RotationType.Custom),
-});
-
-export const RetributionPaladinRotationHoldLastAvengingWrathUntilExecution = InputHelpers.makeRotationBooleanInput<Spec.SpecRetributionPaladin>({
-	fieldName: 'holdLastAvengingWrathUntilExecution',
-	label: 'Hold Avenging Wrath Until Execution',
-	labelTooltip: 'Hold last Avenging Wrath usage until the execution phase. This currently does not work if specific Avenging Wrath CD usage times are specified.',
-	showWhen: (player: Player<Spec.SpecRetributionPaladin>) => (player.getRotation().type == RotationType.Standard) || (player.getRotation().type == RotationType.Custom),
-});
-
-export const RetributionPaladinRotationCancelChaosBane = InputHelpers.makeRotationBooleanInput<Spec.SpecRetributionPaladin>({
-	fieldName: 'cancelChaosBane',
-	label: 'Cancel Chaos Bane Buff From Shadowmourne',
-	labelTooltip: 'Cancels the buff provided when Shadowmourne soul shard buff reaches 10 stacks.',
-	showWhen: (player: Player<Spec.SpecRetributionPaladin>) => player.getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.name == "Shadowmourne",
-});
-
-export const RetributionPaladinRotationPriorityConfig = InputHelpers.makeCustomRotationInput<Spec.SpecRetributionPaladin, SpellOption>({
-	fieldName: 'customRotation',
-	numColumns: 2,
-	values: [
-		{ actionId: ActionId.fromSpellId(53408), value: SpellOption.JudgementOfWisdom },
-		{ actionId: ActionId.fromSpellId(53385), value: SpellOption.DivineStorm },
-		{ actionId: ActionId.fromSpellId(48806), value: SpellOption.HammerOfWrath },
-		{ actionId: ActionId.fromSpellId(48819), value: SpellOption.Consecration },
-		{ actionId: ActionId.fromSpellId(48817), value: SpellOption.HolyWrath },
-		{ actionId: ActionId.fromSpellId(35395), value: SpellOption.CrusaderStrike },
-		{ actionId: ActionId.fromSpellId(48801), value: SpellOption.Exorcism },
-		{ actionId: ActionId.fromSpellId(54428), value: SpellOption.DivinePlea }
-	],
-	showWhen: (player: Player<Spec.SpecRetributionPaladin>) => player.getRotation().type == RotationType.Custom,
-});
-
-export const RetributionPaladinCastSequenceConfig = InputHelpers.makeCustomRotationInput<Spec.SpecRetributionPaladin, SpellOption>({
-	fieldName: 'customCastSequence',
-	numColumns: 2,
-	values: [
-		{ actionId: ActionId.fromSpellId(53408), value: SpellOption.JudgementOfWisdom },
-		{ actionId: ActionId.fromSpellId(53385), value: SpellOption.DivineStorm },
-		{ actionId: ActionId.fromSpellId(48806), value: SpellOption.HammerOfWrath },
-		{ actionId: ActionId.fromSpellId(48819), value: SpellOption.Consecration },
-		{ actionId: ActionId.fromSpellId(48817), value: SpellOption.HolyWrath },
-		{ actionId: ActionId.fromSpellId(35395), value: SpellOption.CrusaderStrike },
-		{ actionId: ActionId.fromSpellId(48801), value: SpellOption.Exorcism },
-		{ actionId: ActionId.fromSpellId(54428), value: SpellOption.DivinePlea }
-	],
-	showWhen: (player: Player<Spec.SpecRetributionPaladin>) => player.getRotation().type == RotationType.CastSequence,
-});
-
-export const RotationSelector = InputHelpers.makeRotationEnumInput<Spec.SpecRetributionPaladin, RotationType>({
-	fieldName: 'type',
-	label: 'Type',
-	labelTooltip:
-		`<ul>
-		<li>
-			<div>Standard: All-in-one rotation for single target and aoe.</div>
-		</li>
-		<li>
-			<div>Custom: Highest spell that is ready will be cast.</div>
-		</li>
-		<li>
-			<div>Cast Sequence: Spells will be cast in the order of the list. (Like 1-button-macro)</div>
-		</li>
-	</ul>`,
-	values: [
-		{ name: 'Standard', value: RotationType.Standard },
-		{ name: 'Custom', value: RotationType.Custom },
-		{ name: 'Cast Sequence', value: RotationType.CastSequence },
-	],
 });
\ No newline at end of file
diff --git a/ui/retribution_paladin/presets.ts b/ui/retribution_paladin/presets.ts
index 1ab4367313..3487c45c7d 100644
--- a/ui/retribution_paladin/presets.ts
+++ b/ui/retribution_paladin/presets.ts
@@ -1,23 +1,17 @@
 import {
 	Conjured,
 	Consumes,
-	CustomRotation,
-	CustomSpell,
 	Flask,
 	Food,
 	Glyphs,
 	Potions,
-	Spec,
 } from '../core/proto/common.js';
 import { SavedTalents } from '../core/proto/ui.js';
 
 import {
 	PaladinAura as PaladinAura,
 	PaladinJudgement as PaladinJudgement,
-	RetributionPaladin_Rotation as RetributionPaladinRotation,
 	RetributionPaladin_Options as RetributionPaladinOptions,
-	RetributionPaladin_Rotation_RotationType as RotationType,
-	RetributionPaladin_Rotation_SpellOption as SpellOption,
 	PaladinMajorGlyph,
 	PaladinMinorGlyph,
 } from '../core/proto/paladin.js';
@@ -41,46 +35,6 @@ export const P4_PRESET = PresetUtils.makePresetGear('P4 Preset', P4Gear);
 import P5Gear from './gear_sets/p5.gear.json';
 export const P5_PRESET = PresetUtils.makePresetGear('P5 Preset', P5Gear);
 
-export const DefaultRotation = RetributionPaladinRotation.create({
-	type: RotationType.Standard,
-	exoSlack: 500,
-	consSlack: 500,
-	useDivinePlea: true,
-	avoidClippingConsecration: true,
-	holdLastAvengingWrathUntilExecution: false,
-	cancelChaosBane: false,
-	divinePleaPercentage: 0.75,
-	holyWrathThreshold: 4,
-	sovTargets: 1,
-	customRotation: CustomRotation.create({
-		spells: [
-			CustomSpell.create({ spell: SpellOption.HammerOfWrath }),
-			CustomSpell.create({ spell: SpellOption.JudgementOfWisdom }),
-			CustomSpell.create({ spell: SpellOption.CrusaderStrike }),
-			CustomSpell.create({ spell: SpellOption.DivineStorm }),
-			CustomSpell.create({ spell: SpellOption.Consecration }),
-			CustomSpell.create({ spell: SpellOption.Exorcism }),
-			CustomSpell.create({ spell: SpellOption.HolyWrath }),
-		],
-	}),
-	customCastSequence: CustomRotation.create({
-		spells: [
-			CustomSpell.create({ spell: SpellOption.JudgementOfWisdom }),
-			CustomSpell.create({ spell: SpellOption.CrusaderStrike }),
-			CustomSpell.create({ spell: SpellOption.DivineStorm }),
-			CustomSpell.create({ spell: SpellOption.Consecration }),
-			CustomSpell.create({ spell: SpellOption.CrusaderStrike }),
-			CustomSpell.create({ spell: SpellOption.Exorcism }),
-			CustomSpell.create({ spell: SpellOption.JudgementOfWisdom }),
-			CustomSpell.create({ spell: SpellOption.CrusaderStrike }),
-			CustomSpell.create({ spell: SpellOption.DivineStorm }),
-			CustomSpell.create({ spell: SpellOption.Consecration }),
-			CustomSpell.create({ spell: SpellOption.CrusaderStrike }),
-		],
-	}),
-});
-
-export const ROTATION_PRESET_LEGACY_DEFAULT = PresetUtils.makePresetLegacyRotation('Legacy Default', Spec.SpecRetributionPaladin, DefaultRotation);
 import DefaultApl from './apls/default.apl.json';
 export const ROTATION_PRESET_DEFAULT = PresetUtils.makePresetAPLRotation('Default', DefaultApl);
 
diff --git a/ui/retribution_paladin/sim.ts b/ui/retribution_paladin/sim.ts
index 578bd9b224..d70127cba4 100644
--- a/ui/retribution_paladin/sim.ts
+++ b/ui/retribution_paladin/sim.ts
@@ -114,8 +114,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRetributionPaladin, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultRotation,
 		// Default talents.
 		talents: Presets.AuraMasteryTalents.data,
 		// Default spec-specific settings.
@@ -167,24 +165,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRetributionPaladin, {
 		RetributionPaladinInputs.JudgementSelection,
 		RetributionPaladinInputs.StartingSealSelection,
 	],
-	// Inputs to include in the 'Rotation' section on the settings tab.
-	rotationInputs: {
-		inputs: [
-			RetributionPaladinInputs.RotationSelector,
-			RetributionPaladinInputs.RetributionPaladinRotationDivinePleaSelection,
-			RetributionPaladinInputs.RetributionPaladinRotationAvoidClippingConsecration,
-			RetributionPaladinInputs.RetributionPaladinRotationHoldLastAvengingWrathUntilExecution,
-			RetributionPaladinInputs.RetributionPaladinRotationCancelChaosBane,
-			RetributionPaladinInputs.RetributionPaladinRotationDivinePleaSelectionAlternate,
-			RetributionPaladinInputs.RetributionPaladinRotationDivinePleaPercentageConfig,
-			RetributionPaladinInputs.RetributionPaladinRotationConsSlackConfig,
-			RetributionPaladinInputs.RetributionPaladinRotationExoSlackConfig,
-			RetributionPaladinInputs.RetributionPaladinRotationHolyWrathConfig,
-			RetributionPaladinInputs.RetributionPaladinSoVTargets,
-			RetributionPaladinInputs.RetributionPaladinRotationPriorityConfig,
-			RetributionPaladinInputs.RetributionPaladinCastSequenceConfig
-		]
-	},
 	// Buff and Debuff inputs to include/exclude, overriding the EP-based defaults.
 	includeBuffDebuffInputs: [
 		IconInputs.ReplenishmentBuff,
@@ -205,7 +185,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRetributionPaladin, {
 
 	presets: {
 		rotations: [
-			Presets.ROTATION_PRESET_LEGACY_DEFAULT,
 			Presets.ROTATION_PRESET_DEFAULT,
 		],
 		// Preset talents that the user can quickly select.
diff --git a/ui/rogue/inputs.ts b/ui/rogue/inputs.ts
index ef29f8d92b..2b9d964f1a 100644
--- a/ui/rogue/inputs.ts
+++ b/ui/rogue/inputs.ts
@@ -5,12 +5,6 @@ import { Player } from '../core/player.js';
 import * as InputHelpers from '../core/components/input_helpers.js';
 
 import {
-	Rogue_Rotation_AssassinationPriority as AssassinationPriority,
-	Rogue_Rotation_CombatPriority as CombatPriority,
-	Rogue_Rotation_CombatBuilder as CombatBuilder,
-	Rogue_Rotation_SubtletyBuilder as SubtletyBuilder,
-	Rogue_Rotation_SubtletyPriority as SubtletyPriority,
-	Rogue_Rotation_Frequency as Frequency,
 	Rogue_Options_PoisonImbue as Poison,
 } from '../core/proto/rogue.js';
 
@@ -74,135 +68,3 @@ export const ApplyPoisonsManually = InputHelpers.makeSpecOptionsBooleanInput<Spe
 	label: 'Configure poisons manually',
 	labelTooltip: 'Prevent automatic poison configuration that is based on equipped weapons.',
 });
-
-export const RogueRotationConfig = {
-	inputs: [
-		InputHelpers.makeRotationEnumInput<Spec.SpecRogue, Frequency>({
-			fieldName: 'exposeArmorFrequency',
-			label: 'Expose Armor',
-			labelTooltip: 'Frequency of Expose Armor casts.',
-			values: [
-				{ name: 'Never', value: Frequency.Never },
-				{ name: 'Cast Once', value: Frequency.Once },
-				{ name: 'Maintain', value: Frequency.Maintain },
-			],
-		}),
-		InputHelpers.makeRotationNumberInput<Spec.SpecRogue>({
-			fieldName: 'minimumComboPointsExposeArmor',
-			label: 'Minimum CP (Expose Armor)',
-			labelTooltip: 'Minimum number of combo points for Expose Armor when only cast once.',
-			showWhen: (player: Player<Spec.SpecRogue>) => player.getRotation().exposeArmorFrequency == Frequency.Once,
-		}),
-		InputHelpers.makeRotationEnumInput<Spec.SpecRogue, Frequency>({
-			fieldName: 'tricksOfTheTradeFrequency',
-			label: 'Tricks of the Trade',
-			labelTooltip: 'Frequency of Tricks of the Trade usage.',
-			values: [
-				{ name: 'Never', value: Frequency.Never },
-				{ name: 'Maintain', value: Frequency.Maintain },
-			],
-		}),
-		InputHelpers.makeRotationEnumInput<Spec.SpecRogue, CombatBuilder>({
-			fieldName: 'combatBuilder',
-			label: "Builder",
-			labelTooltip: 'Use Sinister Strike, Backstab, or Hemorrhage as builder.',
-			values: [
-				{ name: "Sinister Strike", value: CombatBuilder.SinisterStrike },
-				{ name: "Backstab", value: CombatBuilder.Backstab },
-				{ name: "Hemorrhage", value: CombatBuilder.HemorrhageCombat },
-			],
-			showWhen: (player: Player<Spec.SpecRogue>) => player.getTalents().combatPotency > 0
-		}),
-		InputHelpers.makeRotationEnumInput<Spec.SpecRogue, CombatPriority>({
-			fieldName: 'combatFinisherPriority',
-			label: 'Finisher Priority',
-			labelTooltip: 'The finisher that will be cast with highest priority.',
-			values: [
-				{ name: 'Rupture', value: CombatPriority.RuptureEviscerate },
-				{ name: 'Eviscerate', value: CombatPriority.EviscerateRupture },
-			],
-			showWhen: (player: Player<Spec.SpecRogue>) => player.getTalents().combatPotency > 0
-		}),
-		InputHelpers.makeRotationEnumInput<Spec.SpecRogue, AssassinationPriority>({
-			fieldName: 'assassinationFinisherPriority',
-			label: 'Finisher Priority',
-			labelTooltip: 'The finisher that will be cast with highest priority.',
-			values: [
-				{ name: 'Rupture', value: AssassinationPriority.RuptureEnvenom },
-				{ name: 'Envenom', value: AssassinationPriority.EnvenomRupture },
-			],
-			showWhen: (player: Player<Spec.SpecRogue>) => player.getTalents().mutilate
-		}),
-		InputHelpers.makeRotationEnumInput<Spec.SpecRogue, SubtletyBuilder>({
-			fieldName: 'subtletyBuilder',
-			label: "Builder",
-			labelTooltip: 'Use Hemorrhage or Backstab as builder.',
-			values: [
-				{ name: "Hemorrhage", value: SubtletyBuilder.Hemorrhage },
-				{ name: "Backstab", value: SubtletyBuilder.BackstabSub },
-			],
-			showWhen: (player: Player<Spec.SpecRogue>) => player.getTalents().honorAmongThieves > 0
-		}),
-		InputHelpers.makeRotationEnumInput<Spec.SpecRogue, SubtletyPriority>({
-			fieldName: 'subtletyFinisherPriority',
-			label: "Finisher Priority",
-			labelTooltip: 'The finisher that will be cast with highest priority.',
-			values: [
-				{ name: "Eviscerate", value: SubtletyPriority.SubtletyEviscerate },
-				{ name: "Envenom", value: SubtletyPriority.SubtletyEnvenom },
-			],
-			showWhen: (player: Player<Spec.SpecRogue>) => player.getTalents().honorAmongThieves > 0
-		}),
-		InputHelpers.makeRotationEnumInput<Spec.SpecRogue, Frequency>({
-			fieldName: 'multiTargetSliceFrequency',
-			label: 'Multi-Target S&D',
-			labelTooltip: 'Frequency of Slice and Dice cast in multi-target scenarios.',
-			values: [
-				{ name: 'Never', value: Frequency.Never },
-				{ name: 'Once', value: Frequency.Once },
-				{ name: 'Maintain', value: Frequency.Maintain },
-			],
-			showWhen: (player: Player<Spec.SpecRogue>) => player.getRotation().multiTargetSliceFrequency != Frequency.FrequencyUnknown
-		}),
-		InputHelpers.makeRotationNumberInput<Spec.SpecRogue>({
-			fieldName: 'minimumComboPointsMultiTargetSlice',
-			label: 'Minimum CP (Slice)',
-			labelTooltip: 'Minimum number of combo points spent if Slice and Dice has frequency: Once',
-			showWhen: (player: Player<Spec.SpecRogue>) => player.getRotation().multiTargetSliceFrequency == Frequency.Once
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecRogue>({
-			fieldName: 'openWithGarrote',
-			label: 'Open with Garrote',
-			labelTooltip: 'Open the encounter by casting Garrote.',
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecRogue>({
-			fieldName: 'openWithShadowstep',
-			label: 'Open with Shadowstep',
-			labelTooltip: 'Open the encounter by casting Shadowstep.',
-			showWhen: (player: Player<Spec.SpecRogue>) => player.getTalents().shadowstep
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecRogue>({
-			fieldName: 'openWithPremeditation',
-			label: 'Open with Premeditation',
-			labelTooltip: 'Open the encounter by casting Premeditation.',
-			showWhen: (player: Player<Spec.SpecRogue>) => player.getTalents().premeditation
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecRogue>({
-			fieldName: 'useFeint',
-			label: 'Use Feint',
-			labelTooltip: 'Cast Feint on cooldown. Mainly useful when using the associate glyph.'
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecRogue>({
-			fieldName: "useGhostlyStrike",
-			label: 'Use Ghostly Strike',
-			labelTooltip: 'Use Ghostly Strike on cooldown. Mainly useful when using the associate glyph.',
-			showWhen: (player: Player<Spec.SpecRogue>) => player.getTalents().ghostlyStrike
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecRogue>({
-			fieldName: 'ruptureForBleed',
-			label: 'Rupture for Bleed',
-			labelTooltip: 'Cast Rupture as needed to apply a bleed effect for Hunger for Blood',
-			showWhen: (player: Player<Spec.SpecRogue>) => player.getTalents().hungerForBlood
-		}),
-	],
-};
diff --git a/ui/rogue/presets.ts b/ui/rogue/presets.ts
index 004b89b1bd..1fb8fbeb55 100644
--- a/ui/rogue/presets.ts
+++ b/ui/rogue/presets.ts
@@ -11,13 +11,6 @@ import { SavedTalents } from '../core/proto/ui.js';
 import {
 	Rogue_Options as RogueOptions,
 	Rogue_Options_PoisonImbue as Poison,
-	Rogue_Rotation as RogueRotation,
-	Rogue_Rotation_AssassinationPriority,
-	Rogue_Rotation_CombatBuilder,
-	Rogue_Rotation_CombatPriority,
-	Rogue_Rotation_Frequency,
-	Rogue_Rotation_SubtletyBuilder,
-	Rogue_Rotation_SubtletyPriority,
 	RogueMajorGlyph,
 } from '../core/proto/rogue.js';
 
@@ -60,19 +53,6 @@ export const P3_PRESET_HEMO_SUB = PresetUtils.makePresetGear('P3 Hemo Sub', P3He
 import P3DanceSubGear from './gear_sets/p3_dancesub.gear.json';
 export const P3_PRESET_DANCE_SUB = PresetUtils.makePresetGear('P3 Dance Sub', P3DanceSubGear, { talentTree: 2 });
 
-export const DefaultRotation = RogueRotation.create({
-	exposeArmorFrequency: Rogue_Rotation_Frequency.Never,
-	minimumComboPointsExposeArmor: 2,
-	tricksOfTheTradeFrequency: Rogue_Rotation_Frequency.Maintain,
-	assassinationFinisherPriority: Rogue_Rotation_AssassinationPriority.EnvenomRupture,
-	combatBuilder: Rogue_Rotation_CombatBuilder.SinisterStrike,
-	combatFinisherPriority: Rogue_Rotation_CombatPriority.RuptureEviscerate,
-	subtletyBuilder: Rogue_Rotation_SubtletyBuilder.Hemorrhage,
-	subtletyFinisherPriority: Rogue_Rotation_SubtletyPriority.SubtletyEviscerate,
-	minimumComboPointsPrimaryFinisher: 4,
-	minimumComboPointsSecondaryFinisher: 4,
-});
-
 import MutilateApl from './apls/mutilate.apl.json'
 export const ROTATION_PRESET_MUTILATE = PresetUtils.makePresetAPLRotation('Mutilate', MutilateApl, { talentTree: 0 });
 import MutilateExposeApl from './apls/mutilate_expose.apl.json'
diff --git a/ui/rogue/sim.ts b/ui/rogue/sim.ts
index 50e30cdf2a..c23d324704 100644
--- a/ui/rogue/sim.ts
+++ b/ui/rogue/sim.ts
@@ -23,12 +23,6 @@ import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.j
 
 import {
 	Rogue_Options_PoisonImbue,
-	Rogue_Rotation_AssassinationPriority as AssassinationPriority,
-	Rogue_Rotation_CombatBuilder as CombatBuilder,
-	Rogue_Rotation_CombatPriority as CombatPriority,
-	Rogue_Rotation_Frequency as Frequency,
-	Rogue_Rotation_SubtletyPriority as SubtletyPriority,
-	RogueMajorGlyph,
 } from '../core/proto/rogue.js';
 
 import * as IconInputs from '../core/components/icon_inputs.js';
@@ -36,7 +30,6 @@ import * as OtherInputs from '../core/components/other_inputs.js';
 
 import * as RogueInputs from './inputs.js';
 import * as Presets from './presets.js';
-import { DefaultOptions } from './presets.js';
 
 const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, {
 	cssClass: 'rogue-sim-ui',
@@ -81,19 +74,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, {
 				},
 			};
 		},
-		(simUI: IndividualSimUI<Spec.SpecRogue>) => {
-			return {
-				updateOn: simUI.player.changeEmitter,
-				getContent: () => {
-					if (simUI.player.getRotation().combatBuilder == CombatBuilder.Backstab &&
-						simUI.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType != WeaponType.WeaponTypeDagger) {
-						return 'Builder "Backstab" selected, but no dagger equipped.';
-					} else {
-						return '';
-					}
-				},
-			};
-		},
 		(simUI: IndividualSimUI<Spec.SpecRogue>) => {
 			return {
 				updateOn: simUI.player.changeEmitter,
@@ -149,67 +129,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, {
 				},
 			};
 		},
-		(simUI: IndividualSimUI<Spec.SpecRogue>) => {
-			return {
-				updateOn: simUI.player.changeEmitter,
-				getContent: () => {
-					if (simUI.player.getInFrontOfTarget() && (simUI.player.getRotation().combatBuilder == CombatBuilder.Backstab ||
-						simUI.player.getRotation().openWithGarrote)) {
-						return 'Option "In Front of Target" selected, but using Backstab or Garrote as builder or opener.';
-					} else {
-						return '';
-					}
-				},
-			};
-		},
-		(simUI: IndividualSimUI<Spec.SpecRogue>) => {
-			return {
-				updateOn: simUI.player.changeEmitter,
-				getContent: () => {
-					if (simUI.player.getRotation().combatBuilder == CombatBuilder.HemorrhageCombat && !simUI.player.getTalents().hemorrhage) {
-						return 'Builder "Hemorrhage" selected, but Hemorrhage is not talented.';
-					} else {
-						return '';
-					}
-				},
-			};
-		},
-		(simUI: IndividualSimUI<Spec.SpecRogue>) => {
-			return {
-				updateOn: simUI.player.changeEmitter,
-				getContent: () => {
-					if (simUI.player.getRotation().useGhostlyStrike && !simUI.player.getMajorGlyphs().includes(RogueMajorGlyph.GlyphOfGhostlyStrike)) {
-						return '"Use Ghostly Strike" selected, but missing Glyph of Ghostly Strike.';
-					} else {
-						return '';
-					}
-				},
-			};
-		},
-		(simUI: IndividualSimUI<Spec.SpecRogue>) => {
-			return {
-				updateOn: simUI.player.changeEmitter,
-				getContent: () => {
-					if (simUI.player.getRotation().useFeint && !simUI.player.getMajorGlyphs().includes(RogueMajorGlyph.GlyphOfFeint)) {
-						return '"Use Feint" selected, but missing Glyph of Feint.';
-					} else {
-						return '';
-					}
-				},
-			};
-		},
-		(simUI: IndividualSimUI<Spec.SpecRogue>) => {
-			return {
-				updateOn: simUI.player.changeEmitter,
-				getContent: () => {
-					if (simUI.player.getRotation().exposeArmorFrequency == 2 && !simUI.player.getMajorGlyphs().includes(RogueMajorGlyph.GlyphOfExposeArmor) && simUI.player.getTalentTree() == 1) {
-						return '"Maintain Expose Armor" selected, but missing Glyph of Expose Armor.';
-					} else {
-						return '';
-					}
-				},
-			};
-		},
 		(simUI: IndividualSimUI<Spec.SpecRogue>) => {
 			return {
 				updateOn: simUI.player.changeEmitter,
@@ -289,8 +208,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultRotation,
 		// Default talents.
 		talents: Presets.AssassinationTalents137.data,
 		// Default spec-specific settings.
@@ -334,8 +251,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRogue, {
 		RogueInputs.MainHandImbue,
 		RogueInputs.OffHandImbue,
 	],
-	// Inputs to include in the 'Rotation' section on the settings tab.
-	rotationInputs: RogueInputs.RogueRotationConfig,
 	// Buff and Debuff inputs to include/exclude, overriding the EP-based defaults.
 	includeBuffDebuffInputs: [
 		IconInputs.SpellCritBuff,
@@ -488,37 +403,8 @@ export class RogueSimUI extends IndividualSimUI<Spec.SpecRogue> {
 	constructor(parentElem: HTMLElement, player: Player<Spec.SpecRogue>) {
 		super(parentElem, player, SPEC_CONFIG);
 		this.player.changeEmitter.on((c) => {
-			const rotation = this.player.getRotation()
 			const options = this.player.getSpecOptions()
 			const encounter = this.sim.encounter
-			if (this.player.getTalentTree() == 0) {
-				if (rotation.assassinationFinisherPriority == AssassinationPriority.AssassinationPriorityUnknown) {
-					rotation.assassinationFinisherPriority = Presets.DefaultRotation.assassinationFinisherPriority;
-				}
-				rotation.combatFinisherPriority = CombatPriority.CombatPriorityUnknown;
-				rotation.combatBuilder = CombatBuilder.SinisterStrike;
-				rotation.subtletyFinisherPriority = SubtletyPriority.SubtletyPriorityUnknown;
-				options.honorOfThievesCritRate = -1;
-			} else if (this.player.getTalentTree() == 1) {
-				if (rotation.combatFinisherPriority == CombatPriority.CombatPriorityUnknown) {
-					rotation.combatFinisherPriority = Presets.DefaultRotation.combatFinisherPriority;
-					rotation.combatBuilder = Presets.DefaultRotation.combatBuilder;
-				}
-				rotation.assassinationFinisherPriority = AssassinationPriority.AssassinationPriorityUnknown;
-				rotation.subtletyFinisherPriority = SubtletyPriority.SubtletyPriorityUnknown;
-				options.honorOfThievesCritRate = -1;
-			} else {
-				if (rotation.subtletyFinisherPriority == SubtletyPriority.SubtletyPriorityUnknown) {
-					rotation.subtletyFinisherPriority = Presets.DefaultRotation.subtletyFinisherPriority;
-				}
-				rotation.assassinationFinisherPriority = AssassinationPriority.AssassinationPriorityUnknown;
-				rotation.combatFinisherPriority = CombatPriority.CombatPriorityUnknown;
-				rotation.combatBuilder = CombatBuilder.SinisterStrike;
-				if (options.honorOfThievesCritRate == -1) {
-					options.honorOfThievesCritRate = DefaultOptions.honorOfThievesCritRate
-				}
-			}
-			this.player.setRotation(c, rotation)
 			if (!options.applyPoisonsManually) {
 				const mhWeaponSpeed = this.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponSpeed;
 				const ohWeaponSpeed = this.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponSpeed;
@@ -541,17 +427,8 @@ export class RogueSimUI extends IndividualSimUI<Spec.SpecRogue> {
 			this.player.setSpecOptions(c, options)
 		});
 		this.sim.encounter.changeEmitter.on((c) => {
-			const rotation = this.player.getRotation()
 			const options = this.player.getSpecOptions()
 			const encounter = this.sim.encounter
-			if (this.sim.encounter.targets.length >= 3) {
-				if (rotation.multiTargetSliceFrequency == Frequency.FrequencyUnknown) {
-					rotation.multiTargetSliceFrequency = Presets.DefaultRotation.multiTargetSliceFrequency;
-				}
-			} else {
-				rotation.multiTargetSliceFrequency = Frequency.FrequencyUnknown;
-			}
-			this.player.setRotation(c, rotation)
 			if (!options.applyPoisonsManually) {
 				const mhWeaponSpeed = this.player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponSpeed;
 				const ohWeaponSpeed = this.player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponSpeed;
diff --git a/ui/scss/core/components/individual_sim_ui/_custom_rotation_picker.scss b/ui/scss/core/components/individual_sim_ui/_custom_rotation_picker.scss
deleted file mode 100644
index 39966e9f1f..0000000000
--- a/ui/scss/core/components/individual_sim_ui/_custom_rotation_picker.scss
+++ /dev/null
@@ -1,33 +0,0 @@
-.custom-rotation-picker-root {
-	.input-root.custom-spells-picker {
-		flex-wrap: wrap;
-		align-items: flex-start !important;
-
-		.list-picker-title {
-			width: unset;
-			padding: 0;
-			border: 0;
-		}
-
-		.list-picker-items {
-			width: unset;
-		}
-
-		.list-picker-new-button {
-			width: 100%;
-		}
-	}
-}
-
-.custom-spell-picker-root {
-	display: flex;
-	align-items: center;
-
-	&> :not(:last-child) {
-		margin-right: map.get($spacers, 2);
-	}
-
-	.number-picker-input {
-		width: 100% !important;
-	}
-}
\ No newline at end of file
diff --git a/ui/scss/core/components/individual_sim_ui/_settings_tab.scss b/ui/scss/core/components/individual_sim_ui/_settings_tab.scss
index 4bc494696a..daf2b54117 100644
--- a/ui/scss/core/components/individual_sim_ui/_settings_tab.scss
+++ b/ui/scss/core/components/individual_sim_ui/_settings_tab.scss
@@ -1,6 +1,5 @@
 @use "sass:map";
 
-@import "./custom_rotation_picker";
 @import "./consumes_picker";
 @import "./cooldowns_picker";
 
diff --git a/ui/shadow_priest/inputs.ts b/ui/shadow_priest/inputs.ts
index 4bae63e95d..fa6f1ceef4 100644
--- a/ui/shadow_priest/inputs.ts
+++ b/ui/shadow_priest/inputs.ts
@@ -1,8 +1,6 @@
 import { Spec } from '../core/proto/common.js';
 import {
 	ShadowPriest_Options_Armor as Armor,
-	ShadowPriest_Rotation_RotationType as RotationType,
-	ShadowPriest_Rotation_PreCastOption as precastType
 } from '../core/proto/priest.js';
 import { ActionId } from '../core/proto_utils/action_id.js';
 
@@ -18,44 +16,3 @@ export const ArmorInput = InputHelpers.makeSpecOptionsEnumIconInput<Spec.SpecSha
 		{ actionId: ActionId.fromSpellId(48168), value: Armor.InnerFire },
 	],
 });
-
-export const MindBlastInput = InputHelpers.makeSpecOptionsBooleanIconInput<Spec.SpecShadowPriest>({
-	fieldName: 'useMindBlast',
-	id: ActionId.fromSpellId(48127),
-});
-
-export const ShadowWordDeathInput = InputHelpers.makeSpecOptionsBooleanIconInput<Spec.SpecShadowPriest>({
-	fieldName: 'useShadowWordDeath',
-	id: ActionId.fromSpellId(48158),
-});
-
-export const ShadowfiendInput = InputHelpers.makeSpecOptionsBooleanIconInput<Spec.SpecShadowPriest>({
-	fieldName: 'useShadowfiend',
-	id: ActionId.fromSpellId(34433),
-});
-
-export const ShadowPriestRotationConfig = {
-	inputs: [
-		InputHelpers.makeRotationEnumInput<Spec.SpecShadowPriest, RotationType>({
-			fieldName: 'rotationType',
-			label: 'Rotation Type',
-			labelTooltip: 'Choose how to clip your mindflay. Basic will never clip. Clipping will clip for other spells and use a 2xMF2 when there is time for 4 ticks. Ideal will evaluate the DPS gain of every action to determine MF actions.',
-			values: [
-				//{ name: 'Basic', value: RotationType.Basic },
-				//{ name: 'Clipping', value: RotationType.Clipping },
-				{ name: 'Ideal', value: RotationType.Ideal },
-				{ name: 'AoE', value: RotationType.AoE },
-			],
-		}),
-		InputHelpers.makeRotationEnumInput<Spec.SpecShadowPriest, precastType>({
-			fieldName: 'precastType',
-			label: 'PreCast Spell',
-			labelTooltip: 'Choose which spell you want to Precast',
-			values: [
-				{ name: "None", value: precastType.Nothing },
-				{ name: 'Vampiric Touch', value: precastType.PrecastVt },
-				{ name: 'Mind Blast', value: precastType.PrecastMb },
-			],
-		}),
-	],
-};
diff --git a/ui/shadow_priest/presets.ts b/ui/shadow_priest/presets.ts
index b030140a32..f58b46cb1e 100644
--- a/ui/shadow_priest/presets.ts
+++ b/ui/shadow_priest/presets.ts
@@ -14,9 +14,7 @@ import { SavedTalents } from '../core/proto/ui.js';
 
 import {
 	ShadowPriest_Options_Armor as Armor,
-	ShadowPriest_Rotation as Rotation,
 	ShadowPriest_Options as Options,
-	ShadowPriest_Rotation_RotationType,
 	PriestMajorGlyph as MajorGlyph,
 	PriestMinorGlyph as MinorGlyph,
 } from '../core/proto/priest.js';
@@ -38,11 +36,6 @@ 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);
 
-export const DefaultRotation = Rotation.create({
-	rotationType: ShadowPriest_Rotation_RotationType.Ideal,
-});
-
-
 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'
diff --git a/ui/shadow_priest/sim.ts b/ui/shadow_priest/sim.ts
index 45d5c2198e..2c496d09d1 100644
--- a/ui/shadow_priest/sim.ts
+++ b/ui/shadow_priest/sim.ts
@@ -77,8 +77,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultRotation,
 		// Default talents.
 		talents: Presets.StandardTalents.data,
 		// Default spec-specific settings.
@@ -99,13 +97,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecShadowPriest, {
 	playerIconInputs: [
 		ShadowPriestInputs.ArmorInput,
 	],
-	rotationIconInputs: [
-		ShadowPriestInputs.MindBlastInput,
-		ShadowPriestInputs.ShadowWordDeathInput,
-		ShadowPriestInputs.ShadowfiendInput,
-	],
-	// Inputs to include in the 'Rotation' section on the settings tab.
-	rotationInputs: ShadowPriestInputs.ShadowPriestRotationConfig,
 	// Buff and Debuff inputs to include/exclude, overriding the EP-based defaults.
 	includeBuffDebuffInputs: [
 		IconInputs.ReplenishmentBuff,
diff --git a/ui/smite_priest/inputs.ts b/ui/smite_priest/inputs.ts
index 97ea4aa9f6..0a743a8fad 100644
--- a/ui/smite_priest/inputs.ts
+++ b/ui/smite_priest/inputs.ts
@@ -2,7 +2,7 @@ import { UnitReference, UnitReference_Type as UnitType } from '../core/proto/com
 import { Spec } from '../core/proto/common.js';
 import { ActionId } from '../core/proto_utils/action_id.js';
 import { Player } from '../core/player.js';
-import { EventID, TypedEvent } from '../core/typed_event.js';
+import { EventID } from '../core/typed_event.js';
 
 import * as InputHelpers from '../core/components/input_helpers.js';
 
@@ -35,34 +35,3 @@ export const Shadowfiend = InputHelpers.makeSpecOptionsBooleanIconInput<Spec.Spe
 	fieldName: 'useShadowfiend',
 	id: ActionId.fromSpellId(34433),
 });
-
-export const SmitePriestRotationConfig = {
-	inputs: [
-		InputHelpers.makeRotationBooleanInput<Spec.SpecSmitePriest>({
-			fieldName: 'useDevouringPlague',
-			label: 'Use Devouring Plague',
-			labelTooltip: 'Use Devouring Plague whenever its not active.',
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecSmitePriest>({
-			fieldName: 'useShadowWordDeath',
-			label: 'Use Shadow Word: Death',
-			labelTooltip: 'Use Shadow Word: Death whenever off CD.',
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecSmitePriest>({
-			fieldName: 'useMindBlast',
-			label: 'Use Mind Blast',
-			labelTooltip: 'Use Mind Blast whenever off CD.',
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecSmitePriest>({
-			fieldName: 'memeDream',
-			label: 'Meme Dream',
-			labelTooltip: 'Assumes 2nd Smite Priest in raid, so just spams HF + Smite with permanent HF uptime.',
-			extraCssClasses: ['within-raid-sim-hide'],
-		}),
-		InputHelpers.makeRotationNumberInput<Spec.SpecSmitePriest>({
-			fieldName: 'allowedHolyFireDelayMs',
-			label: 'Allowed Delay for HF',
-			labelTooltip: 'Time, in milliseconds, the player is allowed to wait for Holy Fire if it is about to come off CD.',
-		}),
-	],
-};
diff --git a/ui/smite_priest/presets.ts b/ui/smite_priest/presets.ts
index 04e0f2b712..bc8aca5e36 100644
--- a/ui/smite_priest/presets.ts
+++ b/ui/smite_priest/presets.ts
@@ -7,14 +7,12 @@ import {
 	IndividualBuffs,
 	Potions,
 	RaidBuffs,
-	Spec,
 	TristateEffect,
 	UnitReference,
 } from '../core/proto/common.js';
 import { SavedTalents } from '../core/proto/ui.js';
 
 import {
-	SmitePriest_Rotation as Rotation,
 	SmitePriest_Options as Options,
 	PriestMajorGlyph as MajorGlyph,
 	PriestMinorGlyph as MinorGlyph,
@@ -31,12 +29,6 @@ export const PRERAID_PRESET = PresetUtils.makePresetGear('Preraid Preset', Prera
 import P1Gear from './gear_sets/p1.gear.json';
 export const P1_PRESET = PresetUtils.makePresetGear('P1 Preset', P1Gear);
 
-export const DefaultRotation = Rotation.create({
-	useDevouringPlague: true,
-	useShadowWordDeath: false,
-	useMindBlast: false,
-});
-export const ROTATION_PRESET_LEGACY_DEFAULT = PresetUtils.makePresetLegacyRotation('Legacy Default', Spec.SpecSmitePriest, DefaultRotation);
 import DefaultApl from './apls/default.apl.json'
 export const ROTATION_PRESET_APL = PresetUtils.makePresetAPLRotation('Default', DefaultApl);
 
diff --git a/ui/smite_priest/sim.ts b/ui/smite_priest/sim.ts
index 4ab3d1a22e..8e13c4ebb8 100644
--- a/ui/smite_priest/sim.ts
+++ b/ui/smite_priest/sim.ts
@@ -76,8 +76,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecSmitePriest, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultRotation,
 		// Default talents.
 		talents: Presets.StandardTalents.data,
 		// Default spec-specific settings.
@@ -95,8 +93,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecSmitePriest, {
 		SmitePriestInputs.InnerFire,
 		SmitePriestInputs.Shadowfiend,
 	],
-	// Inputs to include in the 'Rotation' section on the settings tab.
-	rotationInputs: SmitePriestInputs.SmitePriestRotationConfig,
 	// Buff and Debuff inputs to include/exclude, overriding the EP-based defaults.
 	includeBuffDebuffInputs: [
 	],
@@ -120,7 +116,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecSmitePriest, {
 		],
 		// Preset rotations that the user can quickly select.
 		rotations: [
-			Presets.ROTATION_PRESET_LEGACY_DEFAULT,
 			Presets.ROTATION_PRESET_APL,
 		],
 		// Preset gear configurations that the user can quickly select.
diff --git a/ui/tank_deathknight/inputs.ts b/ui/tank_deathknight/inputs.ts
index f254f20def..c806fc3504 100644
--- a/ui/tank_deathknight/inputs.ts
+++ b/ui/tank_deathknight/inputs.ts
@@ -1,9 +1,6 @@
 import { Spec } from '../core/proto/common.js';
 
 import {
-	DeathknightTalents as DeathknightTalents,
-	Deathknight_Rotation as DeathknightRotation,
-	Deathknight_Options as DeathknightOptions,
 	TankDeathknight_Rotation_OptimizationSetting as OptimizationSetting,
 	TankDeathknight_Rotation_Opener as Opener,
 	TankDeathknight_Rotation_BloodSpell as BloodSpell,
diff --git a/ui/tank_deathknight/presets.ts b/ui/tank_deathknight/presets.ts
index f761c8c9ea..a2d94ff998 100644
--- a/ui/tank_deathknight/presets.ts
+++ b/ui/tank_deathknight/presets.ts
@@ -4,19 +4,13 @@ import {
 	Food,
 	Glyphs,
 	Potions,
-	Spec,
 } from '../core/proto/common.js';
 import { SavedTalents } from '../core/proto/ui.js';
 
 import {
-	TankDeathknight_Rotation as TankDeathKnightRotation,
 	TankDeathknight_Options as TankDeathKnightOptions,
 	DeathknightMajorGlyph,
 	DeathknightMinorGlyph,
-	TankDeathknight_Rotation_Opener as Opener,
-	TankDeathknight_Rotation_OptimizationSetting as OptimizationSetting,
-	TankDeathknight_Rotation_BloodSpell as BloodSpell,
-	TankDeathknight_Rotation_Presence as Presence,
 } from '../core/proto/deathknight.js';
 
 import * as PresetUtils from '../core/preset_utils.js';
@@ -34,15 +28,6 @@ export const P1_FROST_PRESET = PresetUtils.makePresetGear('P1 Frost', P1FrostGea
 import P2FrostGear from './gear_sets/p2_frost.gear.json';
 export const P2_FROST_PRESET = PresetUtils.makePresetGear('P2 Frost', P2FrostGear);
 
-export const DefaultRotation = TankDeathKnightRotation.create({
-	opener: Opener.Threat,
-	optimizationSetting: OptimizationSetting.Hps,
-	bloodSpell: BloodSpell.BloodStrike,
-	presence: Presence.Frost,
-});
-
-
-export const BLOOD_LEGACY_PRESET_LEGACY_DEFAULT = PresetUtils.makePresetLegacyRotation('Blood Legacy', Spec.SpecTankDeathknight, DefaultRotation);
 import BloodIcyTouchApl from './apls/blood_icy_touch.apl.json';
 export const BLOOD_IT_SPAM_ROTATION_PRESET_DEFAULT = PresetUtils.makePresetAPLRotation('Blood Icy Touch', BloodIcyTouchApl);
 import BloodAggroApl from './apls/blood_aggro.apl.json';
diff --git a/ui/tank_deathknight/sim.ts b/ui/tank_deathknight/sim.ts
index 5857cfe79e..2002043f1c 100644
--- a/ui/tank_deathknight/sim.ts
+++ b/ui/tank_deathknight/sim.ts
@@ -116,8 +116,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecTankDeathknight, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultRotation,
 		// Default talents.
 		talents: Presets.BloodTalents.data,
 		// Default spec-specific settings.
@@ -192,7 +190,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecTankDeathknight, {
 	presets: {
 		// Preset rotations that the user can quickly select.
 		rotations: [
-			Presets.BLOOD_LEGACY_PRESET_LEGACY_DEFAULT,
 			Presets.BLOOD_IT_SPAM_ROTATION_PRESET_DEFAULT,
 			Presets.BLOOD_AGGRO_ROTATION_PRESET_DEFAULT,
 		],
diff --git a/ui/warlock/inputs.ts b/ui/warlock/inputs.ts
index 9368ca7997..f0763c80bc 100644
--- a/ui/warlock/inputs.ts
+++ b/ui/warlock/inputs.ts
@@ -1,22 +1,13 @@
 import {
-	Warlock_Options as WarlockOptions,
-	Warlock_Rotation_Type as RotationType,
-	Warlock_Rotation_Preset as RotationPreset,
-	Warlock_Rotation_PrimarySpell as PrimarySpell,
-	Warlock_Rotation_SecondaryDot as SecondaryDot,
-	Warlock_Rotation_SpecSpell as SpecSpell,
-	Warlock_Rotation_Curse as Curse,
 	Warlock_Options_WeaponImbue as WeaponImbue,
 	Warlock_Options_Armor as Armor,
 	Warlock_Options_Summon as Summon,
 } from '../core/proto/warlock.js';
 
-import { Spec, Glyphs } from '../core/proto/common.js';
+import { Spec } from '../core/proto/common.js';
 import { ActionId } from '../core/proto_utils/action_id.js';
 import { Player } from '../core/player.js';
-import { EventID, TypedEvent } from '../core/typed_event.js';
 
-import * as Presets from './presets.js';
 import * as InputHelpers from '../core/components/input_helpers.js';
 
 // Configuration for spec-specific UI elements on the settings tab.
@@ -54,210 +45,3 @@ export const PetInput = InputHelpers.makeSpecOptionsEnumIconInput<Spec.SpecWarlo
 	],
 	changeEmitter: (player: Player<Spec.SpecWarlock>) => player.changeEmitter,
 });
-
-export const PrimarySpellInput = InputHelpers.makeRotationEnumIconInput<Spec.SpecWarlock, PrimarySpell>({
-	fieldName: 'primarySpell',
-	values: [
-		{ actionId: ActionId.fromSpellId(47809), value: PrimarySpell.ShadowBolt },
-		{ actionId: ActionId.fromSpellId(47838), value: PrimarySpell.Incinerate },
-		{ actionId: ActionId.fromSpellId(47836), value: PrimarySpell.Seed },
-	],
-	setValue: (eventID: EventID, player: Player<Spec.SpecWarlock>, newValue: number) => {
-		const newRotation = player.getRotation();
-		if (newValue == PrimarySpell.Seed && newRotation.corruption == true) {
-			newRotation.corruption = false
-		}
-		newRotation.primarySpell = newValue
-		newRotation.preset = RotationPreset.Manual;
-		player.setRotation(eventID, newRotation);
-	},
-});
-
-export const SecondaryDotInput = InputHelpers.makeRotationEnumIconInput<Spec.SpecWarlock, SecondaryDot>({
-	fieldName: 'secondaryDot',
-	values: [
-		{ value: SecondaryDot.NoSecondaryDot },
-		{ actionId: ActionId.fromSpellId(47811), value: SecondaryDot.Immolate },
-		{
-			actionId: ActionId.fromSpellId(47843), value: SecondaryDot.UnstableAffliction,
-			showWhen: (player: Player<Spec.SpecWarlock>) => player.getTalents().unstableAffliction,
-		},
-	],
-	changeEmitter: (player: Player<Spec.SpecWarlock>) => player.changeEmitter,
-	setValue: (eventID: EventID, player: Player<Spec.SpecWarlock>, newValue: number) => {
-		const newRotation = player.getRotation();
-		newRotation.secondaryDot = newValue;
-		newRotation.preset = RotationPreset.Manual;
-		player.setRotation(eventID, newRotation);
-	},
-});
-
-export const SpecSpellInput = InputHelpers.makeRotationEnumIconInput<Spec.SpecWarlock, SpecSpell>({
-	fieldName: 'specSpell',
-	values: [
-		{ value: SpecSpell.NoSpecSpell },
-		{
-			actionId: ActionId.fromSpellId(59164), value: SpecSpell.Haunt,
-			showWhen: (player: Player<Spec.SpecWarlock>) => player.getTalents().haunt,
-		},
-		{
-			actionId: ActionId.fromSpellId(59172), value: SpecSpell.ChaosBolt,
-			showWhen: (player: Player<Spec.SpecWarlock>) => player.getTalents().chaosBolt,
-		},
-	],
-	changeEmitter: (player: Player<Spec.SpecWarlock>) => player.changeEmitter,
-	showWhen: (player: Player<Spec.SpecWarlock>) => player.getTalents().haunt || player.getTalents().chaosBolt,
-	setValue: (eventID: EventID, player: Player<Spec.SpecWarlock>, newValue: number) => {
-		const newRotation = player.getRotation();
-		newRotation.specSpell = newValue;
-		newRotation.preset = RotationPreset.Manual;
-		player.setRotation(eventID, newRotation);
-	},
-});
-
-
-export const CorruptionSpell = {
-	type: 'icon' as const,
-	id: ActionId.fromSpellId(47813),
-	states: 2,
-	extraCssClasses: [
-		'Corruption-picker',
-	],
-	changedEvent: (player: Player<Spec.SpecWarlock>) => player.changeEmitter,
-	getValue: (player: Player<Spec.SpecWarlock>) => player.getRotation().corruption,
-	setValue: (eventID: EventID, player: Player<Spec.SpecWarlock>, newValue: boolean) => {
-		const newRotation = player.getRotation();
-		if (newRotation.primarySpell == PrimarySpell.Seed && newValue == true) {
-			newRotation.primarySpell = PrimarySpell.ShadowBolt
-		}
-		newRotation.corruption = newValue;
-		newRotation.preset = RotationPreset.Manual;
-		player.setRotation(eventID, newRotation);
-	},
-};
-
-export const WarlockRotationConfig = {
-	inputs: [
-		{
-			type: 'enum' as const,
-
-			label: 'Sim presets',
-			labelTooltip: 'Quick switch between sim spec presets. Will UPDATE TALENTS and SPELLS to defaults.',
-			values: [
-				{ name: 'Affliction', value: RotationType.Affliction },
-				{ name: 'Demonology', value: RotationType.Demonology },
-				{ name: 'Destruction', value: RotationType.Destruction },
-			],
-			changedEvent: (player: Player<Spec.SpecWarlock>) => player.rotationChangeEmitter,
-			getValue: (player: Player<Spec.SpecWarlock>) => player.getRotation().type,
-			setValue: (eventID: EventID, player: Player<Spec.SpecWarlock>, newValue: number) => {
-				var newRotation = player.getRotation();
-				var newOptions: WarlockOptions;
-				var newGlyphs: Glyphs;
-				var newTalents: string;
-				if (newValue == RotationType.Affliction) {
-					newTalents = Presets.AfflictionTalents.data.talentsString
-					newGlyphs = Presets.AfflictionTalents.data.glyphs || Glyphs.create();
-					newRotation = Presets.AfflictionRotation
-					newOptions = Presets.AfflictionOptions
-				} else if (newValue == RotationType.Demonology) {
-					newTalents = Presets.DemonologyTalents.data.talentsString
-					newGlyphs = Presets.DemonologyTalents.data.glyphs || Glyphs.create();
-					newRotation = Presets.DemonologyRotation
-					newOptions = Presets.DemonologyOptions
-				} else if (newValue == RotationType.Destruction) {
-					newTalents = Presets.DestructionTalents.data.talentsString
-					newGlyphs = Presets.DestructionTalents.data.glyphs || Glyphs.create();
-					newRotation = Presets.DestructionRotation
-					newOptions = Presets.DestructionOptions
-				}
-				newRotation.type = newValue;
-				newRotation.preset = RotationPreset.Automatic;
-				TypedEvent.freezeAllAndDo(() => {
-					player.setTalentsString(eventID, newTalents);
-					player.setSpecOptions(eventID, newOptions);
-					player.setGlyphs(eventID, newGlyphs);
-					player.setRotation(eventID, newRotation);
-				});
-			},
-		},
-
-		{
-			type: 'enum' as const,
-			label: 'Spell & Talent',
-			labelTooltip: 'Putting it on Automatic will UPDATE talents and spells to defaults.',
-			values: [
-				{ name: "Manual", value: RotationPreset.Manual },
-				{ name: "Automatic", value: RotationPreset.Automatic },
-			],
-			changedEvent: (player: Player<Spec.SpecWarlock>) => player.rotationChangeEmitter,
-			getValue: (player: Player<Spec.SpecWarlock>) => player.getRotation().preset,
-			setValue: (eventID: EventID, player: Player<Spec.SpecWarlock>, newValue: number) => {
-				var newRotation = player.getRotation();
-				if (newValue == RotationPreset.Automatic) {
-					var newOptions: WarlockOptions;
-					var newGlyphs: Glyphs;
-					var newTalents: string;
-					if (newRotation.type == RotationType.Affliction) {
-						newTalents = Presets.AfflictionTalents.data.talentsString
-						newGlyphs = Presets.AfflictionTalents.data.glyphs || Glyphs.create()
-						newRotation = Presets.AfflictionRotation
-						newOptions = Presets.AfflictionOptions
-					} else if (newRotation.type == RotationType.Demonology) {
-						newTalents = Presets.DemonologyTalents.data.talentsString
-						newGlyphs = Presets.DemonologyTalents.data.glyphs || Glyphs.create()
-						newRotation = Presets.DemonologyRotation
-						newOptions = Presets.DemonologyOptions
-					} else if (newRotation.type == RotationType.Destruction) {
-						newTalents = Presets.DestructionTalents.data.talentsString
-						newGlyphs = Presets.DestructionTalents.data.glyphs || Glyphs.create()
-						newRotation = Presets.DestructionRotation
-						newOptions = Presets.DestructionOptions
-					}
-				}
-				newRotation.preset = newValue;
-				TypedEvent.freezeAllAndDo(() => {
-					if (newValue == RotationPreset.Automatic) {
-						player.setTalentsString(eventID, newTalents);
-						player.setSpecOptions(eventID, newOptions);
-						player.setGlyphs(eventID, newGlyphs);
-					}
-					player.setRotation(eventID, newRotation);
-				});
-			},
-		},
-		{
-			type: 'enum' as const,
-			label: 'Curse',
-			labelTooltip: 'Manual curse selection.',
-			values: [
-				{ name: "None", value: Curse.NoCurse },
-				{ name: "Elements", value: Curse.Elements },
-				{ name: "Weakness", value: Curse.Weakness },
-				{ name: "Doom", value: Curse.Doom },
-				{ name: "Agony", value: Curse.Agony },
-				{ name: "Tongues", value: Curse.Tongues }
-			],
-			changedEvent: (player: Player<Spec.SpecWarlock>) => player.rotationChangeEmitter,
-			getValue: (player: Player<Spec.SpecWarlock>) => player.getRotation().curse,
-			setValue: (eventID: EventID, player: Player<Spec.SpecWarlock>, newValue: number) => {
-				const newRotation = player.getRotation();
-				newRotation.curse = newValue;
-				newRotation.preset = RotationPreset.Manual;
-				player.setRotation(eventID, newRotation);
-			},
-		},
-
-		InputHelpers.makeRotationBooleanInput<Spec.SpecWarlock>({
-			fieldName: 'useInfernal',
-			label: 'Summon Infernal',
-			labelTooltip: 'Casts Inferno 60s before the fight is over',
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecWarlock>({
-			fieldName: 'detonateSeed',
-			label: 'Detonate Seed on Cast',
-			labelTooltip: 'Simulates raid doing damage to targets such that seed detonates immediately on cast.',
-			showWhen: (player: Player<Spec.SpecWarlock>) => player.getRotation().primarySpell == PrimarySpell.Seed,
-		}),
-	],
-};
diff --git a/ui/warlock/presets.ts b/ui/warlock/presets.ts
index 7401555967..fef9006aa3 100644
--- a/ui/warlock/presets.ts
+++ b/ui/warlock/presets.ts
@@ -10,17 +10,12 @@ import {
 	Debuffs,
 	TristateEffect,
 	Faction,
-	Spec, Profession,
+	Profession,
 } from '../core/proto/common.js';
 import { SavedTalents } from '../core/proto/ui.js';
 
 import {
-	Warlock_Rotation as WarlockRotation,
 	Warlock_Options as WarlockOptions,
-	Warlock_Rotation_PrimarySpell as PrimarySpell,
-	Warlock_Rotation_SecondaryDot as SecondaryDot,
-	Warlock_Rotation_SpecSpell as SpecSpell,
-	Warlock_Rotation_Curse as Curse,
 	Warlock_Options_WeaponImbue as WeaponImbue,
 	Warlock_Options_Armor as Armor,
 	Warlock_Options_Summon as Summon,
@@ -65,43 +60,10 @@ export const P3_DESTRO_HORDE_PRESET = PresetUtils.makePresetGear('P3 Destro [H]'
 import P4DestroGear from './gear_sets/p4_destro.gear.json';
 export const P4_DESTRO_PRESET = PresetUtils.makePresetGear('P4 Destro', P4DestroGear, { tooltip: BIS_TOOLTIP, talentTree: 2 });
 
-export const AfflictionRotation = WarlockRotation.create({
-	primarySpell: PrimarySpell.ShadowBolt,
-	secondaryDot: SecondaryDot.UnstableAffliction,
-	specSpell: SpecSpell.Haunt,
-	curse: Curse.Agony,
-	corruption: true,
-	useInfernal: false,
-	detonateSeed: true,
-});
-
-export const DemonologyRotation = WarlockRotation.create({
-	primarySpell: PrimarySpell.ShadowBolt,
-	secondaryDot: SecondaryDot.Immolate,
-	specSpell: SpecSpell.NoSpecSpell,
-	curse: Curse.Doom,
-	corruption: true,
-	useInfernal: false,
-	detonateSeed: true,
-});
-
-export const DestructionRotation = WarlockRotation.create({
-	primarySpell: PrimarySpell.Incinerate,
-	secondaryDot: SecondaryDot.Immolate,
-	specSpell: SpecSpell.ChaosBolt,
-	curse: Curse.Doom,
-	corruption: false,
-	useInfernal: false,
-	detonateSeed: true,
-});
-
-export const APL_Affliction_Legacy = PresetUtils.makePresetLegacyRotation('Affliction Legacy', Spec.SpecWarlock, AfflictionRotation, { talentTree: 0 });
 import AfflictionApl from './apls/affliction.apl.json';
 export const APL_Affliction_Default = PresetUtils.makePresetAPLRotation('Affliction', AfflictionApl, { talentTree: 0 });
-export const APL_Demo_Legacy = PresetUtils.makePresetLegacyRotation('Demo Legacy', Spec.SpecWarlock, DemonologyRotation, { talentTree: 1 });
 import DemoApl from './apls/demo.apl.json';
 export const APL_Demo_Default = PresetUtils.makePresetAPLRotation('Demo', DemoApl, { talentTree: 1 });
-export const APL_Destro_Legacy = PresetUtils.makePresetLegacyRotation('Destro Legacy', Spec.SpecWarlock, DestructionRotation, { talentTree: 2 });
 import DestroApl from './apls/destro.apl.json';
 export const APL_Destro_Default = PresetUtils.makePresetAPLRotation('Destro', DestroApl, { talentTree: 2 });
 
diff --git a/ui/warlock/sim.ts b/ui/warlock/sim.ts
index efb04a3d60..35d02c3813 100644
--- a/ui/warlock/sim.ts
+++ b/ui/warlock/sim.ts
@@ -70,8 +70,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, {
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
 
-		// Default rotation settings.
-		rotation: Presets.AfflictionRotation,
 		// Default talents.
 		talents: Presets.AfflictionTalents.data,
 		// Default spec-specific settings.
@@ -102,7 +100,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, {
 		WarlockInputs.SecondaryDotInput,
 		WarlockInputs.SpecSpellInput,
 	],
-	rotationInputs: WarlockInputs.WarlockRotationConfig,
 
 	// Buff and Debuff inputs to include/exclude, overriding the EP-based defaults.
 	includeBuffDebuffInputs: [
@@ -147,11 +144,8 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, {
 		],
 		// Preset rotations that the user can quickly select.
 		rotations: [
-			Presets.APL_Affliction_Legacy,
 			Presets.APL_Affliction_Default,
-			Presets.APL_Demo_Legacy,
 			Presets.APL_Demo_Default,
-			Presets.APL_Destro_Legacy,
 			Presets.APL_Destro_Default,
 		],
 
diff --git a/ui/warrior/inputs.ts b/ui/warrior/inputs.ts
index a68d1b04bc..afb05c2303 100644
--- a/ui/warrior/inputs.ts
+++ b/ui/warrior/inputs.ts
@@ -1,18 +1,8 @@
 import { Spec } from '../core/proto/common.js';
 import { ActionId } from '../core/proto_utils/action_id.js';
-import { Player } from '../core/player.js';
-import { EventID, TypedEvent } from '../core/typed_event.js';
 
 import {
 	WarriorShout,
-	WarriorTalents as WarriorTalents,
-	Warrior,
-	Warrior_Rotation as WarriorRotation,
-	Warrior_Rotation_SunderArmor as SunderArmor,
-	Warrior_Options as WarriorOptions,
-	Warrior_Rotation_StanceOption as StanceOption,
-	Warrior_Rotation_SpellOption as SpellOption,
-	Warrior_Rotation_MainGcd as MainGcd,
 } from '../core/proto/warrior.js';
 
 import * as InputHelpers from '../core/components/input_helpers.js';
@@ -59,190 +49,3 @@ export const ShoutPicker = InputHelpers.makeSpecOptionsEnumIconInput<Spec.SpecWa
 		{ actionId: ActionId.fromSpellId(469), value: WarriorShout.WarriorShoutCommanding },
 	],
 });
-
-export const WarriorRotationConfig = {
-	inputs: [
-		InputHelpers.makeRotationBooleanInput<Spec.SpecWarrior>({
-			fieldName: 'useMs',
-			label: 'Use Mortal Strike',
-			labelTooltip: 'Use Mortal Strike when rage threshold is met.',
-			changeEmitter: (player: Player<Spec.SpecWarrior>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-			showWhen: (player: Player<Spec.SpecWarrior>) => player.getTalentTree() == 0 && !player.getRotation().customRotationOption,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecWarrior>({
-			fieldName: 'useCleave',
-			label: 'Use Cleave',
-			labelTooltip: 'Use Cleave instead of Heroic Strike.',
-			showWhen: (player: Player<Spec.SpecWarrior>) => !player.getRotation().customRotationOption,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecWarrior>({
-			fieldName: 'useRend',
-			label: 'Use Rend',
-			labelTooltip: 'Use Rend when rage threshold is met and the debuff duration is less than refresh time.',
-			changeEmitter: (player: Player<Spec.SpecWarrior>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-			showWhen: (player: Player<Spec.SpecWarrior>) => !player.getRotation().customRotationOption,
-		}),
-
-		InputHelpers.makeRotationBooleanInput<Spec.SpecWarrior>({
-			fieldName: 'useOverpower',
-			label: 'Use Overpower',
-			labelTooltip: 'Use Overpower whenever it is available on an open GCD.',
-			changeEmitter: (player: Player<Spec.SpecWarrior>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-			showWhen: (player: Player<Spec.SpecWarrior>) => player.getTalentTree() == 1 && !player.getRotation().customRotationOption,
-		}),
-
-		InputHelpers.makeRotationEnumInput<Spec.SpecWarrior, MainGcd>({
-			fieldName: 'mainGcd',
-			label: 'Main GCD',
-			labelTooltip: 'Main GCD ability that will be prioritized above other abilities.',
-			values: [
-				{ name: 'Slam', value: MainGcd.Slam },
-				{ name: 'Bloodthirst', value: MainGcd.Bloodthirst },
-				{ name: 'Whirlwind', value: MainGcd.Whirlwind },
-			],
-			changeEmitter: (player: Player<Spec.SpecWarrior>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-			showWhen: (player: Player<Spec.SpecWarrior>) => player.getTalentTree() == 1 && !player.getRotation().customRotationOption,
-		}),
-		InputHelpers.makeRotationNumberInput<Spec.SpecWarrior>({
-			fieldName: 'msRageThreshold',
-			label: 'Mortal Strike rage threshold',
-			labelTooltip: 'Mortal Strike when rage is above:',
-			changeEmitter: (player: Player<Spec.SpecWarrior>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-			showWhen: (player: Player<Spec.SpecWarrior>) => (player.getRotation().useMs || player.getRotation().customRotationOption) && player.getTalentTree() == 0,
-		}),
-		InputHelpers.makeRotationNumberInput<Spec.SpecWarrior>({
-			fieldName: 'slamRageThreshold',
-			label: 'Slam rage threshold',
-			labelTooltip: 'Slam when rage is above:',
-			changeEmitter: (player: Player<Spec.SpecWarrior>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-			showWhen: (player: Player<Spec.SpecWarrior>) => (player.getRotation().useMs || player.getRotation().customRotationOption) && player.getTalentTree() == 0,
-		}),
-		InputHelpers.makeRotationNumberInput<Spec.SpecWarrior>({
-			fieldName: 'bloodsurgeDurationThreshold',
-			label: 'Exp Slam: Bloodsurge duration threshold (s)',
-			labelTooltip: 'Cast Exp Slam when Bloodsurge duration is below (seconds):',
-			changeEmitter: (player: Player<Spec.SpecWarrior>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-			showWhen: (player: Player<Spec.SpecWarrior>) => player.getRotation().customRotationOption && player.getTalentTree() == 1,
-		}),
-		InputHelpers.makeRotationNumberInput<Spec.SpecWarrior>({
-			fieldName: 'hsRageThreshold',
-			label: 'HS rage threshold',
-			labelTooltip: 'Heroic Strike when rage is above:',
-		}),
-		InputHelpers.makeRotationNumberInput<Spec.SpecWarrior>({
-			fieldName: 'rendHealthThresholdAbove',
-			label: 'Rend health threshold (%)',
-			labelTooltip: 'Rend will only be used when boss health is above this value in %.',
-			changeEmitter: (player: Player<Spec.SpecWarrior>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-			showWhen: (player: Player<Spec.SpecWarrior>) => (player.getRotation().useRend == true || player.getRotation().customRotationOption),
-		}),
-		InputHelpers.makeRotationNumberInput<Spec.SpecWarrior>({
-			fieldName: 'rendRageThresholdBelow',
-			label: 'Rend rage threshold below',
-			labelTooltip: 'Rend will only be used when rage is smaller than this value.',
-			changeEmitter: (player: Player<Spec.SpecWarrior>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-			showWhen: (player: Player<Spec.SpecWarrior>) => (player.getRotation().useRend == true || player.getRotation().customRotationOption) && player.getTalentTree() == 1,
-		}),
-		InputHelpers.makeRotationNumberInput<Spec.SpecWarrior>({
-			fieldName: 'rendCdThreshold',
-			label: 'Rend Refresh Time (s)',
-			labelTooltip: 'Refresh Rend when the remaining duration is less than this amount of time (seconds).',
-			changeEmitter: (player: Player<Spec.SpecWarrior>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-			showWhen: (player: Player<Spec.SpecWarrior>) => player.getRotation().useRend == true || player.getRotation().customRotationOption,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecWarrior>({
-			fieldName: 'useHsDuringExecute',
-			label: 'HS during Execute Phase',
-			labelTooltip: 'Use Heroic Strike during Execute Phase.',
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecWarrior>({
-			fieldName: 'useBtDuringExecute',
-			label: 'BT during Execute Phase',
-			labelTooltip: 'Use Bloodthirst during Execute Phase.',
-			changeEmitter: (player: Player<Spec.SpecWarrior>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-			showWhen: (player: Player<Spec.SpecWarrior>) => player.getTalentTree() == 1,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecWarrior>({
-			fieldName: 'useWwDuringExecute',
-			label: 'WW during Execute Phase',
-			labelTooltip: 'Use Whirlwind during Execute Phase.',
-			changeEmitter: (player: Player<Spec.SpecWarrior>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-			showWhen: (player: Player<Spec.SpecWarrior>) => player.getTalentTree() == 1,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecWarrior>({
-			fieldName: 'useSlamOverExecute',
-			label: 'Slam during Execute Phase',
-			labelTooltip: 'Use Slam Over Execute when Bloodsurge Procs in Execute Phase.',
-			changeEmitter: (player: Player<Spec.SpecWarrior>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-			showWhen: (player: Player<Spec.SpecWarrior>) => player.getTalentTree() == 1,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecWarrior>({
-			fieldName: 'executePhaseOverpower',
-			label: 'Overpower in Execute Phase',
-			labelTooltip: 'Use Overpower instead of Execute whenever it is available.',
-			changeEmitter: (player: Player<Spec.SpecWarrior>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-			showWhen: (player: Player<Spec.SpecWarrior>) => (player.getRotation().useOverpower == true || player.getRotation().customRotationOption) && player.getTalentTree() == 1,
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecWarrior>({
-			fieldName: 'spamExecute',
-			label: 'Spam Execute',
-			labelTooltip: 'Use Execute whenever possible during Execute Phase.',
-			changeEmitter: (player: Player<Spec.SpecWarrior>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-			showWhen: (player: Player<Spec.SpecWarrior>) => player.getTalentTree() == 0,
-		}),
-		InputHelpers.makeRotationEnumInput<Spec.SpecWarrior, SunderArmor>({
-			fieldName: 'sunderArmor',
-			label: 'Sunder Armor',
-			values: [
-				{ name: 'Never', value: SunderArmor.SunderArmorNone },
-				{ name: 'Help Stack', value: SunderArmor.SunderArmorHelpStack },
-				{ name: 'Maintain Debuff', value: SunderArmor.SunderArmorMaintain },
-			],
-		}),
-		InputHelpers.makeRotationEnumInput<Spec.SpecWarrior, StanceOption>({
-			fieldName: 'stanceOption',
-			label: 'Stance Option',
-			labelTooltip: 'Stance to stay on. The default for Fury (Bloodthirst) is Berserker Stance and Battle Stance for everything else.',
-			values: [
-				{ name: 'Default', value: StanceOption.DefaultStance },
-				{ name: 'Battle Stance', value: StanceOption.BattleStance },
-				{ name: 'Berserker Stance', value: StanceOption.BerserkerStance },
-			],
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecWarrior>({
-			fieldName: 'maintainDemoShout',
-			label: 'Maintain Demo Shout',
-			labelTooltip: 'Keep Demo Shout active on the primary target.',
-		}),
-		InputHelpers.makeRotationBooleanInput<Spec.SpecWarrior>({
-			fieldName: 'maintainThunderClap',
-			label: 'Maintain Thunder Clap',
-			labelTooltip: 'Keep Thunder Clap active on the primary target.',
-		}),
-
-		InputHelpers.makeRotationBooleanInput<Spec.SpecWarrior>({
-			fieldName: 'customRotationOption',
-			label: 'Custom Rotation (Advanced)',
-			labelTooltip: 'Create your own rotation action priority list.',
-			showWhen: (player: Player<Spec.SpecWarrior>) => player.sim.getShowExperimental(),
-			changeEmitter: (player: Player<Spec.SpecWarrior>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-		}),
-
-		InputHelpers.makeCustomRotationInput<Spec.SpecWarrior, SpellOption>({
-			fieldName: 'customRotation',
-			numColumns: 3,
-			showWhen: (player: Player<Spec.SpecWarrior>) => player.getRotation().customRotationOption && player.sim.getShowExperimental(),
-			values: [
-				{ actionId: ActionId.fromSpellId(23881), value: SpellOption.BloodthirstCustom },
-				{ actionId: ActionId.fromSpellId(1680), value: SpellOption.WhirlwindCustom },
-				{ actionId: ActionId.fromSpellId(47475), value: SpellOption.SlamCustom },
-				{ actionId: ActionId.fromSpellId(47475), value: SpellOption.SlamExpiring, text: "Exp", showWhen: (player: Player<Spec.SpecWarrior>) => player.getTalentTree() == 1, },
-				{ actionId: ActionId.fromSpellId(47486), value: SpellOption.MortalStrike },
-				{ actionId: ActionId.fromSpellId(47465), value: SpellOption.Rend },
-				{ actionId: ActionId.fromSpellId(7384), value: SpellOption.Overpower },
-				{ actionId: ActionId.fromSpellId(47471), value: SpellOption.Execute },
-				{ actionId: ActionId.fromSpellId(47502), value: SpellOption.ThunderClap },
-			],
-		}),
-	],
-};
diff --git a/ui/warrior/presets.ts b/ui/warrior/presets.ts
index da5f32fdbe..51ca2e0e2a 100644
--- a/ui/warrior/presets.ts
+++ b/ui/warrior/presets.ts
@@ -10,13 +10,9 @@ import { SavedTalents } from '../core/proto/ui.js';
 
 import {
 	WarriorShout,
-	Warrior_Rotation as WarriorRotation,
-	Warrior_Rotation_SunderArmor as SunderArmor,
 	Warrior_Options as WarriorOptions,
 	WarriorMajorGlyph,
 	WarriorMinorGlyph,
-	Warrior_Rotation_StanceOption as StanceOption,
-	Warrior_Rotation_MainGcd as MainGcd,
 } from '../core/proto/warrior.js';
 
 import * as PresetUtils from '../core/preset_utils.js';
@@ -58,43 +54,6 @@ export const P4_FURY_PRESET_ALLIANCE = PresetUtils.makePresetGear('P4 Fury [A]',
 import P4FuryHordeGear from './gear_sets/p4_fury_horde.gear.json';
 export const P4_FURY_PRESET_HORDE = PresetUtils.makePresetGear('P4 Fury [H]', P4FuryHordeGear, { talentTrees: [1,2], faction: Faction.Horde });
 
-export const DefaultRotation = WarriorRotation.create({
-	useRend: true,
-	useCleave: false,
-	useOverpower: false,
-	executePhaseOverpower: false,
-
-	mainGcd: MainGcd.Bloodthirst,
-
-	sunderArmor: SunderArmor.SunderArmorMaintain,
-
-	hsRageThreshold: 12,
-	rendHealthThresholdAbove: 20,
-	rendRageThresholdBelow: 100,
-	slamRageThreshold: 25,
-	rendCdThreshold: 0,
-	useHsDuringExecute: true,
-	useBtDuringExecute: true,
-	useWwDuringExecute: true,
-	useSlamOverExecute: true,
-	stanceOption: StanceOption.DefaultStance,
-});
-
-export const ArmsRotation = WarriorRotation.create({
-	useRend: true,
-	useMs: true,
-	useCleave: false,
-	sunderArmor: SunderArmor.SunderArmorMaintain,
-	msRageThreshold: 30,
-	slamRageThreshold: 25,
-	hsRageThreshold: 50,
-	rendCdThreshold: 0,
-	rendHealthThresholdAbove: 100,
-	useHsDuringExecute: true,
-	spamExecute: true,
-	stanceOption: StanceOption.DefaultStance,
-});
-
 import FuryApl from './apls/fury.apl.json';
 export const ROTATION_FURY = PresetUtils.makePresetAPLRotation('Fury', FuryApl, { talentTree: 1 });
 import FurySunderApl from './apls/fury_sunder.apl.json';
diff --git a/ui/warrior/sim.ts b/ui/warrior/sim.ts
index bf98f00ba9..d81b417f74 100644
--- a/ui/warrior/sim.ts
+++ b/ui/warrior/sim.ts
@@ -102,8 +102,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, {
 		}),
 		// Default consumes settings.
 		consumes: Presets.DefaultConsumes,
-		// Default rotation settings.
-		rotation: Presets.DefaultRotation,
 		// Default talents.
 		talents: Presets.FuryTalents.data,
 		// Default spec-specific settings.
@@ -145,8 +143,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarrior, {
 		WarriorInputs.Recklessness,
 		WarriorInputs.ShatteringThrow,
 	],
-	// Inputs to include in the 'Rotation' section on the settings tab.
-	rotationInputs: WarriorInputs.WarriorRotationConfig,
 	// Buff and Debuff inputs to include/exclude, overriding the EP-based defaults.
 	includeBuffDebuffInputs: [
 		// just for Bryntroll

From 264b43641001db92758340cd662f7c2920d168d3 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Fri, 29 Dec 2023 13:35:22 +0900
Subject: [PATCH 02/28] Rename getRotation() --> getSimpleRotation() for
 clarity

---
 .../individual_sim_ui/cooldowns_picker.ts     | 26 ++---
 .../individual_sim_ui/rotation_tab.ts         |  2 +-
 ui/core/components/input_helpers.ts           | 24 ++---
 ui/core/components/other_inputs.ts            |  8 +-
 ui/core/player.ts                             | 95 +++++++++----------
 ui/feral_druid/inputs.ts                      | 14 +--
 ui/hunter/inputs.ts                           |  4 +-
 ui/warlock/sim.ts                             |  7 --
 8 files changed, 83 insertions(+), 97 deletions(-)

diff --git a/ui/core/components/individual_sim_ui/cooldowns_picker.ts b/ui/core/components/individual_sim_ui/cooldowns_picker.ts
index c682bcc575..7c02bf5dba 100644
--- a/ui/core/components/individual_sim_ui/cooldowns_picker.ts
+++ b/ui/core/components/individual_sim_ui/cooldowns_picker.ts
@@ -18,7 +18,7 @@ export class CooldownsPicker extends Component {
 		this.player = player;
 		this.cooldownPickers = [];
 
-		TypedEvent.onAny([this.player.cooldownsChangeEmitter, this.player.sim.unitMetadataEmitter]).on(eventID => {
+		TypedEvent.onAny([this.player.rotationChangeEmitter, this.player.sim.unitMetadataEmitter]).on(eventID => {
 			this.update();
 		});
 		this.update();
@@ -26,7 +26,7 @@ export class CooldownsPicker extends Component {
 
 	private update() {
 		this.rootElem.innerHTML = '';
-		const cooldowns = this.player.getCooldowns().cooldowns;
+		const cooldowns = this.player.getSimpleCooldowns().cooldowns;
 
 		this.cooldownPickers = [];
 		for (let i = 0; i < cooldowns.length + 1; i++) {
@@ -63,9 +63,9 @@ export class CooldownsPicker extends Component {
 			const deleteButton = deleteButtonFragment.children[0] as HTMLElement;
 			const deleteButtonTooltip = Tooltip.getOrCreateInstance(deleteButton, {title: 'Delete Cooldown'});
 			deleteButton.addEventListener('click', event => {
-				const newCooldowns = this.player.getCooldowns();
+				const newCooldowns = this.player.getSimpleCooldowns();
 				newCooldowns.cooldowns.splice(i, 1);
-				this.player.setCooldowns(TypedEvent.nextEventID(), newCooldowns);
+				this.player.setSimpleCooldowns(TypedEvent.nextEventID(), newCooldowns);
 				deleteButtonTooltip.hide();
 			});
 			row.appendChild(deleteButton);
@@ -90,10 +90,10 @@ export class CooldownsPicker extends Component {
 			equals: (a: ActionIdProto, b: ActionIdProto) => ActionIdProto.equals(a, b),
 			zeroValue: ActionIdProto.create(),
 			backupIconUrl: (value: ActionIdProto) => ActionId.fromProto(value),
-			changedEvent: (player: Player<any>) => player.cooldownsChangeEmitter,
-			getValue: (player: Player<any>) => player.getCooldowns().cooldowns[cooldownIndex]?.id || ActionIdProto.create(),
+			changedEvent: (player: Player<any>) => player.rotationChangeEmitter,
+			getValue: (player: Player<any>) => player.getSimpleCooldowns().cooldowns[cooldownIndex]?.id || ActionIdProto.create(),
 			setValue: (eventID: EventID, player: Player<any>, newValue: ActionIdProto) => {
-				const newCooldowns = player.getCooldowns();
+				const newCooldowns = player.getSimpleCooldowns();
 
 				while (newCooldowns.cooldowns.length < cooldownIndex) {
 					newCooldowns.cooldowns.push(Cooldown.create());
@@ -103,7 +103,7 @@ export class CooldownsPicker extends Component {
 					timings: [],
 				});
 
-				player.setCooldowns(eventID, newCooldowns);
+				player.setSimpleCooldowns(eventID, newCooldowns);
 			},
 		});
 		return actionPicker;
@@ -115,17 +115,17 @@ export class CooldownsPicker extends Component {
 				'cooldown-timings-picker',
 			],
 			placeholder: '20, 40, ...',
-			changedEvent: (player: Player<any>) => player.cooldownsChangeEmitter,
+			changedEvent: (player: Player<any>) => player.rotationChangeEmitter,
 			getValue: (player: Player<any>) => {
-				return player.getCooldowns().cooldowns[cooldownIndex]?.timings || [];
+				return player.getSimpleCooldowns().cooldowns[cooldownIndex]?.timings || [];
 			},
 			setValue: (eventID: EventID, player: Player<any>, newValue: Array<number>) => {
-				const newCooldowns = player.getCooldowns();
+				const newCooldowns = player.getSimpleCooldowns();
 				newCooldowns.cooldowns[cooldownIndex].timings = newValue;
-				player.setCooldowns(eventID, newCooldowns);
+				player.setSimpleCooldowns(eventID, newCooldowns);
 			},
 			enableWhen: (player: Player<any>) => {
-				const curCooldown = player.getCooldowns().cooldowns[cooldownIndex];
+				const curCooldown = player.getSimpleCooldowns().cooldowns[cooldownIndex];
 				return curCooldown && !ActionIdProto.equals(curCooldown.id, ActionIdProto.create());
 			},
 		});
diff --git a/ui/core/components/individual_sim_ui/rotation_tab.ts b/ui/core/components/individual_sim_ui/rotation_tab.ts
index e962848ee1..81742a4f7b 100644
--- a/ui/core/components/individual_sim_ui/rotation_tab.ts
+++ b/ui/core/components/individual_sim_ui/rotation_tab.ts
@@ -197,7 +197,7 @@ export class RotationTab extends SimTab {
 					player.setAplRotation(eventID, newRotation.rotation || APLRotation.create());
 				});
 			},
-			changeEmitters: [this.simUI.player.rotationChangeEmitter, this.simUI.player.cooldownsChangeEmitter, this.simUI.player.talentsChangeEmitter],
+			changeEmitters: [this.simUI.player.rotationChangeEmitter, this.simUI.player.talentsChangeEmitter],
 			equals: (a: SavedRotation, b: SavedRotation) => {
 				// Uncomment this to debug equivalence checks with preset rotations (e.g. the chip doesn't highlight)
 				//console.log(`Rot A: ${SavedRotation.toJsonString(a, {prettySpaces: 2})}\n\nRot B: ${SavedRotation.toJsonString(b, {prettySpaces: 2})}`);
diff --git a/ui/core/components/input_helpers.ts b/ui/core/components/input_helpers.ts
index 4058200c79..4ad78bfd18 100644
--- a/ui/core/components/input_helpers.ts
+++ b/ui/core/components/input_helpers.ts
@@ -80,11 +80,11 @@ export function makeRotationBooleanInput<SpecType extends Spec>(config: PlayerBo
 		label: config.label,
 		labelTooltip: config.labelTooltip,
 		getModObject: (player: Player<SpecType>) => player,
-		getValue: config.getValue || ((player: Player<SpecType>) => player.getRotation()[config.fieldName] as unknown as boolean),
+		getValue: config.getValue || ((player: Player<SpecType>) => player.getSimpleRotation()[config.fieldName] as unknown as boolean),
 		setValue: config.setValue || ((eventID: EventID, player: Player<SpecType>, newVal: boolean) => {
-			const newMessage = player.getRotation();
+			const newMessage = player.getSimpleRotation();
 			(newMessage[config.fieldName] as unknown as boolean) = newVal;
-			player.setRotation(eventID, newMessage);
+			player.setSimpleRotation(eventID, newMessage);
 		}),
 		changedEvent: config.changeEmitter || ((player: Player<SpecType>) => player.rotationChangeEmitter),
 		enableWhen: config.enableWhen,
@@ -163,11 +163,11 @@ export function makeRotationNumberInput<SpecType extends Spec>(config: PlayerNum
 		float: config.float,
 		positive: config.positive,
 		getModObject: (player: Player<SpecType>) => player,
-		getValue: config.getValue || ((player: Player<SpecType>) => player.getRotation()[config.fieldName] as unknown as number),
+		getValue: config.getValue || ((player: Player<SpecType>) => player.getSimpleRotation()[config.fieldName] as unknown as number),
 		setValue: config.setValue || ((eventID: EventID, player: Player<SpecType>, newVal: number) => {
-			const newMessage = player.getRotation();
+			const newMessage = player.getSimpleRotation();
 			(newMessage[config.fieldName] as unknown as number) = newVal;
-			player.setRotation(eventID, newMessage);
+			player.setSimpleRotation(eventID, newMessage);
 		}),
 		changedEvent: config.changeEmitter || ((player: Player<SpecType>) => player.rotationChangeEmitter),
 		enableWhen: config.enableWhen,
@@ -245,11 +245,11 @@ export function makeRotationEnumInput<SpecType extends Spec, T>(config: PlayerEn
 		labelTooltip: config.labelTooltip,
 		values: config.values,
 		getModObject: (player: Player<SpecType>) => player,
-		getValue: config.getValue || ((player: Player<SpecType>) => player.getRotation()[config.fieldName] as unknown as number),
+		getValue: config.getValue || ((player: Player<SpecType>) => player.getSimpleRotation()[config.fieldName] as unknown as number),
 		setValue: config.setValue || ((eventID: EventID, player: Player<SpecType>, newVal: number) => {
-			const newMessage = player.getRotation();
+			const newMessage = player.getSimpleRotation();
 			(newMessage[config.fieldName] as unknown as number) = newVal;
-			player.setRotation(eventID, newMessage);
+			player.setSimpleRotation(eventID, newMessage);
 		}),
 		changedEvent: config.changeEmitter || ((player: Player<SpecType>) => player.rotationChangeEmitter),
 		enableWhen: config.enableWhen,
@@ -434,11 +434,11 @@ export function makeRotationEnumIconInput<SpecType extends Spec, T>(config: Play
 		showWhen: config.showWhen,
 		zeroValue: 0 as unknown as T,
 		getModObject: (player: Player<SpecType>) => player,
-		getValue: config.getValue || ((player: Player<SpecType>) => player.getRotation()[config.fieldName] as unknown as T),
+		getValue: config.getValue || ((player: Player<SpecType>) => player.getSimpleRotation()[config.fieldName] as unknown as T),
 		setValue: config.setValue || ((eventID: EventID, player: Player<SpecType>, newVal: T) => {
-			const newMessage = player.getRotation();
+			const newMessage = player.getSimpleRotation();
 			(newMessage[config.fieldName] as unknown as T) = newVal;
-			player.setRotation(eventID, newMessage);
+			player.setSimpleRotation(eventID, newMessage);
 		}),
 		changedEvent: config.changeEmitter || ((player: Player<SpecType>) => player.rotationChangeEmitter),
 		extraCssClasses: config.extraCssClasses,
diff --git a/ui/core/components/other_inputs.ts b/ui/core/components/other_inputs.ts
index fcac703cd2..2b37af2c48 100644
--- a/ui/core/components/other_inputs.ts
+++ b/ui/core/components/other_inputs.ts
@@ -250,12 +250,12 @@ export const HpPercentForDefensives = {
 		<p>% of Maximum Health, below which defensive cooldowns are allowed to be used.</p>
 		<p>If set to 0, this restriction is disabled.</p>
 	`,
-	changedEvent: (player: Player<any>) => player.cooldownsChangeEmitter,
-	getValue: (player: Player<any>) => player.getCooldowns().hpPercentForDefensives * 100,
+	changedEvent: (player: Player<any>) => player.rotationChangeEmitter,
+	getValue: (player: Player<any>) => player.getSimpleCooldowns().hpPercentForDefensives * 100,
 	setValue: (eventID: EventID, player: Player<any>, newValue: number) => {
-		const cooldowns = player.getCooldowns();
+		const cooldowns = player.getSimpleCooldowns();
 		cooldowns.hpPercentForDefensives = newValue / 100;
-		player.setCooldowns(eventID, cooldowns);
+		player.setSimpleCooldowns(eventID, cooldowns);
 	},
 };
 
diff --git a/ui/core/player.ts b/ui/core/player.ts
index 3a5e80b252..7690346209 100644
--- a/ui/core/player.ts
+++ b/ui/core/player.ts
@@ -246,12 +246,10 @@ export class Player<SpecType extends Spec> {
 	private race: Race;
 	private profession1: Profession = 0;
 	private profession2: Profession = 0;
-	private rotation: SpecRotation<SpecType>;
 	aplRotation: APLRotation = APLRotation.create();
 	private talentsString: string = '';
 	private glyphs: Glyphs = Glyphs.create();
 	private specOptions: SpecOptions<SpecType>;
-	private cooldowns: Cooldowns = Cooldowns.create();
 	private reactionTime: number = 0;
 	private channelClipDelay: number = 0;
 	private inFrontOfTarget: boolean = false;
@@ -290,7 +288,6 @@ export class Player<SpecType extends Spec> {
 	readonly talentsChangeEmitter = new TypedEvent<void>('PlayerTalents');
 	readonly glyphsChangeEmitter = new TypedEvent<void>('PlayerGlyphs');
 	readonly specOptionsChangeEmitter = new TypedEvent<void>('PlayerSpecOptions');
-	readonly cooldownsChangeEmitter = new TypedEvent<void>('PlayerCooldowns');
 	readonly inFrontOfTargetChangeEmitter = new TypedEvent<void>('PlayerInFrontOfTarget');
 	readonly distanceFromTargetChangeEmitter = new TypedEvent<void>('PlayerDistanceFromTarget');
 	readonly healingModelChangeEmitter = new TypedEvent<void>('PlayerHealingModel');
@@ -312,7 +309,6 @@ export class Player<SpecType extends Spec> {
 		this.spec = spec;
 		this.race = specToEligibleRaces[this.spec][0];
 		this.specTypeFunctions = specTypeFunctions[this.spec] as SpecTypeFunctions<SpecType>;
-		this.rotation = this.specTypeFunctions.rotationCreate();
 		this.specOptions = this.specTypeFunctions.optionsCreate();
 
 		const specConfig = SPEC_CONFIGS[this.spec] as PlayerConfig<SpecType>;
@@ -343,7 +339,6 @@ export class Player<SpecType extends Spec> {
 			this.talentsChangeEmitter,
 			this.glyphsChangeEmitter,
 			this.specOptionsChangeEmitter,
-			this.cooldownsChangeEmitter,
 			this.miscOptionsChangeEmitter,
 			this.inFrontOfTargetChangeEmitter,
 			this.distanceFromTargetChangeEmitter,
@@ -597,24 +592,6 @@ export class Player<SpecType extends Spec> {
 		this.consumesChangeEmitter.emit(eventID);
 	}
 
-	getCooldowns(): Cooldowns {
-		// Make a defensive copy
-		return Cooldowns.clone(this.aplRotation.simple?.cooldowns || Cooldowns.create());
-	}
-
-	setCooldowns(eventID: EventID, newCooldowns: Cooldowns) {
-		if (Cooldowns.equals(this.getCooldowns(), newCooldowns))
-			return;
-
-		if (!this.aplRotation.simple) {
-			this.aplRotation.simple = SimpleRotation.create();
-		}
-		this.aplRotation.simple.cooldowns = newCooldowns;
-		this.rotationChangeEmitter.emit(eventID);
-
-		this.cooldownsChangeEmitter.emit(eventID);
-	}
-
 	canDualWield2H(): boolean {
 		return this.getClass() == Class.ClassWarrior && (this.getTalents() as SpecTalents<Spec.SpecWarrior>).titansGrip;
 	}
@@ -751,7 +728,15 @@ export class Player<SpecType extends Spec> {
 		return this.getMeleeCritCapInfo().playerCritCapDelta
 	}
 
-	getRotation(): SpecRotation<SpecType> {
+	setAplRotation(eventID: EventID, newRotation: APLRotation) {
+		if (APLRotation.equals(newRotation, this.aplRotation))
+			return;
+
+		this.aplRotation = APLRotation.clone(newRotation);
+		this.rotationChangeEmitter.emit(eventID);
+	}
+
+	getSimpleRotation(): SpecRotation<SpecType> {
 		const jsonStr = this.aplRotation.simple?.specRotationJson || '';
 		if (!jsonStr) {
 			return this.specTypeFunctions.rotationCreate();
@@ -766,8 +751,8 @@ export class Player<SpecType extends Spec> {
 		}
 	}
 
-	setRotation(eventID: EventID, newRotation: SpecRotation<SpecType>) {
-		if (this.specTypeFunctions.rotationEquals(newRotation, this.getRotation()))
+	setSimpleRotation(eventID: EventID, newRotation: SpecRotation<SpecType>) {
+		if (this.specTypeFunctions.rotationEquals(newRotation, this.getSimpleRotation()))
 			return;
 
 		if (!this.aplRotation.simple) {
@@ -778,11 +763,19 @@ export class Player<SpecType extends Spec> {
 		this.rotationChangeEmitter.emit(eventID);
 	}
 
-	setAplRotation(eventID: EventID, newRotation: APLRotation) {
-		if (APLRotation.equals(newRotation, this.aplRotation))
+	getSimpleCooldowns(): Cooldowns {
+		// Make a defensive copy
+		return Cooldowns.clone(this.aplRotation.simple?.cooldowns || Cooldowns.create());
+	}
+
+	setSimpleCooldowns(eventID: EventID, newCooldowns: Cooldowns) {
+		if (Cooldowns.equals(this.getSimpleCooldowns(), newCooldowns))
 			return;
 
-		this.aplRotation = APLRotation.clone(newRotation);
+		if (!this.aplRotation.simple) {
+			this.aplRotation.simple = SimpleRotation.create();
+		}
+		this.aplRotation.simple.cooldowns = newCooldowns;
 		this.rotationChangeEmitter.emit(eventID);
 	}
 
@@ -809,8 +802,8 @@ export class Player<SpecType extends Spec> {
 			return rot;
 		} else if (type == APLRotationType.TypeSimple && this.simpleRotationGenerator) {
 			// Clone to avoid modifying preset rotations, which are often returned directly.
-			const simpleRot = this.getRotation();
-			const rot = APLRotation.clone(this.simpleRotationGenerator(this, simpleRot, this.getCooldowns()));
+			const simpleRot = this.getSimpleRotation();
+			const rot = APLRotation.clone(this.simpleRotationGenerator(this, simpleRot, this.getSimpleCooldowns()));
 			rot.simple = this.aplRotation.simple;
 			rot.type = APLRotationType.TypeSimple;
 			return rot;
@@ -1342,7 +1335,7 @@ export class Player<SpecType extends Spec> {
 		}
 		if (exportCategory(SimSettingCategories.Rotation)) {
 			PlayerProto.mergePartial(player, {
-				cooldowns: Cooldowns.create({ hpPercentForDefensives: this.getCooldowns().hpPercentForDefensives }),
+				cooldowns: Cooldowns.create({ hpPercentForDefensives: this.getSimpleCooldowns().hpPercentForDefensives }),
 				rotation: aplRotation,
 			});
 		}
@@ -1474,32 +1467,32 @@ export class Player<SpecType extends Spec> {
 			}
 
 			if (this.spec == Spec.SpecBalanceDruid) {
-				const rot = this.getRotation() as SpecRotation<Spec.SpecBalanceDruid>;
+				const rot = this.getSimpleRotation() as SpecRotation<Spec.SpecBalanceDruid>;
 				if (rot.okfPpm) {
 					rot.okfPpm = 0;
-					this.setRotation(eventID, rot as SpecRotation<SpecType>);
+					this.setSimpleRotation(eventID, rot as SpecRotation<SpecType>);
 				}
 			}
 
 			if (this.spec == Spec.SpecHunter) {
-				const rot = this.getRotation() as SpecRotation<Spec.SpecHunter>;
+				const rot = this.getSimpleRotation() as SpecRotation<Spec.SpecHunter>;
 				if (rot.timeToTrapWeaveMs) {
 					const options = this.getSpecOptions() as SpecOptions<Spec.SpecHunter>;
 					options.timeToTrapWeaveMs = rot.timeToTrapWeaveMs;
 					this.setSpecOptions(eventID, options as SpecOptions<SpecType>);
 					rot.timeToTrapWeaveMs = 0;
-					this.setRotation(eventID, rot as SpecRotation<SpecType>);
+					this.setSimpleRotation(eventID, rot as SpecRotation<SpecType>);
 				}
 			}
 
 			if (this.spec == Spec.SpecMage) {
-				const rot = this.getRotation() as SpecRotation<Spec.SpecMage>;
+				const rot = this.getSimpleRotation() as SpecRotation<Spec.SpecMage>;
 				if (rot.waterElementalDisobeyChance) {
 					const options = this.getSpecOptions() as SpecOptions<Spec.SpecMage>;
 					options.waterElementalDisobeyChance = rot.waterElementalDisobeyChance;
 					rot.waterElementalDisobeyChance = 0;
 					this.setSpecOptions(eventID, options as SpecOptions<SpecType>);
-					this.setRotation(eventID, rot as SpecRotation<SpecType>);
+					this.setSimpleRotation(eventID, rot as SpecRotation<SpecType>);
 				}
 			}
 
@@ -1513,13 +1506,13 @@ export class Player<SpecType extends Spec> {
 			}
 
 			if ([Spec.SpecEnhancementShaman, Spec.SpecRestorationShaman, Spec.SpecElementalShaman].includes(this.spec)) {
-				const rot = this.getRotation() as SpecRotation<ShamanSpecs>;
+				const rot = this.getSimpleRotation() as SpecRotation<ShamanSpecs>;
 				if (rot.totems) {
 					const options = this.getSpecOptions() as SpecOptions<ShamanSpecs>;
 					options.totems = rot.totems;
 					this.setSpecOptions(eventID, options as SpecOptions<SpecType>);
 					rot.totems = undefined;
-					this.setRotation(eventID, rot as SpecRotation<SpecType>);
+					this.setSimpleRotation(eventID, rot as SpecRotation<SpecType>);
 				}
 				const opt = this.getSpecOptions() as SpecOptions<ShamanSpecs>;
 
@@ -1527,7 +1520,7 @@ export class Player<SpecType extends Spec> {
 				if (opt.bloodlust) {
 					opt.bloodlust = false;
 
-					var tRot = this.getRotation();
+					var tRot = this.getSimpleRotation();
 					if (this.spec == Spec.SpecElementalShaman) {
 						(tRot as ElementalShaman_Rotation).bloodlust = ElementalShaman_Rotation_BloodlustUse.UseBloodlust;
 					} else if (this.spec == Spec.SpecEnhancementShaman) {
@@ -1536,47 +1529,47 @@ export class Player<SpecType extends Spec> {
 						(tRot as RestorationShaman_Rotation).bloodlust = RestorationShaman_Rotation_BloodlustUse.UseBloodlust;
 					}
 
-					this.setRotation(eventID, tRot as SpecRotation<SpecType>);
+					this.setSimpleRotation(eventID, tRot as SpecRotation<SpecType>);
 					this.setSpecOptions(eventID, opt as SpecOptions<SpecType>);
 				}
 
 				// Update Ele TS range option.
 				if (this.spec == Spec.SpecElementalShaman) {
 					var eleOpt = this.getSpecOptions() as ElementalShaman_Options;
-					var eleRot = this.getRotation() as ElementalShaman_Rotation;
+					var eleRot = this.getSimpleRotation() as ElementalShaman_Rotation;
 					if (eleRot.inThunderstormRange) {
 						eleOpt.thunderstormRange = ElementalShaman_Options_ThunderstormRange.TSInRange;
 						eleRot.inThunderstormRange = false;
-						this.setRotation(eventID, eleRot as SpecRotation<SpecType>);
+						this.setSimpleRotation(eventID, eleRot as SpecRotation<SpecType>);
 						this.setSpecOptions(eventID, eleOpt as SpecOptions<SpecType>);
 					}
 				}
 			}
 
 			if (this.spec == Spec.SpecWarlock || this.spec == Spec.SpecDeathknight) {
-				const rot = this.getRotation() as SpecRotation<Spec.SpecWarlock | Spec.SpecDeathknight>;
+				const rot = this.getSimpleRotation() as SpecRotation<Spec.SpecWarlock | Spec.SpecDeathknight>;
 				if (rot.enableWeaponSwap) {
 					this.setEnableItemSwap(eventID, rot.enableWeaponSwap);
 					rot.enableWeaponSwap = false;
-					this.setRotation(eventID, rot as SpecRotation<SpecType>)
+					this.setSimpleRotation(eventID, rot as SpecRotation<SpecType>)
 				}
 				if (rot.weaponSwap) {
 					this.setItemSwapGear(eventID, this.sim.db.lookupItemSwap(rot.weaponSwap));
 					rot.weaponSwap = undefined;
-					this.setRotation(eventID, rot as SpecRotation<SpecType>)
+					this.setSimpleRotation(eventID, rot as SpecRotation<SpecType>)
 				}
 			}
 			if (this.spec == Spec.SpecEnhancementShaman) {
-				const rot = this.getRotation() as SpecRotation<Spec.SpecEnhancementShaman>;
+				const rot = this.getSimpleRotation() as SpecRotation<Spec.SpecEnhancementShaman>;
 				if (rot.enableItemSwap) {
 					this.setEnableItemSwap(eventID, rot.enableItemSwap);
 					rot.enableItemSwap = false;
-					this.setRotation(eventID, rot as SpecRotation<SpecType>)
+					this.setSimpleRotation(eventID, rot as SpecRotation<SpecType>)
 				}
 				if (rot.itemSwap) {
 					this.setItemSwapGear(eventID, this.sim.db.lookupItemSwap(rot.itemSwap));
 					rot.itemSwap = undefined;
-					this.setRotation(eventID, rot as SpecRotation<SpecType>)
+					this.setSimpleRotation(eventID, rot as SpecRotation<SpecType>)
 				}
 			}
 		});
@@ -1597,7 +1590,7 @@ export class Player<SpecType extends Spec> {
 			this.setHealingModel(eventID, HealingModel.create({
 				burstWindow: isTankSpec(this.spec) ? 6 : 0,
 			}));
-			this.setCooldowns(eventID, Cooldowns.create({
+			this.setSimpleCooldowns(eventID, Cooldowns.create({
 				hpPercentForDefensives: isTankSpec(this.spec) ? 0.35 : 0,
 			}));
 			this.setBonusStats(eventID, new Stats());
diff --git a/ui/feral_druid/inputs.ts b/ui/feral_druid/inputs.ts
index 03e89d8fa6..88b482a56a 100644
--- a/ui/feral_druid/inputs.ts
+++ b/ui/feral_druid/inputs.ts
@@ -45,12 +45,12 @@ export const AssumeBleedActive = InputHelpers.makeSpecOptionsBooleanInput<Spec.S
 })
 
 function ShouldShowAdvParamST(player: Player<Spec.SpecFeralDruid>): boolean {
-	let rot = player.getRotation();
+	let rot = player.getSimpleRotation();
 	return rot.manualParams && rot.rotationType == AplType.SingleTarget;
 }
 
 function ShouldShowAdvParamAoe(player: Player<Spec.SpecFeralDruid>): boolean {
-	let rot = player.getRotation();
+	let rot = player.getSimpleRotation();
 	return rot.manualParams && rot.rotationType == AplType.Aoe;
 }
 
@@ -89,7 +89,7 @@ export const FeralDruidRotationConfig = {
 			labelTooltip: 'Max allowed delay to wait for ff to come off CD in seconds',
 			float: true,
 			positive: true,
-			showWhen: (player: Player<Spec.SpecFeralDruid>) => player.getRotation().manualParams,
+			showWhen: (player: Player<Spec.SpecFeralDruid>) => player.getSimpleRotation().manualParams,
 		}),
 		InputHelpers.makeRotationNumberInput<Spec.SpecFeralDruid>({
 			fieldName: 'minRoarOffset',
@@ -120,7 +120,7 @@ export const FeralDruidRotationConfig = {
 			label: 'Bite Time',
 			labelTooltip: 'Min seconds on Rip/Roar to bite',
 			showWhen: (player: Player<Spec.SpecFeralDruid>) =>
-				ShouldShowAdvParamST(player) && player.getRotation().useBite == true && player.getRotation().biteModeType == BiteModeType.Emperical,
+				ShouldShowAdvParamST(player) && player.getSimpleRotation().useBite == true && player.getSimpleRotation().biteModeType == BiteModeType.Emperical,
 		}),
 		InputHelpers.makeRotationBooleanInput<Spec.SpecFeralDruid>({
 			fieldName: 'flowerWeave',
@@ -133,7 +133,7 @@ export const FeralDruidRotationConfig = {
 			fieldName: 'raidTargets',
 			label: 'GotW Raid Targets',
 			labelTooltip: 'Raid size to assume for clearcast proc chance (can include pets as well, so 25 man raid potentically can be ~30)',
-			showWhen: (player: Player<Spec.SpecFeralDruid>) => player.aplRotation.enabled || (ShouldShowAdvParamAoe(player) && player.getRotation().flowerWeave == true),
+			showWhen: (player: Player<Spec.SpecFeralDruid>) => player.aplRotation.enabled || (ShouldShowAdvParamAoe(player) && player.getSimpleRotation().flowerWeave == true),
 		}),
 		// Can be uncommented if/when analytical bite mode is added
 		//InputHelpers.makeRotationEnumInput<Spec.SpecFeralDruid, BiteModeType>({
@@ -143,14 +143,14 @@ export const FeralDruidRotationConfig = {
 		//	values: [
 		//		{ name: 'Emperical', value: BiteModeType.Emperical },
 		//	],
-		//	showWhen: (player: Player<Spec.SpecFeralDruid>) => player.getRotation().useBite == true
+		//	showWhen: (player: Player<Spec.SpecFeralDruid>) => player.getSimpleRotation().useBite == true
 		//}),
 		InputHelpers.makeRotationNumberInput<Spec.SpecFeralDruid>({
 			fieldName: 'hotUptime',
 			label: 'Revitalize Hot Uptime',
 			labelTooltip: 'Hot uptime percentage to assume when theorizing energy gains',
 			percent: true,
-			showWhen: (player: Player<Spec.SpecFeralDruid>) => player.getRotation().useBite == true && player.getRotation().biteModeType == BiteModeType.Analytical,
+			showWhen: (player: Player<Spec.SpecFeralDruid>) => player.getSimpleRotation().useBite == true && player.getSimpleRotation().biteModeType == BiteModeType.Analytical,
 		}),
 	],
 };
diff --git a/ui/hunter/inputs.ts b/ui/hunter/inputs.ts
index e28f2091da..ba34a5ee91 100644
--- a/ui/hunter/inputs.ts
+++ b/ui/hunter/inputs.ts
@@ -78,7 +78,7 @@ export const HunterRotationConfig = {
 				{ name: 'Scorpid Sting', value: StingType.ScorpidSting },
 				{ name: 'Serpent Sting', value: StingType.SerpentSting },
 			],
-			showWhen: (player: Player<Spec.SpecHunter>) => player.getRotation().type == RotationType.SingleTarget,
+			showWhen: (player: Player<Spec.SpecHunter>) => player.getSimpleRotation().type == RotationType.SingleTarget,
 		}),
 		InputHelpers.makeRotationBooleanInput<Spec.SpecHunter>({
 			fieldName: 'trapWeave',
@@ -89,7 +89,7 @@ export const HunterRotationConfig = {
 			fieldName: 'allowExplosiveShotDownrank',
 			label: 'Allow ES Downrank',
 			labelTooltip: 'Weaves Explosive Shot Rank 3 during LNL procs. This works because the rank 3 and rank 4 dots can stack.',
-			showWhen: (player: Player<Spec.SpecHunter>) => player.getRotation().type != RotationType.Custom && player.getTalents().explosiveShot && player.getTalents().lockAndLoad > 0,
+			showWhen: (player: Player<Spec.SpecHunter>) => player.getSimpleRotation().type != RotationType.Custom && player.getTalents().explosiveShot && player.getTalents().lockAndLoad > 0,
 			changeEmitter: (player: Player<Spec.SpecHunter>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
 		}),
 		InputHelpers.makeRotationBooleanInput<Spec.SpecHunter>({
diff --git a/ui/warlock/sim.ts b/ui/warlock/sim.ts
index 35d02c3813..c35da9c98c 100644
--- a/ui/warlock/sim.ts
+++ b/ui/warlock/sim.ts
@@ -93,13 +93,6 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, {
 		WarlockInputs.ArmorInput,
 		WarlockInputs.WeaponImbueInput,
 	],
-	// Inputs to include in the 'Rotation' section on the settings tab.
-	rotationIconInputs: [
-		WarlockInputs.PrimarySpellInput,
-		WarlockInputs.CorruptionSpell,
-		WarlockInputs.SecondaryDotInput,
-		WarlockInputs.SpecSpellInput,
-	],
 
 	// Buff and Debuff inputs to include/exclude, overriding the EP-based defaults.
 	includeBuffDebuffInputs: [

From e017516454a06239a95abd8618ab8d46f01de91f Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Mon, 1 Jan 2024 20:36:44 -0800
Subject: [PATCH 03/28] Clean up hunter pet rotation code

---
 sim/hunter/pet.go | 44 +++++++++++++-------------------------------
 1 file changed, 13 insertions(+), 31 deletions(-)

diff --git a/sim/hunter/pet.go b/sim/hunter/pet.go
index d471071865..c32203ecc1 100644
--- a/sim/hunter/pet.go
+++ b/sim/hunter/pet.go
@@ -106,42 +106,24 @@ func (hp *HunterPet) ExecuteCustomRotation(sim *core.Simulation) {
 	}
 
 	target := hp.CurrentTarget
-	if hp.config.RandomSelection {
-		if sim.RandomFloat("Hunter Pet Ability") < 0.5 {
-			if !hp.specialAbility.CanCast(sim, target) || !hp.specialAbility.Cast(sim, target) {
-				if !hp.focusDump.Cast(sim, target) {
-					// Do nothing
-				}
-			}
-		} else {
-			if !hp.focusDump.Cast(sim, target) {
-				if !hp.specialAbility.CanCast(sim, target) || !hp.specialAbility.Cast(sim, target) {
-					// Do nothing
-				}
-			}
-		}
+
+	if hp.focusDump == nil {
+		hp.specialAbility.Cast(sim, target)
+		return
+	}
+	if hp.specialAbility == nil {
+		hp.focusDump.Cast(sim, target)
 		return
 	}
 
-	if !hp.specialAbility.CanCast(sim, target) || hp.specialAbility.Cast(sim, target) {
-		// For abilities that don't use the GCD.
-		if hp.GCD.IsReady(sim) {
-			if hp.focusDump != nil {
-				if !hp.focusDump.Cast(sim, target) {
-					// Do nothing
-				}
-			} else {
-				// Do nothing
-			}
-		}
-	} else {
-		if hp.focusDump != nil {
-			if !hp.focusDump.Cast(sim, target) {
-				// Do nothing
-			}
+	if hp.config.RandomSelection {
+		if sim.RandomFloat("Hunter Pet Ability") < 0.5 {
+			_ = hp.specialAbility.Cast(sim, target) || hp.focusDump.Cast(sim, target)
 		} else {
-			// Do nothing
+			_ = hp.focusDump.Cast(sim, target) || hp.specialAbility.Cast(sim, target)
 		}
+	} else {
+		_ = hp.specialAbility.Cast(sim, target) || hp.focusDump.Cast(sim, target)
 	}
 }
 

From 2e6a97a7bfc4563ea4cabac73025e1850a3f2b15 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Tue, 2 Jan 2024 19:58:05 -0800
Subject: [PATCH 04/28] Start cleaning up backend legacy rotation code

---
 proto/apl.proto                              |   4 -
 sim/core/agent.go                            |   9 -
 sim/core/apl.go                              |   4 +-
 sim/core/apl_value.go                        |   2 -
 sim/core/apl_values_spell.go                 |  24 --
 sim/core/attack.go                           |  16 +-
 sim/core/cast.go                             |  14 +-
 sim/core/character.go                        |  16 -
 sim/core/consumes.go                         |   5 -
 sim/core/dot.go                              |   8 +-
 sim/core/energy.go                           |  39 +-
 sim/core/major_cooldown.go                   |  74 ----
 sim/core/mana.go                             |  39 +-
 sim/core/pet.go                              |   2 -
 sim/core/rage.go                             |  19 +-
 sim/core/spell.go                            |   6 +-
 sim/core/target.go                           |   1 -
 sim/core/target_ai.go                        |   6 -
 sim/core/target_dummy.go                     |   4 -
 sim/core/unit.go                             |  14 +-
 sim/druid/tank/tank.go                       |  12 +-
 sim/hunter/aspects.go                        |   4 -
 sim/hunter/hunter.go                         |  23 --
 sim/hunter/pet_abilities.go                  |   3 +-
 sim/hunter/rotation.go                       | 385 -------------------
 sim/hunter/volley.go                         |   3 +-
 sim/warrior/dps/dps_warrior.go               |  20 +-
 sim/warrior/protection/protection_warrior.go |   9 +-
 28 files changed, 42 insertions(+), 723 deletions(-)
 delete mode 100644 sim/hunter/rotation.go

diff --git a/proto/apl.proto b/proto/apl.proto
index 6608420050..2f683604b1 100644
--- a/proto/apl.proto
+++ b/proto/apl.proto
@@ -142,7 +142,6 @@ message APLValue {
         APLValueSpellIsReady spell_is_ready = 20;
         APLValueSpellTimeToReady spell_time_to_ready = 21;
         APLValueSpellCastTime spell_cast_time = 35;
-        APLValueSpellChannelTime spell_channel_time = 36;
         APLValueSpellTravelTime spell_travel_time = 37;
         APLValueSpellCPM spell_cpm = 42;
         APLValueSpellIsChanneling spell_is_channeling = 56;
@@ -448,9 +447,6 @@ message APLValueSpellTimeToReady {
 message APLValueSpellCastTime {
     ActionID spell_id = 1;
 }
-message APLValueSpellChannelTime {
-    ActionID spell_id = 1;
-}
 message APLValueChannelClipDelay {
 }
 message APLValueFrontOfTarget {
diff --git a/sim/core/agent.go b/sim/core/agent.go
index d4b6d981dc..13b9037561 100644
--- a/sim/core/agent.go
+++ b/sim/core/agent.go
@@ -32,15 +32,6 @@ type Agent interface {
 	// and once after the final iteration.
 	Reset(sim *Simulation)
 
-	// Called whenever the GCD becomes ready for this Agent.
-	OnGCDReady(sim *Simulation)
-
-	// Called after each auto attack performed by this Agent.
-	// This is different from Aura.OnSpellHit in that it is invoked fully after
-	// everything related to the attack is complete, and it is only invoked for
-	// auto attacks (white hits or white-hit-replacers).
-	OnAutoAttack(sim *Simulation, spell *Spell)
-
 	// Custom factories for APL values and actions, for cases where the value/action
 	// involves class or spec-specific behavior.
 	//
diff --git a/sim/core/apl.go b/sim/core/apl.go
index 8ee0253195..1fffcf8889 100644
--- a/sim/core/apl.go
+++ b/sim/core/apl.go
@@ -67,7 +67,7 @@ func (unit *Unit) newCustomRotation() *APLRotation {
 }
 
 func (unit *Unit) newAPLRotation(config *proto.APLRotation) *APLRotation {
-	if config == nil || !unit.IsUsingAPL {
+	if config == nil {
 		return nil
 	}
 
@@ -227,8 +227,6 @@ func (apl *APLRotation) DoNextAction(sim *Simulation) {
 	gcdReady := apl.unit.GCD.IsReady(sim)
 	if gcdReady {
 		apl.unit.WaitUntil(sim, sim.CurrentTime+time.Millisecond*50)
-	} else {
-		apl.unit.DoNothing()
 	}
 
 	apl.unit.DoneAPLLoop(sim, !gcdReady)
diff --git a/sim/core/apl_value.go b/sim/core/apl_value.go
index b6f4fe4d0f..7d47505c69 100644
--- a/sim/core/apl_value.go
+++ b/sim/core/apl_value.go
@@ -157,8 +157,6 @@ func (rot *APLRotation) newAPLValue(config *proto.APLValue) APLValue {
 		return rot.newValueSpellTimeToReady(config.GetSpellTimeToReady())
 	case *proto.APLValue_SpellCastTime:
 		return rot.newValueSpellCastTime(config.GetSpellCastTime())
-	case *proto.APLValue_SpellChannelTime:
-		return rot.newValueSpellChannelTime(config.GetSpellChannelTime())
 	case *proto.APLValue_SpellTravelTime:
 		return rot.newValueSpellTravelTime(config.GetSpellTravelTime())
 	case *proto.APLValue_SpellCpm:
diff --git a/sim/core/apl_values_spell.go b/sim/core/apl_values_spell.go
index af2e37cddb..db6d8eff40 100644
--- a/sim/core/apl_values_spell.go
+++ b/sim/core/apl_values_spell.go
@@ -103,30 +103,6 @@ func (value *APLValueSpellCastTime) String() string {
 	return fmt.Sprintf("Cast Time(%s)", value.spell.ActionID)
 }
 
-type APLValueSpellChannelTime struct {
-	DefaultAPLValueImpl
-	spell *Spell
-}
-
-func (rot *APLRotation) newValueSpellChannelTime(config *proto.APLValueSpellChannelTime) APLValue {
-	spell := rot.GetAPLSpell(config.SpellId)
-	if spell == nil {
-		return nil
-	}
-	return &APLValueSpellChannelTime{
-		spell: spell,
-	}
-}
-func (value *APLValueSpellChannelTime) Type() proto.APLValueType {
-	return proto.APLValueType_ValueTypeDuration
-}
-func (value *APLValueSpellChannelTime) GetDuration(_ *Simulation) time.Duration {
-	return value.spell.Unit.ApplyCastSpeedForSpell(value.spell.DefaultCast.ChannelTime, value.spell)
-}
-func (value *APLValueSpellChannelTime) String() string {
-	return fmt.Sprintf("Channel Time(%s)", value.spell.ActionID)
-}
-
 type APLValueSpellTravelTime struct {
 	DefaultAPLValueImpl
 	spell *Spell
diff --git a/sim/core/attack.go b/sim/core/attack.go
index f0eb1e2e6e..aa601b5b16 100644
--- a/sim/core/attack.go
+++ b/sim/core/attack.go
@@ -263,10 +263,9 @@ func (wa *WeaponAttack) swing(sim *Simulation) time.Duration {
 	attackSpell := wa.spell
 
 	if wa.replaceSwing != nil {
-		if wa.unit.IsUsingAPL {
-			// Need to check APL here to allow last-moment HS queue casts.
-			wa.unit.Rotation.DoNextAction(sim)
-		}
+		// Need to check APL here to allow last-moment HS queue casts.
+		wa.unit.Rotation.DoNextAction(sim)
+
 		// Allow MH swing to be overridden for abilities like Heroic Strike.
 		attackSpell = wa.replaceSwing(sim, attackSpell)
 	}
@@ -277,11 +276,7 @@ func (wa *WeaponAttack) swing(sim *Simulation) time.Duration {
 	attackSpell.Cast(sim, wa.unit.CurrentTarget)
 
 	if !sim.Options.Interactive {
-		if wa.unit.IsUsingAPL {
-			wa.unit.Rotation.DoNextAction(sim)
-		} else {
-			wa.agent.OnAutoAttack(sim, attackSpell)
-		}
+		wa.unit.Rotation.DoNextAction(sim)
 	}
 
 	return wa.swingAt
@@ -422,9 +417,6 @@ func (unit *Unit) EnableAutoAttacks(agent Agent, options AutoAttackOptions) {
 	}
 }
 
-// Empty handler so Agents don't have to provide one if they have no logic to add.
-func (unit *Unit) OnAutoAttack(_ *Simulation, _ *Spell) {}
-
 func (aa *AutoAttacks) finalize() {
 	if aa.AutoSwingMelee {
 		aa.mh.spell = aa.mh.unit.GetOrRegisterSpell(aa.mh.config)
diff --git a/sim/core/cast.go b/sim/core/cast.go
index ca86cdbacf..8a2c34a3c9 100644
--- a/sim/core/cast.go
+++ b/sim/core/cast.go
@@ -47,9 +47,6 @@ type Cast struct {
 	// The amount of time between the call to spell.Cast() and when the spell
 	// effects are invoked.
 	CastTime time.Duration
-
-	// Additional GCD delay after the cast completes.
-	ChannelTime time.Duration
 }
 
 func (cast *Cast) EffectiveTime() time.Duration {
@@ -58,15 +55,14 @@ func (cast *Cast) EffectiveTime() time.Duration {
 		// TODO: isn't this wrong for spells like shadowfury, that have a reduced GCD?
 		gcd = max(GCDMin, gcd)
 	}
-	fullCastTime := cast.CastTime + cast.ChannelTime
-	return max(gcd, fullCastTime)
+	return max(gcd, cast.CastTime)
 }
 
 type CastFunc func(*Simulation, *Unit)
 type CastSuccessFunc func(*Simulation, *Unit) bool
 
 func (spell *Spell) castFailureHelper(sim *Simulation, message string, vals ...any) bool {
-	if sim.CurrentTime < 0 && spell.Unit.IsUsingAPL {
+	if sim.CurrentTime < 0 && spell.Unit.Rotation != nil {
 		spell.Unit.Rotation.ValidationWarning(fmt.Sprintf(spell.ActionID.String()+" failed to cast: "+message, vals...))
 	} else {
 		if sim.Log != nil && !spell.Flags.Matches(SpellFlagNoLogs) {
@@ -105,7 +101,6 @@ func (spell *Spell) makeCastFunc(config CastConfig) CastSuccessFunc {
 		if !config.IgnoreHaste {
 			spell.CurCast.GCD = spell.Unit.ApplyCastSpeed(spell.CurCast.GCD)
 			spell.CurCast.CastTime = spell.Unit.ApplyCastSpeedForSpell(spell.CurCast.CastTime, spell)
-			spell.CurCast.ChannelTime = spell.Unit.ApplyCastSpeedForSpell(spell.CurCast.ChannelTime, spell)
 		}
 
 		if config.CD.Timer != nil {
@@ -173,11 +168,6 @@ func (spell *Spell) makeCastFunc(config CastConfig) CastSuccessFunc {
 			return true
 		}
 
-		// Instants/Channels
-		if spell.CurCast.ChannelTime > 0 {
-			spell.Unit.Hardcast = Hardcast{Expires: sim.CurrentTime + spell.CurCast.ChannelTime, ActionID: spell.ActionID}
-		}
-
 		if sim.Log != nil && !spell.Flags.Matches(SpellFlagNoLogs) {
 			spell.Unit.Log(sim, "Casting %s (Cost = %0.03f, Cast Time = %s, Effective Time = %s)",
 				spell.ActionID, max(0, spell.CurCast.Cost), spell.CurCast.CastTime, spell.CurCast.EffectiveTime())
diff --git a/sim/core/character.go b/sim/core/character.go
index 892d2ccb59..2f21f7b69f 100644
--- a/sim/core/character.go
+++ b/sim/core/character.go
@@ -106,10 +106,6 @@ func NewCharacter(party *Party, partyIndex int, player *proto.Player) Character
 			ChannelClipDelay:     max(0, time.Duration(player.ChannelClipDelayMs)*time.Millisecond),
 			DistanceFromTarget:   player.DistanceFromTarget,
 			NibelungAverageCasts: player.NibelungAverageCasts,
-			IsUsingAPL: player.Rotation != nil &&
-				(player.Rotation.Type == proto.APLRotation_TypeAPL ||
-					player.Rotation.Type == proto.APLRotation_TypeAuto ||
-					player.Rotation.Type == proto.APLRotation_TypeSimple),
 		},
 
 		Name:  player.Name,
@@ -439,7 +435,6 @@ func (character *Character) initialize(agent Agent) {
 			if sim.Options.Interactive {
 				if character.GCD.IsReady(sim) {
 					sim.NeedsInput = true
-					character.doNothing = false
 				}
 				return
 			}
@@ -448,17 +443,6 @@ func (character *Character) initialize(agent Agent) {
 				character.Rotation.DoNextAction(sim)
 				return
 			}
-
-			character.TryUseCooldowns(sim)
-			if character.GCD.IsReady(sim) {
-				agent.OnGCDReady(sim)
-
-				if !character.doNothing && character.GCD.IsReady(sim) && (!character.IsWaiting() && !character.IsWaitingForMana()) {
-					msg := fmt.Sprintf("Character `%s` did not perform any actions. Either this is a bug or agent should use 'WaitUntil' or 'WaitForMana' to explicitly wait.\n\tIf character has no action to perform use 'DoNothing'.", character.Label)
-					panic(msg)
-				}
-				character.doNothing = false
-			}
 		},
 	}
 }
diff --git a/sim/core/consumes.go b/sim/core/consumes.go
index 4b00025978..da709a3ae1 100644
--- a/sim/core/consumes.go
+++ b/sim/core/consumes.go
@@ -466,11 +466,6 @@ func registerPotionCD(agent Agent, consumes *proto.Consumes) {
 	startingMCD := makePotionActivation(startingPotion, character, potionCD)
 	if startingMCD.Spell != nil {
 		startingMCD.Spell.Flags |= SpellFlagPrepullPotion
-		if !character.IsUsingAPL {
-			character.RegisterPrepullAction(-1*time.Second, func(sim *Simulation) {
-				startingMCD.Spell.Cast(sim, nil)
-			})
-		}
 	}
 
 	var defaultMCD MajorCooldown
diff --git a/sim/core/dot.go b/sim/core/dot.go
index f66c3c7897..4ad4bf6e4a 100644
--- a/sim/core/dot.go
+++ b/sim/core/dot.go
@@ -204,7 +204,7 @@ func (dot *Dot) TickOnce(sim *Simulation) {
 	dot.lastTickTime = sim.CurrentTime
 	dot.OnTick(sim, dot.Unit, dot)
 
-	if dot.isChanneled && dot.Spell.Unit.IsUsingAPL {
+	if dot.isChanneled {
 		// Note: even if the clip delay is 0ms, need a WaitUntil so that APL is called after the channel aura fully fades.
 		if dot.MaxTicksRemaining() == 0 {
 			if dot.Spell.Unit.GCD.IsReady(sim) {
@@ -278,10 +278,8 @@ func newDot(config Dot) *Dot {
 		}
 		if dot.isChanneled {
 			dot.Spell.Unit.ChanneledDot = nil
-			if dot.Spell.Unit.IsUsingAPL {
-				dot.Spell.Unit.Rotation.interruptChannelIf = nil
-				dot.Spell.Unit.Rotation.allowChannelRecastOnInterrupt = false
-			}
+			dot.Spell.Unit.Rotation.interruptChannelIf = nil
+			dot.Spell.Unit.Rotation.allowChannelRecastOnInterrupt = false
 		}
 	})
 
diff --git a/sim/core/energy.go b/sim/core/energy.go
index 69695547dd..34f44e8f32 100644
--- a/sim/core/energy.go
+++ b/sim/core/energy.go
@@ -13,9 +13,6 @@ import (
 const EnergyTickDuration = time.Millisecond * 100
 const EnergyPerTick = 1.0
 
-// OnEnergyGain is called any time energy is increased.
-type OnEnergyGain func(sim *Simulation)
-
 type energyBar struct {
 	unit *Unit
 
@@ -33,8 +30,6 @@ type energyBar struct {
 	// Increments by 1 at each value of energyDecisionThresholds.
 	cumulativeEnergyDecisionThresholds []int
 
-	onEnergyGain func(*Simulation, bool)
-
 	nextEnergyTick time.Duration
 
 	// Multiplies energy regen from ticks.
@@ -44,27 +39,12 @@ type energyBar struct {
 	EnergyRefundMetrics *ResourceMetrics
 }
 
-func (unit *Unit) EnableEnergyBar(maxEnergy float64, onEnergyGain OnEnergyGain) {
+func (unit *Unit) EnableEnergyBar(maxEnergy float64) {
 	unit.SetCurrentPowerBar(EnergyBar)
 
 	unit.energyBar = energyBar{
-		unit:      unit,
-		maxEnergy: max(100, maxEnergy),
-		onEnergyGain: func(sim *Simulation, crossedThreshold bool) {
-			if sim.CurrentTime < 0 {
-				return
-			}
-
-			if !sim.Options.Interactive && (!unit.IsWaitingForEnergy() || unit.DoneWaitingForEnergy(sim)) {
-				if unit.IsUsingAPL {
-					if crossedThreshold {
-						unit.Rotation.DoNextAction(sim)
-					}
-				} else {
-					onEnergyGain(sim)
-				}
-			}
-		},
+		unit:                 unit,
+		maxEnergy:            max(100, maxEnergy),
 		EnergyTickMultiplier: 1,
 		regenMetrics:         unit.NewEnergyMetrics(ActionID{OtherID: proto.OtherAction_OtherActionEnergyRegen}),
 		EnergyRefundMetrics:  unit.NewEnergyMetrics(ActionID{OtherID: proto.OtherAction_OtherActionRefund}),
@@ -76,9 +56,6 @@ func (eb *energyBar) setupEnergyThresholds() {
 	if eb.unit == nil {
 		return
 	}
-	if !eb.unit.IsUsingAPL {
-		return
-	}
 	var energyThresholds []int
 
 	// Energy thresholds from spell costs.
@@ -153,6 +130,16 @@ func (eb *energyBar) NextEnergyTickAt() time.Duration {
 	return eb.nextEnergyTick
 }
 
+func (eb *energyBar) onEnergyGain(sim *Simulation, crossedThreshold bool) {
+	if sim.CurrentTime < 0 {
+		return
+	}
+
+	if !sim.Options.Interactive && crossedThreshold && (!eb.unit.IsWaitingForEnergy() || eb.unit.DoneWaitingForEnergy(sim)) {
+		eb.unit.Rotation.DoNextAction(sim)
+	}
+}
+
 func (eb *energyBar) addEnergyInternal(sim *Simulation, amount float64, metrics *ResourceMetrics) bool {
 	if amount < 0 {
 		panic("Trying to add negative energy!")
diff --git a/sim/core/major_cooldown.go b/sim/core/major_cooldown.go
index adc5d218bd..17e1805910 100644
--- a/sim/core/major_cooldown.go
+++ b/sim/core/major_cooldown.go
@@ -227,50 +227,6 @@ func (mcdm *majorCooldownManager) finalize() {
 	mcdm.majorCooldowns = make([]*MajorCooldown, len(mcdm.initialMajorCooldowns))
 }
 
-// Adds a delay to the first usage of all CDs so that debuffs have time
-// to be applied. MCDs that have a user-specified timing are not delayed.
-//
-// This function should be called from Agent.Init().
-func (mcdm *majorCooldownManager) DelayDPSCooldownsForArmorDebuffs(delay time.Duration) {
-	if mcdm.character.IsUsingAPL {
-		return
-	}
-	mcdm.character.Env.RegisterPostFinalizeEffect(func() {
-		for i := range mcdm.initialMajorCooldowns {
-			mcd := &mcdm.initialMajorCooldowns[i]
-			if len(mcd.timings) == 0 && mcd.Type.Matches(CooldownTypeDPS) && !mcd.Type.Matches(CooldownTypeExplosive) {
-				oldShouldActivate := mcd.ShouldActivate
-				mcd.ShouldActivate = func(sim *Simulation, character *Character) bool {
-					if oldShouldActivate != nil && !oldShouldActivate(sim, character) {
-						return false
-					}
-					return sim.CurrentTime >= delay
-				}
-			}
-		}
-	})
-}
-
-// Adds a delay to the first usage of all CDs overriding shouldActivate for cooldownTypeDPS,
-// MCDs that have a user-specified timing are not delayed.
-// This function should be called from Agent.Init().
-func (mcdm *majorCooldownManager) DelayDPSCooldowns(delay time.Duration) {
-	mcdm.character.Env.RegisterPostFinalizeEffect(func() {
-		for i := range mcdm.initialMajorCooldowns {
-			mcd := &mcdm.initialMajorCooldowns[i]
-			if len(mcd.timings) == 0 && mcd.Type.Matches(CooldownTypeDPS) {
-				oldShouldActivate := mcd.ShouldActivate
-				mcd.ShouldActivate = func(sim *Simulation, character *Character) bool {
-					if oldShouldActivate != nil && !oldShouldActivate(sim, character) {
-						return false
-					}
-					return sim.CurrentTime >= delay
-				}
-			}
-		}
-	})
-}
-
 func (mcdm *majorCooldownManager) reset(_ *Simulation) {
 	for i := range mcdm.majorCooldowns {
 		newMCD := &MajorCooldown{}
@@ -368,36 +324,6 @@ func (mcdm *majorCooldownManager) getFirstReadyMCD(sim *Simulation) *MajorCooldo
 	return nil
 }
 
-func (mcdm *majorCooldownManager) TryUseCooldowns(sim *Simulation) {
-	if sim.CurrentTime < mcdm.minReady {
-		return
-	}
-
-restart:
-	for _, mcd := range mcdm.majorCooldowns {
-		if !mcd.IsReady(sim) {
-			break
-		}
-
-		if mcd.tryActivateInternal(sim, mcdm.character) {
-			if mcd.IsReady(sim) {
-				continue // activation failed
-			}
-			mcdm.sort()
-
-			if mcd.Spell.DefaultCast.GCD > 0 {
-				// If the GCD was used, don't use any more MCDs until the next cycle so
-				// their durations aren't partially wasted.
-				break
-			}
-
-			// many MCDs are off the GCD, so it makes sense to continue
-			goto restart
-		}
-	}
-	mcdm.minReady = mcdm.majorCooldowns[0].ReadyAt()
-}
-
 // This function should be called if the CD for a major cooldown changes outside
 // of the TryActivate() call.
 func (mcdm *majorCooldownManager) UpdateMajorCooldowns() {
diff --git a/sim/core/mana.go b/sim/core/mana.go
index 29cab3520e..903b802494 100644
--- a/sim/core/mana.go
+++ b/sim/core/mana.go
@@ -11,12 +11,8 @@ import (
 
 const ThreatPerManaGained = 0.5
 
-type OnManaTick func(sim *Simulation)
-
 type manaBar struct {
-	unit       *Unit
-	OnManaTick OnManaTick
-
+	unit     *Unit
 	BaseMana float64
 
 	currentMana           float64
@@ -60,20 +56,6 @@ func (character *Character) EnableManaBarWithModifier(modifier float64) {
 	character.Unit.manaBar.unit = &character.Unit
 }
 
-// EnableResumeAfterManaWait will setup the OnManaTick callback to resume the given callback
-//
-//	once enough mana has been gained after calling unit.WaitForMana()
-func (character *Character) EnableResumeAfterManaWait(callback func(sim *Simulation)) {
-	if callback == nil {
-		panic("attempted to setup a mana tick callback that was nil")
-	}
-	character.OnManaTick = func(sim *Simulation) {
-		if character.FinishedWaitingForManaAndGCDReady(sim) {
-			callback(sim)
-		}
-	}
-}
-
 func (unit *Unit) HasManaBar() bool {
 	return unit.manaBar.unit != nil
 }
@@ -263,26 +245,19 @@ func (sim *Simulation) initManaTickAction() {
 			char := player.GetCharacter()
 			char.ManaTick(sim)
 
-			if char.OnManaTick != nil {
-				// Only execute APL actions after mana ticks once pre-pull has completed.
-				if char.IsUsingAPL && sim.CurrentTime > 0 {
-					if char.IsWaitingForMana() && !char.DoneWaitingForMana(sim) {
-						continue
-					}
-
-					char.Rotation.DoNextAction(sim)
-				} else {
-					char.OnManaTick(sim)
+			// Only execute APL actions after mana ticks once pre-pull has completed.
+			if sim.CurrentTime > 0 {
+				if char.IsWaitingForMana() && !char.DoneWaitingForMana(sim) {
+					continue
 				}
+
+				char.Rotation.DoNextAction(sim)
 			}
 		}
 		for _, petAgent := range petsWithManaBars {
 			pet := petAgent.GetPet()
 			if pet.IsEnabled() {
 				pet.ManaTick(sim)
-				if pet.OnManaTick != nil {
-					pet.OnManaTick(sim)
-				}
 			}
 		}
 
diff --git a/sim/core/pet.go b/sim/core/pet.go
index edbc22f595..a06684fbec 100644
--- a/sim/core/pet.go
+++ b/sim/core/pet.go
@@ -62,7 +62,6 @@ func NewPet(name string, owner *Character, baseStats stats.Stats, statInheritanc
 				PseudoStats: stats.NewPseudoStats(),
 				auraTracker: newAuraTracker(),
 				Metrics:     NewUnitMetrics(),
-				IsUsingAPL:  true,
 
 				StatDependencyManager: stats.NewStatDependencyManager(),
 			},
@@ -238,7 +237,6 @@ func (pet *Pet) Disable(sim *Simulation) {
 	pet.focusBar.disable(sim)
 	pet.AutoAttacks.CancelAutoSwing(sim)
 	pet.enabled = false
-	pet.DoNothing() // mark it is as doing nothing now.
 
 	// If a pet is immediately re-summoned it might try to use GCD, so we need to clear it.
 	pet.Hardcast = Hardcast{}
diff --git a/sim/core/rage.go b/sim/core/rage.go
index 6125a18934..8bcb23f11e 100644
--- a/sim/core/rage.go
+++ b/sim/core/rage.go
@@ -10,17 +10,12 @@ const MaxRage = 100.0
 const RageFactor = 453.3
 const ThreatPerRageGained = 5
 
-// OnRageGain is called any time rage is increased.
-type OnRageGain func(sim *Simulation)
-
 type rageBar struct {
 	unit *Unit
 
 	startingRage float64
 	currentRage  float64
 
-	onRageGain OnRageGain
-
 	RageRefundMetrics *ResourceMetrics
 }
 
@@ -31,7 +26,7 @@ type RageBarOptions struct {
 	OHSwingSpeed   float64
 }
 
-func (unit *Unit) EnableRageBar(options RageBarOptions, onRageGain OnRageGain) {
+func (unit *Unit) EnableRageBar(options RageBarOptions) {
 	rageFromDamageTakenMetrics := unit.NewRageMetrics(ActionID{OtherID: proto.OtherAction_OtherActionDamageTaken})
 
 	unit.SetCurrentPowerBar(RageBar)
@@ -102,10 +97,8 @@ func (unit *Unit) EnableRageBar(options RageBarOptions, onRageGain OnRageGain) {
 	})
 
 	unit.rageBar = rageBar{
-		unit:         unit,
-		startingRage: max(0, min(options.StartingRage, MaxRage)),
-		onRageGain:   onRageGain,
-
+		unit:              unit,
+		startingRage:      max(0, min(options.StartingRage, MaxRage)),
 		RageRefundMetrics: unit.NewRageMetrics(ActionID{OtherID: proto.OtherAction_OtherActionRefund}),
 	}
 }
@@ -132,11 +125,7 @@ func (rb *rageBar) AddRage(sim *Simulation, amount float64, metrics *ResourceMet
 
 	rb.currentRage = newRage
 	if !sim.Options.Interactive {
-		if rb.unit.IsUsingAPL {
-			rb.unit.Rotation.DoNextAction(sim)
-		} else {
-			rb.onRageGain(sim)
-		}
+		rb.unit.Rotation.DoNextAction(sim)
 	}
 }
 
diff --git a/sim/core/spell.go b/sim/core/spell.go
index ae62dbff57..d8848b13a9 100644
--- a/sim/core/spell.go
+++ b/sim/core/spell.go
@@ -165,10 +165,6 @@ func (unit *Unit) RegisterSpell(config SpellConfig) *Spell {
 		config.DamageMultiplier = 1
 	}
 
-	if unit.IsUsingAPL {
-		config.Cast.DefaultCast.ChannelTime = 0
-	}
-
 	if (config.DamageMultiplier != 0 || config.ThreatMultiplier != 0) && config.ProcMask == ProcMaskUnknown {
 		panic("ProcMask for spell " + config.ActionID.String() + " not set")
 	}
@@ -270,7 +266,7 @@ func (unit *Unit) RegisterSpell(config SpellConfig) *Spell {
 		panic("Empty DefaultCast with a cost for spell " + config.ActionID.String())
 	}
 
-	if spell.DefaultCast.GCD == 0 && spell.DefaultCast.CastTime == 0 && spell.DefaultCast.ChannelTime == 0 {
+	if spell.DefaultCast.GCD == 0 && spell.DefaultCast.CastTime == 0 {
 		config.Cast.IgnoreHaste = true
 	}
 
diff --git a/sim/core/target.go b/sim/core/target.go
index 2deb2bdbf4..a5a28b78e8 100644
--- a/sim/core/target.go
+++ b/sim/core/target.go
@@ -155,7 +155,6 @@ func NewTarget(options *proto.Target, targetIndex int32) *Target {
 	preset := GetPresetTargetWithID(options.Id)
 	if preset != nil && preset.AI != nil {
 		target.AI = preset.AI()
-		target.IsUsingAPL = true
 	}
 
 	return target
diff --git a/sim/core/target_ai.go b/sim/core/target_ai.go
index ebfad485d3..6bb2085cbd 100644
--- a/sim/core/target_ai.go
+++ b/sim/core/target_ai.go
@@ -57,12 +57,6 @@ func (target *Target) ApplyTalents()                     {}
 func (target *Target) GetCharacter() *Character          { return nil }
 func (target *Target) Initialize()                       {}
 
-func (target *Target) DoNothing() {
-	target.doNothing = true
-}
-
-func (target *Target) OnAutoAttack(sim *Simulation, _ *Spell) {}
-func (target *Target) OnGCDReady(sim *Simulation)             {}
 func (target *Target) ExecuteCustomRotation(sim *Simulation) {
 	if target.AI != nil {
 		target.AI.ExecuteCustomRotation(sim)
diff --git a/sim/core/target_dummy.go b/sim/core/target_dummy.go
index 81c2251326..b232f9914a 100644
--- a/sim/core/target_dummy.go
+++ b/sim/core/target_dummy.go
@@ -48,8 +48,4 @@ func (td *TargetDummy) AddPartyBuffs(partyBuffs *proto.PartyBuffs) {}
 func (td *TargetDummy) ApplyTalents()                              {}
 func (td *TargetDummy) Initialize()                                {}
 func (td *TargetDummy) Reset(sim *Simulation)                      {}
-func (td *TargetDummy) OnGCDReady(sim *Simulation) {
-	td.DoNothing()
-}
-func (td *TargetDummy) OnAutoAttack(sim *Simulation, spell *Spell) {}
 func (td *TargetDummy) ExecuteCustomRotation(sim *Simulation)      {}
diff --git a/sim/core/unit.go b/sim/core/unit.go
index 7541db3a40..250a666f29 100644
--- a/sim/core/unit.go
+++ b/sim/core/unit.go
@@ -123,8 +123,7 @@ type Unit struct {
 	// Must be enabled to use, with "EnableAutoAttacks()".
 	AutoAttacks AutoAttacks
 
-	IsUsingAPL bool // Used for checks before the finalize() stage, when apl rotations are created.
-	Rotation   *APLRotation
+	Rotation *APLRotation
 
 	// Statistics describing the results of the sim.
 	Metrics UnitMetrics
@@ -134,8 +133,7 @@ type Unit struct {
 	AttackTables                []*AttackTable
 	DynamicDamageTakenModifiers []DynamicDamageTakenModifier
 
-	GCD       *Timer
-	doNothing bool // flags that this character chose to do nothing.
+	GCD *Timer
 
 	// Used for applying the effect of a hardcast spell when casting finishes.
 	//  For channeled spells, only Expires is set.
@@ -174,14 +172,6 @@ func (unit *Unit) IsEnabled() bool {
 	return unit.enabled
 }
 
-// DoNothing will explicitly declare that the character is intentionally doing nothing.
-//
-//	If the GCD is not used during OnGCDReady and this flag is set, OnGCDReady will not be called again
-//	until it is used in some other way (like from an auto attack or resource regeneration).
-func (unit *Unit) DoNothing() {
-	unit.doNothing = true
-}
-
 func (unit *Unit) IsActive() bool {
 	return unit.IsEnabled() && unit.CurrentHealthPercent() > 0
 }
diff --git a/sim/druid/tank/tank.go b/sim/druid/tank/tank.go
index 0c3f2c5bf2..cd8e1f895a 100644
--- a/sim/druid/tank/tank.go
+++ b/sim/druid/tank/tank.go
@@ -38,21 +38,11 @@ func NewFeralTankDruid(character *core.Character, options *proto.Player) *FeralT
 		bear.SelfBuffs.InnervateTarget = tankOptions.Options.InnervateTarget
 	}
 
-	rbo := core.RageBarOptions{
+	bear.EnableRageBar(core.RageBarOptions{
 		StartingRage:   bear.Options.StartingRage,
 		RageMultiplier: 1,
 		MHSwingSpeed:   2.5,
-	}
-
-	bear.EnableRageBar(rbo, func(sim *core.Simulation) {
-		if bear.GCD.IsReady(sim) {
-			bear.TryUseCooldowns(sim)
-			if bear.GCD.IsReady(sim) {
-				bear.doRotation(sim)
-			}
-		}
 	})
-
 	bear.EnableAutoAttacks(bear, core.AutoAttackOptions{
 		// Base paw weapon.
 		MainHand:       bear.GetBearWeapon(),
diff --git a/sim/hunter/aspects.go b/sim/hunter/aspects.go
index 05a49840c7..9788a2058c 100644
--- a/sim/hunter/aspects.go
+++ b/sim/hunter/aspects.go
@@ -142,8 +142,4 @@ func (hunter *Hunter) applySharedAspectConfig(isHawk bool, aura *core.Aura) {
 
 	aura.Duration = core.NeverExpires
 	aura.NewExclusiveEffect("Aspect", true, core.ExclusiveEffect{})
-
-	aura.ApplyOnGain(func(aura *core.Aura, sim *core.Simulation) {
-		hunter.currentAspect = aura
-	})
 }
diff --git a/sim/hunter/hunter.go b/sim/hunter/hunter.go
index 450104892d..617fec6ac6 100644
--- a/sim/hunter/hunter.go
+++ b/sim/hunter/hunter.go
@@ -3,7 +3,6 @@ package hunter
 import (
 	"time"
 
-	"github.com/wowsims/wotlk/sim/common"
 	"github.com/wowsims/wotlk/sim/core"
 	"github.com/wowsims/wotlk/sim/core/proto"
 	"github.com/wowsims/wotlk/sim/core/stats"
@@ -43,12 +42,6 @@ type Hunter struct {
 	AmmoDamageBonus           float64
 	NormalizedAmmoDamageBonus float64
 
-	currentAspect *core.Aura
-
-	// Used for deciding when we can use hawk for the rest of the fight.
-	manaSpentPerSecondAtFirstAspectSwap float64
-	permaHawk                           bool
-
 	// The most recent time at which moving could have started, for trap weaving.
 	mayMoveAt time.Duration
 
@@ -83,10 +76,6 @@ type Hunter struct {
 	RapidFireAura             *core.Aura
 	ScorpidStingAuras         core.AuraArray
 	TalonOfAlarAura           *core.Aura
-
-	CustomRotation     *common.CustomRotation
-	rotationConditions map[*core.Spell]RotationCondition
-	rotationPriority   []*core.Spell
 }
 
 func (hunter *Hunter) GetCharacter() *core.Character {
@@ -146,16 +135,6 @@ func (hunter *Hunter) Initialize() {
 	hunter.registerKillCommandCD()
 	hunter.registerRapidFireCD()
 
-	if !hunter.IsUsingAPL {
-		hunter.DelayDPSCooldownsForArmorDebuffs(time.Second * 10)
-	}
-
-	hunter.initRotation()
-	hunter.CustomRotation = hunter.makeCustomRotation()
-	if hunter.CustomRotation == nil {
-		hunter.Rotation.Type = proto.Hunter_Rotation_SingleTarget
-	}
-
 	if hunter.Options.UseHuntersMark {
 		hunter.RegisterPrepullAction(0, func(sim *core.Simulation) {
 			huntersMarkAura := core.HuntersMarkAura(hunter.CurrentTarget, hunter.Talents.ImprovedHuntersMark, hunter.HasMajorGlyph(proto.HunterMajorGlyph_GlyphOfHuntersMark))
@@ -166,8 +145,6 @@ func (hunter *Hunter) Initialize() {
 
 func (hunter *Hunter) Reset(_ *core.Simulation) {
 	hunter.mayMoveAt = 0
-	hunter.manaSpentPerSecondAtFirstAspectSwap = 0
-	hunter.permaHawk = false
 }
 
 func NewHunter(character *core.Character, options *proto.Player) *Hunter {
diff --git a/sim/hunter/pet_abilities.go b/sim/hunter/pet_abilities.go
index 5e999e4bf2..2de616ea40 100644
--- a/sim/hunter/pet_abilities.go
+++ b/sim/hunter/pet_abilities.go
@@ -462,8 +462,7 @@ func (hp *HunterPet) newPin() *core.Spell {
 
 		Cast: core.CastConfig{
 			DefaultCast: core.Cast{
-				GCD:         PetGCD,
-				ChannelTime: time.Second * 4,
+				GCD: PetGCD,
 			},
 			IgnoreHaste: true,
 			CD: core.Cooldown{
diff --git a/sim/hunter/rotation.go b/sim/hunter/rotation.go
deleted file mode 100644
index 62b6fd1ac0..0000000000
--- a/sim/hunter/rotation.go
+++ /dev/null
@@ -1,385 +0,0 @@
-package hunter
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/common"
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-)
-
-func (hunter *Hunter) OnAutoAttack(sim *core.Simulation, spell *core.Spell) {
-	hunter.mayMoveAt = sim.CurrentTime
-	if !hunter.IsUsingAPL {
-		hunter.TryUseCooldowns(sim)
-	}
-	if hunter.GCD.IsReady(sim) {
-		hunter.rotation(sim)
-	}
-}
-
-func (hunter *Hunter) OnGCDReady(sim *core.Simulation) {
-	hunter.rotation(sim)
-}
-
-func (hunter *Hunter) rotation(sim *core.Simulation) {
-	if hunter.IsUsingAPL {
-		return
-	}
-	hunter.trySwapAspect(sim)
-
-	if hunter.SilencingShot.IsReady(sim) {
-		hunter.SilencingShot.Cast(sim, hunter.CurrentTarget)
-	}
-
-	if hunter.Rotation.Type == proto.Hunter_Rotation_Custom {
-		hunter.CustomRotation.Cast(sim)
-	} else if hunter.Rotation.Type == proto.Hunter_Rotation_Aoe {
-		spell := hunter.aoeChooseSpell(sim)
-
-		success := spell.Cast(sim, hunter.CurrentTarget)
-		if !success {
-			hunter.WaitForMana(sim, spell.CurCast.Cost)
-		}
-	} else {
-		spell, target := hunter.singleTargetChooseSpell(sim)
-		if spell == nil {
-			if hunter.GCD.IsReady(sim) {
-				hunter.WaitUntil(sim, sim.CurrentTime+100*time.Millisecond)
-			}
-		} else {
-			success := spell.Cast(sim, target)
-			if !success {
-				hunter.WaitForMana(sim, spell.CurCast.Cost)
-			}
-		}
-	}
-}
-
-func (hunter *Hunter) aoeChooseSpell(sim *core.Simulation) *core.Spell {
-	if hunter.Rotation.TrapWeave && hunter.ExplosiveTrap.IsReady(sim) && !hunter.ExplosiveTrap.AOEDot().IsActive() {
-		return hunter.TrapWeaveSpell
-	} else {
-		return hunter.Volley
-	}
-}
-
-func (hunter *Hunter) singleTargetChooseSpell(sim *core.Simulation) (*core.Spell, *core.Unit) {
-	for _, spell := range hunter.rotationPriority {
-		if spell == nil {
-			continue
-		}
-
-		for i := int32(0); i < hunter.Env.GetNumTargets(); i++ {
-			if hunter.rotationConditions[spell].CanUse(sim, hunter.Env.GetTargetUnit(i)) {
-				return spell, hunter.Env.GetTargetUnit(i)
-			}
-		}
-	}
-	return nil, nil
-}
-
-// Returns whether an aspect was swapped.
-func (hunter *Hunter) trySwapAspect(sim *core.Simulation) bool {
-	currentMana := hunter.CurrentManaPercent()
-	if hunter.currentAspect == hunter.AspectOfTheViperAura && hunter.Rotation.ViperStartManaPercent < 1 {
-		if !hunter.permaHawk &&
-			hunter.CurrentMana() > hunter.manaSpentPerSecondAtFirstAspectSwap*sim.GetRemainingDuration().Seconds() {
-			hunter.permaHawk = true
-		}
-		if hunter.permaHawk || currentMana > hunter.Rotation.ViperStopManaPercent {
-			hunter.AspectOfTheDragonhawk.Cast(sim, nil)
-			return true
-		}
-	} else if hunter.currentAspect != hunter.AspectOfTheViperAura && !hunter.permaHawk && currentMana < hunter.Rotation.ViperStartManaPercent {
-		if hunter.manaSpentPerSecondAtFirstAspectSwap == 0 {
-			hunter.manaSpentPerSecondAtFirstAspectSwap = (hunter.Metrics.ManaSpent - hunter.Metrics.ManaGained) / sim.CurrentTime.Seconds()
-		}
-		if !hunter.permaHawk &&
-			hunter.CurrentMana() > hunter.manaSpentPerSecondAtFirstAspectSwap*sim.GetRemainingDuration().Seconds() {
-			hunter.permaHawk = true
-		}
-		hunter.AspectOfTheViper.Cast(sim, nil)
-		return true
-	}
-	return false
-}
-
-func (hunter *Hunter) shouldCastSteadyShot(sim *core.Simulation) bool {
-	for _, spell := range hunter.rotationPriority {
-		if spell == nil {
-			continue
-		}
-		ttr := spell.TimeToReady(sim)
-		if ttr > 0 && ttr < time.Duration(hunter.Rotation.SteadyShotMaxDelay)*time.Millisecond {
-			return false
-		}
-	}
-	return true
-}
-
-func (hunter *Hunter) makeCustomRotation() *common.CustomRotation {
-	return common.NewCustomRotation(hunter.Rotation.CustomRotation, hunter.GetCharacter(), map[int32]common.CustomSpell{
-		int32(proto.Hunter_Rotation_ArcaneShot): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := hunter.ArcaneShot.CurCast.Cost
-				return hunter.ArcaneShot.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return hunter.ArcaneShot.IsReady(sim) && (hunter.ExplosiveShotR4 == nil || (!hunter.ExplosiveShotR4.CurDot().IsActive() && !hunter.ExplosiveShotR3.CurDot().IsActive()))
-			},
-		},
-		int32(proto.Hunter_Rotation_AimedShot): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := hunter.AimedShot.CurCast.Cost
-				return hunter.AimedShot.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return hunter.AimedShot.IsReady(sim)
-			},
-		},
-		int32(proto.Hunter_Rotation_BlackArrow): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := hunter.BlackArrow.CurCast.Cost
-				return hunter.BlackArrow.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return hunter.BlackArrow.IsReady(sim)
-			},
-		},
-		int32(proto.Hunter_Rotation_ChimeraShot): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := hunter.ChimeraShot.CurCast.Cost
-				return hunter.ChimeraShot.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return hunter.ChimeraShot.IsReady(sim)
-			},
-		},
-		int32(proto.Hunter_Rotation_ExplosiveShot): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := hunter.ExplosiveShotR4.CurCast.Cost
-				return hunter.ExplosiveShotR4.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return hunter.ExplosiveShotR4.IsReady(sim) && !hunter.ExplosiveShotR4.CurDot().IsActive()
-			},
-		},
-		int32(proto.Hunter_Rotation_ExplosiveShotDownrank): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := hunter.ExplosiveShotR3.CurCast.Cost
-				return hunter.ExplosiveShotR3.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return hunter.ExplosiveShotR3.IsReady(sim) && !hunter.ExplosiveShotR3.CurDot().IsActive()
-			},
-		},
-		int32(proto.Hunter_Rotation_ExplosiveTrap): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := hunter.TrapWeaveSpell.CurCast.Cost
-				return hunter.TrapWeaveSpell.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return hunter.ExplosiveTrap.IsReady(sim) && !hunter.ExplosiveTrap.AOEDot().IsActive()
-			},
-		},
-		int32(proto.Hunter_Rotation_KillShot): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := hunter.KillShot.CurCast.Cost
-				return hunter.KillShot.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return sim.IsExecutePhase20() && hunter.KillShot.IsReady(sim)
-			},
-		},
-		int32(proto.Hunter_Rotation_MultiShot): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := hunter.MultiShot.CurCast.Cost
-				return hunter.MultiShot.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return hunter.MultiShot.IsReady(sim)
-			},
-		},
-		int32(proto.Hunter_Rotation_ScorpidStingSpell): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := hunter.ScorpidSting.CurCast.Cost
-				return hunter.ScorpidSting.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return hunter.Rotation.Sting == proto.Hunter_Rotation_ScorpidSting && !hunter.ScorpidStingAuras.Get(hunter.CurrentTarget).IsActive()
-			},
-		},
-		int32(proto.Hunter_Rotation_SerpentStingSpell): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := hunter.SerpentSting.CurCast.Cost
-				for i := int32(0); i < hunter.Env.GetNumTargets(); i++ {
-					target := hunter.Env.GetTargetUnit(i)
-					if !hunter.SerpentSting.Dot(target).IsActive() {
-						return hunter.SerpentSting.Cast(sim, target), cost
-					}
-				}
-				panic("No valid serpent-sting target found")
-			},
-			Condition: func(sim *core.Simulation) bool {
-				if hunter.Rotation.Sting != proto.Hunter_Rotation_SerpentSting {
-					return false
-				}
-				if hunter.Rotation.MultiDotSerpentSting {
-					for i := int32(0); i < hunter.Env.GetNumTargets(); i++ {
-						if !hunter.SerpentSting.Dot(hunter.Env.GetTargetUnit(i)).IsActive() {
-							return true
-						}
-					}
-				}
-				return !hunter.SerpentSting.CurDot().IsActive()
-			},
-		},
-		int32(proto.Hunter_Rotation_SteadyShot): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := hunter.SteadyShot.CurCast.Cost
-				return hunter.SteadyShot.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return hunter.shouldCastSteadyShot(sim)
-			},
-		},
-		int32(proto.Hunter_Rotation_Volley): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := hunter.Volley.CurCast.Cost
-				return hunter.Volley.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return true
-			},
-		},
-	})
-}
-
-type RotationCondition struct {
-	CanUse func(sim *core.Simulation, target *core.Unit) bool
-}
-
-func (hunter *Hunter) initRotation() {
-	hunter.rotationConditions = map[*core.Spell]RotationCondition{
-		hunter.KillShot: RotationCondition{
-			func(sim *core.Simulation, target *core.Unit) bool {
-				return sim.IsExecutePhase20() && hunter.KillShot.IsReady(sim)
-			},
-		},
-		hunter.ExplosiveShotR4: RotationCondition{
-			func(sim *core.Simulation, target *core.Unit) bool {
-				if target != hunter.CurrentTarget {
-					return false
-				}
-				return hunter.ExplosiveShotR4.IsReady(sim) && !hunter.ExplosiveShotR4.CurDot().IsActive()
-			},
-		},
-		hunter.ExplosiveShotR3: RotationCondition{
-			func(sim *core.Simulation, target *core.Unit) bool {
-				if target != hunter.CurrentTarget {
-					return false
-				}
-				return hunter.Rotation.AllowExplosiveShotDownrank && hunter.ExplosiveShotR3.IsReady(sim) && !hunter.ExplosiveShotR3.CurDot().IsActive()
-			},
-		},
-		hunter.ScorpidSting: RotationCondition{
-			func(sim *core.Simulation, target *core.Unit) bool {
-				if target != hunter.CurrentTarget {
-					return false
-				}
-				return hunter.Rotation.Sting == proto.Hunter_Rotation_ScorpidSting && !hunter.ScorpidStingAuras.Get(hunter.CurrentTarget).IsActive()
-			},
-		},
-		hunter.SerpentSting: RotationCondition{
-			func(sim *core.Simulation, target *core.Unit) bool {
-				if target != hunter.CurrentTarget && !hunter.Rotation.MultiDotSerpentSting {
-					return false
-				}
-				return hunter.Rotation.Sting == proto.Hunter_Rotation_SerpentSting && !hunter.SerpentSting.Dot(target).IsActive()
-			},
-		},
-		hunter.ChimeraShot: RotationCondition{
-			func(sim *core.Simulation, target *core.Unit) bool {
-				return hunter.ChimeraShot.IsReady(sim)
-			},
-		},
-		hunter.BlackArrow: RotationCondition{
-			func(sim *core.Simulation, target *core.Unit) bool {
-				return !hunter.Rotation.TrapWeave && hunter.BlackArrow.IsReady(sim)
-			},
-		},
-		hunter.TrapWeaveSpell: RotationCondition{
-			func(sim *core.Simulation, target *core.Unit) bool {
-				return hunter.Rotation.TrapWeave && hunter.ExplosiveTrap.IsReady(sim) && !hunter.ExplosiveTrap.AOEDot().IsActive()
-			},
-		},
-		hunter.AimedShot: RotationCondition{
-			func(sim *core.Simulation, target *core.Unit) bool {
-				return hunter.AimedShot.IsReady(sim)
-			},
-		},
-		hunter.MultiShot: RotationCondition{
-			func(sim *core.Simulation, target *core.Unit) bool {
-				return hunter.MultiShot.IsReady(sim)
-			},
-		},
-		hunter.ArcaneShot: RotationCondition{
-			func(sim *core.Simulation, target *core.Unit) bool {
-				return hunter.ArcaneShot.IsReady(sim) && (!hunter.ExplosiveShotR4.CurDot().IsActive() && !hunter.ExplosiveShotR3.CurDot().IsActive())
-			},
-		},
-		hunter.SteadyShot: RotationCondition{
-			func(sim *core.Simulation, target *core.Unit) bool {
-				return hunter.SteadyShot.IsReady(sim) && hunter.shouldCastSteadyShot(sim)
-			},
-		},
-	}
-
-	if hunter.PrimaryTalentTree == 0 {
-		// BM
-		hunter.rotationPriority = []*core.Spell{
-			hunter.KillShot,
-			hunter.TrapWeaveSpell,
-			hunter.SerpentSting,
-			hunter.ScorpidSting,
-			hunter.AimedShot,
-			hunter.MultiShot,
-			hunter.SteadyShot,
-		}
-	} else if hunter.PrimaryTalentTree == 1 {
-		// MM
-		hunter.rotationPriority = []*core.Spell{
-			hunter.KillShot,
-			hunter.SerpentSting,
-			hunter.ScorpidSting,
-			hunter.TrapWeaveSpell,
-			hunter.ChimeraShot,
-			hunter.AimedShot,
-			hunter.MultiShot,
-			hunter.SteadyShot,
-		}
-	} else {
-		// SV
-		hunter.rotationPriority = []*core.Spell{
-			hunter.KillShot,
-			hunter.ExplosiveShotR4,
-			hunter.ExplosiveShotR3,
-			hunter.TrapWeaveSpell,
-			hunter.SerpentSting,
-			hunter.ScorpidSting,
-			hunter.BlackArrow,
-			hunter.AimedShot,
-			hunter.MultiShot,
-			hunter.SteadyShot,
-		}
-	}
-
-	if hunter.Env.GetNumTargets() > 1 {
-		for i, spell := range hunter.rotationPriority {
-			if spell == hunter.AimedShot {
-				hunter.rotationPriority[i] = nil
-			}
-		}
-	}
-}
diff --git a/sim/hunter/volley.go b/sim/hunter/volley.go
index 205c4c871d..c865cd8440 100644
--- a/sim/hunter/volley.go
+++ b/sim/hunter/volley.go
@@ -20,8 +20,7 @@ func (hunter *Hunter) registerVolleySpell() {
 		},
 		Cast: core.CastConfig{
 			DefaultCast: core.Cast{
-				GCD:         core.GCDDefault,
-				ChannelTime: time.Second * 6,
+				GCD: core.GCDDefault,
 			},
 		},
 
diff --git a/sim/warrior/dps/dps_warrior.go b/sim/warrior/dps/dps_warrior.go
index cfbbc96319..fcc5c19535 100644
--- a/sim/warrior/dps/dps_warrior.go
+++ b/sim/warrior/dps/dps_warrior.go
@@ -69,23 +69,7 @@ func NewDpsWarrior(character *core.Character, options *proto.Player) *DpsWarrior
 		rbo.OHSwingSpeed = oh.SwingSpeed
 	}
 
-	war.EnableRageBar(rbo, func(sim *core.Simulation) {
-		if war.GCD.IsReady(sim) {
-			war.TryUseCooldowns(sim)
-			if war.GCD.IsReady(sim) {
-				// Pause rotation until after AM ticks to detect procs that happened right after the ticks
-				if war.LastAMTick == sim.CurrentTime {
-					war.WaitUntil(sim, sim.CurrentTime+time.Microsecond*1)
-					core.StartDelayedAction(sim, core.DelayedActionOptions{
-						DoAt:     sim.CurrentTime + time.Microsecond*1,
-						OnAction: war.doRotation,
-					})
-				} else {
-					war.doRotation(sim)
-				}
-			}
-		}
-	})
+	war.EnableRageBar(rbo)
 	war.EnableAutoAttacks(war, core.AutoAttackOptions{
 		MainHand:       war.WeaponFromMainHand(war.DefaultMeleeCritMultiplier()),
 		OffHand:        war.WeaponFromOffHand(war.DefaultMeleeCritMultiplier()),
@@ -137,8 +121,6 @@ func (war *DpsWarrior) Initialize() {
 	} else if war.Rotation.StanceOption == proto.Warrior_Rotation_BattleStance {
 		war.BattleStanceAura.BuildPhase = core.CharacterBuildPhaseTalents
 	}
-
-	war.DelayDPSCooldownsForArmorDebuffs(time.Second * 10)
 }
 
 func (war *DpsWarrior) Reset(sim *core.Simulation) {
diff --git a/sim/warrior/protection/protection_warrior.go b/sim/warrior/protection/protection_warrior.go
index 9a7849ea1c..d8c4ce30fb 100644
--- a/sim/warrior/protection/protection_warrior.go
+++ b/sim/warrior/protection/protection_warrior.go
@@ -55,14 +55,7 @@ func NewProtectionWarrior(character *core.Character, options *proto.Player) *Pro
 		rbo.OHSwingSpeed = oh.SwingSpeed
 	}
 
-	war.EnableRageBar(rbo, func(sim *core.Simulation) {
-		if war.GCD.IsReady(sim) {
-			war.TryUseCooldowns(sim)
-			if war.GCD.IsReady(sim) {
-				war.doRotation(sim)
-			}
-		}
-	})
+	war.EnableRageBar(rbo)
 	war.EnableAutoAttacks(war, core.AutoAttackOptions{
 		MainHand:       war.WeaponFromMainHand(war.DefaultMeleeCritMultiplier()),
 		OffHand:        war.WeaponFromOffHand(war.DefaultMeleeCritMultiplier()),

From 681d4cd9ae7f0f47ee21384f74449edce495af29 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Wed, 3 Jan 2024 18:18:15 -0800
Subject: [PATCH 05/28] Commiting to update from master

---
 sim/core/mana.go                              |  29 +-
 sim/deathknight/army_of_the_dead.go           |   3 +-
 sim/deathknight/blood_boil.go                 |   7 -
 sim/deathknight/blood_strike.go               |   1 -
 sim/deathknight/death_strike.go               |   1 -
 sim/deathknight/deathknight.go                | 194 -----
 sim/deathknight/dps/dps_deathknight.go        | 469 ------------
 sim/deathknight/dps/rotation_blood.go         | 357 ---------
 sim/deathknight/dps/rotation_blood_helper.go  | 213 ------
 sim/deathknight/dps/rotation_frost.go         | 203 ------
 sim/deathknight/dps/rotation_frost_aoe.go     | 185 -----
 sim/deathknight/dps/rotation_frost_helper.go  | 259 -------
 .../dps/rotation_frost_sub_blood.go           | 417 -----------
 .../dps/rotation_frost_sub_blood_desync.go    | 243 -------
 .../dps/rotation_frost_sub_unholy.go          | 267 -------
 sim/deathknight/dps/rotation_shared_helper.go | 111 ---
 sim/deathknight/dps/rotation_unholy.go        | 676 ------------------
 sim/deathknight/dps/rotation_unholy_helper.go | 457 ------------
 sim/deathknight/frost_strike.go               |   1 -
 sim/deathknight/ghoul_frenzy.go               |  12 -
 sim/deathknight/heart_strike.go               |   1 -
 sim/deathknight/horn_of_winter.go             |   8 -
 sim/deathknight/howling_blast.go              |   7 -
 sim/deathknight/icy_touch.go                  |   1 -
 sim/deathknight/items.go                      |  12 +-
 sim/deathknight/obliterate.go                 |   1 -
 sim/deathknight/pestilence.go                 |   1 -
 sim/deathknight/plague_strike.go              |   1 -
 sim/deathknight/rotation.go                   | 296 --------
 sim/deathknight/rotation_helper.go            |  53 --
 sim/deathknight/scourge_strike.go             |   1 -
 sim/deathknight/tank/openers.go               | 130 ----
 sim/deathknight/tank/rotation_hps.go          |  46 --
 sim/deathknight/tank/rotation_shared.go       |  63 --
 sim/deathknight/tank/rotation_tps.go          |  73 --
 sim/deathknight/tank/tank_deathknight.go      |  53 +-
 sim/deathknight/unholy_frenzy.go              |   2 +-
 sim/hunter/hunter.go                          |   9 +-
 38 files changed, 22 insertions(+), 4841 deletions(-)
 delete mode 100644 sim/deathknight/dps/rotation_blood.go
 delete mode 100644 sim/deathknight/dps/rotation_blood_helper.go
 delete mode 100644 sim/deathknight/dps/rotation_frost.go
 delete mode 100644 sim/deathknight/dps/rotation_frost_aoe.go
 delete mode 100644 sim/deathknight/dps/rotation_frost_helper.go
 delete mode 100644 sim/deathknight/dps/rotation_frost_sub_blood.go
 delete mode 100644 sim/deathknight/dps/rotation_frost_sub_blood_desync.go
 delete mode 100644 sim/deathknight/dps/rotation_frost_sub_unholy.go
 delete mode 100644 sim/deathknight/dps/rotation_shared_helper.go
 delete mode 100644 sim/deathknight/dps/rotation_unholy.go
 delete mode 100644 sim/deathknight/dps/rotation_unholy_helper.go
 delete mode 100644 sim/deathknight/rotation.go
 delete mode 100644 sim/deathknight/rotation_helper.go
 delete mode 100644 sim/deathknight/tank/openers.go
 delete mode 100644 sim/deathknight/tank/rotation_hps.go
 delete mode 100644 sim/deathknight/tank/rotation_shared.go
 delete mode 100644 sim/deathknight/tank/rotation_tps.go

diff --git a/sim/core/mana.go b/sim/core/mana.go
index 903b802494..59f245afb5 100644
--- a/sim/core/mana.go
+++ b/sim/core/mana.go
@@ -213,25 +213,24 @@ func (unit *Unit) TimeUntilManaRegen(desiredMana float64) time.Duration {
 }
 
 func (sim *Simulation) initManaTickAction() {
-	var playersWithManaBars []Agent
-	var petsWithManaBars []PetAgent
+	var unitsWithManaBars []*Unit
 
 	for _, party := range sim.Raid.Parties {
 		for _, player := range party.Players {
 			character := player.GetCharacter()
 			if character.HasManaBar() {
-				playersWithManaBars = append(playersWithManaBars, player)
+				unitsWithManaBars = append(unitsWithManaBars, &player.GetCharacter().Unit)
 			}
 
 			for _, petAgent := range character.PetAgents {
 				if petAgent.GetPet().HasManaBar() {
-					petsWithManaBars = append(petsWithManaBars, petAgent)
+					unitsWithManaBars = append(unitsWithManaBars, &petAgent.GetCharacter().Unit)
 				}
 			}
 		}
 	}
 
-	if len(playersWithManaBars) == 0 && len(petsWithManaBars) == 0 {
+	if len(unitsWithManaBars) == 0 {
 		return
 	}
 
@@ -241,23 +240,9 @@ func (sim *Simulation) initManaTickAction() {
 		Priority:     ActionPriorityRegen,
 	}
 	pa.OnAction = func(sim *Simulation) {
-		for _, player := range playersWithManaBars {
-			char := player.GetCharacter()
-			char.ManaTick(sim)
-
-			// Only execute APL actions after mana ticks once pre-pull has completed.
-			if sim.CurrentTime > 0 {
-				if char.IsWaitingForMana() && !char.DoneWaitingForMana(sim) {
-					continue
-				}
-
-				char.Rotation.DoNextAction(sim)
-			}
-		}
-		for _, petAgent := range petsWithManaBars {
-			pet := petAgent.GetPet()
-			if pet.IsEnabled() {
-				pet.ManaTick(sim)
+		for _, unit := range unitsWithManaBars {
+			if unit.IsEnabled() {
+				unit.ManaTick(sim)
 			}
 		}
 
diff --git a/sim/deathknight/army_of_the_dead.go b/sim/deathknight/army_of_the_dead.go
index 73a284e330..a2bde0ae51 100644
--- a/sim/deathknight/army_of_the_dead.go
+++ b/sim/deathknight/army_of_the_dead.go
@@ -51,8 +51,7 @@ func (dk *Deathknight) registerArmyOfTheDeadCD() {
 		},
 		Cast: core.CastConfig{
 			DefaultCast: core.Cast{
-				ChannelTime: time.Second * 4,
-				GCD:         core.GCDDefault,
+				GCD: core.GCDDefault,
 			},
 			CD: core.Cooldown{
 				Timer:    dk.NewTimer(),
diff --git a/sim/deathknight/blood_boil.go b/sim/deathknight/blood_boil.go
index b0847d391c..943aa60057 100644
--- a/sim/deathknight/blood_boil.go
+++ b/sim/deathknight/blood_boil.go
@@ -31,21 +31,14 @@ func (dk *Deathknight) registerBloodBoilSpell() {
 		ThreatMultiplier: 1.0,
 
 		ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
-			dk.AoESpellNumTargetsHit = 0
-
 			for _, aoeTarget := range sim.Encounter.TargetUnits {
 				baseDamage := (sim.Roll(180, 220) + 0.06*dk.getImpurityBonus(spell)) * dk.RoRTSBonus(aoeTarget) * core.TernaryFloat64(dk.DiseasesAreActive(aoeTarget), 1.5, 1.0)
 				baseDamage *= sim.Encounter.AOECapMultiplier()
 
 				result := spell.CalcAndDealDamage(sim, aoeTarget, baseDamage, spell.OutcomeMagicHitAndCrit)
 
-				if result.Landed() {
-					dk.AoESpellNumTargetsHit++
-				}
-
 				if aoeTarget == target {
 					spell.SpendRefundableCost(sim, result)
-					dk.LastOutcome = result.Outcome
 				}
 			}
 		},
diff --git a/sim/deathknight/blood_strike.go b/sim/deathknight/blood_strike.go
index 399725e619..96d53a6f16 100644
--- a/sim/deathknight/blood_strike.go
+++ b/sim/deathknight/blood_strike.go
@@ -60,7 +60,6 @@ func (dk *Deathknight) newBloodStrikeSpell(isMH bool) *core.Spell {
 			if isMH {
 				spell.SpendRefundableCostAndConvertBloodRune(sim, result, deathConvertChance)
 				dk.threatOfThassarianProc(sim, result, dk.BloodStrikeOhHit)
-				dk.LastOutcome = result.Outcome
 
 				if result.Landed() {
 					if dk.DesolationAura != nil {
diff --git a/sim/deathknight/death_strike.go b/sim/deathknight/death_strike.go
index f45bb4d5a1..de85bd7a11 100644
--- a/sim/deathknight/death_strike.go
+++ b/sim/deathknight/death_strike.go
@@ -66,7 +66,6 @@ func (dk *Deathknight) newDeathStrikeSpell(isMH bool) *core.Spell {
 
 			if isMH {
 				spell.SpendRefundableCostAndConvertFrostOrUnholyRune(sim, result, deathConvertChance)
-				dk.LastOutcome = result.Outcome
 
 				if result.Landed() {
 					healingAmount := 0.05 * dk.dkCountActiveDiseases(target) * dk.MaxHealth() * (1.0 + 0.5*float64(dk.Talents.ImprovedDeathStrike))
diff --git a/sim/deathknight/deathknight.go b/sim/deathknight/deathknight.go
index 469285f364..9c05192037 100644
--- a/sim/deathknight/deathknight.go
+++ b/sim/deathknight/deathknight.go
@@ -75,8 +75,6 @@ type Deathknight struct {
 
 	Inputs DeathknightInputs
 
-	RotationHelper
-
 	Ghoul     *GhoulPet
 	RaiseDead *core.Spell
 
@@ -314,33 +312,6 @@ func (dk *Deathknight) Initialize() {
 	dk.registerDeathPactSpell()
 	dk.registerUnholyFrenzyCD()
 
-	dk.RegisterAura(core.Aura{
-		Label:    "Last Cast Assigner",
-		Duration: core.NeverExpires,
-		OnReset: func(aura *core.Aura, sim *core.Simulation) {
-			aura.Activate(sim)
-		},
-		OnCastComplete: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell) {
-			if spell.DefaultCast.GCD > 0 {
-				dk.LastCast = spell
-			}
-		},
-	})
-
-	if !dk.IsUsingAPL {
-		if dk.Inputs.PrecastHornOfWinter {
-			dk.RegisterPrepullAction(-1500*time.Millisecond, func(sim *core.Simulation) {
-				dk.HornOfWinter.Cast(sim, nil)
-			})
-		}
-
-		if dk.Inputs.ArmyOfTheDeadType == proto.Deathknight_Rotation_PreCast {
-			dk.RegisterPrepullAction(-10*time.Second, func(sim *core.Simulation) {
-				dk.ArmyOfTheDead.Cast(sim, nil)
-			})
-		}
-	}
-
 	// allows us to use these auras in the APL pre-pull actions
 	wotlk.CreateBlackMagicProcAura(&dk.Character)
 	CreateVirulenceProcAura(&dk.Character)
@@ -393,8 +364,6 @@ func (dk *Deathknight) ResetBonusCoeffs() {
 
 func (dk *Deathknight) Reset(sim *core.Simulation) {
 	dk.LastTickTime = -1
-	dk.LastCast = nil
-	dk.NextCast = nil
 	dk.DeathStrikeHeals = dk.DeathStrikeHeals[:0]
 	dk.MakeTSRoRAssumptions = sim.Raid.Size() <= 1
 }
@@ -475,7 +444,6 @@ func NewDeathknight(character *core.Character, inputs DeathknightInputs, talents
 		dk.RuneWeapon = dk.NewRuneWeapon()
 	}
 
-	dk.RotationSequence = &Sequence{}
 	// done here so enchants that modify stats are applied before stats are calculated
 	dk.registerItems()
 
@@ -498,26 +466,6 @@ func (dk *Deathknight) bonusCritMultiplier(bonusTalentPoints int32) float64 {
 	return dk.MeleeCritMultiplier(1, 0.15*float64(bonusTalentPoints))
 }
 
-func (dk *Deathknight) AverageDSHeal() float64 {
-	count := len(dk.DeathStrikeHeals)
-	if count >= 5 {
-		sum := dk.DeathStrikeHeals[count-1]
-		sum += dk.DeathStrikeHeals[count-2]
-		sum += dk.DeathStrikeHeals[count-3]
-		sum += dk.DeathStrikeHeals[count-4]
-		sum += dk.DeathStrikeHeals[count-5]
-		return sum / 5.0
-	} else if count > 0 {
-		sum := dk.DeathStrikeHeals[count-1]
-		for i := 1; i < count; i++ {
-			sum += dk.DeathStrikeHeals[count-i-1]
-		}
-		return sum / float64(count)
-	} else {
-		return 0
-	}
-}
-
 // Agent is a generic way to access underlying warrior on any of the agents.
 
 func (dk *Deathknight) GetDeathKnight() *Deathknight {
@@ -527,145 +475,3 @@ func (dk *Deathknight) GetDeathKnight() *Deathknight {
 type DeathKnightAgent interface {
 	GetDeathKnight() *Deathknight
 }
-
-func PointsInTalents(talents *proto.DeathknightTalents) (int, int, int) {
-	blood := 0
-	blood += int(talents.Butchery)
-	blood += int(talents.Subversion)
-	blood += int(talents.BladeBarrier)
-	blood += int(talents.BladedArmor)
-	blood += int(talents.ScentOfBlood)
-	blood += int(talents.TwoHandedWeaponSpecialization)
-	blood += int(talents.DarkConviction)
-	blood += int(talents.DeathRuneMastery)
-	blood += int(talents.ImprovedRuneTap)
-	blood += int(talents.SpellDeflection)
-	blood += int(talents.Vendetta)
-	blood += int(talents.BloodyStrikes)
-	blood += int(talents.VeteranOfTheThirdWar)
-	blood += int(talents.BloodyVengeance)
-	blood += int(talents.AbominationsMight)
-	blood += int(talents.Bloodworms)
-	blood += int(talents.ImprovedBloodPresence)
-	blood += int(talents.ImprovedDeathStrike)
-	blood += int(talents.SuddenDoom)
-	blood += int(talents.WillOfTheNecropolis)
-	blood += int(talents.MightOfMograine)
-	blood += int(talents.BloodGorged)
-	if talents.RuneTap {
-		blood++
-	}
-	if talents.Hysteria {
-		blood++
-	}
-	if talents.MarkOfBlood {
-		blood++
-	}
-	if talents.VampiricBlood {
-		blood++
-	}
-	if talents.HeartStrike {
-		blood++
-	}
-	if talents.DancingRuneWeapon {
-		blood++
-	}
-
-	frost := 0
-
-	frost += int(talents.ImprovedIcyTouch)
-	frost += int(talents.RunicPowerMastery)
-	frost += int(talents.Toughness)
-	frost += int(talents.IcyReach)
-	frost += int(talents.BlackIce)
-	frost += int(talents.NervesOfColdSteel)
-	frost += int(talents.IcyTalons)
-	frost += int(talents.Annihilation)
-	frost += int(talents.KillingMachine)
-	frost += int(talents.ChillOfTheGrave)
-	frost += int(talents.EndlessWinter)
-	frost += int(talents.FrigidDreadplate)
-	frost += int(talents.GlacierRot)
-	frost += int(talents.MercilessCombat)
-	frost += int(talents.Rime)
-	frost += int(talents.Chilblains)
-	frost += int(talents.ImprovedFrostPresence)
-	frost += int(talents.ThreatOfThassarian)
-	frost += int(talents.BloodOfTheNorth)
-	frost += int(talents.Acclimation)
-	frost += int(talents.GuileOfGorefiend)
-	frost += int(talents.TundraStalker)
-	if talents.HowlingBlast {
-		frost++
-	}
-	if talents.Lichborne {
-		frost++
-	}
-	if talents.Deathchill {
-		frost++
-	}
-	if talents.ImprovedIcyTalons {
-		frost++
-	}
-	if talents.HungeringCold {
-		frost++
-	}
-	if talents.UnbreakableArmor {
-		frost++
-	}
-	if talents.FrostStrike {
-		frost++
-	}
-
-	unholy := 0
-
-	unholy += int(talents.ViciousStrikes)
-	unholy += int(talents.Virulence)
-	unholy += int(talents.Anticipation)
-	unholy += int(talents.Epidemic)
-	unholy += int(talents.Morbidity)
-	unholy += int(talents.UnholyCommand)
-	unholy += int(talents.RavenousDead)
-	unholy += int(talents.Outbreak)
-	unholy += int(talents.Necrosis)
-	unholy += int(talents.OnAPaleHorse)
-	unholy += int(talents.BloodCakedBlade)
-	unholy += int(talents.NightOfTheDead)
-	unholy += int(talents.Impurity)
-	unholy += int(talents.Dirge)
-	unholy += int(talents.Desecration)
-	unholy += int(talents.MagicSuppression)
-	unholy += int(talents.Reaping)
-	unholy += int(talents.Desolation)
-	unholy += int(talents.ImprovedUnholyPresence)
-	unholy += int(talents.CryptFever)
-	unholy += int(talents.WanderingPlague)
-	unholy += int(talents.EbonPlaguebringer)
-	unholy += int(talents.RageOfRivendare)
-	if talents.CorpseExplosion {
-		unholy++
-	}
-	if talents.UnholyBlight {
-		unholy++
-	}
-	if talents.MasterOfGhouls {
-		unholy++
-	}
-	if talents.AntiMagicZone {
-		unholy++
-	}
-	if talents.GhoulFrenzy {
-		unholy++
-	}
-	if talents.BoneShield {
-		unholy++
-	}
-	if talents.ScourgeStrike {
-		unholy++
-	}
-	if talents.SummonGargoyle {
-		unholy++
-	}
-
-	return blood, frost, unholy
-}
diff --git a/sim/deathknight/dps/dps_deathknight.go b/sim/deathknight/dps/dps_deathknight.go
index e342615622..458fb5876f 100644
--- a/sim/deathknight/dps/dps_deathknight.go
+++ b/sim/deathknight/dps/dps_deathknight.go
@@ -1,9 +1,6 @@
 package dps
 
 import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/common"
 	"github.com/wowsims/wotlk/sim/core"
 	"github.com/wowsims/wotlk/sim/core/proto"
 	"github.com/wowsims/wotlk/sim/deathknight"
@@ -28,17 +25,6 @@ func RegisterDpsDeathknight() {
 
 type DpsDeathknight struct {
 	*deathknight.Deathknight
-
-	sr SharedRotation
-	br BloodRotation
-	fr FrostRotation
-	ur UnholyRotation
-
-	CustomRotation *common.CustomRotation
-
-	Rotation *proto.Deathknight_Rotation
-
-	rotationSetup func()
 }
 
 func NewDpsDeathknight(character *core.Character, player *proto.Player) *DpsDeathknight {
@@ -64,7 +50,6 @@ func NewDpsDeathknight(character *core.Character, player *proto.Player) *DpsDeat
 			AvgAMSSuccessRate:   dk.Rotation.AvgAmsSuccessRate,
 			AvgAMSHit:           dk.Rotation.AvgAmsHit,
 		}, player.TalentsString, dk.Rotation.PreNerfedGargoyle),
-		Rotation: dk.Rotation,
 	}
 
 	dpsDk.Inputs.UnholyFrenzyTarget = dk.Options.UnholyFrenzyTarget
@@ -75,10 +60,6 @@ func NewDpsDeathknight(character *core.Character, player *proto.Player) *DpsDeat
 		AutoSwingMelee: true,
 	})
 
-	dpsDk.br.dk = dpsDk
-	dpsDk.sr.dk = dpsDk
-	dpsDk.ur.dk = dpsDk
-
 	return dpsDk
 }
 
@@ -90,465 +71,15 @@ func (dk *DpsDeathknight) FrostPointsInUnholy() int32 {
 	return dk.Talents.ViciousStrikes + dk.Talents.Virulence + dk.Talents.Epidemic + dk.Talents.RavenousDead + dk.Talents.Necrosis + dk.Talents.BloodCakedBlade
 }
 
-func (dk *DpsDeathknight) SetupRotations() {
-	bl, fr, uh := deathknight.PointsInTalents(dk.Talents)
-
-	if dk.Rotation.AutoRotation {
-		if uh > fr && uh > bl {
-			// Unholy
-			dk.Rotation.BtGhoulFrenzy = false
-			dk.Rotation.UseEmpowerRuneWeapon = true
-			dk.Rotation.HoldErwArmy = true
-			dk.Rotation.UseGargoyle = true
-			dk.Inputs.ArmyOfTheDeadType = proto.Deathknight_Rotation_AsMajorCd
-			dk.Rotation.BloodTap = proto.Deathknight_Rotation_GhoulFrenzy
-			dk.Rotation.FirstDisease = proto.Deathknight_Rotation_FrostFever
-			dk.Rotation.StartingPresence = proto.Deathknight_Rotation_Unholy
-			dk.Rotation.BlPresence = proto.Deathknight_Rotation_Blood
-			dk.Rotation.Presence = proto.Deathknight_Rotation_Blood
-			dk.Rotation.GargoylePresence = proto.Deathknight_Rotation_Unholy
-
-			mh := dk.GetMHWeapon()
-			oh := dk.GetOHWeapon()
-
-			if mh != nil && oh != nil {
-				// DW
-				dk.Rotation.BloodRuneFiller = proto.Deathknight_Rotation_BloodBoil
-				dk.Rotation.UseDeathAndDecay = true
-			} else {
-				// 2h
-				if dk.Env.GetNumTargets() > 1 {
-					dk.Rotation.BloodRuneFiller = proto.Deathknight_Rotation_BloodBoil
-					dk.Rotation.UseDeathAndDecay = true
-				} else {
-					dk.Rotation.BloodRuneFiller = proto.Deathknight_Rotation_BloodStrike
-					dk.Rotation.UseDeathAndDecay = false
-				}
-			}
-			// Always use DnD if you have the glyph.
-			if dk.HasMajorGlyph(proto.DeathknightMajorGlyph_GlyphOfDeathAndDecay) {
-				dk.Rotation.UseDeathAndDecay = true
-			}
-		} else if fr > uh && fr > bl {
-			// Frost rotations here.
-		} else if bl > fr && bl > uh {
-			// Blood rotations here.
-
-			// AotD not good as Major CD in blood due to DRW confclits
-			if dk.Inputs.ArmyOfTheDeadType == proto.Deathknight_Rotation_AsMajorCd {
-				dk.Inputs.ArmyOfTheDeadType = proto.Deathknight_Rotation_PreCast
-				dk.Rotation.HoldErwArmy = false
-			}
-		} else {
-			// some weird spec where two trees are equal...
-		}
-	}
-
-	dk.RotationSequence.Clear()
-
-	dk.Inputs.FuStrike = deathknight.FuStrike_Obliterate
-	dk.rotationSetup = dk.setupBloodRotations
-	dk.CustomRotation = dk.makeCustomRotation()
-	if dk.CustomRotation == nil || dk.Rotation.FrostRotationType == proto.Deathknight_Rotation_SingleTarget {
-		dk.Rotation.FrostRotationType = proto.Deathknight_Rotation_SingleTarget
-		if fr > uh && fr > bl {
-			// AotD as major CD doesn't work well with frost
-			if dk.Inputs.ArmyOfTheDeadType == proto.Deathknight_Rotation_AsMajorCd {
-				dk.Inputs.ArmyOfTheDeadType = proto.Deathknight_Rotation_PreCast
-				dk.Rotation.HoldErwArmy = false
-			}
-			if bl > uh {
-				if dk.Rotation.DesyncRotation {
-					dk.rotationSetup = dk.setupFrostSubBloodDesyncOpener
-				} else if dk.Rotation.UseEmpowerRuneWeapon {
-					dk.rotationSetup = dk.setupFrostSubBloodERWOpener
-				} else {
-					dk.rotationSetup = dk.setupFrostSubBloodNoERWOpener
-				}
-			} else {
-				dk.Rotation.FrostRotationType = proto.Deathknight_Rotation_SingleTarget
-				if dk.Rotation.UseEmpowerRuneWeapon {
-					dk.rotationSetup = dk.setupFrostSubUnholyERWOpener
-				} else {
-					// TODO you can't unh sub without ERW in the opener...yet
-					dk.Rotation.UseEmpowerRuneWeapon = true
-					dk.rotationSetup = dk.setupFrostSubUnholyERWOpener
-				}
-			}
-		} else if uh > fr && uh > bl {
-			dk.rotationSetup = dk.setupUnholyRotations
-		} else if bl > fr && bl > uh {
-			if dk.Inputs.ArmyOfTheDeadType == proto.Deathknight_Rotation_AsMajorCd {
-				dk.Inputs.ArmyOfTheDeadType = proto.Deathknight_Rotation_PreCast
-				dk.Rotation.HoldErwArmy = false
-			}
-			dk.rotationSetup = dk.setupBloodRotations
-		}
-	} else {
-		dk.rotationSetup = dk.setupCustomRotations
-	}
-
-	dk.rotationSetup()
-}
-
 func (dk *DpsDeathknight) GetDeathknight() *deathknight.Deathknight {
 	return dk.Deathknight
 }
 
 func (dk *DpsDeathknight) Initialize() {
 	dk.Deathknight.Initialize()
-
-	dk.sr.Initialize(dk)
-	dk.br.Initialize(dk)
-	dk.fr.Initialize(dk)
-	dk.ur.Initialize(dk)
-
-	dk.SetupRotations()
-}
-
-func (dk *DpsDeathknight) setupGargProcTrackers() {
-	snapshotManager := dk.ur.gargoyleSnapshot
-
-	// Don't need to wait for haste snapshots anymore
-	if dk.Rotation.PreNerfedGargoyle {
-		snapshotManager.AddProc(40211, "Potion of Speed", true)
-		snapshotManager.AddProc(54999, "Hyperspeed Acceleration", true)
-		snapshotManager.AddProc(26297, "Berserking (Troll)", true)
-		snapshotManager.AddProc(33697, "Blood Fury", true)
-
-		snapshotManager.AddProc(55379, "Thundering Skyflare Diamond Proc", false)
-		snapshotManager.AddProc(59626, "Black Magic Proc", false)
-		snapshotManager.AddProc(53344, "Rune Of The Fallen Crusader Proc", false)
-
-		snapshotManager.AddProc(37390, "Meteorite Whetstone Proc", false)
-		snapshotManager.AddProc(39229, "Embrace of the Spider Proc", false)
-		snapshotManager.AddProc(44308, "Signet of Edward the Odd Proc", false)
-		snapshotManager.AddProc(43573, "Tears of Bitter Anguish Proc", false)
-		snapshotManager.AddProc(45609, "Comet's Trail Proc", false)
-		snapshotManager.AddProc(45866, "Elemental Focus Stone Proc", false)
-
-		snapshotManager.AddProc(53344, "Rune Of The Fallen Crusader Proc", false)
-	} else {
-		fcEnchantId := int32(3368)
-		// Only worth snapshotting if both are on (might want to re-visit this after P2)
-		if mh, oh := dk.Character.MainHand(), dk.Character.OffHand(); mh.Enchant.EffectID == fcEnchantId && oh.Enchant.EffectID == fcEnchantId {
-			snapshotManager.AddProc(53344, "Rune Of The Fallen Crusader Proc", false)
-		}
-	}
-
-	snapshotManager.AddProc(42987, "DMC Greatness Strength Proc", false)
-
-	snapshotManager.AddProc(47115, "Deaths Verdict Strength Proc", false)
-	snapshotManager.AddProc(47131, "Deaths Verdict H Strength Proc", false)
-	snapshotManager.AddProc(47303, "Deaths Choice Strength Proc", false)
-	snapshotManager.AddProc(47464, "Deaths Choice H Strength Proc", false)
-
-	snapshotManager.AddProc(71484, "Deathbringer's Will Strength Proc", false)
-	snapshotManager.AddProc(71492, "Deathbringer's Will Haste Proc", false)
-	snapshotManager.AddProc(71561, "Deathbringer's Will H Strength Proc", false)
-	snapshotManager.AddProc(71560, "Deathbringer's Will H Haste Proc", false)
-
-	snapshotManager.AddProc(40684, "Mirror of Truth Proc", false)
-	snapshotManager.AddProc(40767, "Sonic Booster Proc", false)
-	snapshotManager.AddProc(44914, "Anvil of Titans Proc", false)
-	snapshotManager.AddProc(45286, "Pyrite Infuser Proc", false)
-	snapshotManager.AddProc(45522, "Blood of the Old God Proc", false)
-	snapshotManager.AddProc(47214, "Banner of Victory Proc", false)
-	snapshotManager.AddProc(49074, "Coren's Chromium Coaster Proc", false)
-	snapshotManager.AddProc(50342, "Whispering Fanged Skull Proc", false)
-	snapshotManager.AddProc(50343, "Whispering Fanged Skull H Proc", false)
-	snapshotManager.AddProc(50401, "Ashen Band of Unmatched Vengeance Proc", false)
-	snapshotManager.AddProc(50402, "Ashen Band of Endless Vengeance Proc", false)
-	snapshotManager.AddProc(52571, "Ashen Band of Unmatched Might Proc", false)
-	snapshotManager.AddProc(52572, "Ashen Band of Endless Might Proc", false)
-	snapshotManager.AddProc(54569, "Sharpened Twilight Scale Proc", false)
-	snapshotManager.AddProc(54590, "Sharpened Twilight Scale H Proc", false)
-}
-
-func (dk *DpsDeathknight) setupGargoyleCooldowns() {
-	dk.ur.gargoyleSnapshot.ClearMajorCooldowns()
-
-	// hyperspeed accelerators
-	dk.gargoyleHasteCooldownSync(core.ActionID{SpellID: 54758}, false)
-
-	// berserking (troll)
-	dk.gargoyleHasteCooldownSync(core.ActionID{SpellID: 26297}, false)
-
-	// blood fury (orc)
-	dk.gargoyleAPCooldownSync(core.ActionID{SpellID: 33697}, false)
-
-	// potion of speed
-	dk.gargoyleHasteCooldownSync(core.ActionID{ItemID: 40211}, true)
-
-	// active ap trinkets
-	dk.gargoyleAPCooldownSync(core.ActionID{ItemID: 35937}, false)
-	dk.gargoyleAPCooldownSync(core.ActionID{ItemID: 36871}, false)
-	dk.gargoyleAPCooldownSync(core.ActionID{ItemID: 37166}, false)
-	dk.gargoyleAPCooldownSync(core.ActionID{ItemID: 37556}, false)
-	dk.gargoyleAPCooldownSync(core.ActionID{ItemID: 37557}, false)
-	dk.gargoyleAPCooldownSync(core.ActionID{ItemID: 38080}, false)
-	dk.gargoyleAPCooldownSync(core.ActionID{ItemID: 38081}, false)
-	dk.gargoyleAPCooldownSync(core.ActionID{ItemID: 38761}, false)
-	dk.gargoyleAPCooldownSync(core.ActionID{ItemID: 39257}, false)
-	dk.gargoyleAPCooldownSync(core.ActionID{ItemID: 45263}, false)
-	dk.gargoyleAPCooldownSync(core.ActionID{ItemID: 46086}, false)
-	dk.gargoyleAPCooldownSync(core.ActionID{ItemID: 47734}, false)
-
-	// active haste trinkets
-	dk.gargoyleHasteCooldownSync(core.ActionID{ItemID: 36972}, false)
-	dk.gargoyleHasteCooldownSync(core.ActionID{ItemID: 37558}, false)
-	dk.gargoyleHasteCooldownSync(core.ActionID{ItemID: 37560}, false)
-	dk.gargoyleHasteCooldownSync(core.ActionID{ItemID: 37562}, false)
-	dk.gargoyleHasteCooldownSync(core.ActionID{ItemID: 38070}, false)
-	dk.gargoyleHasteCooldownSync(core.ActionID{ItemID: 38258}, false)
-	dk.gargoyleHasteCooldownSync(core.ActionID{ItemID: 38259}, false)
-	dk.gargoyleHasteCooldownSync(core.ActionID{ItemID: 38764}, false)
-	dk.gargoyleHasteCooldownSync(core.ActionID{ItemID: 40531}, false)
-	dk.gargoyleHasteCooldownSync(core.ActionID{ItemID: 43836}, false)
-	dk.gargoyleHasteCooldownSync(core.ActionID{ItemID: 45466}, false)
-	dk.gargoyleHasteCooldownSync(core.ActionID{ItemID: 46088}, false)
-	dk.gargoyleHasteCooldownSync(core.ActionID{ItemID: 48722}, false)
-	dk.gargoyleHasteCooldownSync(core.ActionID{ItemID: 50260}, false)
-}
-
-func (dk *DpsDeathknight) gargoyleAPCooldownSync(actionID core.ActionID, isPotion bool) {
-	if majorCd := dk.Character.GetMajorCooldown(actionID); majorCd != nil {
-
-		majorCd.ShouldActivate = func(sim *core.Simulation, character *core.Character) bool {
-			if dk.ur.activatingGargoyle {
-				return true
-			}
-			if dk.SummonGargoyle.CD.TimeToReady(sim) > majorCd.Spell.CD.Duration && !isPotion {
-				return true
-			}
-			if dk.SummonGargoyle.CD.ReadyAt() > sim.Duration {
-				return true
-			}
-
-			return false
-		}
-
-		dk.ur.gargoyleSnapshot.AddMajorCooldown(majorCd)
-	}
-}
-
-func (dk *DpsDeathknight) gargoyleHasteCooldownSync(actionID core.ActionID, isPotion bool) {
-	if majorCd := dk.Character.GetMajorCooldown(actionID); majorCd != nil {
-
-		majorCd.ShouldActivate = func(sim *core.Simulation, character *core.Character) bool {
-			if !dk.Rotation.PreNerfedGargoyle {
-				aura := dk.SummonGargoyleAura
-
-				if aura != nil && aura.IsActive() {
-					return true
-				}
-				if dk.SummonGargoyle.CD.TimeToReady(sim) > majorCd.Spell.CD.Duration-10*time.Second && !isPotion {
-					return true
-				}
-				if dk.SummonGargoyle.CD.ReadyAt() > sim.Duration {
-					return true
-				}
-
-				return false
-			} else {
-				if dk.ur.activatingGargoyle {
-					return true
-				}
-				if dk.SummonGargoyle.CD.TimeToReady(sim) > majorCd.Spell.CD.Duration-10*time.Second && !isPotion {
-					return true
-				}
-				if dk.SummonGargoyle.CD.ReadyAt() > sim.Duration {
-					return true
-				}
-			}
-
-			return false
-		}
-
-		dk.ur.gargoyleSnapshot.AddMajorCooldown(majorCd)
-	}
-}
-
-func (dk *DpsDeathknight) setupDrwProcTrackers() {
-	snapshotManager := dk.br.drwSnapshot
-
-	snapshotManager.AddProc(40211, "Potion of Speed", true)
-	snapshotManager.AddProc(54999, "Hyperspeed Acceleration", true)
-	snapshotManager.AddProc(26297, "Berserking (Troll)", true)
-	snapshotManager.AddProc(33697, "Blood Fury", true)
-
-	snapshotManager.AddProc(55379, "Thundering Skyflare Diamond Proc", false)
-	snapshotManager.AddProc(59626, "Black Magic Proc", false)
-	snapshotManager.AddProc(53344, "Rune Of The Fallen Crusader Proc", false)
-
-	snapshotManager.AddProc(37390, "Meteorite Whetstone Proc", false)
-	snapshotManager.AddProc(39229, "Embrace of the Spider Proc", false)
-	snapshotManager.AddProc(44308, "Signet of Edward the Odd Proc", false)
-	snapshotManager.AddProc(43573, "Tears of Bitter Anguish Proc", false)
-	snapshotManager.AddProc(45609, "Comet's Trail Proc", false)
-	snapshotManager.AddProc(45866, "Elemental Focus Stone Proc", false)
-
-	snapshotManager.AddProc(53344, "Rune Of The Fallen Crusader Proc", false)
-
-	snapshotManager.AddProc(42987, "DMC Greatness Strength Proc", false)
-
-	snapshotManager.AddProc(47115, "Deaths Verdict Strength Proc", false)
-	snapshotManager.AddProc(47131, "Deaths Verdict H Strength Proc", false)
-	snapshotManager.AddProc(47303, "Deaths Choice Strength Proc", false)
-	snapshotManager.AddProc(47464, "Deaths Choice H Strength Proc", false)
-
-	snapshotManager.AddProc(71484, "Deathbringer's Will Strength Proc", false)
-	snapshotManager.AddProc(71492, "Deathbringer's Will Haste Proc", false)
-	snapshotManager.AddProc(71491, "Deathbringer's Will Crit Proc", false)
-	snapshotManager.AddProc(71561, "Deathbringer's Will H Strength Proc", false)
-	snapshotManager.AddProc(71560, "Deathbringer's Will H Haste Proc", false)
-	snapshotManager.AddProc(71559, "Deathbringer's Will H Crit Proc", false)
-
-	snapshotManager.AddProc(40684, "Mirror of Truth Proc", false)
-	snapshotManager.AddProc(40767, "Sonic Booster Proc", false)
-	snapshotManager.AddProc(44914, "Anvil of Titans Proc", false)
-	snapshotManager.AddProc(45286, "Pyrite Infuser Proc", false)
-	snapshotManager.AddProc(45522, "Blood of the Old God Proc", false)
-	snapshotManager.AddProc(47214, "Banner of Victory Proc", false)
-	snapshotManager.AddProc(49074, "Coren's Chromium Coaster Proc", false)
-	snapshotManager.AddProc(50342, "Whispering Fanged Skull Proc", false)
-	snapshotManager.AddProc(50343, "Whispering Fanged Skull H Proc", false)
-	snapshotManager.AddProc(50401, "Ashen Band of Unmatched Vengeance Proc", false)
-	snapshotManager.AddProc(50402, "Ashen Band of Endless Vengeance Proc", false)
-	snapshotManager.AddProc(52571, "Ashen Band of Unmatched Might Proc", false)
-	snapshotManager.AddProc(52572, "Ashen Band of Endless Might Proc", false)
-	snapshotManager.AddProc(54569, "Sharpened Twilight Scale Proc", false)
-	snapshotManager.AddProc(54590, "Sharpened Twilight Scale H Proc", false)
-
-	//snapshotManager.AddProc(40256, "Grim Toll Proc", false)
-	//snapshotManager.AddProc(45931, "Mjolnir Runestone Proc", false)
-	snapshotManager.AddProc(46038, "Dark Matter Proc", false)
-	snapshotManager.AddProc(50198, "Needle-Encrusted Scorpion Proc", false)
-}
-
-func (dk *DpsDeathknight) setupDrwCooldowns() {
-	dk.br.drwSnapshot.ClearMajorCooldowns()
-
-	// Unholy Frenzy
-	dk.drwCooldownSync(core.ActionID{SpellID: 49016, Tag: dk.Index}, false)
-
-	// hyperspeed accelerators
-	dk.drwCooldownSync(core.ActionID{SpellID: 54758}, false)
-
-	// berserking (troll)
-	dk.drwCooldownSync(core.ActionID{SpellID: 26297}, false)
-
-	// blood fury (orc)
-	dk.drwCooldownSync(core.ActionID{SpellID: 33697}, false)
-
-	// potion of speed
-	dk.drwCooldownSync(core.ActionID{ItemID: 40211}, true)
-
-	// active ap trinkets
-	dk.drwCooldownSync(core.ActionID{ItemID: 35937}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 36871}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 37166}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 37556}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 37557}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 38080}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 38081}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 38761}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 39257}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 45263}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 46086}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 47734}, false)
-
-	// active haste trinkets
-	dk.drwCooldownSync(core.ActionID{ItemID: 36972}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 37558}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 37560}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 37562}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 38070}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 38258}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 38259}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 38764}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 40531}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 43836}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 45466}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 46088}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 48722}, false)
-	dk.drwCooldownSync(core.ActionID{ItemID: 50260}, false)
-}
-
-func (dk *DpsDeathknight) drwCooldownSync(actionID core.ActionID, isPotion bool) {
-	if majorCd := dk.Character.GetMajorCooldown(actionID); majorCd != nil {
-
-		majorCd.ShouldActivate = func(sim *core.Simulation, character *core.Character) bool {
-			if character != &dk.Character {
-				return true
-			}
-			// Opener use everything
-			if sim.CurrentTime < 2*time.Second {
-				return true
-			}
-			// If the fight is long enough for Unholy Frenzy we use potion with it
-			if isPotion && dk.br.activatingDrw && sim.Duration > 200*time.Second {
-				if dk.UnholyFrenzy.IsReady(sim) || dk.UnholyFrenzyAura.IsActive() {
-					return true
-				}
-				return false
-			}
-			if dk.br.activatingDrw {
-				return true
-			}
-
-			if dk.DancingRuneWeapon.CD.TimeToReady(sim) > majorCd.Spell.CD.Duration && !isPotion {
-				return true
-			}
-			if !dk.DancingRuneWeapon.IsReady(sim) && dk.DancingRuneWeapon.CD.ReadyAt() > sim.Duration {
-				return true
-			}
-
-			return false
-		}
-
-		dk.br.drwSnapshot.AddMajorCooldown(majorCd)
-	}
 }
 
 func (dk *DpsDeathknight) Reset(sim *core.Simulation) {
 	dk.Deathknight.Reset(sim)
-
-	dk.sr.Reset(sim)
-	dk.br.Reset(sim)
-	dk.fr.Reset(sim)
-	dk.ur.Reset(sim)
-
-	dk.RotationSequence.Clear()
-	dk.rotationSetup()
-
 	dk.Presence = deathknight.UnsetPresence
-
-	b, f, u := deathknight.PointsInTalents(dk.Talents)
-
-	if dk.IsUsingAPL {
-		return
-	}
-
-	if f > u && f > b {
-		if dk.Rotation.Presence == proto.Deathknight_Rotation_Blood {
-			dk.ChangePresence(sim, deathknight.BloodPresence)
-		} else if dk.Rotation.Presence == proto.Deathknight_Rotation_Frost {
-			dk.ChangePresence(sim, deathknight.FrostPresence)
-		} else if dk.Rotation.Presence == proto.Deathknight_Rotation_Unholy {
-			dk.ChangePresence(sim, deathknight.UnholyPresence)
-		}
-	}
-
-	if u > f && u > b {
-		if dk.Rotation.StartingPresence == proto.Deathknight_Rotation_Unholy {
-			dk.ChangePresence(sim, deathknight.UnholyPresence)
-		} else if dk.Talents.SummonGargoyle {
-			dk.ChangePresence(sim, deathknight.BloodPresence)
-		}
-	}
-
-	if b > f && b > u {
-		dk.ChangePresence(sim, deathknight.BloodPresence)
-	}
 }
diff --git a/sim/deathknight/dps/rotation_blood.go b/sim/deathknight/dps/rotation_blood.go
deleted file mode 100644
index 4b675587b5..0000000000
--- a/sim/deathknight/dps/rotation_blood.go
+++ /dev/null
@@ -1,357 +0,0 @@
-package dps
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-	"github.com/wowsims/wotlk/sim/deathknight"
-)
-
-func (dk *DpsDeathknight) setupBloodRotations() {
-	dk.Inputs.FuStrike = deathknight.FuStrike_DeathStrike
-	if dk.Talents.Annihilation > 0 {
-		dk.Inputs.FuStrike = deathknight.FuStrike_Obliterate
-	}
-
-	switch dk.Rotation.BloodSpender {
-	case proto.Deathknight_Rotation_HS:
-		dk.br.bloodSpell = dk.HeartStrike
-		if !dk.Talents.HeartStrike {
-			dk.br.bloodSpell = dk.BloodStrike
-		}
-	case proto.Deathknight_Rotation_BB:
-		dk.br.bloodSpell = dk.BloodBoil
-	case proto.Deathknight_Rotation_BS:
-		dk.br.bloodSpell = dk.BloodStrike
-	}
-
-	if dk.Rotation.UseDancingRuneWeapon && dk.Talents.DancingRuneWeapon {
-		dk.setupDrwCooldowns()
-	}
-
-	dk.RotationSequence.Clear().
-		NewAction(dk.RotationActionCallback_IT).
-		NewAction(dk.RotationActionCallback_PS).
-		NewAction(dk.RotationActionBL_FU).
-		NewAction(dk.blBloodRuneAction()).
-		NewAction(dk.RotationActionBL_BS).
-		NewAction(dk.RotationActionCallback_ERW).
-		NewAction(dk.RotationActionCallback_RD).
-		NewAction(dk.RotationActionBL_DRW_Custom)
-
-	if dk.Rotation.UseDancingRuneWeapon && dk.sr.hasGod && dk.Rotation.DrwDiseases == proto.Deathknight_Rotation_Pestilence {
-		dk.RotationSequence.
-			NewAction(dk.RotationActionBL_FU).
-			NewAction(dk.RotationActionCallback_Pesti).
-			NewAction(dk.RotationActionCallback_BT).
-			NewAction(dk.blBloodRuneAction()).
-			NewAction(dk.RotationActionBL_BS).
-			NewAction(dk.RotationActionBL_BS).
-			NewAction(dk.RotationActionBL_BS)
-	} else if dk.Rotation.UseDancingRuneWeapon && dk.Rotation.DrwDiseases == proto.Deathknight_Rotation_Normal {
-		dk.RotationSequence.
-			NewAction(dk.RotationActionCallback_IT).
-			NewAction(dk.RotationActionCallback_PS).
-			NewAction(dk.blBloodRuneAction()).
-			NewAction(dk.RotationActionCallback_BT).
-			NewAction(dk.RotationActionBL_BS).
-			NewAction(dk.RotationActionBL_BS).
-			NewAction(dk.RotationActionBL_BS).
-			NewAction(dk.RotationActionBL_BS)
-	} else {
-		dk.RotationSequence.
-			NewAction(dk.RotationActionBL_FU).
-			NewAction(dk.RotationActionBL_BS).
-			NewAction(dk.RotationActionCallback_BT).
-			NewAction(dk.RotationActionBL_BS).
-			NewAction(dk.RotationActionBL_BS).
-			NewAction(dk.RotationActionBL_BS).
-			NewAction(dk.RotationActionBL_BS)
-	}
-
-	dk.RotationSequence.NewAction(dk.RotationActionBL_BloodRotation)
-}
-
-func (dk *DpsDeathknight) RotationActionBL_BloodRotation(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := false
-
-	if !dk.blDiseaseCheck(sim, target, dk.RaiseDead, true, 1) {
-		dk.blRecastDiseasesSequence(sim)
-		return sim.CurrentTime
-	} else if dk.blDrwCheck(sim, target, 100*time.Millisecond) {
-		dk.blAfterDrwSequence(sim)
-		return sim.CurrentTime
-	}
-
-	if dk.blBloodTapCheck(sim, target) {
-		return sim.CurrentTime
-	}
-
-	if dk.RaiseDead.CanCast(sim, nil) && sim.GetRemainingDuration() >= time.Second*30 {
-		if dk.Talents.DancingRuneWeapon && dk.Rotation.UseDancingRuneWeapon && (dk.DancingRuneWeapon.IsReady(sim) || dk.DancingRuneWeapon.TimeToReady(sim) < 5*time.Second) {
-			// Use CDs
-			dk.br.activatingDrw = true
-			dk.br.drwSnapshot.ActivateMajorCooldowns(sim)
-			dk.UpdateMajorCooldowns()
-			dk.br.activatingDrw = false
-		}
-		dk.RaiseDead.Cast(sim, target)
-		return sim.CurrentTime
-	}
-
-	fuStrike := dk.DeathStrike
-	if dk.Inputs.FuStrike == deathknight.FuStrike_Obliterate {
-		fuStrike = dk.Obliterate
-	}
-
-	if !casted {
-		if dk.blDiseaseCheck(sim, target, dk.br.bloodSpell, true, 1) {
-			if dk.shShouldSpreadDisease(sim) {
-				return dk.blSpreadDiseases(sim, target, s)
-			} else {
-				casted = dk.br.bloodSpell.Cast(sim, target)
-			}
-		} else {
-			dk.blRecastDiseasesSequence(sim)
-			return sim.CurrentTime
-		}
-		if !casted {
-			if dk.blDiseaseCheck(sim, target, fuStrike, true, 1) {
-				casted = fuStrike.Cast(sim, target)
-			} else {
-				dk.blRecastDiseasesSequence(sim)
-				return sim.CurrentTime
-			}
-			if !casted {
-				if dk.blDeathCoilCheck(sim) {
-					casted = dk.DeathCoil.Cast(sim, target)
-				}
-				if !casted && dk.HornOfWinter.CanCast(sim, nil) {
-					dk.HornOfWinter.Cast(sim, target)
-				}
-			}
-		}
-	}
-
-	return -1
-}
-
-func (dk *DpsDeathknight) blAfterDrwSequence(sim *core.Simulation) {
-	dk.RotationSequence.Clear()
-
-	if dk.sr.hasGod && dk.Rotation.DrwDiseases == proto.Deathknight_Rotation_Pestilence {
-		dk.RotationSequence.NewAction(dk.RotationActionBL_Pesti_DRW)
-	} else if dk.Rotation.DrwDiseases == proto.Deathknight_Rotation_Normal {
-		dk.RotationSequence.
-			NewAction(dk.RotationActionBL_IT_DRW).
-			NewAction(dk.RotationActionBL_PS_DRW)
-	}
-
-	dk.RotationSequence.
-		NewAction(dk.RotationActionBL_ResetToBloodMain)
-}
-
-func (dk *DpsDeathknight) blRecastDiseasesSequence(sim *core.Simulation) {
-	dk.RotationSequence.Clear()
-
-	// If we have glyph of Disease and both dots active try to refresh with pesti
-	didPesti := false
-	if dk.sr.hasGod {
-		if dk.FrostFeverSpell.Dot(dk.CurrentTarget).IsActive() && dk.BloodPlagueSpell.Dot(dk.CurrentTarget).IsActive() {
-			didPesti = true
-			dk.RotationSequence.NewAction(dk.RotationActionCallback_Pesti_Custom)
-		}
-	}
-
-	// If we did not pesti queue normal dot refresh
-	if !didPesti {
-		dk.RotationSequence.
-			NewAction(dk.RotationActionBL_FF_ClipCheck).
-			NewAction(dk.RotationActionBL_IT_Custom).
-			NewAction(dk.RotationActionBL_BP_ClipCheck).
-			NewAction(dk.RotationActionBL_PS_Custom)
-	}
-
-	dk.RotationSequence.
-		NewAction(dk.RotationActionBL_ResetToBloodMain)
-}
-
-func (dk *DpsDeathknight) RotationActionBL_ResetToBloodMain(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	dk.RotationSequence.Clear().
-		NewAction(dk.RotationActionBL_BloodRotation)
-
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) RotationActionBL_DRW_Snapshot(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	dk.br.activatingDrw = true
-	dk.br.drwSnapshot.ActivateMajorCooldowns(sim)
-	dk.UpdateMajorCooldowns()
-	dk.br.activatingDrw = false
-	s.Advance()
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) RotationActionBL_DRW_Custom(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	if dk.Rotation.UseDancingRuneWeapon && dk.Talents.DancingRuneWeapon {
-		casted := dk.DancingRuneWeapon.Cast(sim, target)
-		if casted {
-			dk.br.drwSnapshot.ResetProcTrackers()
-			dk.br.drwMaxDelay = -1
-		}
-		s.ConditionalAdvance(casted)
-	} else {
-		s.Advance()
-	}
-	return -1
-}
-
-func (dk *DpsDeathknight) RotationActionBL_BS(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	ffRemaining := dk.FrostFeverSpell.Dot(target).RemainingDuration(sim)
-	bpRemaining := dk.BloodPlagueSpell.Dot(target).RemainingDuration(sim)
-	casted := false
-
-	// FF is not active or will drop before Gcd is ready after this cast
-	if dk.sr.hasGod && (!dk.FrostFeverSpell.Dot(target).IsActive() || ffRemaining <= core.GCDDefault || !dk.BloodPlagueSpell.Dot(target).IsActive() || bpRemaining <= core.GCDDefault) {
-		casted = dk.Pestilence.Cast(sim, target)
-	} else {
-		casted = dk.br.bloodSpell.Cast(sim, target)
-	}
-
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func (dk *DpsDeathknight) RotationActionBL_BS_ERW(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := dk.br.bloodSpell.Cast(sim, target)
-	advance := casted && dk.LastOutcome.Matches(core.OutcomeLanded)
-
-	if advance {
-		casted = dk.EmpowerRuneWeapon.Cast(sim, target)
-		advance = casted && advance
-		s.ConditionalAdvance(advance)
-	}
-	return -1
-}
-
-func (dk *DpsDeathknight) RotationActionBL_FU(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := false
-	if dk.Inputs.FuStrike == deathknight.FuStrike_DeathStrike {
-		casted = dk.DeathStrike.Cast(sim, target)
-	} else if dk.Inputs.FuStrike == deathknight.FuStrike_Obliterate {
-		casted = dk.Obliterate.Cast(sim, target)
-	}
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func (dk *DpsDeathknight) RotationActionBL_Pesti_DRW(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := dk.Pestilence.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-
-	if !casted {
-		if dk.Inputs.FuStrike == deathknight.FuStrike_DeathStrike {
-			dk.DeathStrike.Cast(sim, target)
-		} else if dk.Inputs.FuStrike == deathknight.FuStrike_Obliterate {
-			dk.Obliterate.Cast(sim, target)
-		}
-	}
-
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-// Custom PS callback for tracking recasts for pestilence disease sync
-func (dk *DpsDeathknight) RotationActionBL_PS_DRW(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := dk.PlagueStrike.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-
-	if !casted {
-		if dk.Talents.HeartStrike {
-			dk.HeartStrike.Cast(sim, target)
-		} else {
-			dk.BloodStrike.Cast(sim, target)
-		}
-	}
-
-	dk.sr.recastedBP = casted && advance
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-// Custom IT callback for tracking recasts for pestilence disease sync
-func (dk *DpsDeathknight) RotationActionBL_IT_DRW(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := dk.IcyTouch.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-
-	if !casted {
-		if dk.Talents.HeartStrike {
-			dk.HeartStrike.Cast(sim, target)
-		} else {
-			dk.BloodStrike.Cast(sim, target)
-		}
-	}
-
-	dk.sr.recastedFF = casted && advance
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-// Custom PS callback for tracking recasts for pestilence disease sync
-func (dk *DpsDeathknight) RotationActionBL_PS_Custom(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := dk.PlagueStrike.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-
-	dk.sr.recastedBP = casted && advance
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-// Custom IT callback for tracking recasts for pestilence disease sync
-func (dk *DpsDeathknight) RotationActionBL_IT_Custom(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := dk.IcyTouch.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-	dk.sr.recastedFF = casted && advance
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func (dk *DpsDeathknight) RotationActionBL_FF_ClipCheck(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	dot := dk.FrostFeverSpell.Dot(target)
-	gracePeriod := dk.FrostRuneGraceRemaining(sim)
-	return dk.RotationActionBL_DiseaseClipCheck(dot, gracePeriod, sim, target, s)
-}
-
-func (dk *DpsDeathknight) RotationActionBL_BP_ClipCheck(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	dot := dk.BloodPlagueSpell.Dot(target)
-	gracePeriod := dk.UnholyRuneGraceRemaining(sim)
-	return dk.RotationActionBL_DiseaseClipCheck(dot, gracePeriod, sim, target, s)
-}
-
-// Check if we have enough rune grace period to delay the disease cast
-// so we get more ticks without losing on rune cd
-func (dk *DpsDeathknight) RotationActionBL_DiseaseClipCheck(dot *core.Dot, gracePeriod time.Duration, sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	// TODO: Play around with allowing rune cd to be wasted
-	// for more disease ticks and see if its a worth option for the ui
-	//runeCdWaste := 0 * time.Millisecond
-	var waitUntil time.Duration
-	if dot.TickCount < dot.NumberOfTicks-1 {
-		nextTickAt := dot.ExpiresAt() - dot.TickLength*time.Duration((dot.NumberOfTicks-1)-dot.TickCount)
-		if nextTickAt > sim.CurrentTime && (nextTickAt < sim.CurrentTime+gracePeriod || nextTickAt < sim.CurrentTime+400*time.Millisecond) {
-			// Delay disease for next tick
-			dk.LastOutcome = core.OutcomeMiss
-			waitUntil = nextTickAt + 50*time.Millisecond
-		} else {
-			waitUntil = sim.CurrentTime
-		}
-	} else {
-		waitUntil = sim.CurrentTime
-	}
-
-	s.Advance()
-	return waitUntil
-}
diff --git a/sim/deathknight/dps/rotation_blood_helper.go b/sim/deathknight/dps/rotation_blood_helper.go
deleted file mode 100644
index 50e1e93a20..0000000000
--- a/sim/deathknight/dps/rotation_blood_helper.go
+++ /dev/null
@@ -1,213 +0,0 @@
-package dps
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-	"github.com/wowsims/wotlk/sim/deathknight"
-)
-
-type BloodRotation struct {
-	dk *DpsDeathknight
-
-	drwSnapshot *core.SnapshotManager
-	drwMaxDelay time.Duration
-
-	bloodSpell *core.Spell
-
-	activatingDrw bool
-	dsGlyphed     bool
-}
-
-func (br *BloodRotation) Reset(_ *core.Simulation) {
-	br.activatingDrw = false
-	if br.drwSnapshot != nil {
-		br.drwSnapshot.ResetProcTrackers()
-	}
-	br.drwMaxDelay = -1
-}
-
-func (br *BloodRotation) Initialize(dk *DpsDeathknight) {
-	if dk.Talents.DancingRuneWeapon {
-		dk.br.drwSnapshot = core.NewSnapshotManager(dk.GetCharacter())
-		dk.setupDrwProcTrackers()
-	}
-
-	br.dsGlyphed = dk.HasMajorGlyph(proto.DeathknightMajorGlyph_GlyphOfDeathStrike)
-}
-
-func (dk *DpsDeathknight) blBloodRuneAction() deathknight.RotationAction {
-	if dk.Env.GetNumTargets() > 1 {
-		return dk.RotationActionCallback_Pesti
-	} else {
-		return dk.RotationActionBL_BS
-	}
-}
-
-func (dk *DpsDeathknight) blDiseaseCheck(sim *core.Simulation, target *core.Unit, spell *core.Spell, costRunes bool, casts int) bool {
-	// Early exit at end of fight
-	if sim.GetRemainingDuration() < 10*time.Second {
-		return true
-	}
-
-	ffRemaining := dk.FrostFeverSpell.Dot(target).RemainingDuration(sim)
-	bpRemaining := dk.BloodPlagueSpell.Dot(target).RemainingDuration(sim)
-	castGcd := core.GCDDefault * time.Duration(casts)
-
-	// FF is not active or will drop before Gcd is ready after this cast
-	if !dk.FrostFeverSpell.Dot(target).IsActive() || ffRemaining <= castGcd {
-		return false
-	}
-	// BP is not active or will drop before Gcd is ready after this cast
-	if !dk.BloodPlagueSpell.Dot(target).IsActive() || bpRemaining <= castGcd {
-		return false
-	}
-
-	// If the ability we want to cast spends runes we check for possible disease drops
-	// in the time we won't have runes to recast the disease
-	if spell.CanCast(sim, nil) && costRunes {
-		ffExpiresAt := ffRemaining + sim.CurrentTime
-		bpExpiresAt := bpRemaining + sim.CurrentTime
-
-		afterCastTime := sim.CurrentTime + castGcd
-		if ffExpiresAt <= afterCastTime || bpExpiresAt <= afterCastTime {
-			return false
-		}
-
-		spellCost := dk.OptimalRuneCost(core.RuneCost(spell.DefaultCast.Cost))
-
-		crpb := dk.Predictor()
-		crpb.SpendRuneCost(sim, spellCost)
-
-		if dk.sr.hasGod {
-			currentBloodRunes := crpb.CurrentBloodRunes()
-			nextBloodRuneAt := crpb.BloodRuneReadyAt(sim)
-
-			// If FF is gonna drop while our runes are on CD
-			if dk.shRecastAvailableCheck(ffExpiresAt, afterCastTime, int(spellCost.Blood()), int32(currentBloodRunes), nextBloodRuneAt) {
-				return false
-			}
-
-			// If BP is gonna drop while our runes are on CD
-			if dk.shRecastAvailableCheck(bpExpiresAt, afterCastTime, int(spellCost.Blood()), int32(currentBloodRunes), nextBloodRuneAt) {
-				return false
-			}
-		} else {
-			currentFrostRunes := crpb.CurrentFrostRunes()
-			currentUnholyRunes := crpb.CurrentUnholyRunes()
-			nextFrostRuneAt := crpb.FrostRuneReadyAt(sim)
-			nextUnholyRuneAt := crpb.UnholyRuneReadyAt(sim)
-
-			// If FF is gonna drop while our runes are on CD
-			if dk.shRecastAvailableCheck(ffExpiresAt, afterCastTime, int(spellCost.Frost()), int32(currentFrostRunes), nextFrostRuneAt) {
-				return false
-			}
-
-			// If BP is gonna drop while our runes are on CD
-			if dk.shRecastAvailableCheck(bpExpiresAt, afterCastTime, int(spellCost.Unholy()), int32(currentUnholyRunes), nextUnholyRuneAt) {
-				return false
-			}
-		}
-	}
-
-	return true
-}
-
-func (dk *DpsDeathknight) blSpreadDiseases(sim *core.Simulation, target *core.Unit, _ *deathknight.Sequence) time.Duration {
-	if dk.blDiseaseCheck(sim, target, dk.Pestilence, true, 1) {
-		casted := dk.Pestilence.Cast(sim, target)
-		landed := dk.LastOutcome.Matches(core.OutcomeLanded)
-
-		// Reset flags on succesfull cast
-		dk.sr.recastedFF = !(casted && landed)
-		dk.sr.recastedBP = !(casted && landed)
-		return -1
-	} else {
-		dk.blRecastDiseasesSequence(sim)
-		return sim.CurrentTime
-	}
-}
-
-// Save up Runic Power for DRW - Allow casts above 100 RP when DRW is ready or above 65 (for death strike glyph) when not
-func (dk *DpsDeathknight) blDeathCoilCheck(sim *core.Simulation) bool {
-	canCastDrw := dk.Talents.DancingRuneWeapon && dk.DancingRuneWeapon != nil && (dk.DancingRuneWeapon.IsReady(sim) || dk.DancingRuneWeapon.CD.TimeToReady(sim) < 5*time.Second)
-	currentRP := dk.CurrentRunicPower()
-	willCastDS := dk.CurrentFrostRunes() > 0 && dk.CurrentUnholyRunes() > 1 && dk.CurrentBloodRunes() == 0
-	return (!canCastDrw && currentRP >= float64(core.TernaryInt(dk.br.dsGlyphed && willCastDS, 65, 40))) || (canCastDrw && currentRP >= 100)
-}
-
-func (dk *DpsDeathknight) blBloodTapCheck(sim *core.Simulation, target *core.Unit) bool {
-	if dk.CurrentBloodRunes() > 0 {
-		return false
-	}
-
-	if (!dk.Talents.DancingRuneWeapon || dk.RuneWeapon.IsEnabled()) && dk.BloodTap.IsReady(sim) {
-		return dk.BloodTap.Cast(sim, target)
-	}
-
-	return false
-}
-
-// Combined checks for casting gargoyle sequence & going back to blood presence after
-func (dk *DpsDeathknight) blDrwCheck(sim *core.Simulation, target *core.Unit, castTime time.Duration) bool {
-	if dk.blDrwCanCast(sim, castTime) {
-
-		dk.br.activatingDrw = true
-		dk.br.drwSnapshot.ActivateMajorCooldowns(sim)
-		dk.UpdateMajorCooldowns()
-		dk.br.activatingDrw = false
-
-		if dk.DancingRuneWeapon.Cast(sim, target) {
-			dk.br.drwSnapshot.ResetProcTrackers()
-			dk.br.drwMaxDelay = -1
-		}
-		return true
-	}
-
-	return false
-}
-
-func (dk *DpsDeathknight) blDrwCanCast(sim *core.Simulation, castTime time.Duration) bool {
-	if !dk.Talents.DancingRuneWeapon {
-		return false
-	}
-	if !dk.Rotation.UseDancingRuneWeapon {
-		return false
-	}
-	if !dk.DancingRuneWeapon.IsReady(sim) {
-		return false
-	}
-	if dk.CurrentRunicPower() < 60 {
-		return false
-	}
-	// Setup max delay possible
-	if dk.br.drwMaxDelay == -1 {
-		drwCd := dk.DancingRuneWeapon.CD.Duration
-		timeLeft := sim.GetRemainingDuration()
-		for timeLeft > drwCd {
-			timeLeft = timeLeft - (drwCd + time.Second)
-		}
-		dk.br.drwMaxDelay = timeLeft - time.Second
-	}
-	// Cast it if holding will result in less total DRWs for the encounter
-	if sim.CurrentTime > dk.br.drwMaxDelay {
-		return true
-	}
-	// Cast it if holding will take from its duration
-	if sim.GetRemainingDuration() < 20*time.Second {
-		return true
-	}
-	// Make sure we can instantly put diseases up with the rune weapon
-	if !dk.sr.hasGod && dk.Rotation.DrwDiseases == proto.Deathknight_Rotation_Normal && (dk.CurrentFrostRunes() < 1 || dk.CurrentUnholyRunes() < 1) {
-		return false
-	}
-	if dk.sr.hasGod && dk.Rotation.DrwDiseases == proto.Deathknight_Rotation_Pestilence && dk.CurrentBloodRunes() < 1 {
-		return false
-	}
-	if !dk.br.drwSnapshot.CanSnapShot(sim, castTime) {
-		return false
-	}
-
-	return true
-}
diff --git a/sim/deathknight/dps/rotation_frost.go b/sim/deathknight/dps/rotation_frost.go
deleted file mode 100644
index 04c11d34ee..0000000000
--- a/sim/deathknight/dps/rotation_frost.go
+++ /dev/null
@@ -1,203 +0,0 @@
-package dps
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/deathknight"
-)
-
-type FrostRotation struct {
-	oblitCount int32
-
-	// CDS
-	hyperSpeedMCD           *core.MajorCooldown
-	stoneformMCD            *core.MajorCooldown
-	bloodfuryMCD            *core.MajorCooldown
-	berserkingMCD           *core.MajorCooldown
-	potionOfSpeedMCD        *core.MajorCooldown
-	indestructiblePotionMCD *core.MajorCooldown
-	potionUsed              bool
-
-	onUseTrinkets []*core.MajorCooldown
-
-	oblitRPRegen float64
-
-	fuSpellPriority []*core.Spell
-	bloodSpell      *core.Spell
-}
-
-func (fr *FrostRotation) Initialize(dk *DpsDeathknight) {
-	fr.oblitRPRegen = core.TernaryFloat64(dk.HasSetBonus(deathknight.ItemSetScourgeborneBattlegear, 4), 25.0, 20.0)
-	fr.onUseTrinkets = make([]*core.MajorCooldown, 0)
-
-	if dk.Env.GetNumTargets() > 2 {
-		fr.fuSpellPriority = []*core.Spell{dk.HowlingBlast, dk.Obliterate}
-		fr.bloodSpell = dk.BloodBoil
-	} else {
-		fr.fuSpellPriority = []*core.Spell{dk.Obliterate}
-		fr.bloodSpell = dk.BloodStrike
-	}
-}
-
-func (fr *FrostRotation) Reset(_ *core.Simulation) {
-	fr.oblitCount = 0
-
-	fr.hyperSpeedMCD = nil
-	fr.stoneformMCD = nil
-	fr.bloodfuryMCD = nil
-	fr.berserkingMCD = nil
-	fr.potionOfSpeedMCD = nil
-	fr.indestructiblePotionMCD = nil
-	fr.onUseTrinkets = nil
-	fr.potionUsed = false
-}
-
-func (dk *DpsDeathknight) addOnUseTrinketCooldown(actionID core.ActionID) {
-	if majorCd := dk.Character.GetMajorCooldown(actionID); majorCd != nil {
-		majorCd.Disable()
-		dk.fr.onUseTrinkets = append(dk.fr.onUseTrinkets, majorCd)
-	}
-}
-
-func (dk *DpsDeathknight) getMajorCooldown(actionID core.ActionID) *core.MajorCooldown {
-	if majorCd := dk.Character.GetMajorCooldown(actionID); majorCd != nil {
-		majorCd.Disable()
-		return majorCd
-	}
-	return nil
-}
-
-func (dk *DpsDeathknight) setupUnbreakableArmorCooldowns() {
-	fr := &dk.fr
-
-	// hyperspeed accelerators
-	fr.hyperSpeedMCD = dk.getMajorCooldown(core.ActionID{SpellID: 54758})
-
-	// stoneform (dwarf)
-	fr.stoneformMCD = dk.getMajorCooldown(core.ActionID{SpellID: 20594})
-
-	// bloodfury (orc)
-	fr.bloodfuryMCD = dk.getMajorCooldown(core.ActionID{SpellID: 33697})
-
-	// berserking (troll)
-	fr.berserkingMCD = dk.getMajorCooldown(core.ActionID{SpellID: 26297})
-
-	// potion of speed
-	fr.potionOfSpeedMCD = dk.getMajorCooldown(core.ActionID{ItemID: 40211})
-
-	// indestructible potion
-	fr.indestructiblePotionMCD = dk.getMajorCooldown(core.ActionID{ItemID: 40093})
-
-	// On use trinkets
-	dk.addOnUseTrinketCooldown(core.ActionID{ItemID: 40531}) // Mark of nogganon
-	dk.addOnUseTrinketCooldown(core.ActionID{ItemID: 37166}) // Sphere of Red Dragon's Blood
-	dk.addOnUseTrinketCooldown(core.ActionID{ItemID: 37723}) // Incisor Fragment
-	dk.addOnUseTrinketCooldown(core.ActionID{ItemID: 39257}) // Loatheb's Shadow
-	dk.addOnUseTrinketCooldown(core.ActionID{ItemID: 44014}) // Fezzik's Pocketwatch
-}
-
-func (dk *DpsDeathknight) castMajorCooldown(mcd *core.MajorCooldown, sim *core.Simulation, target *core.Unit) {
-	if mcd != nil {
-		if mcd.Spell.IsReady(sim) && (dk.GCD.IsReady(sim) || mcd.Spell.DefaultCast.GCD == 0) {
-			mcd.Spell.Cast(sim, target)
-		}
-	}
-}
-
-func (dk *DpsDeathknight) castMajorCooldownConditional(mcd *core.MajorCooldown, conditionalMCDs []*core.MajorCooldown, sim *core.Simulation, target *core.Unit) {
-	if mcd != nil && !dk.fr.potionUsed {
-		if mcd.Spell.IsReady(sim) && dk.GCD.IsReady(sim) {
-			allReady := true
-			for i := range conditionalMCDs {
-				if conditionalMCDs[i] != nil && allReady {
-					spell := conditionalMCDs[i].Spell
-					allReady = allReady && spell.CD.IsReady(sim)
-					//// TODO: Find a way to get the racial aura durations
-					if !allReady {
-						if dk.Env.Encounter.Duration < time.Duration(float64(spell.CD.ReadyAt())*1.25) {
-							allReady = true
-						}
-					}
-				}
-			}
-
-			if allReady {
-				mcd.Spell.Cast(sim, target)
-
-				dk.fr.potionUsed = true
-			}
-		}
-	}
-}
-
-func (dk *DpsDeathknight) castAllMajorCooldowns(sim *core.Simulation) {
-	fr := &dk.fr
-	target := dk.CurrentTarget
-
-	racialConditionals := []*core.MajorCooldown{fr.bloodfuryMCD, fr.berserkingMCD, fr.stoneformMCD}
-
-	dk.castMajorCooldownConditional(fr.potionOfSpeedMCD, racialConditionals, sim, target)
-	dk.castMajorCooldownConditional(fr.indestructiblePotionMCD, racialConditionals, sim, target)
-	dk.castMajorCooldown(fr.hyperSpeedMCD, sim, target)
-	dk.castMajorCooldown(fr.stoneformMCD, sim, target)
-	dk.castMajorCooldown(fr.bloodfuryMCD, sim, target)
-	dk.castMajorCooldown(fr.berserkingMCD, sim, target)
-
-	for _, trinket := range fr.onUseTrinkets {
-		dk.castMajorCooldown(trinket, sim, target)
-	}
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_UA_Frost(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	if dk.UnbreakableArmor != nil {
-		if !dk.LeftBloodRuneReady() && dk.BloodTap.CanCast(sim, nil) {
-			dk.BloodTap.Cast(sim, nil)
-		}
-		casted := dk.UnbreakableArmor.Cast(sim, target)
-
-		if casted {
-			dk.castAllMajorCooldowns(sim)
-			s.ConditionalAdvance(casted)
-			return sim.CurrentTime
-		} else {
-			s.ConditionalAdvance(casted)
-			return -1
-		}
-	} else {
-		casted := dk.BloodStrike.Cast(sim, target)
-		if casted {
-			dk.castAllMajorCooldowns(sim)
-			s.ConditionalAdvance(casted)
-			return sim.CurrentTime
-		} else {
-			s.ConditionalAdvance(casted)
-			return -1
-		}
-	}
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_Frost_FS_HB(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	if dk.FreezingFogAura.IsActive() && dk.Talents.HowlingBlast {
-		dk.HowlingBlast.Cast(sim, target)
-	} else if dk.Talents.FrostStrike {
-		dk.FrostStrike.Cast(sim, target)
-	}
-
-	s.Advance()
-	return -1
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_Frost_Pesti_ERW(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	// Casts Pesti then ERW in the same GCD (rather than chaining them sequentially which will cause ERW to be delayed by pesti GCD)
-	// This is a DPS increase since it allows rune grace to start as soon as possible
-	casted := dk.Pestilence.Cast(sim, target)
-	advance := casted && dk.LastOutcome.Matches(core.OutcomeLanded)
-
-	if advance {
-		casted = dk.EmpowerRuneWeapon.Cast(sim, target)
-		advance = casted && advance
-		s.ConditionalAdvance(advance)
-	}
-	return -1
-}
diff --git a/sim/deathknight/dps/rotation_frost_aoe.go b/sim/deathknight/dps/rotation_frost_aoe.go
deleted file mode 100644
index a0749e8ece..0000000000
--- a/sim/deathknight/dps/rotation_frost_aoe.go
+++ /dev/null
@@ -1,185 +0,0 @@
-package dps
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/common"
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-	"github.com/wowsims/wotlk/sim/deathknight"
-)
-
-func (dk *DpsDeathknight) setupCustomRotations() {
-	dk.RotationSequence.NewAction(func(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-		if dk.CustomRotation != nil {
-			if !dk.CustomRotation.Cast(sim) {
-				return -1
-			}
-		} else {
-			dk.LastCast = nil
-		}
-
-		if dk.LastCast == dk.EmpowerRuneWeapon || dk.LastCast == dk.BloodTap || dk.LastCast == dk.UnbreakableArmor {
-			return sim.CurrentTime
-		}
-
-		return -1
-	})
-}
-
-func (dk *DpsDeathknight) makeCustomRotation() *common.CustomRotation {
-	return common.NewCustomRotation(dk.Rotation.FrostCustomRotation, dk.GetCharacter(), map[int32]common.CustomSpell{
-		int32(proto.Deathknight_Rotation_CustomIcyTouch): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := dk.IcyTouch.CurCast.Cost
-				return dk.IcyTouch.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return !dk.FrostFeverSpell.Dot(dk.CurrentTarget).IsActive() && dk.IcyTouch.CanCast(sim, nil)
-			},
-		},
-		int32(proto.Deathknight_Rotation_CustomPlagueStrike): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := dk.PlagueStrike.CurCast.Cost
-				return dk.PlagueStrike.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return !dk.BloodPlagueSpell.Dot(dk.CurrentTarget).IsActive() && dk.PlagueStrike.CanCast(sim, nil)
-			},
-		},
-		int32(proto.Deathknight_Rotation_CustomPestilence): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := dk.Pestilence.CurCast.Cost
-				return dk.Pestilence.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				if !dk.Pestilence.CanCast(sim, nil) {
-					return false
-				}
-
-				ff := dk.FrostFeverSpell.Dot(dk.CurrentTarget).ExpiresAt() - sim.CurrentTime
-				bp := dk.BloodPlagueSpell.Dot(dk.CurrentTarget).ExpiresAt() - sim.CurrentTime
-				ffHalfDuration := time.Duration(0.5 * float64(dk.FrostFeverSpell.Dot(dk.CurrentTarget).Duration))
-				bpHalfDuration := time.Duration(0.5 * float64(dk.BloodPlagueSpell.Dot(dk.CurrentTarget).Duration))
-				if ff <= 2*time.Second && bp <= 2*time.Second && sim.GetRemainingDuration() >= ffHalfDuration && sim.GetRemainingDuration() >= bpHalfDuration {
-					return true
-				}
-
-				numHits := dk.Env.GetNumTargets()
-				numDiseased := numHits
-				for i := int32(0); i < numHits; i++ {
-					target := &dk.Env.GetTarget(i).Unit
-					diseases := dk.FrostFeverSpell.Dot(target).IsActive() && dk.BloodPlagueSpell.Dot(target).IsActive()
-
-					if !diseases {
-						numDiseased--
-					}
-				}
-
-				return float64(numDiseased)/float64(numHits) <= 0.5
-			},
-		},
-		int32(proto.Deathknight_Rotation_CustomObliterate): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := dk.Obliterate.CurCast.Cost
-				if dk.Deathchill != nil && dk.Deathchill.IsReady(sim) {
-					dk.Deathchill.Cast(sim, target)
-				}
-				return dk.Obliterate.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return dk.Obliterate.CanCast(sim, nil)
-			},
-		},
-		int32(proto.Deathknight_Rotation_CustomHowlingBlast): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := dk.HowlingBlast.CurCast.Cost
-				return dk.HowlingBlast.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return dk.HowlingBlast.CanCast(sim, nil)
-			},
-		},
-		int32(proto.Deathknight_Rotation_CustomHowlingBlastRime): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := dk.HowlingBlast.CurCast.Cost
-				return dk.HowlingBlast.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return dk.HowlingBlast.CanCast(sim, nil) && dk.FreezingFogAura.IsActive()
-			},
-		},
-		int32(proto.Deathknight_Rotation_CustomBloodBoil): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := dk.BloodBoil.CurCast.Cost
-				return dk.BloodBoil.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return dk.BloodBoil.CanCast(sim, nil)
-			},
-		},
-		int32(proto.Deathknight_Rotation_CustomBloodStrike): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := dk.BloodStrike.CurCast.Cost
-				return dk.BloodStrike.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return dk.BloodStrike.CanCast(sim, nil)
-			},
-		},
-		int32(proto.Deathknight_Rotation_CustomDeathAndDecay): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := dk.DeathAndDecay.CurCast.Cost
-				return dk.DeathAndDecay.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return dk.DeathAndDecay.CanCast(sim, nil)
-			},
-		},
-		int32(proto.Deathknight_Rotation_CustomHornOfWinter): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := dk.HornOfWinter.CurCast.Cost
-				return dk.HornOfWinter.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return dk.HornOfWinter.CanCast(sim, nil)
-			},
-		},
-		int32(proto.Deathknight_Rotation_CustomUnbreakableArmor): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := dk.UnbreakableArmor.CurCast.Cost
-				return dk.UnbreakableArmor.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return dk.UnbreakableArmor.CanCast(sim, nil)
-			},
-		},
-		int32(proto.Deathknight_Rotation_CustomBloodTap): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := dk.BloodTap.CurCast.Cost
-				return dk.BloodTap.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return dk.BloodTap.CanCast(sim, nil)
-			},
-		},
-		int32(proto.Deathknight_Rotation_CustomEmpoweredRuneWeapon): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := dk.EmpowerRuneWeapon.CurCast.Cost
-				return dk.EmpowerRuneWeapon.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return dk.EmpowerRuneWeapon.CanCast(sim, nil)
-			},
-		},
-		int32(proto.Deathknight_Rotation_CustomFrostStrike): {
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				cost := dk.FrostStrike.CurCast.Cost
-				return dk.FrostStrike.Cast(sim, target), cost
-			},
-			Condition: func(sim *core.Simulation) bool {
-				return dk.FrostStrike.CanCast(sim, nil)
-			},
-		},
-	})
-}
diff --git a/sim/deathknight/dps/rotation_frost_helper.go b/sim/deathknight/dps/rotation_frost_helper.go
deleted file mode 100644
index 37bd960337..0000000000
--- a/sim/deathknight/dps/rotation_frost_helper.go
+++ /dev/null
@@ -1,259 +0,0 @@
-package dps
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/deathknight"
-)
-
-func (dk *DpsDeathknight) canCastFrostUnholySpell(sim *core.Simulation, target *core.Unit) bool {
-	for _, spell := range dk.fr.fuSpellPriority {
-		if spell.CanCast(sim, target) {
-			return true
-		}
-	}
-	return false
-}
-
-func (dk *DpsDeathknight) castFrostUnholySpell(sim *core.Simulation, target *core.Unit) bool {
-	for _, spell := range dk.fr.fuSpellPriority {
-		if spell.CanCast(sim, target) {
-			return spell.Cast(sim, target)
-		}
-	}
-	return false
-}
-
-func (dk *DpsDeathknight) canCastBloodSpell(sim *core.Simulation, target *core.Unit) bool {
-	return dk.fr.bloodSpell.CanCast(sim, target)
-}
-
-func (dk *DpsDeathknight) castBloodSpell(sim *core.Simulation, target *core.Unit) bool {
-	return dk.fr.bloodSpell.Cast(sim, target)
-}
-
-// end of fight oblit does not check diseases, it just presses it regardless, but will retry if fails to land.
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubUnh_EndOfFight_Obli(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := false
-	advance := true
-	waitTime := time.Duration(-1)
-	if dk.canCastFrostUnholySpell(sim, target) {
-		if dk.Deathchill != nil && dk.Deathchill.IsReady(sim) {
-			dk.Deathchill.Cast(sim, target)
-		}
-		casted = dk.castFrostUnholySpell(sim, target)
-		advance = dk.LastOutcome.Matches(core.OutcomeLanded)
-	}
-	s.ConditionalAdvance(casted && advance)
-	return core.TernaryDuration(casted, -1, waitTime)
-}
-
-func (dk *DpsDeathknight) RegularPrioPickSpell(sim *core.Simulation, _ *core.Unit, untilTime time.Duration) *core.Spell {
-	abGcd := 1500 * time.Millisecond
-	spGcd := dk.SpellGCD()
-	canCastAbility := sim.CurrentTime+abGcd <= untilTime
-	canCastSpell := sim.CurrentTime+spGcd <= untilTime
-
-	km := dk.KillingMachineAura.IsActive()
-	rime := dk.FreezingFogAura.IsActive()
-	if canCastSpell && dk.RaiseDead.CanCast(sim, nil) && sim.GetRemainingDuration() >= time.Second*30 {
-		return dk.RaiseDead
-	} else if canCastSpell && dk.HowlingBlast.CanCast(sim, nil) && rime {
-		return dk.HowlingBlast
-	} else if canCastAbility && dk.FrostStrike.CanCast(sim, nil) && km {
-		return dk.FrostStrike
-	} else if canCastAbility && dk.FrostStrike.CanCast(sim, nil) && dk.CurrentRunicPower() >= 100.0 {
-		return dk.FrostStrike
-	} else if canCastAbility && dk.FrostStrike.CanCast(sim, nil) {
-		return dk.FrostStrike
-	} else if canCastSpell && dk.HornOfWinter.CanCast(sim, nil) {
-		return dk.HornOfWinter
-	} else {
-		return nil
-	}
-}
-
-//end of fight functions
-
-func (dk *DpsDeathknight) RotationActionCallback_EndOfFightCheck(sim *core.Simulation, _ *core.Unit, s *deathknight.Sequence) time.Duration {
-	simDur := sim.CurrentTime + sim.GetRemainingDuration()
-
-	if sim.CurrentTime+7000*time.Millisecond > simDur {
-		s.Clear().NewAction(dk.RotationActionCallback_EndOfFightPrio)
-	} else {
-		s.Advance()
-	}
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_EndOfFightPrio(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	simDur := sim.CurrentTime + sim.GetRemainingDuration()
-	simTimeLeft := sim.GetRemainingDuration()
-	ffExpiresAt := dk.FrostFeverSpell.Dot(target).ExpiresAt()
-	bpExpiresAt := dk.BloodPlagueSpell.Dot(target).ExpiresAt()
-	diseaseExpiresAt := min(ffExpiresAt, bpExpiresAt)
-	abGcd := 1500 * time.Millisecond
-	spGcd := dk.SpellGCD()
-	frAt := dk.NormalFrostRuneReadyAt(sim)
-	uhAt := dk.NormalUnholyRuneReadyAt(sim)
-	obAt := max(frAt, uhAt)
-	fsCost := float64(core.RuneCost(dk.FrostStrike.CurCast.Cost).RunicPower())
-	bothblAt := dk.BloodDeathRuneBothReadyAt()
-	hasRime := dk.FreezingFogAura.IsActive() && dk.Talents.HowlingBlast
-
-	if bothblAt == core.NeverExpires {
-		bothblAt = 1
-	}
-	if bothblAt == -1 {
-		bothblAt = core.NeverExpires
-	}
-
-	if dk.Talents.Epidemic == 2 || diseaseExpiresAt >= simDur {
-		obAt = min(obAt, bothblAt)
-	}
-
-	if diseaseExpiresAt >= simDur { //diseases last until end of fight
-		if sim.CurrentTime >= obAt { //have runes to oblit
-			s.Clear().
-				NewAction(dk.RotationActionCallback_FrostSubUnh_EndOfFight_Obli).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if spGcd > simTimeLeft && (obAt <= simDur || bothblAt <= simDur) { //cant fit a spell GCD before end of fight, and will be able to oblit
-			s.Clear().
-				NewAction(dk.RotationActionCallback_FrostSubUnh_EndOfFight_Obli).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if spGcd < simTimeLeft && abGcd > simTimeLeft && obAt < simDur && hasRime { //can fit a spell but not melee ability before last GCD of fight and have rime
-			s.Clear().
-				NewAction(dk.RotationActionCallback_HB).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if (abGcd > simTimeLeft || sim.CurrentTime+abGcd > obAt) && obAt < simDur { //oblit can be used, it's the last GCD or it goes over oblit
-			s.Clear().
-				NewAction(dk.RotationActionCallback_FrostSubUnh_EndOfFight_Obli).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if dk.CurrentRunicPower() >= fsCost && sim.CurrentTime+abGcd < obAt { //can FS and wont cross oblit
-			s.Clear().
-				NewAction(dk.RotationActionCallback_FS).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if hasRime && sim.CurrentTime+spGcd < obAt { //can rime and wont cross oblit
-			s.Clear().
-				NewAction(dk.RotationActionCallback_HB).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if (dk.CurrentBloodRunes() >= 1 || dk.CurrentDeathRunes() == 1) && (sim.CurrentTime+abGcd < obAt) { //have runes for BS, and it cant be used for oblit instead
-			s.Clear().
-				NewAction(dk.RotationActionCallback_BS).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if dk.HornOfWinter.IsReady(sim) && sim.CurrentTime+spGcd < obAt && simTimeLeft > spGcd { //can horn and wont cross oblit
-			s.Clear().
-				NewAction(dk.RotationActionCallback_HW).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else { //cant do anything, wait to oblit
-			s.Clear().
-				NewAction(dk.RotationActionCallback_FrostSubUnh_EndOfFight_Obli).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		}
-	} else if diseaseExpiresAt >= simDur-abGcd { //disease expires less than 1 gcd before end of fight
-		if sim.CurrentTime >= obAt || (sim.CurrentTime >= bothblAt && dk.CurrentDeathRunes() >= 2) { //have runes to oblit, will not refresh diseases
-			s.Clear().
-				NewAction(dk.RotationActionCallback_FrostSubUnh_EndOfFight_Obli).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if spGcd > simTimeLeft && obAt < simDur { //cant fit a spell GCD before end of fight, and will be able to oblit
-			s.Clear().
-				NewAction(dk.RotationActionCallback_FrostSubUnh_EndOfFight_Obli).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if spGcd < simTimeLeft && abGcd > simTimeLeft && obAt < simDur && hasRime { //can fit a spell but not melee ability before last GCD of fight and have rime
-			s.Clear().
-				NewAction(dk.RotationActionCallback_HB).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if abGcd > simTimeLeft && obAt < simDur { //oblit can be used, it's the last GCD or it goes over oblit
-			s.Clear().
-				NewAction(dk.RotationActionCallback_FrostSubUnh_EndOfFight_Obli).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if dk.CurrentRunicPower() >= fsCost && sim.CurrentTime+abGcd < obAt { //can FS and wont cross oblit
-			s.Clear().
-				NewAction(dk.RotationActionCallback_FS).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if hasRime && sim.CurrentTime+spGcd < obAt { //can rime and wont cross oblit
-			s.Clear().
-				NewAction(dk.RotationActionCallback_HB).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if dk.CurrentBloodRunes() >= 1 || sim.CurrentTime < diseaseExpiresAt { //can pesti and diseases are still up, this serves to pesti if there's nothing more valuable to press
-			s.Clear().
-				NewAction(dk.RotationActionCallback_Pesti).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if dk.HornOfWinter.IsReady(sim) && simTimeLeft > spGcd { //can't do anything but horn
-			s.Clear().
-				NewAction(dk.RotationActionCallback_HW).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		}
-	} else if sim.CurrentTime+spGcd < diseaseExpiresAt && sim.CurrentTime+abGcd > diseaseExpiresAt && dk.CurrentRunicPower() < 100 && (hasRime || dk.HornOfWinter.IsReady(sim)) { //if you can fit a spellgcd before disease dropping, and only if spell hit cap is reached
-		if hasRime && dk.CurrentRunicPower() < 100 { //rime prio
-			s.Clear().
-				NewAction(dk.RotationActionCallback_HB).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if dk.HornOfWinter.IsReady(sim) && dk.CurrentRunicPower() < 100 { //fit a horn in if you can
-			s.Clear().
-				NewAction(dk.RotationActionCallback_HW).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		}
-	} else if sim.CurrentTime+2*abGcd < diseaseExpiresAt { //there's at least 2 physical GCDs until disease fall so press normal prio, but do not double death oblit as you need it for pesti
-		if sim.CurrentTime >= obAt && (dk.Talents.Epidemic == 2 || (dk.CurrentFrostRunes() >= 1 && dk.CurrentUnholyRunes() >= 1)) { //can oblit, either be unh sub or have frost/unh runes ready
-			s.Clear().
-				NewAction(dk.RotationActionCallback_FrostSubUnh_EndOfFight_Obli).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if sim.CurrentTime+spGcd > obAt && (dk.Talents.Epidemic == 2 || (dk.CurrentFrostRunes() >= 1 && dk.CurrentUnholyRunes() >= 1)) { //same as above, no time to spellGCD before oblit
-			s.Clear().
-				NewAction(dk.RotationActionCallback_FrostSubUnh_EndOfFight_Obli).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if sim.CurrentTime+spGcd < obAt && sim.CurrentTime+abGcd > obAt && (hasRime || dk.CurrentRunicPower() < fsCost*4-2*dk.fr.oblitRPRegen && dk.HornOfWinter.IsReady(sim)) { //if you can fit a spGcd before oblit and won't overcap RP with horn
-			if hasRime {
-				s.Clear().
-					NewAction(dk.RotationActionCallback_HB).
-					NewAction(dk.RotationActionCallback_EndOfFightCheck)
-			} else if dk.CurrentRunicPower() < fsCost*4-2*dk.fr.oblitRPRegen && dk.HornOfWinter.IsReady(sim) { //if u wont overflow RP
-				s.Clear().
-					NewAction(dk.RotationActionCallback_HW).
-					NewAction(dk.RotationActionCallback_EndOfFightCheck)
-			}
-		} else if sim.CurrentTime+abGcd > obAt { //if no time to abGcd before oblit
-			s.Clear().
-				NewAction(dk.RotationActionCallback_FrostSubUnh_EndOfFight_Obli).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if dk.CurrentRunicPower() >= fsCost { //fs if can
-			s.Clear().
-				NewAction(dk.RotationActionCallback_FS).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if hasRime { //rime if can
-			s.Clear().
-				NewAction(dk.RotationActionCallback_HB).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if (dk.CurrentBloodRunes() >= 1 || dk.CurrentDeathRunes() == 1) && diseaseExpiresAt > simDur-abGcd { //if can BS and there's only 1 gcd left
-			s.Clear().
-				NewAction(dk.RotationActionCallback_BS).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if (dk.CurrentBloodRunes() >= 1 || dk.CurrentDeathRunes() == 1) && diseaseExpiresAt < simDur-abGcd { //if can pesti and there's more than 1 gcd left
-			s.Clear().
-				NewAction(dk.RotationActionCallback_Pesti).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else if dk.HornOfWinter.IsReady(sim) && simTimeLeft > spGcd { //if can horn
-			s.Clear().
-				NewAction(dk.RotationActionCallback_HW).
-				NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		} else { //wait for oblit
-			dk.WaitUntil(sim, obAt)
-			s.NewAction(dk.RotationActionCallback_EndOfFightCheck)
-		}
-	} else if sim.CurrentTime+2*abGcd > diseaseExpiresAt { //if u can only fit 1 spGcd + 1 abGcd before disease falls, do pesti first as it might miss
-		s.Clear().
-			NewAction(dk.RotationActionCallback_Pesti).
-			NewAction(dk.RotationActionCallback_EndOfFightCheck)
-	} else {
-		return -1
-	}
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_BS_Frost(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	dk.castBloodSpell(sim, target)
-	s.Advance()
-	return -1
-}
diff --git a/sim/deathknight/dps/rotation_frost_sub_blood.go b/sim/deathknight/dps/rotation_frost_sub_blood.go
deleted file mode 100644
index 8d167b3e75..0000000000
--- a/sim/deathknight/dps/rotation_frost_sub_blood.go
+++ /dev/null
@@ -1,417 +0,0 @@
-package dps
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/deathknight"
-)
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubBlood_Obli(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := false
-	advance := true
-	waitTime := time.Duration(-1)
-
-	ffExpiresAt := dk.FrostFeverSpell.Dot(target).ExpiresAt()
-	bpExpiresAt := dk.BloodPlagueSpell.Dot(target).ExpiresAt()
-	if sim.CurrentTime+1500*time.Millisecond < min(ffExpiresAt, bpExpiresAt) {
-		if dk.canCastFrostUnholySpell(sim, target) {
-			if dk.Deathchill != nil && dk.Deathchill.IsReady(sim) {
-				dk.Deathchill.Cast(sim, target)
-			}
-			casted = dk.castFrostUnholySpell(sim, target)
-			advance = dk.LastOutcome.Matches(core.OutcomeLanded)
-		}
-
-		s.ConditionalAdvance(casted && advance)
-	} else {
-		waitTime = sim.CurrentTime
-		s.Advance()
-	}
-
-	return core.TernaryDuration(casted, -1, waitTime)
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_LastSecondsCast(sim *core.Simulation, target *core.Unit) bool {
-	casted := false
-
-	ffActive := dk.FrostFeverSpell.Dot(target).IsActive()
-	bpActive := dk.BloodPlagueSpell.Dot(target).IsActive()
-	ffExpiresAt := dk.FrostFeverSpell.Dot(target).ExpiresAt()
-	bpExpiresAt := dk.BloodPlagueSpell.Dot(target).ExpiresAt()
-	fsCost := float64(core.RuneCost(dk.FrostStrike.CurCast.Cost).RunicPower())
-
-	km := dk.KillingMachineAura.IsActive()
-	if min(ffExpiresAt, bpExpiresAt) > sim.CurrentTime+sim.GetRemainingDuration() {
-		if dk.canCastFrostUnholySpell(sim, target) && ffActive && bpActive {
-			if dk.Deathchill != nil && dk.Deathchill.IsReady(sim) {
-				dk.Deathchill.Cast(sim, target)
-			}
-			casted = dk.castFrostUnholySpell(sim, target)
-		} else if dk.FrostStrike.CanCast(sim, nil) && km {
-			casted = dk.FrostStrike.Cast(sim, target)
-		} else if dk.canCastFrostUnholySpell(sim, target) {
-			if dk.Deathchill != nil && dk.Deathchill.IsReady(sim) {
-				dk.Deathchill.Cast(sim, target)
-			}
-			casted = dk.castFrostUnholySpell(sim, target)
-		} else if dk.FrostStrike.CanCast(sim, nil) {
-			casted = dk.FrostStrike.Cast(sim, target)
-		} else if dk.HowlingBlast.CanCast(sim, nil) {
-			casted = dk.HowlingBlast.Cast(sim, target)
-		} else if dk.canCastBloodSpell(sim, target) {
-			casted = dk.castBloodSpell(sim, target)
-		} else if dk.HornOfWinter.CanCast(sim, nil) && dk.CurrentRunicPower() < fsCost && sim.GetRemainingDuration() > dk.SpellGCD() {
-			casted = dk.HornOfWinter.Cast(sim, target)
-		}
-	}
-
-	return casted
-}
-
-// TODO: Improve this
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubBlood_FS_KM(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := false
-	ffExpiresAt := dk.FrostFeverSpell.Dot(target).ExpiresAt()
-	bpExpiresAt := dk.BloodPlagueSpell.Dot(target).ExpiresAt()
-
-	casted = dk.RotationActionCallback_LastSecondsCast(sim, target)
-	if !casted {
-		km := dk.KillingMachineAura.IsActive()
-		if km && sim.CurrentTime+1500*time.Millisecond < min(ffExpiresAt, bpExpiresAt) {
-			if dk.FrostStrike.CanCast(sim, nil) {
-				dk.FrostStrike.Cast(sim, target)
-			}
-		}
-
-		s.Advance()
-	}
-
-	return core.TernaryDuration(casted, -1, sim.CurrentTime)
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubBlood_Dump_UntilBR(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := false
-	waitUntil := time.Duration(-1)
-
-	casted = dk.RotationActionCallback_LastSecondsCast(sim, target)
-	if !casted {
-		br := dk.CurrentBloodRunes() + dk.CurrentDeathRunes()
-
-		if br == 0 {
-			spell := dk.RegularPrioPickSpell(sim, target, core.NeverExpires)
-			if spell != nil {
-				casted = spell.Cast(sim, target)
-			}
-		} else if br >= 1 {
-			km := dk.KillingMachineAura.IsActive()
-			if km {
-				if dk.FrostStrike.CanCast(sim, nil) {
-					casted = dk.FrostStrike.Cast(sim, target)
-				}
-			}
-
-			s.Advance()
-			waitUntil = sim.CurrentTime
-		}
-	}
-
-	return core.TernaryDuration(casted, -1, waitUntil)
-}
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubBlood_FS_Dump_UntilUA(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := false
-	waitUntil := time.Duration(-1)
-
-	casted = dk.RotationActionCallback_LastSecondsCast(sim, target)
-	if !casted {
-		if !dk.UnbreakableArmor.IsReady(sim) {
-			spell := dk.RegularPrioPickSpell(sim, target, core.NeverExpires)
-			if spell != nil {
-				casted = spell.Cast(sim, target)
-			} else {
-				s.Advance()
-				waitUntil = dk.UnbreakableArmor.ReadyAt()
-			}
-		} else {
-			s.Advance()
-			waitUntil = sim.CurrentTime
-		}
-	}
-
-	return core.TernaryDuration(casted, -1, waitUntil)
-}
-
-func (dk *DpsDeathknight) getOblitDrift(sim *core.Simulation, castIn time.Duration) time.Duration {
-	spendAt := sim.CurrentTime + castIn
-	oblit1 := max(dk.RuneReadyAt(sim, 2), dk.RuneReadyAt(sim, 3))
-	oblit2 := max(dk.SpendRuneReadyAt(4, spendAt), dk.SpendRuneReadyAt(5, spendAt))
-	return oblit2 - oblit1
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubBlood_FS_Dump(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := false
-	waitUntil := time.Duration(-1)
-
-	casted = dk.RotationActionCallback_LastSecondsCast(sim, target)
-	if !casted {
-		fr := dk.CurrentFrostRunes()
-		uh := dk.CurrentUnholyRunes()
-		bAt := dk.BloodRuneReadyAt(sim)
-		frAt := dk.NormalFrostRuneReadyAt(sim)
-		uhAt := dk.NormalUnholyRuneReadyAt(sim)
-		obAt := max(frAt, uhAt)
-		abGCD := core.GCDDefault
-		allowedObDrift := 3000 * time.Millisecond
-
-		if fr == 2 && uh == 2 {
-			casted := false
-			waitTime := time.Duration(-1)
-
-			ffExpiresAt := dk.FrostFeverSpell.Dot(target).ExpiresAt()
-			bpExpiresAt := dk.BloodPlagueSpell.Dot(target).ExpiresAt()
-			if sim.CurrentTime+1500*time.Millisecond < min(ffExpiresAt, bpExpiresAt) {
-				if dk.canCastFrostUnholySpell(sim, target) {
-					if dk.Deathchill != nil && dk.Deathchill.IsReady(sim) {
-						dk.Deathchill.Cast(sim, target)
-					}
-					casted = dk.castFrostUnholySpell(sim, target)
-					if casted && dk.LastOutcome.Matches(core.OutcomeLanded) {
-						dk.fr.oblitCount += 1
-					}
-				}
-			} else {
-				waitTime = sim.CurrentTime
-				s.Advance()
-			}
-
-			return core.TernaryDuration(casted, -1, waitTime)
-		} else if fr > 0 && uh > 0 {
-			casted := false
-			ffExpiresAt := dk.FrostFeverSpell.Dot(target).ExpiresAt()
-			bpExpiresAt := dk.BloodPlagueSpell.Dot(target).ExpiresAt()
-			km := dk.KillingMachineAura.IsActive()
-			if dk.fr.oblitCount == 1 && dk.FrostStrike.CanCast(sim, nil) && km && sim.CurrentTime+1500*time.Millisecond < min(ffExpiresAt, bpExpiresAt) && dk.getOblitDrift(sim, abGCD) <= allowedObDrift {
-				casted = dk.FrostStrike.Cast(sim, target)
-			} else {
-				if dk.canCastFrostUnholySpell(sim, target) {
-					dndIn := max(bAt-sim.CurrentTime, dk.DeathAndDecay.TimeToReady(sim))
-					if dk.Rotation.UseDeathAndDecay && dk.Env.GetNumTargets() >= 3 && dndIn < 3*time.Second && !dk.UnbreakableArmorAura.IsActive() && dk.UnbreakableArmor.TimeToReady(sim) > 8*time.Second {
-						if dk.DeathAndDecay.CanCast(sim, target) {
-							casted = dk.DeathAndDecay.Cast(sim, target)
-							dk.fr.oblitCount += 1
-						} else {
-							waitUntil = sim.CurrentTime + dndIn
-						}
-					} else {
-						if dk.Deathchill != nil && dk.Deathchill.IsReady(sim) {
-							dk.Deathchill.Cast(sim, target)
-						}
-						casted = dk.castFrostUnholySpell(sim, target)
-						advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-						if casted && advance {
-							dk.fr.oblitCount += 1
-						}
-					}
-					if dk.fr.oblitCount == 2 {
-						s.Advance()
-						dk.fr.oblitCount = 0
-					}
-				} else {
-					s.Advance()
-				}
-			}
-		} else {
-			spell := dk.RegularPrioPickSpell(sim, target, obAt+2500*time.Millisecond)
-			if spell != nil {
-				casted = spell.Cast(sim, target)
-			} else {
-				waitUntil = obAt
-			}
-		}
-	}
-	return core.TernaryDuration(casted, -1, waitUntil)
-}
-
-type FrostSubBloodUAState uint8
-
-const (
-	FrostSubBloodUAState_Now FrostSubBloodUAState = iota
-	FrostSubBloodUAState_Soon
-	FrostSubBloodUAState_CD
-)
-
-func (dk *DpsDeathknight) frCheckForUATime(sim *core.Simulation) FrostSubBloodUAState {
-	if dk.UnbreakableArmor.IsReady(sim) {
-		return FrostSubBloodUAState_Now
-	} else {
-		if dk.UnbreakableArmor.ReadyAt() < sim.CurrentTime+dk.SpellGCD()*2 {
-			return FrostSubBloodUAState_Soon
-		} else {
-			return FrostSubBloodUAState_CD
-		}
-	}
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubBlood_UA_Check(sim *core.Simulation, _ *core.Unit, s *deathknight.Sequence) time.Duration {
-	uaState := dk.frCheckForUATime(sim)
-
-	if uaState == FrostSubBloodUAState_CD {
-		s.Clear().NewAction(dk.RotationActionCallback_FrostSubBlood_Obli_Check)
-	} else {
-		s.Clear().
-			NewAction(dk.RotationActionCallback_FrostSubBlood_FS_Dump_UntilUA).
-			NewAction(dk.RotationActionCallback_FrostSubBlood_Dump_UntilBR).
-			NewAction(dk.RotationActionCallback_UA_Frost).
-			NewAction(dk.RotationActionCallback_FrostSubBlood_Sequence_Pesti).
-			NewAction(dk.RotationActionCallback_FrostSubBlood_SequenceRotation)
-	}
-
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubBlood_Obli_Check(sim *core.Simulation, _ *core.Unit, s *deathknight.Sequence) time.Duration {
-	if dk.BloodTap.IsReady(sim) {
-		s.Clear().
-			NewAction(dk.RotationActionCallback_FrostSubBlood_Dump_UntilBR).
-			NewAction(dk.RotationActionCallback_FrostSubBlood_Sequence_Pesti).
-			NewAction(dk.RotationActionCallback_BT).
-			NewAction(dk.RotationActionCallback_FrostSubBlood_Obli).
-			NewAction(dk.RotationActionCallback_FrostSubBlood_SequenceRotation)
-	} else {
-		s.Clear().
-			NewAction(dk.RotationActionCallback_FrostSubBlood_Dump_UntilBR).
-			NewAction(dk.RotationActionCallback_FrostSubBlood_Sequence_Pesti).
-			NewAction(dk.RotationActionCallback_FrostSubBlood_FS_KM).
-			NewAction(dk.RotationActionCallback_BS_Frost).
-			NewAction(dk.RotationActionCallback_FrostSubBlood_SequenceRotation)
-	}
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubBlood_SequenceRotation(sim *core.Simulation, _ *core.Unit, s *deathknight.Sequence) time.Duration {
-	s.Clear().
-		NewAction(dk.RotationActionCallback_FrostSubBlood_FS_Dump)
-
-	if dk.UnbreakableArmor != nil {
-		s.NewAction(dk.RotationActionCallback_FrostSubBlood_UA_Check)
-	} else {
-		s.NewAction(dk.RotationActionCallback_FrostSubBlood_Obli_Check)
-	}
-
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) setupFrostSubBloodERWOpener() {
-	dk.setupUnbreakableArmorCooldowns()
-
-	dk.RotationSequence.
-		NewAction(dk.RotationActionCallback_IT).
-		NewAction(dk.RotationActionCallback_PS).
-		NewAction(dk.RotationActionCallback_UA_Frost).
-		NewAction(dk.RotationActionCallback_BT).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Obli).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_Frost_Pesti_ERW).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Obli).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Obli).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Obli).
-		NewAction(dk.RotationActionCallback_RD).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Obli).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Obli).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Sequence_Pesti).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_BS_Frost).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_SequenceRotation)
-}
-
-func (dk *DpsDeathknight) setupFrostSubBloodNoERWOpener() {
-	dk.setupUnbreakableArmorCooldowns()
-
-	dk.RotationSequence.
-		NewAction(dk.RotationActionCallback_IT).
-		NewAction(dk.RotationActionCallback_PS).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Obli).
-		NewAction(dk.RotationActionCallback_BS_Frost).
-		NewAction(dk.RotationActionCallback_BT).
-		NewAction(dk.RotationActionCallback_UA_Frost).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Sequence_Pesti).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_RD).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Obli).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Obli).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_BS_Frost).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Sequence_Pesti).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_SequenceRotation)
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubBlood_RecoverFromPestiMiss(sim *core.Simulation, _ *core.Unit, s *deathknight.Sequence) time.Duration {
-	if dk.LastCast == dk.fr.bloodSpell {
-		s.Clear().
-			NewAction(dk.RotationActionCallback_BS_Frost).
-			NewAction(dk.RotationActionCallback_FS).
-			NewAction(dk.RotationActionCallback_IT).
-			NewAction(dk.RotationActionCallback_PS).
-			NewAction(dk.RotationActionCallback_FrostSubBlood_Obli).
-			NewAction(dk.RotationActionCallback_Frost_FS_HB).
-			NewAction(dk.RotationActionCallback_BS_Frost).
-			NewAction(dk.RotationActionCallback_Frost_FS_HB).
-			NewAction(dk.RotationActionCallback_FrostSubBlood_Sequence_Pesti).
-			NewAction(dk.RotationActionCallback_FrostSubBlood_SequenceRotation)
-	} else {
-		s.Clear().
-			NewAction(dk.RotationActionCallback_BS_Frost).
-			NewAction(dk.RotationActionCallback_BS_Frost).
-			NewAction(dk.RotationActionCallback_FS).
-			NewAction(dk.RotationActionCallback_IT).
-			NewAction(dk.RotationActionCallback_PS).
-			NewAction(dk.RotationActionCallback_FrostSubBlood_Obli).
-			NewAction(dk.RotationActionCallback_Frost_FS_HB).
-			NewAction(dk.RotationActionCallback_BS_Frost).
-			NewAction(dk.RotationActionCallback_Frost_FS_HB).
-			NewAction(dk.RotationActionCallback_FrostSubBlood_Sequence_Pesti).
-			NewAction(dk.RotationActionCallback_FrostSubBlood_SequenceRotation)
-	}
-
-	dk.NextCast = nil
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubBlood_Sequence_Pesti(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := false
-	waitUntil := time.Duration(-1)
-
-	ffActive := dk.FrostFeverSpell.Dot(target).IsActive()
-	bpActive := dk.BloodPlagueSpell.Dot(target).IsActive()
-	ffExpiresAt := dk.FrostFeverSpell.Dot(target).ExpiresAt()
-	bpExpiresAt := dk.BloodPlagueSpell.Dot(target).ExpiresAt()
-
-	if dk.RotationActionCallback_LastSecondsCast(sim, target) {
-		return -1
-	}
-
-	if !ffActive || !bpActive {
-		return dk.RotationActionCallback_FrostSubBlood_RecoverFromPestiMiss(sim, target, s)
-	} else {
-		casted = dk.Pestilence.Cast(sim, target)
-		advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-		if !casted || (casted && !dk.LastOutcome.Matches(core.OutcomeLanded)) {
-
-			if sim.CurrentTime+dk.SpellGCD() > ffExpiresAt || sim.CurrentTime+dk.SpellGCD() > bpExpiresAt {
-				return dk.RotationActionCallback_FrostSubBlood_RecoverFromPestiMiss(sim, target, s)
-			} else {
-				s.ConditionalAdvance(casted && advance)
-				return core.TernaryDuration(casted, -1, waitUntil)
-			}
-		} else {
-			s.ConditionalAdvance(casted && advance)
-			return core.TernaryDuration(casted, -1, waitUntil)
-		}
-	}
-}
diff --git a/sim/deathknight/dps/rotation_frost_sub_blood_desync.go b/sim/deathknight/dps/rotation_frost_sub_blood_desync.go
deleted file mode 100644
index 78ac5cd873..0000000000
--- a/sim/deathknight/dps/rotation_frost_sub_blood_desync.go
+++ /dev/null
@@ -1,243 +0,0 @@
-package dps
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/deathknight"
-)
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostDesync_Obli(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	if dk.Deathchill != nil && dk.Deathchill.IsReady(sim) {
-		dk.Deathchill.Cast(sim, target)
-	}
-	casted := dk.castFrostUnholySpell(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func (dk *DpsDeathknight) setupFrostSubBloodDesyncOpener() {
-	if dk.Rotation.UseEmpowerRuneWeapon {
-		dk.setupFrostSubBloodDesyncERWOpener()
-	} else {
-		dk.setupFrostSubBloodDesyncNoERWOpener()
-	}
-}
-
-func (dk *DpsDeathknight) setupFrostSubBloodDesyncERWOpener() {
-	dk.setupUnbreakableArmorCooldowns()
-
-	dk.RotationSequence.
-		// Start standard sub-blood opener
-		NewAction(dk.RotationActionCallback_IT).
-		NewAction(dk.RotationActionCallback_PS).
-		NewAction(dk.RotationActionCallback_UA_Frost).
-		NewAction(dk.RotationActionCallback_BT).
-		NewAction(dk.RotationActionCallback_FrostDesync_Obli).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_Frost_Pesti_ERW).
-		NewAction(dk.RotationActionCallback_FrostDesync_Obli).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_FrostDesync_Obli).
-		NewAction(dk.RotationActionCallback_FrostDesync_Obli).
-		NewAction(dk.RotationActionCallback_RD).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_FrostDesync_Obli).
-		NewAction(dk.RotationActionCallback_FrostDesync_Obli).
-		NewAction(dk.RotationAction_CancelBT).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Sequence_Pesti).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_BS).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		// End standard sub-blood opener
-
-		NewAction(dk.RotationActionCallback_FrostDesync_Obli).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_FrostDesync_Obli).
-
-		// Get death runes again
-		NewAction(dk.RotationActionCallback_BS).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Sequence_Pesti).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-
-		// Re-cast IT then desync f1 u1 runes
-		NewAction(dk.RotationActionCallback_IT).
-		NewAction(dk.RotationActionCallback_Force_Desync).
-		NewAction(dk.RotationActionCallback_FrostDesync_Obli).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Sequence_Pesti).
-
-		// Continue desync rotation
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Desync_Sequence1)
-}
-
-func (dk *DpsDeathknight) setupFrostSubBloodDesyncNoERWOpener() {
-	dk.setupUnbreakableArmorCooldowns()
-
-	dk.RotationSequence.
-		NewAction(dk.RotationActionCallback_IT).
-		NewAction(dk.RotationActionCallback_PS).
-		NewAction(dk.RotationActionCallback_UA_Frost).
-		NewAction(dk.RotationActionCallback_BT).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Obli).
-		NewAction(dk.RotationAction_CancelBT).
-		NewAction(dk.RotationActionCallback_Pesti).
-		NewAction(dk.RotationActionCallback_Frost_FS_HB).
-		NewAction(dk.RotationActionCallback_RD).
-		NewAction(dk.RotationActionCallback_FS).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Desync_Sequence1)
-}
-
-// We need to keep the B2 and F1 runes in sync and immediately use them for obliterate
-// otherwise if an unholy rune comes up then we can't continue the Desync rotation without
-// re-casting IT + PS
-func (dk *DpsDeathknight) firstOblitAt(sim *core.Simulation) time.Duration {
-	return max(dk.RuneReadyAt(sim, 1), dk.RuneReadyAt(sim, 2))
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_Force_Desync(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	frostReadyAt := dk.RuneReadyAt(sim, 2)
-	unholyReadyAt := dk.SpendRuneReadyAt(4, sim.CurrentTime)
-	drift := unholyReadyAt - frostReadyAt
-	desiredDrift := 1000 * time.Millisecond
-
-	if drift >= desiredDrift {
-		dk.Obliterate.Cast(sim, target)
-		s.Advance()
-		return -1
-	} else {
-		dk.FrostStrike.Cast(sim, target)
-	}
-
-	return -1
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubBlood_Desync_Obli(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := dk.RotationActionCallback_LastSecondsCast(sim, target)
-	if casted {
-		return -1
-	}
-
-	casted = false
-	advance := true
-
-	ff := dk.FrostFeverSpell.Dot(target).IsActive()
-	bp := dk.BloodPlagueSpell.Dot(target).IsActive()
-
-	if ff && bp {
-		if dk.canCastFrostUnholySpell(sim, target) {
-			if dk.Deathchill != nil && dk.Deathchill.IsReady(sim) {
-				dk.Deathchill.Cast(sim, target)
-			}
-			casted = dk.castFrostUnholySpell(sim, target)
-			advance = dk.LastOutcome.Matches(core.OutcomeLanded)
-			s.ConditionalAdvance(casted && advance)
-		} else {
-			dk.desync_Filler(sim, target)
-		}
-	} else if !ff {
-		casted = dk.IcyTouch.Cast(sim, target)
-		advance = dk.LastOutcome.Matches(core.OutcomeLanded)
-		s.ConditionalAdvance(casted && advance)
-	} else {
-		casted = dk.PlagueStrike.Cast(sim, target)
-		advance = dk.LastOutcome.Matches(core.OutcomeLanded)
-		s.ConditionalAdvance(casted && advance)
-	}
-
-	return -1
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubBlood_Desync_UA(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	runeGrace := dk.RuneGraceAt(0, sim.CurrentTime)
-	waitFor := 5 * time.Millisecond
-
-	if dk.UnbreakableArmor.IsReady(sim) && dk.BloodTap.IsReady(sim) {
-		dk.BloodTap.Cast(sim, target)
-		return sim.CurrentTime + waitFor
-	} else if dk.UnbreakableArmor.IsReady(sim) && runeGrace >= waitFor {
-		dk.castAllMajorCooldowns(sim)
-		dk.UnbreakableArmor.Cast(sim, target)
-	}
-
-	s.Advance()
-	return -1
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubBlood_Detect_Broken_Desync(sim *core.Simulation, _ *core.Unit, s *deathknight.Sequence) time.Duration {
-	frost := dk.FrostRuneReadyAt(sim)
-	unholy := dk.UnholyRuneReadyAt(sim)
-
-	if frost == unholy {
-		s.Clear().NewAction(dk.RotationActionCallback_FrostSubBlood_SequenceRotation)
-	} else {
-		s.Advance()
-	}
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubBlood_Desync_Pesti(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := dk.RotationActionCallback_LastSecondsCast(sim, target)
-	if casted {
-		return -1
-	}
-	casted = dk.Pestilence.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubBlood_Desync_Sequence1(sim *core.Simulation, _ *core.Unit, s *deathknight.Sequence) time.Duration {
-	s.Clear().
-		// f1 u1
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Desync_Obli).
-		// f2 u2
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Desync_Obli).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Desync_FS_Dump).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Desync_Pesti).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Desync_Sequence2)
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubBlood_Desync_Sequence2(sim *core.Simulation, _ *core.Unit, s *deathknight.Sequence) time.Duration {
-	s.Clear().
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Detect_Broken_Desync).
-		// d2 f1
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Desync_Obli).
-		// f2 u1
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Desync_Obli).
-		// u2 d1
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Desync_Obli).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Desync_UA).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Desync_FS_Dump).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Desync_Pesti).
-		NewAction(dk.RotationAction_CancelBT).
-		NewAction(dk.RotationActionCallback_FrostSubBlood_Desync_Sequence1)
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) desync_Filler(sim *core.Simulation, target *core.Unit) {
-	spell := dk.RegularPrioPickSpell(sim, target, dk.firstOblitAt(sim))
-	if spell != nil {
-		spell.Cast(sim, target)
-	}
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubBlood_Desync_FS_Dump(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := dk.RotationActionCallback_LastSecondsCast(sim, target)
-	if casted {
-		return -1
-	}
-
-	if !dk.AllRunesSpent() {
-		s.Advance()
-		return sim.CurrentTime
-	}
-
-	dk.desync_Filler(sim, target)
-
-	return -1
-}
diff --git a/sim/deathknight/dps/rotation_frost_sub_unholy.go b/sim/deathknight/dps/rotation_frost_sub_unholy.go
deleted file mode 100644
index b07eddfce1..0000000000
--- a/sim/deathknight/dps/rotation_frost_sub_unholy.go
+++ /dev/null
@@ -1,267 +0,0 @@
-package dps
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/deathknight"
-)
-
-func (dk *DpsDeathknight) setupFrostSubUnholyERWOpener() {
-	dk.setupUnbreakableArmorCooldowns()
-
-	dk.RotationSequence.
-		NewAction(dk.RotationActionCallback_IT).
-		NewAction(dk.RotationActionCallback_PS).
-		NewAction(dk.RotationActionCallback_BT).
-		NewAction(dk.RotationActionCallback_Pesti).
-		NewAction(dk.RotationActionCallback_UA_Frost).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_Obli).
-		NewAction(dk.RotationActionCallback_ERW).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_FS_HB).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_Obli).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_Obli).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_FS_HB).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_Obli).
-		NewAction(dk.RotationAction_CancelBT).
-		NewAction(dk.RotationActionCallback_RD).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_FS_HB).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_Obli).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_Obli).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_FS_HB).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_FS_HB).
-		NewAction(dk.RotationActionCallback_Pesti).
-		NewAction(dk.RotationActionCallback_BS).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_Sequence1)
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubUnholy_FS_HB(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	if !dk.canCastAbilityBeforeDiseasesExpire(sim, target) {
-		s.Advance()
-		return sim.CurrentTime
-	}
-	return dk.RotationActionCallback_Frost_FS_HB(sim, target, s)
-}
-
-func (dk *DpsDeathknight) canCastAbilityBeforeDiseasesExpire(sim *core.Simulation, target *core.Unit) bool {
-	ffExpiresAt := dk.FrostFeverSpell.Dot(target).ExpiresAt()
-	bpExpiresAt := dk.BloodPlagueSpell.Dot(target).ExpiresAt()
-	return sim.CurrentTime+1500*time.Millisecond < min(ffExpiresAt, bpExpiresAt)
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubUnholy_Obli(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := false
-	advance := true
-	waitTime := time.Duration(-1)
-
-	if dk.canCastAbilityBeforeDiseasesExpire(sim, target) {
-		if dk.canCastFrostUnholySpell(sim, target) {
-			if dk.Deathchill != nil && dk.Deathchill.IsReady(sim) {
-				dk.Deathchill.Cast(sim, target)
-			}
-			casted = dk.castFrostUnholySpell(sim, target)
-			advance = dk.LastOutcome.Matches(core.OutcomeLanded)
-		}
-
-		s.ConditionalAdvance(casted && advance)
-	} else {
-		if dk.canCastFrostUnholySpell(sim, target) {
-			if dk.Deathchill != nil && dk.Deathchill.IsReady(sim) {
-				dk.Deathchill.Cast(sim, target)
-			}
-			casted = dk.castFrostUnholySpell(sim, target)
-			advance = dk.LastOutcome.Matches(core.OutcomeLanded)
-
-			if casted && advance {
-				return dk.RotationActionCallback_FrostSubUnholy_RecoverFromPestiMiss(sim, target, s)
-			}
-		}
-	}
-
-	return core.TernaryDuration(casted, -1, waitTime)
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubUnholy_FS_KM(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := dk.RotationActionCallback_LastSecondsCast(sim, target)
-
-	if !casted {
-		if !dk.canCastAbilityBeforeDiseasesExpire(sim, target) {
-			s.Advance()
-			return sim.CurrentTime
-		}
-
-		spell := dk.RegularPrioPickSpell(sim, target, core.NeverExpires)
-		if spell != nil {
-			casted = spell.Cast(sim, target)
-		}
-
-		s.Advance()
-	}
-
-	return core.TernaryDuration(casted, -1, sim.CurrentTime)
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubUnholy_Dump_Until_Deaths(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	/*
-		We need to have the first death up before we UA + BT + Oblit, since if only the 2nd
-		death rune is up, UA then Blood Tap will convert and refresh the first and the second will have a 10s CD from UA
-	*/
-	if dk.LeftBloodRuneReady() {
-		s.Advance()
-		return sim.CurrentTime
-	}
-
-	spell := dk.RegularPrioPickSpell(sim, target, dk.DeathRuneRegenAt(1))
-
-	if spell != nil {
-		spell.Cast(sim, target)
-	}
-
-	return -1
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubUnholy_UA_Check1(sim *core.Simulation, _ *core.Unit, s *deathknight.Sequence) time.Duration {
-	if dk.UnbreakableArmor.CanCast(sim, nil) && dk.BloodTap.CanCast(sim, nil) {
-		s.Clear().
-			NewAction(dk.RotationActionCallback_FrostSubUnholy_Dump_Until_Deaths).
-			NewAction(dk.RotationActionCallback_UA_Frost).
-			NewAction(dk.RotationActionCallback_BT).
-			NewAction(dk.RotationActionCallback_FrostSubUnholy_Obli).
-			NewAction(dk.RotationActionCallback_FrostSubUnholy_Sequence2)
-	} else {
-		s.Advance()
-	}
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubUnholy_UA_Check2(sim *core.Simulation, _ *core.Unit, s *deathknight.Sequence) time.Duration {
-	if dk.UnbreakableArmor.CanCast(sim, nil) && dk.BloodTap.CanCast(sim, nil) {
-		s.Clear().
-			NewAction(dk.RotationActionCallback_UA_Frost).
-			NewAction(dk.RotationActionCallback_BT).
-			NewAction(dk.RotationActionCallback_FrostSubUnholy_Sequence2)
-	} else {
-		s.Advance()
-	}
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubUnholy_UA_Check3(sim *core.Simulation, _ *core.Unit, s *deathknight.Sequence) time.Duration {
-	if (dk.UnbreakableArmor.TimeToReady(sim) < 2500*time.Millisecond+sim.CurrentTime) && (dk.BloodTap.TimeToReady(sim) < 2500*time.Millisecond+sim.CurrentTime) {
-		s.Clear().
-			NewAction(dk.RotationActionCallback_BT).
-			NewAction(dk.RotationActionCallback_UA_Frost).
-			NewAction(dk.RotationActionCallback_BS).
-			NewAction(dk.RotationActionCallback_FrostSubUnholy_Sequence1)
-	} else {
-		s.Advance()
-	}
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubUnholy_Sequence1(sim *core.Simulation, _ *core.Unit, s *deathknight.Sequence) time.Duration {
-	s.Clear().
-		NewAction(dk.RotationActionCallback_EndOfFightCheck).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_FS_Dump).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_Obli).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_Obli).
-		NewAction(dk.RotationActionCallback_FS).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_UA_Check1).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_Obli).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_Sequence2)
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubUnholy_Pesti(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	desiredGrace := 10 * time.Millisecond
-	currentGrace := dk.RuneGraceAt(0, sim.CurrentTime)
-	ffExpiresAt := dk.FrostFeverSpell.Dot(target).ExpiresAt()
-	bpExpiresAt := dk.BloodPlagueSpell.Dot(target).ExpiresAt()
-	diseaseExpiresAt := min(ffExpiresAt, bpExpiresAt)
-	waitUntil := sim.CurrentTime + (desiredGrace - currentGrace)
-
-	if diseaseExpiresAt <= sim.CurrentTime {
-		return dk.RotationActionCallback_FrostSubUnholy_RecoverFromPestiMiss(sim, target, s)
-	}
-	if currentGrace < desiredGrace && diseaseExpiresAt > waitUntil {
-		return waitUntil
-	}
-	casted := dk.Pestilence.Cast(sim, target)
-	s.ConditionalAdvance(casted && dk.LastOutcome.Matches(core.OutcomeLanded))
-	return -1
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubUnholy_Sequence2(sim *core.Simulation, _ *core.Unit, s *deathknight.Sequence) time.Duration {
-	s.Clear().
-		NewAction(dk.RotationAction_CancelBT).
-		NewAction(dk.RotationActionCallback_EndOfFightCheck).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_FS_Dump).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_Obli).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_Obli).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_FS_KM).
-		NewAction(dk.RotationActionCallback_EndOfFightCheck).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_Pesti).
-		//NewAction(dk.RotationActionCallback_FrostSubUnholy_UA_Check3).
-		NewAction(dk.RotationActionCallback_BS).
-		NewAction(dk.RotationActionCallback_FrostSubUnholy_Sequence1)
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubUnholy_RecoverFromPestiMiss(sim *core.Simulation, _ *core.Unit, s *deathknight.Sequence) time.Duration {
-	if dk.LastCast == dk.BloodStrike {
-		s.Clear().
-			NewAction(dk.RotationActionCallback_BS).
-			NewAction(dk.RotationActionCallback_FS).
-			NewAction(dk.RotationActionCallback_IT).
-			NewAction(dk.RotationActionCallback_PS).
-			NewAction(dk.RotationActionCallback_FrostSubUnholy_Obli).
-			NewAction(dk.RotationActionCallback_FrostSubUnholy_Obli).
-			NewAction(dk.RotationActionCallback_Frost_FS_HB).
-			NewAction(dk.RotationActionCallback_FrostSubUnholy_FS_KM).
-			NewAction(dk.RotationActionCallback_FrostSubUnholy_Sequence1)
-	} else {
-		s.Clear().
-			NewAction(dk.RotationActionCallback_BS).
-			NewAction(dk.RotationActionCallback_BS).
-			NewAction(dk.RotationActionCallback_FS).
-			NewAction(dk.RotationActionCallback_IT).
-			NewAction(dk.RotationActionCallback_PS).
-			NewAction(dk.RotationActionCallback_FrostSubUnholy_Obli).
-			NewAction(dk.RotationActionCallback_FrostSubUnholy_Obli).
-			NewAction(dk.RotationActionCallback_Frost_FS_HB).
-			NewAction(dk.RotationActionCallback_FrostSubUnholy_FS_KM).
-			NewAction(dk.RotationActionCallback_FrostSubUnholy_Sequence1)
-	}
-
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_FrostSubUnholy_FS_Dump(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := false
-	waitUntil := time.Duration(-1)
-
-	fr := dk.CurrentFrostRunes()
-	uh := dk.CurrentUnholyRunes()
-
-	if fr > 0 && uh > 0 {
-		s.Advance()
-		return sim.CurrentTime
-	}
-
-	casted = dk.RotationActionCallback_LastSecondsCast(sim, target)
-	if !casted {
-		frAt := dk.NormalFrostRuneReadyAt(sim)
-		uhAt := dk.NormalUnholyRuneReadyAt(sim)
-		obAt := max(frAt, uhAt)
-		delayAmount := time.Second
-		spell := dk.RegularPrioPickSpell(sim, target, obAt+delayAmount)
-		if spell != nil {
-			casted = spell.Cast(sim, target)
-		} else {
-			waitUntil = obAt
-			s.Advance()
-		}
-	}
-
-	return core.TernaryDuration(casted, -1, waitUntil)
-}
diff --git a/sim/deathknight/dps/rotation_shared_helper.go b/sim/deathknight/dps/rotation_shared_helper.go
deleted file mode 100644
index a770219183..0000000000
--- a/sim/deathknight/dps/rotation_shared_helper.go
+++ /dev/null
@@ -1,111 +0,0 @@
-package dps
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-	"github.com/wowsims/wotlk/sim/deathknight"
-)
-
-type SharedRotation struct {
-	dk         *DpsDeathknight
-	recastedFF bool
-	recastedBP bool
-
-	ffFirst bool
-	hasGod  bool
-}
-
-func (sr *SharedRotation) Reset(_ *core.Simulation) {
-	sr.recastedFF = false
-	sr.recastedBP = false
-}
-
-func (sr *SharedRotation) Initialize(dk *DpsDeathknight) {
-	dk.sr.ffFirst = dk.Rotation.FirstDisease == proto.Deathknight_Rotation_FrostFever
-	dk.sr.hasGod = dk.HasMajorGlyph(proto.DeathknightMajorGlyph_GlyphOfDisease)
-}
-
-func (dk *DpsDeathknight) shDiseaseCheck(sim *core.Simulation, target *core.Unit, spell *core.Spell, costRunes bool, casts int, ffSyncTime time.Duration) bool {
-	ffRemaining := dk.FrostFeverSpell.Dot(target).RemainingDuration(sim)
-	bpRemaining := dk.BloodPlagueSpell.Dot(target).RemainingDuration(sim)
-	castGcd := dk.SpellGCD() * time.Duration(casts)
-
-	// FF is not active or will drop before Gcd is ready after this cast
-	if !dk.FrostFeverSpell.Dot(target).IsActive() || ffRemaining < castGcd {
-		return false
-	}
-	// BP is not active or will drop before Gcd is ready after this cast
-	if !dk.BloodPlagueSpell.Dot(target).IsActive() || bpRemaining < castGcd {
-		return false
-	}
-
-	// If the ability we want to cast spends runes we check for possible disease drops
-	// in the time we won't have runes to recast the disease
-	if spell.CanCast(sim, nil) && costRunes {
-		ffExpiresAt := ffRemaining + sim.CurrentTime
-		bpExpiresAt := bpRemaining + sim.CurrentTime
-
-		spellCost := dk.OptimalRuneCost(core.RuneCost(spell.DefaultCast.Cost))
-
-		crpb := dk.Predictor()
-		crpb.SpendRuneCost(sim, spellCost)
-
-		afterCastTime := sim.CurrentTime + castGcd
-		currentFrostRunes := crpb.CurrentFrostRunes()
-		currentUnholyRunes := crpb.CurrentUnholyRunes()
-		nextFrostRuneAt := crpb.FrostRuneReadyAt(sim)
-		nextUnholyRuneAt := crpb.UnholyRuneReadyAt(sim)
-
-		// If FF is gonna drop while our runes are on CD
-		if dk.shRecastAvailableCheck(ffExpiresAt-ffSyncTime, afterCastTime, int(spellCost.Frost()), int32(currentFrostRunes), nextFrostRuneAt) {
-			return false
-		}
-
-		// If BP is gonna drop while our runes are on CD
-		if dk.shRecastAvailableCheck(bpExpiresAt, afterCastTime, int(spellCost.Unholy()), int32(currentUnholyRunes), nextUnholyRuneAt) {
-			return false
-		}
-	}
-
-	return true
-}
-
-func (dk *DpsDeathknight) shRecastAvailableCheck(expiresAt time.Duration, afterCastTime time.Duration,
-	spellCost int, currentRunes int32, nextRuneAt time.Duration) bool {
-	// Allow disease drops in unholy as its a dps increase sometimes
-	downTime := time.Duration(core.TernaryFloat64(dk.PrimaryTalentTree == 2, dk.Inputs.DiseaseDowntime, 0)) * time.Second
-	if spellCost > 0 && currentRunes == 0 {
-		if expiresAt+downTime <= nextRuneAt {
-			return true
-		}
-	} else if afterCastTime >= expiresAt+downTime {
-		return true
-	}
-	return false
-}
-
-func (dk *DpsDeathknight) shShouldSpreadDisease(_ *core.Simulation) bool {
-	prioritizeSpread := dk.Env.GetNumTargets() > 1
-
-	// on 2 or 3 targets, we don't want to spread if we have diseases up on all targets already (to maximize Desolation uptime)
-	// on 4+ targets always spread to maximize disease and Wandering Plague uptime
-	if dk.Env.GetNumTargets() > 1 && dk.Env.GetNumTargets() < 4 {
-		for i := int32(1); i < dk.Env.GetNumTargets(); i++ {
-			target := dk.Env.GetTargetUnit(i)
-			if dk.FrostFeverSpell.Dot(target).IsActive() && dk.BloodPlagueSpell.Dot(target).IsActive() {
-				prioritizeSpread = false
-				break
-			}
-		}
-	}
-
-	return dk.sr.recastedFF && dk.sr.recastedBP && prioritizeSpread
-}
-
-func (dk *DpsDeathknight) RotationAction_CancelBT(sim *core.Simulation, _ *core.Unit, s *deathknight.Sequence) time.Duration {
-	dk.BloodTapAura.Deactivate(sim)
-	s.Advance()
-	return sim.CurrentTime
-}
diff --git a/sim/deathknight/dps/rotation_unholy.go b/sim/deathknight/dps/rotation_unholy.go
deleted file mode 100644
index 27108d1bb0..0000000000
--- a/sim/deathknight/dps/rotation_unholy.go
+++ /dev/null
@@ -1,676 +0,0 @@
-package dps
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-	"github.com/wowsims/wotlk/sim/deathknight"
-)
-
-func (dk *DpsDeathknight) setupUnholyRotations() {
-	if dk.Rotation.BloodTap == proto.Deathknight_Rotation_GhoulFrenzy && !dk.Talents.GhoulFrenzy {
-		dk.Rotation.BloodTap = proto.Deathknight_Rotation_IcyTouch
-	}
-
-	dk.Inputs.FuStrike = deathknight.FuStrike_ScourgeStrike
-	if dk.Talents.Annihilation > 0 {
-		dk.Inputs.FuStrike = deathknight.FuStrike_Obliterate
-	}
-
-	if dk.Talents.SummonGargoyle && dk.Rotation.UseGargoyle {
-		dk.setupGargoyleCooldowns()
-	}
-
-	dk.RotationSequence.Clear().
-		NewAction(dk.RotationActionCallback_MindFreezeFiller).
-		NewAction(dk.getFirstDiseaseAction()).
-		NewAction(dk.getSecondDiseaseAction()).
-		NewAction(dk.uhBloodRuneAction(true))
-
-	fuStrikeAction := dk.RotationActionCallback_DS
-	if dk.Talents.ScourgeStrike {
-		fuStrikeAction = dk.RotationActionCallback_SS
-	}
-
-	if dk.ur.sigil == Sigil_Virulence {
-		dk.RotationSequence.
-			NewAction(fuStrikeAction).
-			NewAction(dk.RotationActionUH_BS).
-			NewAction(dk.RotationActionUH_SS_Sigil).
-			NewAction(dk.RotationActionUH_BS).
-			NewAction(dk.RotationActionUH_SS_Sigil).
-			NewAction(dk.RotationActionUH_BS)
-	} else if dk.ur.sigil == Sigil_HangedMan {
-		dk.RotationSequence.
-			NewAction(fuStrikeAction).
-			NewAction(dk.RotationActionUH_BS).
-			NewAction(dk.RotationActionCallback_DC).
-			NewAction(dk.RotationActionCallback_ERW).
-			NewAction(fuStrikeAction).
-			NewAction(dk.RotationActionUH_BS).
-			NewAction(fuStrikeAction).
-			NewAction(dk.RotationActionUH_BS)
-	}
-
-	if dk.Rotation.UseDeathAndDecay || (!dk.Talents.ScourgeStrike && dk.Talents.Annihilation == 0) {
-		if !dk.Talents.ScourgeStrike || dk.ur.sigil == Sigil_Other {
-			dk.RotationSequence.
-				NewAction(dk.RotationActionCallback_DND)
-		}
-		dk.RotationSequence.
-			NewAction(dk.RotationActionCallback_UnholyDndRotation)
-	} else {
-		dk.RotationSequence.NewAction(dk.RotationActionCallback_UnholySsRotation)
-	}
-}
-
-func (dk *DpsDeathknight) setupWeaponSwap() {
-	if !dk.ItemSwap.IsEnabled() {
-		return
-	}
-
-	if mh := dk.ItemSwap.GetItem(proto.ItemSlot_ItemSlotMainHand); mh != nil {
-		if mh.Enchant.EffectID == 3790 {
-			dk.ur.mhSwap = WeaponSwap_BlackMagic
-		} else if mh.Enchant.EffectID == 3789 {
-			dk.ur.mhSwap = WeaponSwap_Berserking
-		} else if mh.Enchant.EffectID == 3368 {
-			dk.ur.mhSwap = WeaponSwap_FallenCrusader
-		}
-	}
-
-	if oh := dk.ItemSwap.GetItem(proto.ItemSlot_ItemSlotOffHand); oh != nil {
-		if oh.Enchant.EffectID == 3790 {
-			dk.ur.ohSwap = WeaponSwap_BlackMagic
-		} else if oh.Enchant.EffectID == 3789 {
-			dk.ur.ohSwap = WeaponSwap_Berserking
-		} else if oh.Enchant.EffectID == 3368 {
-			dk.ur.ohSwap = WeaponSwap_FallenCrusader
-		}
-	}
-
-	if dk.ur.mhSwap != WeaponSwap_None || dk.ur.ohSwap != WeaponSwap_None {
-		core.MakePermanent(dk.GetOrRegisterAura(core.Aura{
-			Label: "Weapon Swap Check",
-			OnCastComplete: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell) {
-				if dk.GCD.TimeToReady(sim) >= time.Second && dk.Hardcast.Expires < sim.CurrentTime {
-					if sim.Log != nil {
-						sim.Log("Swap Check: %0.2f", dk.GCD.TimeToReady(sim).Seconds())
-					}
-					dk.weaponSwapCheck(sim)
-				}
-			},
-		}))
-	}
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_UnholyDndRotation(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	if dk.uhGargoyleCheck(sim, target, 100*time.Millisecond) {
-		dk.uhAfterGargoyleSequence(sim)
-		return sim.CurrentTime
-	}
-
-	if dk.CurrentRunicPower() > 100 && dk.GCD.IsReady(sim) && dk.DeathCoil.Cast(sim, target) {
-		return -1
-	}
-
-	if dk.Talents.GhoulFrenzy && !dk.uhShouldWaitForDnD(sim, false, true, true) {
-		if dk.uhGhoulFrenzyCheck(sim, target) {
-			return sim.CurrentTime
-		}
-	}
-
-	if dk.uhBloodTap(sim, target) {
-		return sim.CurrentTime
-	}
-
-	if dk.uhEmpoweredRuneWeapon(sim, target) {
-		return sim.CurrentTime
-	}
-
-	if dk.uhMindFreeze(sim, target) {
-		return sim.CurrentTime
-	}
-
-	cast := false
-	if dk.uhDiseaseCheck(sim, target, dk.DeathAndDecay, true, 1) {
-		if dk.uhVirulenceRotationCheck(sim, dk.Talents.SummonGargoyle) {
-			if dk.uhGargoyleCheck(sim, target, core.GCDDefault*2+50*time.Millisecond) {
-				dk.uhAfterGargoyleSequence(sim)
-				return sim.CurrentTime
-			}
-			cast = dk.uhCastVirulenceStrike(sim, target)
-			if cast {
-				if dk.LastOutcome.Matches(core.OutcomeLanded) {
-					dk.RotationSequence.Clear().
-						NewAction(dk.RotationActionUH_BS).
-						NewAction(dk.RotationActionCallback_UnholyDndRotation)
-				}
-			}
-		} else {
-			if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) {
-				dk.uhAfterGargoyleSequence(sim)
-				return sim.CurrentTime
-			}
-			if cast = dk.DeathAndDecay.CanCast(sim, target); cast {
-				dk.DeathAndDecay.Cast(sim, target)
-			}
-		}
-	} else {
-		if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+core.GCDDefault+50*time.Millisecond) {
-			dk.uhAfterGargoyleSequence(sim)
-			return sim.CurrentTime
-		}
-		dk.uhRecastDiseasesSequence(sim)
-		return sim.CurrentTime
-	}
-
-	if !cast {
-		if dk.uhDiseaseCheck(sim, target, dk.ScourgeStrike, true, 1) {
-			if !dk.uhShouldWaitForDnD(sim, true, true, true) {
-				if dk.uhVirulenceRotationCheck(sim, false) && dk.DeathStrike.IsReady(sim) {
-					if dk.uhGargoyleCheck(sim, target, core.GCDDefault*2+50*time.Millisecond) {
-						dk.uhAfterGargoyleSequence(sim)
-						return sim.CurrentTime
-					}
-					cast = dk.uhCastVirulenceStrike(sim, target)
-					if cast {
-						if dk.LastOutcome.Matches(core.OutcomeLanded) {
-							dk.RotationSequence.Clear().
-								NewAction(dk.RotationActionUH_BS).
-								NewAction(dk.RotationActionCallback_UnholyDndRotation)
-						}
-					}
-				} else if dk.CurrentDeathRunes() > 0 {
-					dk.RotationSequence.Clear().
-						NewAction(dk.RotationActionUH_BS).
-						NewAction(dk.RotationActionCallback_UnholyDndRotation)
-					return sim.CurrentTime
-				} else if dk.IcyTouch.CanCast(sim, nil) && dk.PlagueStrike.CanCast(sim, nil) {
-					if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+core.GCDDefault*2+50*time.Millisecond) {
-						dk.uhAfterGargoyleSequence(sim)
-						return sim.CurrentTime
-					}
-					dk.uhRecastDiseasesSequence(sim)
-					return sim.CurrentTime
-				}
-			}
-		} else {
-			dk.uhRecastDiseasesSequence(sim)
-			return sim.CurrentTime
-		}
-		if !cast {
-			gargCheck := dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond)
-			if dk.shShouldSpreadDisease(sim) {
-				if !dk.uhShouldWaitForDnD(sim, true, false, false) {
-					if gargCheck {
-						dk.uhAfterGargoyleSequence(sim)
-						return sim.CurrentTime
-					}
-					cast = dk.uhSpreadDiseases(sim, target, s)
-				}
-			}
-			if !cast {
-				if dk.uhDeathCoilCheck(sim) {
-					if gargCheck {
-						dk.uhAfterGargoyleSequence(sim)
-						return sim.CurrentTime
-					}
-					cast = dk.DeathCoil.Cast(sim, target)
-				}
-				if !cast {
-					if gargCheck {
-						dk.uhAfterGargoyleSequence(sim)
-						return sim.CurrentTime
-					}
-
-					if dk.HornOfWinter.CanCast(sim, nil) {
-						dk.HornOfWinter.Cast(sim, target)
-					}
-				}
-			}
-		}
-	}
-
-	// Gargoyle cast needs to be checked more often then default rotation on gcd/resource gain checks
-	if dk.Rotation.UseGargoyle && dk.SummonGargoyle.IsReady(sim) && dk.GCD.IsReady(sim) {
-		return sim.CurrentTime + 100*time.Millisecond
-	}
-
-	return -1
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_UnholySsRotation(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	if dk.uhGargoyleCheck(sim, target, 100*time.Millisecond) {
-		dk.uhAfterGargoyleSequence(sim)
-		return sim.CurrentTime
-	}
-
-	if dk.Talents.GhoulFrenzy {
-		if dk.uhGhoulFrenzyCheck(sim, target) {
-			return sim.CurrentTime
-		}
-	}
-
-	if dk.uhBloodTap(sim, target) {
-		return sim.CurrentTime
-	}
-
-	if dk.uhEmpoweredRuneWeapon(sim, target) {
-		return sim.CurrentTime
-	}
-
-	if dk.uhMindFreeze(sim, target) {
-		return sim.CurrentTime
-	}
-
-	casted := false
-	fuStrike := dk.ScourgeStrike
-	if dk.Inputs.FuStrike == deathknight.FuStrike_Obliterate {
-		fuStrike = dk.Obliterate
-	}
-	if dk.uhDiseaseCheck(sim, target, fuStrike, true, 1) {
-		if dk.uhGargoyleCheck(sim, target, core.GCDDefault+50*time.Millisecond) {
-			dk.uhAfterGargoyleSequence(sim)
-			return sim.CurrentTime
-		}
-		casted = fuStrike.Cast(sim, target)
-	} else {
-		if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+core.GCDDefault*2+50*time.Millisecond) {
-			dk.uhAfterGargoyleSequence(sim)
-			return sim.CurrentTime
-		}
-		dk.uhRecastDiseasesSequence(sim)
-		return sim.CurrentTime
-	}
-	if !casted {
-		gargCheck := dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond)
-		if dk.shShouldSpreadDisease(sim) {
-			if gargCheck {
-				dk.uhAfterGargoyleSequence(sim)
-				return sim.CurrentTime
-			}
-			casted = dk.uhSpreadDiseases(sim, target, s)
-		} else {
-			if dk.uhDiseaseCheck(sim, target, dk.BloodStrike, true, 1) {
-				if dk.uhGargoyleCheck(sim, target, core.GCDDefault+50*time.Millisecond) {
-					dk.uhAfterGargoyleSequence(sim)
-					return sim.CurrentTime
-				}
-				if dk.desolationAuraCheck(sim) {
-					casted = dk.BloodStrike.Cast(sim, target)
-				} else {
-					casted = dk.BloodBoil.Cast(sim, target)
-				}
-			} else {
-				if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+core.GCDDefault*2+50*time.Millisecond) {
-					dk.uhAfterGargoyleSequence(sim)
-					return sim.CurrentTime
-				}
-				dk.uhRecastDiseasesSequence(sim)
-				return sim.CurrentTime
-			}
-		}
-		if !casted {
-			if dk.uhDeathCoilCheck(sim) {
-				if gargCheck {
-					dk.uhAfterGargoyleSequence(sim)
-					return sim.CurrentTime
-				}
-				casted = dk.DeathCoil.Cast(sim, target)
-			}
-			if !casted {
-				if gargCheck {
-					dk.uhAfterGargoyleSequence(sim)
-					return sim.CurrentTime
-				}
-
-				if dk.HornOfWinter.CanCast(sim, nil) {
-					dk.HornOfWinter.Cast(sim, target)
-				}
-			}
-		}
-	}
-
-	// Gargoyle cast needs to be checked more often then default rotation on gcd/resource gain checks
-	if dk.Rotation.UseGargoyle && dk.SummonGargoyle.IsReady(sim) && dk.GCD.IsReady(sim) {
-		return sim.CurrentTime + 100*time.Millisecond
-	}
-
-	return -1
-}
-
-func (dk *DpsDeathknight) uhAfterGargoyleSequence(sim *core.Simulation) {
-	if dk.Rotation.UseEmpowerRuneWeapon && dk.EmpowerRuneWeapon.IsReady(sim) {
-		dk.RotationSequence.Clear()
-
-		if dk.BloodTapAura.IsActive() {
-			dk.RotationSequence.NewAction(dk.RotationAction_CancelBT)
-		}
-
-		didErw := false
-		if dk.Inputs.ArmyOfTheDeadType != proto.Deathknight_Rotation_DoNotUse && dk.ArmyOfTheDead.IsReady(sim) {
-			// If not enough runes for aotd cast ERW
-			if dk.CurrentBloodRunes() < 1 || dk.CurrentFrostRunes() < 1 || dk.CurrentUnholyRunes() < 1 {
-				dk.RotationSequence.NewAction(dk.RotationActionCallback_ERW)
-				didErw = true
-			}
-			dk.RotationSequence.
-				NewAction(dk.RotationActionCallback_AOTD)
-		}
-
-		if dk.Rotation.BlPresence == proto.Deathknight_Rotation_Blood && !dk.PresenceMatches(deathknight.BloodPresence) && (dk.Rotation.PreNerfedGargoyle || dk.Rotation.GargoylePresence == proto.Deathknight_Rotation_Blood) {
-			if didErw || dk.CurrentBloodRunes() > 0 {
-				dk.RotationSequence.NewAction(dk.RotationActionCallback_BP)
-			} else if !didErw && !dk.Rotation.BtGhoulFrenzy && dk.BloodTap.IsReady(sim) {
-				dk.RotationSequence.
-					NewAction(dk.RotationActionCallback_BT).
-					NewAction(dk.RotationActionCallback_BP).
-					NewAction(dk.RotationAction_CancelBT)
-			}
-		}
-
-		if dk.Rotation.UseDeathAndDecay || (!dk.Talents.ScourgeStrike && dk.Talents.Annihilation == 0) {
-			dk.RotationSequence.NewAction(dk.RotationActionUH_ResetToDndMain)
-		} else {
-			dk.RotationSequence.NewAction(dk.RotationActionUH_ResetToSsMain)
-		}
-	} else if dk.Inputs.ArmyOfTheDeadType == proto.Deathknight_Rotation_AsMajorCd && dk.ArmyOfTheDead.IsReady(sim) {
-		dk.RotationSequence.Clear()
-		dk.RotationSequence.
-			NewAction(dk.RotationActionCallback_Haste_Snapshot).
-			NewAction(dk.RotationActionCallback_AOTD)
-
-		if dk.Rotation.UseDeathAndDecay || (!dk.Talents.ScourgeStrike && dk.Talents.Annihilation == 0) {
-			dk.RotationSequence.NewAction(dk.RotationActionUH_ResetToDndMain)
-		} else {
-			dk.RotationSequence.NewAction(dk.RotationActionUH_ResetToSsMain)
-		}
-	}
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_Haste_Snapshot(sim *core.Simulation, _ *core.Unit, s *deathknight.Sequence) time.Duration {
-	dk.ur.gargoyleSnapshot.ActivateMajorCooldowns(sim)
-	dk.UpdateMajorCooldowns()
-	s.Advance()
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) uhGhoulFrenzySequence(_ *core.Simulation, bloodTap bool) {
-	if bloodTap {
-		dk.RotationSequence.Clear().
-			NewAction(dk.RotationActionCallback_BT).
-			NewAction(dk.RotationActionCallback_GF).
-			NewAction(dk.RotationAction_CancelBT)
-	} else {
-		if dk.sr.ffFirst {
-			dk.RotationSequence.Clear().
-				NewAction(dk.RotationActionUH_IT_SetSync).
-				NewAction(dk.RotationActionCallback_GF).
-				NewAction(dk.RotationActionUH_BS)
-		} else {
-			dk.RotationSequence.Clear().
-				NewAction(dk.RotationActionCallback_GF).
-				NewAction(dk.RotationActionUH_IT_SetSync).
-				NewAction(dk.RotationActionUH_BS)
-		}
-	}
-
-	if dk.Rotation.UseDeathAndDecay || (!dk.Talents.ScourgeStrike && dk.Talents.Annihilation == 0) {
-		dk.RotationSequence.NewAction(dk.RotationActionUH_ResetToDndMain)
-	} else {
-		dk.RotationSequence.NewAction(dk.RotationActionUH_ResetToSsMain)
-	}
-}
-
-func (dk *DpsDeathknight) uhRecastDiseasesSequence(_ *core.Simulation) {
-	dk.RotationSequence.Clear()
-
-	// If we have glyph of Disease and both dots active try to refresh with pesti
-	didPesti := false
-	if dk.sr.hasGod {
-		if dk.FrostFeverSpell.Dot(dk.CurrentTarget).IsActive() && dk.BloodPlagueSpell.Dot(dk.CurrentTarget).IsActive() {
-			didPesti = true
-			dk.RotationSequence.NewAction(dk.RotationActionCallback_Pesti_Custom)
-		}
-	}
-
-	// If we did not pesti queue normal dot refresh
-	if !didPesti {
-		if dk.sr.ffFirst {
-			dk.RotationSequence.
-				NewAction(dk.RotationActionUH_FF_ClipCheck).
-				NewAction(dk.RotationActionUH_IT_Custom).
-				NewAction(dk.RotationActionUH_BP_ClipCheck).
-				NewAction(dk.RotationActionUH_PS_Custom).
-				NewAction(dk.RotationActionUH_BS)
-		} else {
-			dk.RotationSequence.
-				NewAction(dk.RotationActionUH_BP_ClipCheck).
-				NewAction(dk.RotationActionUH_PS_Custom).
-				NewAction(dk.RotationActionUH_FF_ClipCheck).
-				NewAction(dk.RotationActionUH_IT_Custom).
-				NewAction(dk.RotationActionUH_BS)
-		}
-	}
-
-	if dk.Rotation.UseDeathAndDecay || (!dk.Talents.ScourgeStrike && dk.Talents.Annihilation == 0) {
-		dk.RotationSequence.NewAction(dk.RotationActionUH_ResetToDndMain)
-	} else {
-		dk.RotationSequence.NewAction(dk.RotationActionUH_ResetToSsMain)
-	}
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_MindFreezeFiller(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	// Use Mind Freeze off GCD to proc extra effects in the opener
-	if dk.Talents.EndlessWinter == 2 && dk.SummonGargoyle.IsReady(sim) {
-		if dk.MindFreezeSpell.IsReady(sim) {
-			dk.MindFreezeSpell.Cast(sim, target)
-		}
-	}
-	s.Advance()
-	return sim.CurrentTime
-}
-
-// Custom BS Callback
-func (dk *DpsDeathknight) RotationActionUH_BS(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	if dk.CurrentBloodRunes()+dk.CurrentDeathRunes() == 0 {
-		s.Advance()
-		return sim.CurrentTime
-	}
-
-	bloodPresenceSwitch := false
-	// Go back to Blood Presence after Gargoyle
-	if !dk.Rotation.PreNerfedGargoyle && !dk.SummonGargoyle.IsReady(sim) && dk.Rotation.Presence == proto.Deathknight_Rotation_Blood && dk.Rotation.GargoylePresence == proto.Deathknight_Rotation_Unholy && dk.PresenceMatches(deathknight.UnholyPresence) && !dk.SummonGargoyleAura.IsActive() {
-		bloodPresenceSwitch = true
-	}
-
-	// Do not switch presences if gargoyle is still up if it's nerfed gargoyle
-	if !dk.Rotation.PreNerfedGargoyle && !dk.SummonGargoyleAura.IsActive() {
-		// Go back to Blood Presence after Bloodlust
-		if dk.Rotation.Presence == proto.Deathknight_Rotation_Blood && dk.Rotation.BlPresence == proto.Deathknight_Rotation_Unholy && dk.PresenceMatches(deathknight.UnholyPresence) && !dk.HasActiveAuraWithTag("Bloodlust") {
-			bloodPresenceSwitch = true
-		}
-
-		// Go back to Blood Presence after gargoyle cast
-		if dk.Rotation.BlPresence == proto.Deathknight_Rotation_Blood && dk.PresenceMatches(deathknight.UnholyPresence) && !dk.SummonGargoyle.IsReady(sim) && dk.HasActiveAuraWithTag("Bloodlust") {
-			bloodPresenceSwitch = true
-		}
-	}
-
-	if bloodPresenceSwitch {
-		if dk.BloodTapAura.IsActive() {
-			dk.BloodTapAura.Deactivate(sim)
-		}
-		dk.BloodPresence.Cast(sim, target)
-		s.Advance()
-		return sim.CurrentTime
-	}
-
-	if dk.shShouldSpreadDisease(sim) {
-		casted := dk.uhSpreadDiseases(sim, target, s)
-		advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-		s.ConditionalAdvance(casted && advance)
-	} else {
-		bloodSpell := dk.BloodBoil
-		prioBs := dk.unholyMightRotationChecks(sim)
-		if dk.desolationAuraCheck(sim) || prioBs {
-			bloodSpell = dk.BloodStrike
-		}
-		casted := bloodSpell.Cast(sim, target)
-		advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-		s.ConditionalAdvance(casted && advance)
-	}
-
-	return -1
-}
-
-// Custom SS Callback for when we have virulence sigil
-func (dk *DpsDeathknight) RotationActionUH_SS_Sigil(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	if dk.HasActiveAura("Sigil of Virulence Proc") || !dk.Rotation.UseEmpowerRuneWeapon {
-		dk.RotationSequence.Clear()
-		if dk.Rotation.UseDeathAndDecay || (!dk.Talents.ScourgeStrike && dk.Talents.Annihilation == 0) {
-			dk.RotationSequence.NewAction(dk.RotationActionUH_ResetToDndMain)
-		} else {
-			dk.RotationSequence.NewAction(dk.RotationActionUH_ResetToSsMain)
-		}
-		return sim.CurrentTime
-	}
-
-	if dk.EmpowerRuneWeapon.IsReady(sim) {
-		dk.EmpowerRuneWeapon.Cast(sim, target)
-	}
-
-	casted := dk.uhCastVirulenceStrike(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func (dk *DpsDeathknight) RotationActionCallback_Pesti_Custom(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	// If we have both dots active try to refresh with pesti and move to normal rotation
-	if dk.FrostFeverSpell.Dot(dk.CurrentTarget).IsActive() && dk.BloodPlagueSpell.Dot(dk.CurrentTarget).IsActive() {
-		dk.Pestilence.Cast(sim, target)
-		s.Advance()
-
-		return -1
-	} else {
-		// If a disease has dropped do normal reapply
-		dk.RotationSequence.Clear()
-
-		if dk.sr.ffFirst {
-			dk.RotationSequence.
-				NewAction(dk.RotationActionUH_FF_ClipCheck).
-				NewAction(dk.RotationActionUH_IT_Custom).
-				NewAction(dk.RotationActionUH_BP_ClipCheck).
-				NewAction(dk.RotationActionUH_PS_Custom).
-				NewAction(dk.RotationActionUH_BS)
-		} else {
-			dk.RotationSequence.
-				NewAction(dk.RotationActionUH_BP_ClipCheck).
-				NewAction(dk.RotationActionUH_PS_Custom).
-				NewAction(dk.RotationActionUH_FF_ClipCheck).
-				NewAction(dk.RotationActionUH_IT_Custom).
-				NewAction(dk.RotationActionUH_BS)
-		}
-		return sim.CurrentTime
-	}
-}
-
-func (dk *DpsDeathknight) RotationActionUH_ResetToSsMain(sim *core.Simulation, _ *core.Unit, _ *deathknight.Sequence) time.Duration {
-	dk.RotationSequence.Clear().
-		NewAction(dk.RotationActionCallback_UnholySsRotation)
-	return sim.CurrentTime
-}
-
-func (dk *DpsDeathknight) RotationActionUH_ResetToDndMain(sim *core.Simulation, _ *core.Unit, _ *deathknight.Sequence) time.Duration {
-	dk.RotationSequence.Clear().
-		NewAction(dk.RotationActionCallback_UnholyDndRotation)
-	return sim.CurrentTime
-}
-
-// Custom PS callback for tracking recasts for pestilence disease sync
-func (dk *DpsDeathknight) RotationActionUH_PS_Custom(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) {
-		dk.uhAfterGargoyleSequence(sim)
-		return sim.CurrentTime
-	}
-	casted := dk.PlagueStrike.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-
-	dk.sr.recastedBP = casted && advance
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-// Custom IT callback for tracking recasts for pestilence disease sync
-func (dk *DpsDeathknight) RotationActionUH_IT_Custom(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()+50*time.Millisecond) {
-		dk.uhAfterGargoyleSequence(sim)
-		return sim.CurrentTime
-	}
-	casted := dk.IcyTouch.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-	if casted && advance {
-		dk.sr.recastedFF = true
-		dk.ur.syncTimeFF = 0
-	}
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-// Custom IT callback for ghoul frenzy frost rune sync
-func (dk *DpsDeathknight) RotationActionUH_IT_SetSync(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	ffRemaining := dk.FrostFeverSpell.Dot(target).RemainingDuration(sim)
-	dk.RotationActionCallback_IT(sim, target, s)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-	if !dk.GCD.IsReady(sim) && advance {
-		dk.ur.syncTimeFF = dk.FrostFeverSpell.Dot(target).Duration - ffRemaining
-	}
-
-	return -1
-}
-
-func (dk *DpsDeathknight) RotationActionUH_FF_ClipCheck(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	dot := dk.FrostFeverSpell.Dot(target)
-	gracePeriod := dk.FrostRuneGraceRemaining(sim)
-	return dk.RotationActionUH_DiseaseClipCheck(dot, gracePeriod, sim, target, s)
-}
-
-func (dk *DpsDeathknight) RotationActionUH_BP_ClipCheck(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	dot := dk.BloodPlagueSpell.Dot(target)
-	gracePeriod := dk.UnholyRuneGraceRemaining(sim)
-	return dk.RotationActionUH_DiseaseClipCheck(dot, gracePeriod, sim, target, s)
-}
-
-// Check if we have enough rune grace period to delay the disease cast
-// so we get more ticks without losing on rune cd
-func (dk *DpsDeathknight) RotationActionUH_DiseaseClipCheck(dot *core.Dot, gracePeriod time.Duration, sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	// TODO: Play around with allowing rune cd to be wasted
-	// for more disease ticks and see if its a worth option for the ui
-	//runeCdWaste := 0 * time.Millisecond
-	waitTime := sim.CurrentTime
-	if dot.TickCount < dot.NumberOfTicks-1 {
-		nextTickAt := dot.ExpiresAt() - dot.TickLength*time.Duration((dot.NumberOfTicks-1)-dot.TickCount)
-		if nextTickAt > sim.CurrentTime && (nextTickAt < sim.CurrentTime+gracePeriod || nextTickAt < sim.CurrentTime+400*time.Millisecond) {
-			// Delay disease for next tick
-			dk.LastOutcome = core.OutcomeMiss
-
-			if dk.uhGargoyleCheck(sim, target, nextTickAt-sim.CurrentTime+50*time.Millisecond) {
-				dk.uhAfterGargoyleSequence(sim)
-				return waitTime
-			}
-
-			waitTime = nextTickAt + 50*time.Millisecond
-		} else {
-			waitTime = sim.CurrentTime
-		}
-	} else {
-		waitTime = sim.CurrentTime
-	}
-
-	s.Advance()
-	return waitTime
-}
diff --git a/sim/deathknight/dps/rotation_unholy_helper.go b/sim/deathknight/dps/rotation_unholy_helper.go
deleted file mode 100644
index e02f8015b1..0000000000
--- a/sim/deathknight/dps/rotation_unholy_helper.go
+++ /dev/null
@@ -1,457 +0,0 @@
-package dps
-
-import (
-	"math"
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-	"github.com/wowsims/wotlk/sim/deathknight"
-)
-
-type WeaponSwapType int32
-
-const (
-	WeaponSwap_None WeaponSwapType = iota
-	WeaponSwap_BlackMagic
-	WeaponSwap_Berserking
-	WeaponSwap_FallenCrusader
-)
-
-type SigilType int32
-
-const (
-	Sigil_Other SigilType = iota
-	Sigil_Virulence
-	Sigil_HangedMan
-)
-
-type UnholyRotation struct {
-	dk *DpsDeathknight
-
-	syncTimeFF time.Duration
-
-	gargoyleSnapshot   *core.SnapshotManager
-	activatingGargoyle bool
-	gargoyleMaxDelay   time.Duration
-	gargoyleMinTime    time.Duration
-
-	mhSwap    WeaponSwapType
-	mhSwapped bool
-
-	ohSwap    WeaponSwapType
-	ohSwapped bool
-
-	bmIcd time.Duration
-
-	sigil       SigilType
-	unholyMight bool
-
-	virulenceAura      *core.Aura
-	unholyMightAura    *core.Aura
-	blackMagicProc     *core.Aura
-	fallenCrusaderProc *core.Aura
-	berserkingMh       *core.Aura
-	berserkingOh       *core.Aura
-}
-
-func (ur *UnholyRotation) Reset(_ *core.Simulation) {
-	ur.syncTimeFF = 0
-	ur.activatingGargoyle = false
-	ur.gargoyleMaxDelay = -1
-
-	ur.mhSwapped = false
-	ur.ohSwapped = false
-	ur.bmIcd = -1
-
-	if ur.dk.Talents.SummonGargoyle {
-		gargMcd := ur.dk.getMajorCooldown(ur.dk.SummonGargoyle.ActionID)
-		if gargMcd != nil {
-			timings := gargMcd.GetTimings()
-			if len(timings) > 0 {
-				ur.gargoyleMinTime = timings[0]
-			}
-		}
-
-		ur.gargoyleSnapshot.ResetProcTrackers()
-	}
-}
-
-func (ur *UnholyRotation) Initialize(dk *DpsDeathknight) {
-	dk.ur.gargoyleSnapshot = core.NewSnapshotManager(dk.GetCharacter())
-	dk.setupGargProcTrackers()
-
-	if dk.Talents.SummonGargoyle && dk.Rotation.UseGargoyle {
-		dk.setupWeaponSwap()
-		ur.blackMagicProc = dk.GetAura("Black Magic Proc")
-		ur.fallenCrusaderProc = dk.GetAura("Rune Of The Fallen Crusader Proc")
-		ur.berserkingMh = dk.GetAura("Berserking MH Proc")
-		ur.berserkingOh = dk.GetAura("Berserking OH Proc")
-	}
-
-	// Init Sigil of Virulence Rotation
-	if dk.Ranged().ID == 47673 {
-		ur.sigil = Sigil_Virulence
-		ur.virulenceAura = dk.GetAura("Sigil of Virulence Proc")
-	}
-
-	// Init T9 2P Proc
-	if dk.HasSetBonus(deathknight.ItemSetThassariansBattlegear, 2) {
-		ur.unholyMight = true
-		ur.unholyMightAura = dk.GetAura("Unholy Might Proc")
-	}
-}
-
-func (dk *DpsDeathknight) getFirstDiseaseAction() deathknight.RotationAction {
-	if dk.sr.ffFirst {
-		return dk.RotationActionCallback_IT
-	}
-	return dk.RotationActionCallback_PS
-}
-
-func (dk *DpsDeathknight) getSecondDiseaseAction() deathknight.RotationAction {
-	if dk.sr.ffFirst {
-		return dk.RotationActionCallback_PS
-	}
-	return dk.RotationActionCallback_IT
-}
-
-func (dk *DpsDeathknight) uhBloodRuneAction(isFirst bool) deathknight.RotationAction {
-	if isFirst {
-		if dk.Env.GetNumTargets() > 1 {
-			return dk.RotationActionCallback_Pesti
-		} else {
-			return dk.RotationActionCallback_BS
-		}
-	} else {
-		return dk.RotationActionCallback_BS
-	}
-}
-
-func (dk *DpsDeathknight) uhCastVirulenceStrike(sim *core.Simulation, target *core.Unit) bool {
-	if dk.Talents.ScourgeStrike {
-		return dk.ScourgeStrike.Cast(sim, target)
-	} else {
-		return dk.DeathStrike.Cast(sim, target)
-	}
-}
-
-func (dk *DpsDeathknight) uhVirulenceRotationCheck(sim *core.Simulation, gargCheck bool) bool {
-	// If we have sigil of virulence
-	// Higher prio SS then Dnd when gargoyle is ready
-	virulenceRefresh := math.Max(0, 10-dk.Inputs.VirulenceRefresh)
-	waitTime := time.Duration(virulenceRefresh) * time.Second
-	prioVirulenceStrike := false
-	if dk.ur.sigil == Sigil_Virulence && (!gargCheck || (dk.SummonGargoyle.IsReady(sim) || dk.SummonGargoyle.CD.TimeToReady(sim) < 10*time.Second)) {
-		prioVirulenceStrike = !dk.ur.virulenceAura.IsActive() || dk.ur.virulenceAura.RemainingDuration(sim) <= waitTime
-	}
-	return prioVirulenceStrike
-}
-
-func (dk *DpsDeathknight) unholyMightRotationChecks(sim *core.Simulation) bool {
-	// If we have T9 2P we prio BS over BB for refreshing the buff when out of ICD
-	prioBs := false
-	if dk.ur.unholyMight {
-		prioBs = dk.ur.unholyMightAura.StartedAt() == 0 || dk.ur.unholyMightAura.StartedAt() < sim.CurrentTime-45*time.Second
-	}
-	return prioBs
-}
-
-func (dk *DpsDeathknight) weaponSwapCheck(sim *core.Simulation) bool {
-	if !dk.ItemSwap.IsEnabled() {
-		return false
-	}
-
-	// Swap if gargoyle will still be on CD for full ICD or if gargoyle is already active
-	shouldSwapBm := dk.ur.bmIcd < sim.CurrentTime && (dk.SummonGargoyle.CD.TimeToReady(sim) > 45*time.Second || dk.SummonGargoyleAura.IsActive())
-	shouldSwapBackFromBm := dk.ur.blackMagicProc.IsActive() // || dk.GetAura("Rune Of The Fallen Crusader Proc").RemainingDuration(sim) < 5*time.Second
-
-	if dk.ur.mhSwap == WeaponSwap_BlackMagic {
-		if !dk.ur.mhSwapped && shouldSwapBm {
-			// Swap to BM
-			dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand})
-			dk.ur.mhSwapped = true
-		} else if dk.ur.mhSwapped && shouldSwapBackFromBm {
-			// Swap to Normal set and set BM Icd tracker
-			dk.ur.bmIcd = dk.ur.blackMagicProc.ExpiresAt() + 35*time.Second
-			dk.ur.mhSwapped = false
-			dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand})
-		}
-	}
-
-	if dk.ur.ohSwap == WeaponSwap_BlackMagic {
-		if !dk.ur.ohSwapped && shouldSwapBm {
-			// Swap to BM
-			dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotOffHand})
-			dk.ur.ohSwapped = true
-		} else if dk.ur.ohSwapped && shouldSwapBackFromBm {
-			// Swap to Normal set and set BM Icd tracker
-			dk.ur.bmIcd = dk.ur.blackMagicProc.ExpiresAt() + 35*time.Second
-			dk.ur.ohSwapped = false
-			dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotOffHand})
-		}
-	}
-
-	shouldSwapBerserking := dk.ur.fallenCrusaderProc.IsActive() &&
-		dk.ur.fallenCrusaderProc.RemainingDuration(sim) > time.Second*10
-
-	shouldSwapBackfromBerserking := false //dk.GetAura("Rune Of The Fallen Crusader Proc").RemainingDuration(sim) < 5*time.Second
-
-	if dk.ur.mhSwap == WeaponSwap_Berserking {
-		if !dk.ur.mhSwapped && !dk.ur.berserkingMh.IsActive() && shouldSwapBerserking {
-			// Swap to Berserking
-			dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand})
-			dk.ur.mhSwapped = true
-		} else if dk.ur.mhSwapped && (dk.ur.berserkingMh.IsActive() || shouldSwapBackfromBerserking) {
-			// Swap to Normal set
-			dk.ur.mhSwapped = false
-			dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand})
-		}
-	}
-
-	if dk.ur.ohSwap == WeaponSwap_Berserking {
-		if !dk.ur.ohSwapped && !dk.ur.berserkingOh.IsActive() && shouldSwapBerserking {
-			// Swap to Berserking
-			dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotOffHand})
-			dk.ur.ohSwapped = true
-		} else if dk.ur.ohSwapped && (dk.ur.berserkingOh.IsActive() || shouldSwapBackfromBerserking) {
-			// Swap to Normal set
-			dk.ur.ohSwapped = false
-			dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotOffHand})
-		}
-	}
-
-	return false
-}
-
-func (dk *DpsDeathknight) desolationAuraCheck(sim *core.Simulation) bool {
-	return !dk.DesolationAura.IsActive() || dk.DesolationAura.RemainingDuration(sim) < 10*time.Second ||
-		dk.Rotation.BloodRuneFiller == proto.Deathknight_Rotation_BloodStrike
-}
-
-func (dk *DpsDeathknight) uhDiseaseCheck(sim *core.Simulation, target *core.Unit, spell *core.Spell, costRunes bool, casts int) bool {
-	return dk.shDiseaseCheck(sim, target, spell, costRunes, casts, 0)
-}
-
-func (dk *DpsDeathknight) uhSpreadDiseases(sim *core.Simulation, target *core.Unit, _ *deathknight.Sequence) bool {
-	if dk.uhDiseaseCheck(sim, target, dk.Pestilence, true, 1) {
-		casted := dk.Pestilence.Cast(sim, target)
-		landed := dk.LastOutcome.Matches(core.OutcomeLanded)
-
-		// Reset flags on successful cast
-		dk.sr.recastedFF = !(casted && landed)
-		dk.sr.recastedBP = !(casted && landed)
-		return casted
-	} else {
-		dk.uhRecastDiseasesSequence(sim)
-		return true
-	}
-}
-
-// Simpler but somehow more effective for overall dps dnd check
-func (dk *DpsDeathknight) uhShouldWaitForDnD(sim *core.Simulation, blood bool, frost bool, unholy bool) bool {
-	if dk.Talents.ImprovedUnholyPresence > 0 {
-		return dk.DeathAndDecay.IsReady(sim) || ((!blood || dk.CurrentBloodRunes() > 1) && (!frost || dk.CurrentFrostRunes() > 1) && (!unholy || dk.CurrentUnholyRunes() > 1))
-	} else {
-		return !(!(dk.DeathAndDecay.CD.IsReady(sim) || dk.DeathAndDecay.CD.TimeToReady(sim) <= 4*time.Second) || ((!blood || dk.CurrentBloodRunes() > 1) && (!frost || dk.CurrentFrostRunes() > 1) && (!unholy || dk.CurrentUnholyRunes() > 1)))
-	}
-}
-
-func (dk *DpsDeathknight) uhGhoulFrenzyCheck(sim *core.Simulation, target *core.Unit) bool {
-	if !dk.Ghoul.Pet.IsEnabled() {
-		return false
-	}
-
-	// If no Ghoul Frenzy Aura or duration less then 10 seconds we try recasting
-	if !dk.GhoulFrenzyAura.IsActive() || dk.GhoulFrenzyAura.RemainingDuration(sim) < 10*time.Second {
-		// Use Ghoul Frenzy with a Blood Tap and Blood rune if all blood runes are on CD and Garg wont come off cd in less then a minute.
-		if (dk.Rotation.BloodTap == proto.Deathknight_Rotation_GhoulFrenzy || dk.Rotation.BtGhoulFrenzy) && dk.BloodTap.CanCast(sim, nil) && dk.GhoulFrenzy.IsReady(sim) && dk.CurrentBloodRunes() == 0 && dk.CurrentUnholyRunes() == 0 {
-			if dk.uhDiseaseCheck(sim, target, dk.GhoulFrenzy, true, 1) {
-				dk.uhGhoulFrenzySequence(sim, true)
-				return true
-			} else {
-				dk.uhRecastDiseasesSequence(sim)
-				return true
-			}
-		} else if !dk.Rotation.BtGhoulFrenzy && dk.GhoulFrenzy.CanCast(sim, nil) && dk.IcyTouch.CanCast(sim, nil) {
-			if dk.uhGargoyleCheck(sim, target, dk.SpellGCD()*2+50*time.Millisecond) {
-				dk.uhAfterGargoyleSequence(sim)
-				return true
-			}
-			// Use Ghoul Frenzy with an Unholy Rune and sync the frost rune with Icy Touch
-			if dk.uhDiseaseCheck(sim, target, dk.GhoulFrenzy, true, 5) && dk.uhDiseaseCheck(sim, target, dk.IcyTouch, true, 5) {
-				// TODO: This can spend runes that should be spent on DnD fix it!
-				dk.uhGhoulFrenzySequence(sim, false)
-				return true
-			} else {
-				dk.uhRecastDiseasesSequence(sim)
-				return true
-			}
-		}
-	}
-	return false
-}
-
-func (dk *DpsDeathknight) uhBloodTap(sim *core.Simulation, target *core.Unit) bool {
-	if !dk.GCD.IsReady(sim) {
-		return false
-	}
-
-	if dk.Rotation.BloodTap != proto.Deathknight_Rotation_GhoulFrenzy && dk.BloodTap.IsReady(sim) && dk.CurrentBloodRunes() == 0 {
-		switch dk.Rotation.BloodTap {
-		case proto.Deathknight_Rotation_IcyTouch:
-			if dk.CurrentFrostRunes() == 0 {
-				dk.BloodTap.Cast(sim, dk.CurrentTarget)
-				dk.IcyTouch.Cast(sim, target)
-				return true
-			}
-		case proto.Deathknight_Rotation_BloodStrikeBT:
-			dk.BloodTap.Cast(sim, dk.CurrentTarget)
-			dk.BloodStrike.Cast(sim, target)
-			return true
-		case proto.Deathknight_Rotation_BloodBoilBT:
-			dk.BloodTap.Cast(sim, dk.CurrentTarget)
-			dk.BloodBoil.Cast(sim, target)
-			return true
-		}
-	}
-
-	return false
-}
-
-func (dk *DpsDeathknight) uhEmpoweredRuneWeapon(sim *core.Simulation, target *core.Unit) bool {
-	if !dk.Rotation.UseEmpowerRuneWeapon {
-		return false
-	}
-
-	if !dk.EmpowerRuneWeapon.IsReady(sim) {
-		return false
-	}
-
-	// Save ERW for best Army Snapshot after Garg
-	if dk.Inputs.ArmyOfTheDeadType == proto.Deathknight_Rotation_AsMajorCd && dk.Rotation.HoldErwArmy && dk.ArmyOfTheDead.IsReady(sim) {
-		return false
-	}
-
-	if !dk.AllRunesSpent() {
-		return false
-	}
-
-	timeToNextRune := dk.AnyRuneReadyAt(sim) - sim.CurrentTime
-	if timeToNextRune < 2*time.Second {
-		return false
-	}
-
-	dk.EmpowerRuneWeapon.Cast(sim, target)
-	return true
-}
-
-func (dk *DpsDeathknight) uhMindFreeze(sim *core.Simulation, target *core.Unit) bool {
-	if dk.Talents.EndlessWinter == 2 && dk.SummonGargoyle.IsReady(sim) {
-		if dk.MindFreezeSpell.IsReady(sim) {
-			dk.MindFreezeSpell.Cast(sim, target)
-			return true
-		}
-	}
-	return false
-}
-
-// Save up Runic Power for Summon Gargoyle - Allow casts above 100 rp or garg CD > 5 sec
-func (dk *DpsDeathknight) uhDeathCoilCheck(sim *core.Simulation) bool {
-	return !dk.Talents.SummonGargoyle || !(dk.SummonGargoyle.IsReady(sim) || dk.SummonGargoyle.CD.TimeToReady(sim) < 5*time.Second) || sim.CurrentTime < dk.ur.gargoyleMinTime-5*time.Second || dk.CurrentRunicPower() >= 100 || !dk.Rotation.UseGargoyle
-}
-
-// Combined checks for casting gargoyle sequence & going back to blood presence after
-func (dk *DpsDeathknight) uhGargoyleCheck(sim *core.Simulation, target *core.Unit, castTime time.Duration) bool {
-	if !dk.Rotation.UseGargoyle {
-		return false
-	}
-
-	if dk.uhGargoyleCanCast(sim, castTime) {
-		if !dk.PresenceMatches(deathknight.UnholyPresence) && (dk.Rotation.PreNerfedGargoyle || dk.Rotation.GargoylePresence == proto.Deathknight_Rotation_Unholy) {
-			if dk.CurrentUnholyRunes() == 0 {
-				if dk.BloodTap.IsReady(sim) {
-					dk.BloodTap.Cast(sim, dk.CurrentTarget)
-				} else {
-					return false
-				}
-			}
-			dk.UnholyPresence.Cast(sim, dk.CurrentTarget)
-		}
-
-		dk.ur.activatingGargoyle = true
-		dk.OnGargoyleStartFirstCast = func() {
-			dk.ur.gargoyleSnapshot.ActivateMajorCooldowns(sim)
-			dk.UpdateMajorCooldowns()
-		}
-		dk.ur.gargoyleSnapshot.ActivateMajorCooldowns(sim)
-		dk.UpdateMajorCooldowns()
-		dk.ur.activatingGargoyle = false
-
-		if dk.SummonGargoyle.Cast(sim, target) {
-			dk.UpdateMajorCooldowns()
-			dk.ur.gargoyleSnapshot.ResetProcTrackers()
-			dk.ur.gargoyleMaxDelay = -1
-			return true
-		}
-	}
-
-	// Go back to Unholy Presence after Gargoyle
-	if !dk.Rotation.PreNerfedGargoyle && !dk.SummonGargoyle.IsReady(sim) && dk.Rotation.Presence == proto.Deathknight_Rotation_Unholy && dk.Rotation.GargoylePresence == proto.Deathknight_Rotation_Blood && dk.PresenceMatches(deathknight.BloodPresence) && !dk.SummonGargoyleAura.IsActive() {
-		if dk.BloodTapAura.IsActive() {
-			dk.BloodTapAura.Deactivate(sim)
-		}
-		return dk.UnholyPresence.Cast(sim, target)
-	}
-
-	// Do not switch presences if gargoyle is still up if it's nerfed gargoyle
-	if !dk.Rotation.PreNerfedGargoyle && dk.SummonGargoyleAura.IsActive() {
-		return false
-	}
-
-	// Go back to Unholy Presence after Bloodlust
-	if dk.Rotation.Presence == proto.Deathknight_Rotation_Unholy && dk.Rotation.BlPresence == proto.Deathknight_Rotation_Blood && dk.PresenceMatches(deathknight.BloodPresence) && !dk.HasActiveAuraWithTag("Bloodlust") {
-		if dk.BloodTapAura.IsActive() {
-			dk.BloodTapAura.Deactivate(sim)
-		}
-		return dk.UnholyPresence.Cast(sim, target)
-	}
-	return false
-}
-
-func (dk *DpsDeathknight) uhGargoyleCanCast(sim *core.Simulation, castTime time.Duration) bool {
-	if !dk.SummonGargoyle.IsReady(sim) {
-		return false
-	}
-	if sim.CurrentTime < dk.ur.gargoyleMinTime {
-		return false
-	}
-	if dk.CurrentRunicPower() < 60 {
-		return false
-	}
-	// Setup max delay possible
-	if dk.ur.gargoyleMaxDelay == -1 {
-		gargCd := dk.SummonGargoyle.CD.Duration
-		timeLeft := sim.GetRemainingDuration()
-		for timeLeft > gargCd {
-			timeLeft = timeLeft - (gargCd + 2*time.Second)
-		}
-		dk.ur.gargoyleMaxDelay = sim.CurrentTime + timeLeft - 2*time.Second
-	}
-	// Cast it if holding will result in less total Gargoyles for the encounter
-	if sim.CurrentTime > dk.ur.gargoyleMaxDelay {
-		return true
-	}
-	// Cast it if holding will take from its duration
-	if sim.GetRemainingDuration() < 32*time.Second {
-		return true
-	}
-	if !dk.PresenceMatches(deathknight.UnholyPresence) && (!dk.BloodTap.CanCast(sim, nil) && dk.CurrentUnholyRunes() == 0) {
-		return false
-	}
-	if !dk.ur.gargoyleSnapshot.CanSnapShot(sim, castTime) {
-		return false
-	}
-
-	return true
-}
diff --git a/sim/deathknight/frost_strike.go b/sim/deathknight/frost_strike.go
index 1cc195f576..be66fea405 100644
--- a/sim/deathknight/frost_strike.go
+++ b/sim/deathknight/frost_strike.go
@@ -63,7 +63,6 @@ func (dk *Deathknight) newFrostStrikeHitSpell(isMH bool) *core.Spell {
 
 			if isMH {
 				spell.SpendRefundableCost(sim, result)
-				dk.LastOutcome = result.Outcome
 				dk.threatOfThassarianProc(sim, result, dk.FrostStrikeOhHit)
 			}
 
diff --git a/sim/deathknight/ghoul_frenzy.go b/sim/deathknight/ghoul_frenzy.go
index 3b65aa7bdc..7f58727694 100644
--- a/sim/deathknight/ghoul_frenzy.go
+++ b/sim/deathknight/ghoul_frenzy.go
@@ -65,24 +65,12 @@ func (dk *Deathknight) registerGhoulFrenzySpell() {
 		ActionID: core.ActionID{SpellID: 63560},
 		Label:    "Ghoul Frenzy",
 		Duration: time.Second * 30.0,
-		OnReset: func(aura *core.Aura, sim *core.Simulation) {
-			if !dk.IsUsingAPL && dk.Inputs.PrecastGhoulFrenzy {
-				dk.GhoulFrenzyAura.Activate(sim)
-				dk.GhoulFrenzyAura.UpdateExpires(sim.CurrentTime + time.Second*20)
-			}
-		},
 	})
 
 	dk.Ghoul.GhoulFrenzyAura = dk.Ghoul.RegisterAura(core.Aura{
 		ActionID: core.ActionID{SpellID: 63560},
 		Label:    "Ghoul Frenzy",
 		Duration: time.Second * 30.0,
-		OnReset: func(aura *core.Aura, sim *core.Simulation) {
-			if !dk.IsUsingAPL && dk.Inputs.PrecastGhoulFrenzy {
-				dk.Ghoul.GhoulFrenzyAura.Activate(sim)
-				dk.Ghoul.GhoulFrenzyAura.UpdateExpires(sim.CurrentTime + time.Second*20)
-			}
-		},
 		OnGain: func(aura *core.Aura, sim *core.Simulation) {
 			dk.Ghoul.MultiplyMeleeSpeed(sim, 1.25)
 		},
diff --git a/sim/deathknight/heart_strike.go b/sim/deathknight/heart_strike.go
index 28ea32f67e..1fa9ebf983 100644
--- a/sim/deathknight/heart_strike.go
+++ b/sim/deathknight/heart_strike.go
@@ -65,7 +65,6 @@ func (dk *Deathknight) newHeartStrikeSpell(isMainTarget bool, isDrw bool) *core.
 					if dk.Env.GetNumTargets() > 1 {
 						dk.HeartStrikeOffHit.Cast(sim, dk.Env.NextTargetUnit(target))
 					}
-					dk.LastOutcome = result.Outcome
 				}
 			}
 		},
diff --git a/sim/deathknight/horn_of_winter.go b/sim/deathknight/horn_of_winter.go
index 7cea15ace5..8554b8a488 100644
--- a/sim/deathknight/horn_of_winter.go
+++ b/sim/deathknight/horn_of_winter.go
@@ -20,11 +20,6 @@ func (dk *Deathknight) registerHornOfWinterSpell() {
 		Label:    "Horn of Winter",
 		ActionID: actionID,
 		Duration: duration,
-		OnReset: func(aura *core.Aura, sim *core.Simulation) {
-			if !dk.IsUsingAPL && dk.Inputs.PrecastHornOfWinter && dk.Inputs.RefreshHornOfWinter {
-				aura.Activate(sim)
-			}
-		},
 		OnGain: func(aura *core.Aura, sim *core.Simulation) {
 			if !dk.OtherRelevantStrAgiActive {
 				dk.HornOfWinterAura.Unit.AddStatsDynamic(sim, bonusStats)
@@ -52,9 +47,6 @@ func (dk *Deathknight) registerHornOfWinterSpell() {
 			IgnoreHaste: true,
 		},
 		ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
-			if !dk.IsUsingAPL && dk.Inputs.RefreshHornOfWinter {
-				dk.HornOfWinterAura.Activate(sim)
-			}
 			dk.AddRunicPower(sim, 10, rpMetrics)
 		},
 	})
diff --git a/sim/deathknight/howling_blast.go b/sim/deathknight/howling_blast.go
index 1662402f29..eb35f49173 100644
--- a/sim/deathknight/howling_blast.go
+++ b/sim/deathknight/howling_blast.go
@@ -44,8 +44,6 @@ func (dk *Deathknight) registerHowlingBlastSpell() {
 		ThreatMultiplier: 1,
 
 		ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
-			dk.AoESpellNumTargetsHit = 0
-
 			for _, aoeTarget := range sim.Encounter.TargetUnits {
 				baseDamage := (sim.Roll(518, 562) + 0.2*dk.getImpurityBonus(spell)) *
 					dk.glacielRotBonus(aoeTarget) *
@@ -55,13 +53,8 @@ func (dk *Deathknight) registerHowlingBlastSpell() {
 
 				result := spell.CalcDamage(sim, aoeTarget, baseDamage, spell.OutcomeMagicHitAndCrit)
 
-				if result.Landed() {
-					dk.AoESpellNumTargetsHit++
-				}
-
 				if aoeTarget == target {
 					spell.SpendRefundableCost(sim, result)
-					dk.LastOutcome = result.Outcome
 				}
 				if rpBonus > 0 && result.Landed() {
 					dk.AddRunicPower(sim, rpBonus, spell.RunicPowerMetrics())
diff --git a/sim/deathknight/icy_touch.go b/sim/deathknight/icy_touch.go
index b450cf7c62..e622ee282b 100644
--- a/sim/deathknight/icy_touch.go
+++ b/sim/deathknight/icy_touch.go
@@ -40,7 +40,6 @@ func (dk *Deathknight) registerIcyTouchSpell() {
 			result := spell.CalcDamage(sim, target, baseDamage, spell.OutcomeMagicHitAndCrit)
 			spell.SpendRefundableCost(sim, result)
 
-			dk.LastOutcome = result.Outcome
 			if result.Landed() {
 				dk.FrostFeverExtended[target.Index] = 0
 				dk.FrostFeverSpell.Cast(sim, target)
diff --git a/sim/deathknight/items.go b/sim/deathknight/items.go
index 0bdc00211e..9bddfbaea6 100644
--- a/sim/deathknight/items.go
+++ b/sim/deathknight/items.go
@@ -495,6 +495,8 @@ func (dk *Deathknight) registerItems() {
 		IcyTouchActionID,
 	}
 
+	targetsHit := 0
+
 	dk.RegisterAura(core.Aura{
 		ActionID:  core.ActionID{SpellID: 53386},
 		Label:     "Cinderglacier",
@@ -513,9 +515,15 @@ func (dk *Deathknight) registerItems() {
 		},
 		OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
 			if spell.ActionID == HowlingBlastActionID || spell.ActionID == BloodBoilActionID {
+				if result.Target.Index == 0 {
+					targetsHit = 0
+				}
+				if result.Landed() {
+					targetsHit++
+				}
 				if result.Target.Index == sim.GetNumTargets()-1 {
 					// Last target, consume a stack for every target hit
-					for i := int32(0); i < dk.AoESpellNumTargetsHit; i++ {
+					for i := 0; i < targetsHit; i++ {
 						if aura.IsActive() {
 							aura.RemoveStack(sim)
 						}
@@ -524,7 +532,7 @@ func (dk *Deathknight) registerItems() {
 				return
 			}
 
-			if !result.Outcome.Matches(core.OutcomeLanded) {
+			if !result.Landed() {
 				return
 			}
 
diff --git a/sim/deathknight/obliterate.go b/sim/deathknight/obliterate.go
index a296c34e41..249e517bfc 100644
--- a/sim/deathknight/obliterate.go
+++ b/sim/deathknight/obliterate.go
@@ -63,7 +63,6 @@ func (dk *Deathknight) newObliterateHitSpell(isMH bool) *core.Spell {
 
 			if isMH {
 				spell.SpendRefundableCostAndConvertFrostOrUnholyRune(sim, result, deathConvertChance)
-				dk.LastOutcome = result.Outcome
 				dk.threatOfThassarianProc(sim, result, dk.ObliterateOhHit)
 
 				if sim.RandomFloat("Annihilation") < diseaseConsumptionChance {
diff --git a/sim/deathknight/pestilence.go b/sim/deathknight/pestilence.go
index 1cd2c962d3..f469766812 100644
--- a/sim/deathknight/pestilence.go
+++ b/sim/deathknight/pestilence.go
@@ -38,7 +38,6 @@ func (dk *Deathknight) registerPestilenceSpell() {
 
 				if aoeTarget == target {
 					spell.SpendRefundableCostAndConvertBloodRune(sim, result, deathConvertChance)
-					dk.LastOutcome = result.Outcome
 				}
 				if result.Landed() {
 					refreshCFEP := false
diff --git a/sim/deathknight/plague_strike.go b/sim/deathknight/plague_strike.go
index 5695c3e7e3..d6635013c5 100644
--- a/sim/deathknight/plague_strike.go
+++ b/sim/deathknight/plague_strike.go
@@ -53,7 +53,6 @@ func (dk *Deathknight) newPlagueStrikeSpell(isMH bool) *core.Spell {
 			if isMH {
 				spell.SpendRefundableCost(sim, result)
 				dk.threatOfThassarianProc(sim, result, dk.PlagueStrikeOhHit)
-				dk.LastOutcome = result.Outcome
 				if result.Landed() {
 					dk.BloodPlagueExtended[target.Index] = 0
 					dk.BloodPlagueSpell.Cast(sim, target)
diff --git a/sim/deathknight/rotation.go b/sim/deathknight/rotation.go
deleted file mode 100644
index 7937106d56..0000000000
--- a/sim/deathknight/rotation.go
+++ /dev/null
@@ -1,296 +0,0 @@
-package deathknight
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-)
-
-func (dk *Deathknight) OnAutoAttack(_ *core.Simulation, _ *core.Spell) {
-}
-
-func (dk *Deathknight) OnGCDReady(sim *core.Simulation) {
-	dk.tryUseGCD(sim)
-}
-
-func (dk *Deathknight) tryUseGCD(sim *core.Simulation) {
-	dk.DoRotation(sim)
-}
-
-func (dk *Deathknight) RotationActionCallback_IT(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.IcyTouch.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_PS(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.PlagueStrike.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_HW(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	dk.HornOfWinter.Cast(sim, target)
-
-	s.Advance()
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_DRW(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.DancingRuneWeapon.Cast(sim, target)
-
-	s.ConditionalAdvance(casted)
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_UF(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.UnholyFrenzy.Cast(sim, target)
-
-	s.ConditionalAdvance(casted)
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_DS(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.DeathStrike.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_HS(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := false
-	if dk.Talents.HeartStrike {
-		casted = dk.HeartStrike.Cast(sim, target)
-	} else {
-		casted = dk.BloodStrike.Cast(sim, target)
-	}
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_BT(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.BloodTap.Cast(sim, target)
-	s.ConditionalAdvance(casted)
-	return sim.CurrentTime
-}
-
-func (dk *Deathknight) RotationActionCallback_ERW(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.EmpowerRuneWeapon.Cast(sim, target)
-	s.ConditionalAdvance(casted)
-	return sim.CurrentTime
-}
-
-func (dk *Deathknight) RotationActionCallback_Obli(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	if dk.Deathchill != nil && dk.Deathchill.IsReady(sim) {
-		dk.Deathchill.Cast(sim, target)
-	}
-	casted := dk.Obliterate.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_FS(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	dk.FrostStrike.Cast(sim, target)
-
-	s.Advance()
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_HB(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	dk.HowlingBlast.Cast(sim, target)
-
-	s.Advance()
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_Pesti(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.Pestilence.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_BS(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.BloodStrike.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_BB(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.BloodBoil.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_SS(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.ScourgeStrike.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_DND(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.DeathAndDecay.Cast(sim, target)
-	s.ConditionalAdvance(casted)
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_GF(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.GhoulFrenzy.Cast(sim, target)
-	s.ConditionalAdvance(casted)
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_DC(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	dk.DeathCoil.Cast(sim, target)
-	s.Advance()
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_AOTD(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.ArmyOfTheDead.Cast(sim, target)
-	s.ConditionalAdvance(casted)
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_Garg(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.SummonGargoyle.Cast(sim, target)
-	s.ConditionalAdvance(casted)
-	return -1
-}
-
-func (dk *Deathknight) RotationActionCallback_BP(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.BloodPresence.Cast(sim, target)
-
-	waitTime := time.Duration(-1)
-	if !casted && !dk.BloodPresence.IsReady(sim) {
-		if dk.BloodPresence.CD.ReadyAt() != sim.CurrentTime {
-			waitTime = dk.BloodPresence.CD.ReadyAt()
-		}
-	}
-
-	s.ConditionalAdvance(casted)
-	return waitTime
-}
-
-func (dk *Deathknight) RotationActionCallback_FP(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.FrostPresence.Cast(sim, target)
-
-	waitTime := time.Duration(-1)
-	if !casted && !dk.FrostPresence.IsReady(sim) {
-		if dk.FrostPresence.CD.ReadyAt() != sim.CurrentTime {
-			waitTime = dk.FrostPresence.CD.ReadyAt()
-		}
-	}
-	s.ConditionalAdvance(casted)
-	return waitTime
-}
-
-func (dk *Deathknight) RotationActionCallback_UP(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	casted := dk.UnholyPresence.Cast(sim, target)
-
-	waitTime := time.Duration(-1)
-	if !casted && !dk.UnholyPresence.IsReady(sim) {
-		if dk.UnholyPresence.CD.ReadyAt() != sim.CurrentTime {
-			waitTime = dk.UnholyPresence.CD.ReadyAt()
-		}
-	}
-	s.ConditionalAdvance(casted)
-	return waitTime
-}
-
-func (dk *Deathknight) RotationActionCallback_RD(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration {
-	if !dk.Talents.MasterOfGhouls {
-		dk.RaiseDead.Cast(sim, target)
-	}
-
-	s.Advance()
-	return -1
-}
-
-func (s *Sequence) DoAction(sim *core.Simulation, target *core.Unit, _ *Deathknight) time.Duration {
-	action := s.actions[s.idx]
-	return action(sim, target, s)
-}
-
-func (dk *Deathknight) Wait(sim *core.Simulation) {
-	waitUntil := sim.CurrentTime + time.Millisecond*200
-
-	anyRuneAt := dk.AnyRuneReadyAt(sim)
-	if anyRuneAt != sim.CurrentTime {
-		waitUntil = min(waitUntil, anyRuneAt)
-	} else {
-		waitUntil = min(waitUntil, dk.AnySpentRuneReadyAt())
-	}
-	if sim.Log != nil {
-		dk.Log(sim, "DK Wait: %s, any at: %s, any spent at: %s", waitUntil, anyRuneAt, dk.AnySpentRuneReadyAt())
-	}
-
-	if dk.ButcheryPA != nil {
-		waitUntil = min(dk.ButcheryPA.NextActionAt, waitUntil)
-	}
-	waitUntil = max(sim.CurrentTime, waitUntil)
-
-	if !dk.Inputs.IsDps {
-		target := dk.CurrentTarget
-		if dk.IsMainTank() {
-			if targetSwingAt := target.AutoAttacks.NextAttackAt(); targetSwingAt > sim.CurrentTime {
-				waitUntil = min(waitUntil, targetSwingAt)
-			}
-		}
-	}
-
-	dk.WaitUntil(sim, waitUntil)
-}
-
-func (dk *Deathknight) IsMainTank() bool {
-	return dk.CurrentTarget.CurrentTarget == &dk.Unit
-}
-
-func (dk *Deathknight) DoRotation(sim *core.Simulation) {
-	if dk.IsUsingAPL {
-		return
-	}
-
-	target := dk.CurrentTarget
-
-	optWait := time.Duration(-1)
-	if dk.RotationSequence.IsOngoing() {
-		if sim.Log != nil {
-			dk.Log(sim, "DoSequenceAction")
-		}
-		optWait = dk.RotationSequence.DoAction(sim, target, dk)
-	}
-
-	if dk.GCD.IsReady(sim) {
-		if sim.Log != nil {
-			dk.Log(sim, "DoGCD")
-		}
-		for optWait == 0 && dk.GCD.IsReady(sim) {
-			if sim.Log != nil {
-				dk.Log(sim, "DoAction")
-			}
-			optWait = dk.RotationSequence.DoAction(sim, target, dk)
-		}
-
-		if optWait != -1 {
-			if optWait < sim.CurrentTime {
-				dk.Wait(sim)
-			} else {
-				dk.WaitUntil(sim, optWait)
-			}
-		} else if dk.GCD.IsReady(sim) {
-			dk.Wait(sim)
-		}
-	}
-}
diff --git a/sim/deathknight/rotation_helper.go b/sim/deathknight/rotation_helper.go
deleted file mode 100644
index f65e5d1003..0000000000
--- a/sim/deathknight/rotation_helper.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package deathknight
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-)
-
-// return bool is if its on GCD
-// return duration is an optional wait time
-type RotationAction func(sim *core.Simulation, target *core.Unit, s *Sequence) time.Duration
-
-// Add your UH rotation Actions here and then on the DoNext function
-
-type Sequence struct {
-	idx     int
-	actions []RotationAction
-}
-
-func (s *Sequence) IsOngoing() bool {
-	return s.idx < len(s.actions)
-}
-
-func (s *Sequence) Advance() {
-	s.idx += 1
-}
-
-func (s *Sequence) ConditionalAdvance(condition bool) {
-	if condition {
-		s.idx += 1
-	}
-}
-
-func (s *Sequence) NewAction(action RotationAction) *Sequence {
-	s.actions = append(s.actions, action)
-	return s
-}
-
-func (s *Sequence) Clear() *Sequence {
-	s.actions = s.actions[:0]
-	s.idx = 0
-	return s
-}
-
-type RotationHelper struct {
-	RotationSequence *Sequence
-
-	LastOutcome core.HitOutcome
-	LastCast    *core.Spell
-	NextCast    *core.Spell
-
-	AoESpellNumTargetsHit int32
-}
diff --git a/sim/deathknight/scourge_strike.go b/sim/deathknight/scourge_strike.go
index 4bec2961ca..ad72c18a5e 100644
--- a/sim/deathknight/scourge_strike.go
+++ b/sim/deathknight/scourge_strike.go
@@ -82,7 +82,6 @@ func (dk *Deathknight) registerScourgeStrikeSpell() {
 
 			spell.SpendRefundableCost(sim, result)
 
-			dk.LastOutcome = result.Outcome
 			if result.Landed() && dk.DiseasesAreActive(target) {
 				dk.LastScourgeStrikeDamage = result.Damage
 				shadowDamageSpell.Cast(sim, target)
diff --git a/sim/deathknight/tank/openers.go b/sim/deathknight/tank/openers.go
deleted file mode 100644
index 9496f9940b..0000000000
--- a/sim/deathknight/tank/openers.go
+++ /dev/null
@@ -1,130 +0,0 @@
-package tank
-
-import (
-	"github.com/wowsims/wotlk/sim/core/proto"
-	"slices"
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/deathknight"
-)
-
-const threatOpenerCastsBeforeBloodTap = 2
-const normalOpenerCastsBeforeBloodTap = 3
-
-func (dk *TankDeathknight) TankRA_BloodSpell(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := dk.BloodSpell.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func (dk *TankDeathknight) TankRA_FuSpell(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := dk.FuSpell.Cast(sim, target)
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func (dk *TankDeathknight) TankRA_IT(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	casted := dk.IcyTouch.Cast(sim, target)
-	if !casted && dk.Talents.UnbreakableArmor && !dk.UnbreakableArmor.IsReady(sim) {
-		s.Advance()
-		return -1
-	}
-	advance := dk.LastOutcome.Matches(core.OutcomeLanded)
-
-	s.ConditionalAdvance(casted && advance)
-	return -1
-}
-
-func shouldUseBloodTapInOpener(dk *TankDeathknight) bool {
-	bloodTapDefensiveCd := dk.GetMajorCooldown(dk.BloodTap.ActionID)
-	if bloodTapDefensiveCd != nil {
-		timings := bloodTapDefensiveCd.GetTimings()
-		slices.Sort(timings)
-		if len(timings) == 0 ||
-			(len(timings) > 0 && timings[0] <
-				dk.BloodTap.CD.Duration+getPlannedOpenerBloodTapUsageTime(dk)) {
-			return false
-		} else {
-			return true
-		}
-	}
-	return true
-}
-
-func getPlannedOpenerBloodTapUsageTime(dk *TankDeathknight) time.Duration {
-	if dk.Rotation.Opener == proto.TankDeathknight_Rotation_Threat {
-		return threatOpenerCastsBeforeBloodTap * core.GCDDefault
-	}
-	return normalOpenerCastsBeforeBloodTap * core.GCDDefault
-}
-
-func (dk *TankDeathknight) setupTankRegularERWOpener() {
-	if shouldUseBloodTapInOpener(dk) {
-		dk.setupTankRegularERWOpenerWithBloodTap()
-	} else {
-		dk.setupTankRegularERWOpenerWithoutBloodTap()
-	}
-}
-
-func (dk *TankDeathknight) setupTankRegularERWOpenerWithBloodTap() {
-	dk.RotationSequence.
-		NewAction(dk.TankRA_IT).
-		NewAction(dk.RotationActionCallback_PS).
-		NewAction(dk.TankRA_FuSpell).
-		NewAction(dk.RotationActionCallback_BT).
-		NewAction(dk.TankRA_IT).
-		NewAction(dk.TankRA_BloodSpell).
-		NewAction(dk.RotationActionCallback_ERW).
-		NewAction(dk.RotationActionCallback_Pesti).
-		NewAction(dk.TankRA_IT).
-		NewAction(dk.TankRA_IT).
-		NewAction(dk.TankRA_IT).
-		NewAction(dk.RotationActionCallback_RD).
-		NewAction(dk.TankRA_FuSpell)
-}
-
-func (dk *TankDeathknight) setupTankRegularERWOpenerWithoutBloodTap() {
-	dk.RotationSequence.
-		NewAction(dk.TankRA_IT).
-		NewAction(dk.RotationActionCallback_PS).
-		NewAction(dk.TankRA_FuSpell).
-		NewAction(dk.TankRA_BloodSpell).
-		NewAction(dk.RotationActionCallback_ERW).
-		NewAction(dk.RotationActionCallback_Pesti).
-		NewAction(dk.TankRA_IT).
-		NewAction(dk.TankRA_IT).
-		NewAction(dk.TankRA_IT).
-		NewAction(dk.RotationActionCallback_RD).
-		NewAction(dk.TankRA_FuSpell)
-}
-
-func (dk *TankDeathknight) setupTankThreatERWOpener() {
-	if shouldUseBloodTapInOpener(dk) {
-		dk.setupTankThreatERWOpenerWithBloodTap()
-	} else {
-		dk.setupTankThreatERWOpenerWithoutBloodTap()
-	}
-}
-
-func (dk *TankDeathknight) setupTankThreatERWOpenerWithBloodTap() {
-	dk.RotationSequence.
-		NewAction(dk.TankRA_IT).
-		NewAction(dk.TankRA_IT).
-		NewAction(dk.RotationActionCallback_BT).
-		NewAction(dk.TankRA_IT).
-		NewAction(dk.RotationActionCallback_ERW).
-		NewAction(dk.TankRA_IT).
-		NewAction(dk.TankRA_IT).
-		NewAction(dk.TankRA_IT).
-		NewAction(dk.RotationActionCallback_PS).
-		NewAction(dk.TankRA_BloodSpell)
-}
-
-// If maintaining GCD parity is desired, the threat opener without BT should cast DS with
-// the GCD previously allocated to the Blood Tapped IT before using ERW
-func (dk *TankDeathknight) setupTankThreatERWOpenerWithoutBloodTap() {
-	dk.setupTankRegularERWOpenerWithoutBloodTap()
-}
diff --git a/sim/deathknight/tank/rotation_hps.go b/sim/deathknight/tank/rotation_hps.go
deleted file mode 100644
index 9aca447a84..0000000000
--- a/sim/deathknight/tank/rotation_hps.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package tank
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-	"github.com/wowsims/wotlk/sim/deathknight"
-)
-
-func (dk *TankDeathknight) TankRA_Hps(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	if !dk.GCD.IsReady(sim) {
-		return dk.NextGCDAt()
-	}
-
-	if dk.DoDiseaseChecks(sim, target, s) {
-		return -1
-	}
-
-	fd := dk.CurrentFrostRunes() + dk.CurrentDeathRunes()
-	ud := dk.CurrentUnholyRunes() + dk.CurrentDeathRunes()
-
-	if fd > 0 && ud > 0 && dk.FuSpell.CanCast(sim, target) && (dk.CurrentHealthPercent() < 0.75 || dk.FuSpell == dk.Obliterate) {
-		dk.FuSpell.Cast(sim, target)
-		return -1
-	}
-
-	if dk.Rotation.BloodTapPrio == proto.TankDeathknight_Rotation_Offensive {
-		if dk.BloodTap.CanCast(sim, target) {
-			dk.BloodTap.Cast(sim, target)
-			dk.IcyTouch.Cast(sim, target)
-			dk.CancelBloodTap(sim)
-			return -1
-		}
-	}
-
-	if dk.DoFrostCast(sim, target, s) {
-		return -1
-	}
-
-	if dk.DoBloodCast(sim, target, s) {
-		return -1
-	}
-
-	return -1
-}
diff --git a/sim/deathknight/tank/rotation_shared.go b/sim/deathknight/tank/rotation_shared.go
deleted file mode 100644
index 0dd5778384..0000000000
--- a/sim/deathknight/tank/rotation_shared.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package tank
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/deathknight"
-)
-
-func (dk *TankDeathknight) DoDiseaseChecks(sim *core.Simulation, target *core.Unit, _ *deathknight.Sequence) bool {
-	t := sim.CurrentTime
-	recast := 3 * time.Second // 2 GCDs for miss
-	ff := dk.FrostFeverSpell.Dot(target).ExpiresAt() - t
-	bp := dk.BloodPlagueSpell.Dot(target).ExpiresAt() - t
-
-	if ff <= 0 && dk.IcyTouch.CanCast(sim, target) {
-		dk.IcyTouch.Cast(sim, target)
-		return true
-	}
-
-	if bp <= 0 && dk.PlagueStrike.CanCast(sim, target) {
-		dk.PlagueStrike.Cast(sim, target)
-		return true
-	}
-
-	if ff <= recast || bp <= recast && dk.Pestilence.CanCast(sim, target) {
-		dk.Pestilence.Cast(sim, target)
-		return true
-	}
-
-	return false
-}
-
-func (dk *TankDeathknight) DoFrostCast(sim *core.Simulation, target *core.Unit, _ *deathknight.Sequence) bool {
-	if dk.Talents.FrostStrike && dk.FrostStrike.CanCast(sim, target) {
-		dk.FrostStrike.Cast(sim, target)
-		return true
-	}
-
-	if dk.Talents.HowlingBlast && dk.FreezingFogAura.IsActive() && dk.HowlingBlast.CanCast(sim, target) {
-		dk.HowlingBlast.Cast(sim, target)
-		return true
-	}
-
-	return false
-}
-
-func (dk *TankDeathknight) DoBloodCast(sim *core.Simulation, target *core.Unit, _ *deathknight.Sequence) bool {
-	t := sim.CurrentTime
-	recast := 3 * time.Second // 2 GCDs for miss
-	ff := dk.FrostFeverSpell.Dot(target).ExpiresAt() - t
-	bp := dk.BloodPlagueSpell.Dot(target).ExpiresAt() - t
-	b := dk.CurrentBloodRunes()
-
-	if b >= 1 {
-		if dk.NormalSpentBloodRuneReadyAt(sim)-t < ff-recast && dk.NormalSpentBloodRuneReadyAt(sim)-t < bp-recast {
-			dk.BloodSpell.Cast(sim, target)
-			return true
-		}
-	}
-
-	return false
-}
diff --git a/sim/deathknight/tank/rotation_tps.go b/sim/deathknight/tank/rotation_tps.go
deleted file mode 100644
index fe7f80afe6..0000000000
--- a/sim/deathknight/tank/rotation_tps.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package tank
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-	"github.com/wowsims/wotlk/sim/deathknight"
-)
-
-func (dk *TankDeathknight) TankRA_Tps(sim *core.Simulation, target *core.Unit, s *deathknight.Sequence) time.Duration {
-	if !dk.GCD.IsReady(sim) {
-		return dk.NextGCDAt()
-	}
-
-	t := sim.CurrentTime
-	ff := dk.FrostFeverSpell.Dot(target).ExpiresAt() - t
-	bp := dk.BloodPlagueSpell.Dot(target).ExpiresAt() - t
-
-	if ff <= 0 && dk.IcyTouch.CanCast(sim, target) {
-		dk.IcyTouch.Cast(sim, target)
-		return -1
-	}
-
-	if bp <= 0 && dk.PlagueStrike.CanCast(sim, target) {
-		dk.PlagueStrike.Cast(sim, target)
-		return -1
-	}
-
-	if ff <= 2*time.Second || bp <= 2*time.Second && dk.Pestilence.CanCast(sim, target) {
-		dk.Pestilence.Cast(sim, target)
-		return -1
-	}
-
-	if dk.switchIT && dk.IcyTouch.CanCast(sim, target) {
-		dk.IcyTouch.Cast(sim, target)
-
-		if dk.DeathRunesInFU() == 0 {
-			dk.switchIT = false
-		}
-
-		return -1
-	}
-
-	if !dk.switchIT && dk.FuSpell.CanCast(sim, target) {
-		dk.FuSpell.Cast(sim, target)
-
-		if dk.DeathRunesInFU() == 4 {
-			dk.switchIT = true
-		}
-
-		return -1
-	}
-
-	if dk.Rotation.BloodTapPrio == proto.TankDeathknight_Rotation_Offensive {
-		if dk.BloodTap.CanCast(sim, target) {
-			dk.BloodTap.Cast(sim, target)
-			dk.IcyTouch.Cast(sim, target)
-			dk.CancelBloodTap(sim)
-			return -1
-		}
-	}
-
-	if dk.DoFrostCast(sim, target, s) {
-		return -1
-	}
-
-	if dk.DoBloodCast(sim, target, s) {
-		return -1
-	}
-
-	return -1
-}
diff --git a/sim/deathknight/tank/tank_deathknight.go b/sim/deathknight/tank/tank_deathknight.go
index 8483a3a40d..6ede0bec8e 100644
--- a/sim/deathknight/tank/tank_deathknight.go
+++ b/sim/deathknight/tank/tank_deathknight.go
@@ -29,8 +29,6 @@ type TankDeathknight struct {
 	switchIT   bool
 	BloodSpell *core.Spell
 	FuSpell    *core.Spell
-
-	Rotation *proto.TankDeathknight_Rotation
 }
 
 func NewTankDeathknight(character *core.Character, options *proto.Player) *TankDeathknight {
@@ -41,7 +39,6 @@ func NewTankDeathknight(character *core.Character, options *proto.Player) *TankD
 			IsDps:              false,
 			StartingRunicPower: dkOptions.Options.StartingRunicPower,
 		}, options.TalentsString, false),
-		Rotation: dkOptions.Rotation,
 	}
 
 	tankDk.Inputs.UnholyFrenzyTarget = dkOptions.Options.UnholyFrenzyTarget
@@ -51,7 +48,7 @@ func NewTankDeathknight(character *core.Character, options *proto.Player) *TankD
 		OffHand:        tankDk.WeaponFromOffHand(tankDk.DefaultMeleeCritMultiplier()),
 		AutoSwingMelee: true,
 		ReplaceMHSwing: func(sim *core.Simulation, mhSwingSpell *core.Spell) *core.Spell {
-			if (!tankDk.IsUsingAPL || tankDk.RuneStrikeQueued) && tankDk.RuneStrike.CanCast(sim, nil) {
+			if tankDk.RuneStrikeQueued && tankDk.RuneStrike.CanCast(sim, nil) {
 				return tankDk.RuneStrike
 			} else {
 				return mhSwingSpell
@@ -77,40 +74,6 @@ func (dk *TankDeathknight) Initialize() {
 	dk.Deathknight.Initialize()
 }
 
-func (dk *TankDeathknight) SetupRotations() {
-	dk.RotationSequence.Clear()
-
-	if dk.Rotation.Opener == proto.TankDeathknight_Rotation_Regular {
-		dk.setupTankRegularERWOpener()
-	} else if dk.Rotation.Opener == proto.TankDeathknight_Rotation_Threat {
-		dk.setupTankThreatERWOpener()
-	}
-
-	if dk.Rotation.OptimizationSetting == proto.TankDeathknight_Rotation_Hps {
-		dk.RotationSequence.NewAction(dk.TankRA_Hps)
-	} else if dk.Rotation.OptimizationSetting == proto.TankDeathknight_Rotation_Tps {
-		dk.RotationSequence.NewAction(dk.TankRA_Tps)
-	}
-
-	if dk.Rotation.BloodSpell == proto.TankDeathknight_Rotation_BloodStrike {
-		dk.BloodSpell = dk.BloodStrike
-	} else if dk.Rotation.BloodSpell == proto.TankDeathknight_Rotation_BloodBoil {
-		dk.BloodSpell = dk.BloodBoil
-	} else if dk.Rotation.BloodSpell == proto.TankDeathknight_Rotation_HeartStrike {
-		if dk.HeartStrike != nil {
-			dk.BloodSpell = dk.HeartStrike
-		} else {
-			dk.BloodSpell = dk.BloodStrike
-		}
-	}
-
-	if dk.Talents.Annihilation == 3 {
-		dk.FuSpell = dk.Obliterate
-	} else {
-		dk.FuSpell = dk.DeathStrike
-	}
-}
-
 func (dk *TankDeathknight) Reset(sim *core.Simulation) {
 	dk.Deathknight.Reset(sim)
 
@@ -118,18 +81,4 @@ func (dk *TankDeathknight) Reset(sim *core.Simulation) {
 
 	dk.Presence = deathknight.UnsetPresence
 	dk.Deathknight.PseudoStats.Stunned = false
-
-	if dk.IsUsingAPL {
-		return
-	}
-
-	if dk.Rotation.Presence == proto.TankDeathknight_Rotation_Blood {
-		dk.ChangePresence(sim, deathknight.BloodPresence)
-	} else if dk.Rotation.Presence == proto.TankDeathknight_Rotation_Frost {
-		dk.ChangePresence(sim, deathknight.FrostPresence)
-	} else if dk.Rotation.Presence == proto.TankDeathknight_Rotation_Unholy {
-		dk.ChangePresence(sim, deathknight.UnholyPresence)
-	}
-
-	dk.SetupRotations()
 }
diff --git a/sim/deathknight/unholy_frenzy.go b/sim/deathknight/unholy_frenzy.go
index f3635e752c..08a15f2538 100644
--- a/sim/deathknight/unholy_frenzy.go
+++ b/sim/deathknight/unholy_frenzy.go
@@ -13,7 +13,7 @@ func (dk *Deathknight) registerUnholyFrenzyCD() {
 
 	actionID := core.ActionID{SpellID: 49016, Tag: dk.Index}
 	unholyFrenzyTarget := dk.GetUnit(dk.Inputs.UnholyFrenzyTarget)
-	if unholyFrenzyTarget == nil && dk.IsUsingAPL {
+	if unholyFrenzyTarget == nil {
 		unholyFrenzyTarget = &dk.Unit
 	}
 
diff --git a/sim/hunter/hunter.go b/sim/hunter/hunter.go
index 617fec6ac6..a215250b50 100644
--- a/sim/hunter/hunter.go
+++ b/sim/hunter/hunter.go
@@ -32,9 +32,8 @@ func RegisterHunter() {
 type Hunter struct {
 	core.Character
 
-	Talents  *proto.HunterTalents
-	Options  *proto.Hunter_Options
-	Rotation *proto.Hunter_Rotation
+	Talents *proto.HunterTalents
+	Options *proto.Hunter_Options
 
 	pet *HunterPet
 
@@ -154,10 +153,6 @@ func NewHunter(character *core.Character, options *proto.Player) *Hunter {
 		Character: *character,
 		Talents:   &proto.HunterTalents{},
 		Options:   hunterOptions.Options,
-		Rotation:  hunterOptions.Rotation,
-	}
-	if hunter.Rotation == nil {
-		hunter.Rotation = &proto.Hunter_Rotation{}
 	}
 	core.FillTalentsProto(hunter.Talents.ProtoReflect(), options.TalentsString, TalentTreeSizes)
 	hunter.EnableManaBar()

From 93a32c0966a343c85ff9a444b23d70c65b8e12c9 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Thu, 4 Jan 2024 19:38:09 -0800
Subject: [PATCH 06/28] Another commit before making a separate fix

---
 sim/core/attack.go                     |   2 +-
 sim/deathknight/dps/TestBlood.results  |  12 +-
 sim/deathknight/dps/TestUnholy.results | 568 ++++++++++++-------------
 sim/druid/balance/balance.go           |  84 +---
 sim/druid/balance/rotation.go          | 256 -----------
 sim/druid/berserk.go                   |  10 +-
 sim/druid/druid.go                     |   7 +-
 sim/druid/feral/feral.go               |  20 +-
 sim/druid/feral/rotation.go            |  98 +----
 sim/druid/hurricane.go                 |   3 +-
 sim/druid/maul.go                      |  16 +-
 sim/druid/talents.go                   |   2 +-
 sim/druid/tank/rotation.go             |  88 ----
 sim/druid/tank/tank.go                 |  10 +-
 14 files changed, 316 insertions(+), 860 deletions(-)
 delete mode 100644 sim/druid/balance/rotation.go
 delete mode 100644 sim/druid/tank/rotation.go

diff --git a/sim/core/attack.go b/sim/core/attack.go
index aa601b5b16..4827d143eb 100644
--- a/sim/core/attack.go
+++ b/sim/core/attack.go
@@ -275,7 +275,7 @@ func (wa *WeaponAttack) swing(sim *Simulation) time.Duration {
 	wa.swingAt = sim.CurrentTime + wa.curSwingDuration
 	attackSpell.Cast(sim, wa.unit.CurrentTarget)
 
-	if !sim.Options.Interactive {
+	if !sim.Options.Interactive && wa.unit.Rotation != nil {
 		wa.unit.Rotation.DoNextAction(sim)
 	}
 
diff --git a/sim/deathknight/dps/TestBlood.results b/sim/deathknight/dps/TestBlood.results
index 5102649157..a4f19e677b 100644
--- a/sim/deathknight/dps/TestBlood.results
+++ b/sim/deathknight/dps/TestBlood.results
@@ -287,8 +287,8 @@ dps_results: {
 dps_results: {
  key: "TestBlood-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 10467.28296
-  tps: 5296.53902
+  dps: 10443.90447
+  tps: 5277.80696
  }
 }
 dps_results: {
@@ -666,8 +666,8 @@ dps_results: {
 dps_results: {
  key: "TestBlood-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 10350.69235
-  tps: 5235.5789
+  dps: 10383.49361
+  tps: 5254.72979
  }
 }
 dps_results: {
@@ -778,8 +778,8 @@ dps_results: {
 dps_results: {
  key: "TestBlood-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 10317.50284
-  tps: 5211.82802
+  dps: 10379.62764
+  tps: 5242.10503
  }
 }
 dps_results: {
diff --git a/sim/deathknight/dps/TestUnholy.results b/sim/deathknight/dps/TestUnholy.results
index e31fee5d05..b00336dda8 100644
--- a/sim/deathknight/dps/TestUnholy.results
+++ b/sim/deathknight/dps/TestUnholy.results
@@ -46,984 +46,984 @@ character_stats_results: {
 dps_results: {
  key: "TestUnholy-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 9680.47429
-  tps: 5704.45855
+  dps: 9710.58503
+  tps: 5711.54761
   hps: 63.52617
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 9680.47429
-  tps: 5704.45855
+  dps: 9710.58503
+  tps: 5711.54761
   hps: 66.3402
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 10217.92511
-  tps: 5880.532
+  dps: 10248.30721
+  tps: 5886.95782
   hps: 42.71551
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 9893.9745
-  tps: 5859.94867
+  dps: 9924.17016
+  tps: 5866.99266
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 9680.48618
-  tps: 5704.5175
+  dps: 9710.59693
+  tps: 5711.60656
   hps: 134.78881
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 9680.48618
-  tps: 5704.5175
+  dps: 9710.59693
+  tps: 5711.60656
   hps: 134.78881
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 10240.42421
-  tps: 5900.33685
+  dps: 10270.83734
+  tps: 5906.78165
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Beast-tamer'sShoulders-30892"
  value: {
-  dps: 9561.27606
-  tps: 5428.86625
+  dps: 9590.63003
+  tps: 5434.59411
   hps: 40.4349
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BlessedBattlegearofUndeadSlaying"
  value: {
-  dps: 7500.6957
-  tps: 4266.20781
+  dps: 7528.9716
+  tps: 4272.7715
   hps: 34.38394
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BlessedGarboftheUndeadSlayer"
  value: {
-  dps: 7390.28759
-  tps: 4234.65809
+  dps: 7417.57124
+  tps: 4240.52367
   hps: 32.96775
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 7173.57707
-  tps: 4090.382
+  dps: 7201.24066
+  tps: 4096.56066
   hps: 32.72866
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 10217.92511
-  tps: 5762.92136
+  dps: 10248.30721
+  tps: 5769.21867
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Bryntroll,theBoneArbiter-50415"
  value: {
-  dps: 10346.91539
-  tps: 5989.61257
+  dps: 10377.44167
+  tps: 5996.15064
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Bryntroll,theBoneArbiter-50709"
  value: {
-  dps: 10346.91539
-  tps: 5989.61257
+  dps: 10377.44167
+  tps: 5996.15064
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 10353.5962
-  tps: 5995.41414
+  dps: 10384.12635
+  tps: 6001.95118
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 110.32034
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 9811.44494
-  tps: 5817.02786
+  dps: 9841.85262
+  tps: 5824.32161
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 9873.64594
-  tps: 5864.21871
+  dps: 9904.01516
+  tps: 5871.41583
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 9986.29823
-  tps: 5844.5239
+  dps: 10016.40898
+  tps: 5851.61296
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DarkrunedBattlegear"
  value: {
-  dps: 8313.25385
-  tps: 4722.11602
+  dps: 8342.40652
+  tps: 4728.74456
   hps: 38.90837
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DarkrunedPlate"
  value: {
-  dps: 7427.64393
-  tps: 4189.2183
+  dps: 7455.04498
+  tps: 4195.27998
   hps: 40.98666
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DeadlyGladiator'sSigilofStrife-42620"
  value: {
-  dps: 10398.41427
-  tps: 6019.99796
+  dps: 10428.94056
+  tps: 6026.53603
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 9784.41797
-  tps: 5793.32415
+  dps: 9814.76517
+  tps: 5800.54798
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 10212.23545
-  tps: 6011.7831
+  dps: 10244.26393
+  tps: 6019.38836
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 10304.74071
-  tps: 6126.35152
+  dps: 10336.93352
+  tps: 6134.04278
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Defender'sCode-40257"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 10245.17112
-  tps: 5903.63941
+  dps: 10275.66073
+  tps: 5910.13257
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DislodgedForeignObject-50348"
  value: {
-  dps: 10027.7374
-  tps: 5913.21833
+  dps: 10058.86423
+  tps: 5920.2823
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 10015.90074
-  tps: 5863.92416
+  dps: 10045.90755
+  tps: 5870.04449
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 10217.92511
-  tps: 5880.532
+  dps: 10248.30721
+  tps: 5886.95782
   hps: 42.71551
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 10217.92511
-  tps: 5880.532
+  dps: 10248.30721
+  tps: 5886.95782
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 10240.42421
-  tps: 5900.33685
+  dps: 10270.83734
+  tps: 5906.78165
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 10238.88528
-  tps: 5898.8624
+  dps: 10269.30196
+  tps: 5905.31005
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 9957.51073
-  tps: 5686.58021
+  dps: 9960.43305
+  tps: 5713.77531
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 44.16848
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 10217.92511
-  tps: 5880.532
+  dps: 10248.30721
+  tps: 5886.95782
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 9854.14952
-  tps: 5848.27771
+  dps: 9884.46426
+  tps: 5855.45523
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 9799.48063
-  tps: 5805.4028
+  dps: 9829.83582
+  tps: 5812.66149
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ForgeEmber-37660"
  value: {
-  dps: 9778.71591
-  tps: 5788.07856
+  dps: 9809.03194
+  tps: 5795.31409
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 10217.92511
-  tps: 5880.532
+  dps: 10248.30721
+  tps: 5886.95782
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 10217.92511
-  tps: 5880.532
+  dps: 10248.30721
+  tps: 5886.95782
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-FuriousGladiator'sSigilofStrife-42621"
  value: {
-  dps: 10408.71405
-  tps: 6026.07503
+  dps: 10439.24034
+  tps: 6032.61311
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 9905.69267
-  tps: 5871.20429
+  dps: 9935.80342
+  tps: 5878.29335
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-FuturesightRune-38763"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 9832.05026
-  tps: 5830.41915
+  dps: 9862.40069
+  tps: 5837.60588
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-HatefulGladiator'sSigilofStrife-42619"
  value: {
-  dps: 10376.42406
-  tps: 6005.85502
+  dps: 10406.95035
+  tps: 6012.39309
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 10240.42421
-  tps: 5900.33685
+  dps: 10270.83734
+  tps: 5906.78165
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 10238.88528
-  tps: 5898.8624
+  dps: 10269.30196
+  tps: 5905.31005
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-IncisorFragment-37723"
  value: {
-  dps: 9858.84411
-  tps: 5841.99589
+  dps: 9889.84971
+  tps: 5849.80123
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 10217.92511
-  tps: 5880.532
+  dps: 10248.30721
+  tps: 5886.95782
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 10250.43861
-  tps: 5903.12227
+  dps: 10280.82071
+  tps: 5909.54809
   hps: 58.71565
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-LastWord-50179"
  value: {
-  dps: 10346.91539
-  tps: 5989.61257
+  dps: 10377.44167
+  tps: 5996.15064
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-LastWord-50708"
  value: {
-  dps: 10346.91539
-  tps: 5989.61257
+  dps: 10377.44167
+  tps: 5996.15064
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 10002.66676
-  tps: 5837.61294
+  dps: 10032.0625
+  tps: 5843.61227
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 9829.70588
-  tps: 5834.98049
+  dps: 9859.78621
+  tps: 5842.11561
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 10244.24556
-  tps: 5898.81936
+  dps: 10274.62766
+  tps: 5905.24519
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 10250.43861
-  tps: 5903.12227
+  dps: 10280.82071
+  tps: 5909.54809
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 10217.92511
-  tps: 5880.532
+  dps: 10248.30721
+  tps: 5886.95782
   hps: 42.60516
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 10217.92511
-  tps: 5880.532
+  dps: 10248.30721
+  tps: 5886.95782
   hps: 42.71551
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 9754.94571
-  tps: 5763.56884
+  dps: 9785.06353
+  tps: 5770.6385
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 9762.03411
-  tps: 5769.56958
+  dps: 9792.15193
+  tps: 5776.63924
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 10346.91539
-  tps: 5989.61257
+  dps: 10377.44167
+  tps: 5996.15064
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-RelentlessGladiator'sSigilofStrife-42622"
  value: {
-  dps: 10420.73045
-  tps: 6033.16496
+  dps: 10451.25674
+  tps: 6039.70303
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 10217.92511
-  tps: 5880.532
+  dps: 10248.30721
+  tps: 5886.95782
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SavageGladiator'sSigilofStrife-42618"
  value: {
-  dps: 10373.08345
-  tps: 6004.01625
+  dps: 10403.60974
+  tps: 6010.55432
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ScourgeborneBattlegear"
  value: {
-  dps: 8000.74515
-  tps: 4559.84406
+  dps: 8029.52875
+  tps: 4566.12376
   hps: 36.48062
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ScourgebornePlate"
  value: {
-  dps: 7338.02073
-  tps: 4115.32448
+  dps: 7364.82744
+  tps: 4120.861
   hps: 38.04394
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Scourgelord'sBattlegear"
  value: {
-  dps: 9330.42072
-  tps: 5515.28971
+  dps: 9358.46371
+  tps: 5521.64302
   hps: 44.60989
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Scourgelord'sPlate"
  value: {
-  dps: 8192.96648
-  tps: 4636.93277
+  dps: 8219.42444
+  tps: 4642.08454
   hps: 48.06758
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Shadowmourne-49623"
  value: {
-  dps: 10346.91539
-  tps: 5989.61257
+  dps: 10377.44167
+  tps: 5996.15064
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigilofDeflection-45144"
  value: {
-  dps: 10346.91539
-  tps: 5989.61257
+  dps: 10377.44167
+  tps: 5996.15064
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigilofHauntedDreams-40715"
  value: {
-  dps: 10376.62308
-  tps: 6015.42194
+  dps: 10407.04323
+  tps: 6021.87725
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigilofInsolence-47672"
  value: {
-  dps: 10346.91539
-  tps: 5989.61257
+  dps: 10377.44167
+  tps: 5996.15064
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigiloftheBoneGryphon-50462"
  value: {
-  dps: 10346.91539
-  tps: 5989.61257
+  dps: 10377.44167
+  tps: 5996.15064
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigiloftheHangedMan-50459"
  value: {
-  dps: 10346.91539
-  tps: 5989.61257
+  dps: 10377.44167
+  tps: 5996.15064
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigiloftheUnfalteringKnight-40714"
  value: {
-  dps: 10346.91539
-  tps: 5989.61257
+  dps: 10377.44167
+  tps: 5996.15064
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 46.32034
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SoulPreserver-37111"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SouloftheDead-40382"
  value: {
-  dps: 9802.43285
-  tps: 5808.86704
+  dps: 9832.82416
+  tps: 5816.15563
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SparkofLife-37657"
  value: {
-  dps: 9832.06089
-  tps: 5797.8349
+  dps: 9862.05029
+  tps: 5804.30139
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 9800.6582
-  tps: 5811.52763
+  dps: 9913.75663
+  tps: 5850.97982
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-StormshroudArmor"
  value: {
-  dps: 7067.22499
-  tps: 4050.80338
+  dps: 7095.52999
+  tps: 4057.87955
   hps: 30.7975
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 10250.43861
-  tps: 5903.12227
+  dps: 10280.82071
+  tps: 5909.54809
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 10244.24556
-  tps: 5898.81936
+  dps: 10274.62766
+  tps: 5905.24519
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 10233.40773
-  tps: 5891.28927
+  dps: 10263.78983
+  tps: 5897.7151
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Thassarian'sBattlegear"
  value: {
-  dps: 8590.35604
-  tps: 4952.61627
+  dps: 8618.51653
+  tps: 4958.8001
   hps: 38.68766
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Thassarian'sPlate"
  value: {
-  dps: 7458.635
-  tps: 4188.82468
+  dps: 7486.10663
+  tps: 4194.90667
   hps: 40.30616
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TheTwinBladesofAzzinoth"
  value: {
-  dps: 8979.88721
-  tps: 5025.14863
+  dps: 9010.98248
+  tps: 5032.12724
   hps: 40.74757
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 10275.22899
-  tps: 5912.27746
+  dps: 10305.50604
+  tps: 5918.26645
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 9924.59603
-  tps: 5907.08552
+  dps: 9954.00729
+  tps: 5913.58867
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 9890.45908
-  tps: 5873.52857
+  dps: 9920.42339
+  tps: 5880.41645
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 10217.92511
-  tps: 5880.532
+  dps: 10248.30721
+  tps: 5886.95782
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 10217.92511
-  tps: 5880.532
+  dps: 10248.30721
+  tps: 5886.95782
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 9826.9966
-  tps: 5751.9898
+  dps: 9840.77218
+  tps: 5774.12915
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 10217.92511
-  tps: 5880.532
+  dps: 10248.30721
+  tps: 5886.95782
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 10217.92511
-  tps: 5880.532
+  dps: 10248.30721
+  tps: 5886.95782
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-UndeadSlayer'sBlessedArmor"
  value: {
-  dps: 7395.21167
-  tps: 4250.00861
+  dps: 7423.03446
+  tps: 4256.35867
   hps: 33.68504
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Val'anyr,HammerofAncientKings-46017"
  value: {
-  dps: 9602.82536
-  tps: 5420.6152
+  dps: 9633.49282
+  tps: 5427.26413
   hps: 42.81036
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-WingedTalisman-37844"
  value: {
-  dps: 9680.49504
-  tps: 5704.45555
+  dps: 9710.60578
+  tps: 5711.54461
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-WrathfulGladiator'sSigilofStrife-51417"
  value: {
-  dps: 10434.46349
-  tps: 6041.26773
+  dps: 10464.98978
+  tps: 6047.8058
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-Average-Default"
  value: {
-  dps: 10279.57916
-  tps: 5964.26539
+  dps: 10310.79563
+  tps: 5971.29709
   hps: 42.14164
  }
 }
@@ -1174,152 +1174,152 @@ dps_results: {
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-FullBuffs-LongMultiTarget"
  value: {
-  dps: 14275.55112
-  tps: 9275.62941
+  dps: 14312.2404
+  tps: 9287.052
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-FullBuffs-LongSingleTarget"
  value: {
-  dps: 10346.91539
-  tps: 5989.61257
+  dps: 10377.44167
+  tps: 5996.15064
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 15451.74278
-  tps: 7165.07213
+  dps: 15559.83451
+  tps: 7162.15963
   hps: 210.63484
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-NoBuffs-LongMultiTarget"
  value: {
-  dps: 6761.4395
-  tps: 4575.413
+  dps: 6783.40918
+  tps: 4583.6299
   hps: 27.7272
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-NoBuffs-LongSingleTarget"
  value: {
-  dps: 5085.97636
-  tps: 3172.95627
+  dps: 5103.61606
+  tps: 3177.62391
   hps: 27.7272
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 6701.62049
-  tps: 3491.63258
+  dps: 6757.40436
+  tps: 3489.08968
   hps: 138.636
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 59321.04606
-  tps: 61361.65332
+  dps: 59452.42669
+  tps: 61476.73056
   hps: 236.33229
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 11318.78456
-  tps: 7472.9393
+  dps: 11354.57131
+  tps: 7485.7125
   hps: 236.33229
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 16412.5451
-  tps: 8686.56163
+  dps: 16535.42503
+  tps: 8698.33633
   hps: 337.01574
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 35117.90018
-  tps: 37478.75367
+  dps: 35212.15599
+  tps: 37565.37369
   hps: 155.82686
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 5750.84606
-  tps: 4175.86894
+  dps: 5772.20815
+  tps: 4185.27192
   hps: 155.27232
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 7280.02322
-  tps: 4471.44132
+  dps: 7347.6605
+  tps: 4481.15645
   hps: 221.8176
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-FullBuffs-LongMultiTarget"
  value: {
-  dps: 36238.79517
-  tps: 43195.85148
+  dps: 36324.85779
+  tps: 43276.25499
   hps: 236.33229
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-FullBuffs-LongSingleTarget"
  value: {
-  dps: 11434.83628
-  tps: 7578.90963
+  dps: 11470.17283
+  tps: 7591.32914
   hps: 236.33229
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 16487.2343
-  tps: 8798.61633
+  dps: 16608.09173
+  tps: 8808.92846
   hps: 339.12209
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-NoBuffs-LongMultiTarget"
  value: {
-  dps: 18696.93865
-  tps: 22871.51167
+  dps: 18749.83496
+  tps: 22923.25478
   hps: 155.82686
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-NoBuffs-LongSingleTarget"
  value: {
-  dps: 5876.43297
-  tps: 4281.49641
+  dps: 5897.73441
+  tps: 4290.70565
   hps: 155.27232
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 7351.97253
-  tps: 4548.398
+  dps: 7417.6708
+  tps: 4556.06307
   hps: 221.8176
  }
 }
 dps_results: {
  key: "TestUnholy-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 9815.01951
-  tps: 5629.20041
+  dps: 9845.43373
+  tps: 5635.64895
   hps: 42.12697
  }
 }
diff --git a/sim/druid/balance/balance.go b/sim/druid/balance/balance.go
index f5206e3878..d0cea4e797 100644
--- a/sim/druid/balance/balance.go
+++ b/sim/druid/balance/balance.go
@@ -32,9 +32,8 @@ func NewBalanceDruid(character *core.Character, options *proto.Player) *BalanceD
 	selfBuffs := druid.SelfBuffs{}
 
 	moonkin := &BalanceDruid{
-		Druid:    druid.New(character, druid.Moonkin, selfBuffs, options.TalentsString),
-		Options:  balanceOptions.Options,
-		Rotation: balanceOptions.Rotation,
+		Druid:   druid.New(character, druid.Moonkin, selfBuffs, options.TalentsString),
+		Options: balanceOptions.Options,
 	}
 
 	moonkin.SelfBuffs.InnervateTarget = &proto.UnitReference{}
@@ -42,7 +41,6 @@ func NewBalanceDruid(character *core.Character, options *proto.Player) *BalanceD
 		moonkin.SelfBuffs.InnervateTarget = balanceOptions.Options.InnervateTarget
 	}
 
-	moonkin.EnableResumeAfterManaWait(moonkin.tryUseGCD)
 	wotlk.ConstructValkyrPets(&moonkin.Character)
 	return moonkin
 }
@@ -55,19 +53,7 @@ type BalanceOnUseTrinket struct {
 type BalanceDruid struct {
 	*druid.Druid
 
-	Options            *proto.BalanceDruid_Options
-	Rotation           *proto.BalanceDruid_Rotation
-	CooldownsAvailable []*core.MajorCooldown
-	LastCast           *druid.DruidSpell
-
-	// CDS
-	hyperSpeedMCD      *core.MajorCooldown
-	potionSpeedMCD     *core.MajorCooldown
-	potionWildMagicMCD *core.MajorCooldown
-	powerInfusion      *core.MajorCooldown
-	onUseTrinket1      BalanceOnUseTrinket
-	onUseTrinket2      BalanceOnUseTrinket
-	potionUsed         bool
+	Options *proto.BalanceDruid_Options
 }
 
 func (moonkin *BalanceDruid) GetDruid() *druid.Druid {
@@ -78,70 +64,14 @@ func (moonkin *BalanceDruid) Initialize() {
 	moonkin.Druid.Initialize()
 	moonkin.RegisterBalanceSpells()
 
-	moonkin.Env.RegisterPreFinalizeEffect(func() {
-		if moonkin.OwlkinFrenzyAura != nil && moonkin.Options.OkfUptime > 0 {
+	if moonkin.OwlkinFrenzyAura != nil && moonkin.Options.OkfUptime > 0 {
+		moonkin.Env.RegisterPreFinalizeEffect(func() {
 			core.ApplyFixedUptimeAura(moonkin.OwlkinFrenzyAura, float64(moonkin.Options.OkfUptime), time.Second*5, 0)
-		}
-	})
+		})
+	}
 }
 
 func (moonkin *BalanceDruid) Reset(sim *core.Simulation) {
 	moonkin.Druid.Reset(sim)
 	moonkin.RebirthTiming = moonkin.Env.BaseDuration.Seconds() * sim.RandomFloat("Rebirth Timing")
-
-	if moonkin.Rotation.Type == proto.BalanceDruid_Rotation_Default {
-		moonkin.Rotation.MfUsage = proto.BalanceDruid_Rotation_BeforeLunar
-		moonkin.Rotation.IsUsage = proto.BalanceDruid_Rotation_OptimizeIs
-		moonkin.Rotation.WrathUsage = proto.BalanceDruid_Rotation_RegularWrath
-		moonkin.Rotation.UseBattleRes = false
-		moonkin.Rotation.UseStarfire = true
-		moonkin.Rotation.UseTyphoon = false
-		moonkin.Rotation.UseHurricane = false
-		moonkin.Rotation.UseSmartCooldowns = true
-		moonkin.Rotation.MaintainFaerieFire = true
-		moonkin.Rotation.PlayerLatency = 200
-	}
-
-	if moonkin.Rotation.UseSmartCooldowns {
-		moonkin.potionUsed = false
-		consumes := moonkin.Consumes
-
-		if consumes.DefaultPotion == proto.Potions_PotionOfSpeed {
-			moonkin.potionSpeedMCD = moonkin.getBalanceMajorCooldown(core.ActionID{ItemID: 40211})
-		}
-		if consumes.DefaultPotion == proto.Potions_PotionOfWildMagic {
-			moonkin.potionWildMagicMCD = moonkin.getBalanceMajorCooldown(core.ActionID{ItemID: 40212})
-		}
-		if moonkin.HasProfession(proto.Profession_Engineering) {
-			moonkin.hyperSpeedMCD = moonkin.getBalanceMajorCooldown(core.ActionID{SpellID: 54758})
-		}
-		moonkin.powerInfusion = moonkin.getBalanceMajorCooldown(core.ActionID{SpellID: 10060})
-		moonkin.onUseTrinket1 = BalanceOnUseTrinket{
-			Cooldown: moonkin.getBalanceMajorCooldown(core.ActionID{ItemID: moonkin.Trinket1().ID}),
-			Stat:     getOnUseTrinketStat(moonkin.Trinket1().ID),
-		}
-		moonkin.onUseTrinket2 = BalanceOnUseTrinket{
-			Cooldown: moonkin.getBalanceMajorCooldown(core.ActionID{ItemID: moonkin.Trinket2().ID}),
-			Stat:     getOnUseTrinketStat(moonkin.Trinket2().ID),
-		}
-	}
-}
-
-// Takes out a Cooldown from the generic MajorCooldownManager and adds it to a custom Slice of Cooldowns
-func (moonkin *BalanceDruid) getBalanceMajorCooldown(actionID core.ActionID) *core.MajorCooldown {
-	if majorCd := moonkin.Character.GetMajorCooldownIgnoreTag(actionID); majorCd != nil {
-		majorCd.Disable()
-		return majorCd
-	}
-	return nil
-}
-
-func getOnUseTrinketStat(itemId int32) stats.Stat {
-	if itemId == 45466 || itemId == 48722 || itemId == 47726 || itemId == 47946 || itemId == 36972 {
-		return stats.SpellHaste
-	}
-	if itemId == 50259 {
-		return stats.SpellCrit
-	}
-	return stats.SpellPower
 }
diff --git a/sim/druid/balance/rotation.go b/sim/druid/balance/rotation.go
deleted file mode 100644
index e0a633aad4..0000000000
--- a/sim/druid/balance/rotation.go
+++ /dev/null
@@ -1,256 +0,0 @@
-package balance
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-	"github.com/wowsims/wotlk/sim/core/stats"
-	"github.com/wowsims/wotlk/sim/druid"
-)
-
-func (moonkin *BalanceDruid) OnGCDReady(sim *core.Simulation) {
-	moonkin.tryUseGCD(sim)
-}
-
-func (moonkin *BalanceDruid) tryUseGCD(sim *core.Simulation) {
-	spell, target := moonkin.rotation(sim)
-	if success := spell.Cast(sim, target); !success {
-		moonkin.WaitForMana(sim, spell.CurCast.Cost)
-	}
-	moonkin.LastCast = spell
-}
-
-func (moonkin *BalanceDruid) rotation(sim *core.Simulation) (*druid.DruidSpell, *core.Unit) {
-	moonkin.CurrentTarget = sim.Environment.GetTargetUnit(0)
-	rotation := moonkin.Rotation
-	target := moonkin.CurrentTarget
-
-	if rotation.MaintainFaerieFire && moonkin.ShouldFaerieFire(sim, moonkin.CurrentTarget) {
-		return moonkin.FaerieFire, target
-	}
-
-	if sim.GetRemainingDuration() < 15*time.Second {
-		moonkin.castMajorCooldown(moonkin.hyperSpeedMCD, sim, target)
-		moonkin.castMajorCooldown(moonkin.potionSpeedMCD, sim, target)
-		moonkin.castMajorCooldown(moonkin.potionWildMagicMCD, sim, target)
-		moonkin.useTrinkets(stats.SpellHaste, sim, target)
-		moonkin.useTrinkets(stats.SpellPower, sim, target)
-		moonkin.useTrinkets(stats.SpellCrit, sim, target)
-	}
-
-	if rotation.SnapshotMf {
-		flareProc := moonkin.GetAura("Flare of the Heavens Proc")
-		pleaProc := moonkin.GetAura("Pandora's Plea Proc")
-		wildMagicProc := moonkin.GetAura("Potion of Wild Magic")
-		shouldCheckForSnapshot := flareProc.IsActive() || pleaProc.IsActive() || (wildMagicProc.IsActive() && sim.CurrentTime > 60*time.Second)
-		if shouldCheckForSnapshot {
-			if moonkin.shouldSnapshotMf(sim, flareProc) || moonkin.shouldSnapshotMf(sim, wildMagicProc) || moonkin.shouldSnapshotMf(sim, pleaProc) {
-				return moonkin.Moonfire, target
-			}
-		}
-	}
-
-	var lunarUptime time.Duration
-	shouldRefreshMf := moonkin.Moonfire.CurDot().RemainingDuration(sim) <= 0
-	hasLunarFury := core.Ternary(moonkin.Ranged().ID == 47670, true, false)
-	lunarIsActive := moonkin.LunarEclipseProcAura.IsActive()
-	maximizeMf := !(rotation.MfUsage == proto.BalanceDruid_Rotation_NoMf) && !(rotation.MfUsage == proto.BalanceDruid_Rotation_BeforeLunar) && !(rotation.SnapshotMf)
-
-	if moonkin.LunarEclipseProcAura != nil {
-		lunarUptime = moonkin.LunarEclipseProcAura.RemainingDuration(sim)
-	}
-	if moonkin.MoonkinT84PCAura.IsActive() && moonkin.MoonkinT84PCAura.RemainingDuration(sim) < moonkin.SpellGCD() {
-		return moonkin.Starfire, target
-	} else if rotation.UseBattleRes && sim.GetRemainingDuration().Seconds() < moonkin.RebirthTiming && moonkin.Rebirth.IsReady(sim) {
-		return moonkin.Rebirth, target
-	} else if maximizeMf && shouldRefreshMf && hasLunarFury {
-		return moonkin.Moonfire, target
-	} else if moonkin.Talents.ForceOfNature && moonkin.ForceOfNature.IsReady(sim) && !lunarIsActive {
-		moonkin.useTrinkets(stats.SpellPower, sim, target)
-		return moonkin.ForceOfNature, target
-	} else if moonkin.Starfall.IsReady(sim) && !lunarIsActive {
-		moonkin.useTrinkets(stats.SpellPower, sim, target)
-		return moonkin.Starfall, target
-	} else if moonkin.Typhoon.IsReady(sim) && rotation.UseTyphoon {
-		return moonkin.Typhoon, target
-	} else if rotation.UseHurricane {
-		return moonkin.Hurricane, target
-	}
-
-	shouldHoldIs := core.Ternary(moonkin.MoonkinT84PCAura == nil, lunarIsActive, lunarIsActive && moonkin.HasActiveAura("Bloodlust-"+core.BloodlustActionID.WithTag(-1).String()))
-
-	// Max IS uptime
-	if rotation.IsUsage == proto.BalanceDruid_Rotation_OptimizeIs && !shouldHoldIs {
-		if moonkin.InsectSwarm.CurDot().RemainingDuration(sim) <= 0 {
-			return moonkin.InsectSwarm, target
-		}
-	} else if rotation.IsUsage == proto.BalanceDruid_Rotation_MultidotIs {
-		for range sim.Encounter.Targets {
-			if moonkin.InsectSwarm.CurDot().RemainingDuration(sim) <= 0 {
-				return moonkin.InsectSwarm, moonkin.CurrentTarget
-			}
-			moonkin.CurrentTarget = sim.Environment.NextTargetUnit(moonkin.CurrentTarget)
-		}
-	}
-
-	// Max MF uptime
-	if rotation.MfUsage == proto.BalanceDruid_Rotation_MaximizeMf && shouldRefreshMf {
-		return moonkin.Moonfire, target
-	} else if rotation.MfUsage == proto.BalanceDruid_Rotation_MultidotMf {
-		for range sim.Encounter.Targets {
-			if moonkin.Moonfire.CurDot().RemainingDuration(sim) <= 0 {
-				return moonkin.Moonfire, moonkin.CurrentTarget
-			}
-			moonkin.CurrentTarget = sim.Environment.NextTargetUnit(moonkin.CurrentTarget)
-		}
-	}
-
-	// Player "brain" latency
-	playerLatency := time.Duration(max(rotation.PlayerLatency, 0)) * time.Millisecond
-	lunarICD := moonkin.LunarICD.Timer.TimeToReady(sim)
-	solarICD := moonkin.SolarICD.Timer.TimeToReady(sim)
-	canExtendMf := rotation.MfExtension == proto.BalanceDruid_Rotation_ExtendAlways || rotation.MfExtension == proto.BalanceDruid_Rotation_ExtendOutsideSolar
-
-	if moonkin.Talents.Eclipse > 0 {
-		solarUptime := moonkin.SolarEclipseProcAura.ExpiresAt() - sim.CurrentTime
-		solarIsActive := moonkin.SolarEclipseProcAura.IsActive()
-
-		//"Dispelling" eclipse effects before casting if needed
-		if float64(lunarUptime-moonkin.Starfire.CurCast.CastTime) <= 0 {
-			moonkin.LunarEclipseProcAura.Deactivate(sim)
-			lunarIsActive = false
-		}
-		if float64(solarUptime-moonkin.Wrath.CurCast.CastTime) <= 0 {
-			moonkin.SolarEclipseProcAura.Deactivate(sim)
-			solarIsActive = false
-		}
-		// Player latency adjustments
-		if lunarIsActive {
-			lunarIsActive = lunarUptime < (moonkin.LunarEclipseProcAura.Duration - playerLatency)
-		}
-		if solarIsActive {
-			solarIsActive = solarUptime < (moonkin.SolarEclipseProcAura.Duration - playerLatency)
-			solarICD = 0
-		}
-
-		// Eclipse
-		if solarIsActive || lunarIsActive {
-			if lunarIsActive {
-				if canExtendMf && moonkin.ExtendingMoonfireStacks == 0 {
-					if extendTarget := moonkin.tryExtendMoonfire(sim); extendTarget != nil {
-						return moonkin.Moonfire, extendTarget
-					}
-				}
-				if (rotation.UseSmartCooldowns && lunarUptime > 10*time.Second) || sim.GetRemainingDuration() < 15*time.Second {
-					moonkin.castMajorCooldown(moonkin.hyperSpeedMCD, sim, target)
-					moonkin.castMajorCooldown(moonkin.potionSpeedMCD, sim, target)
-					moonkin.useTrinkets(stats.SpellHaste, sim, target)
-					if !moonkin.HasActiveAura("Bloodlust-" + core.BloodlustActionID.WithTag(-1).String()) {
-						moonkin.castMajorCooldown(moonkin.powerInfusion, sim, target)
-					}
-				}
-				return moonkin.Starfire, target
-			} else if solarIsActive {
-				if moonkin.MoonkinT84PCAura.IsActive() {
-					if moonkin.MoonkinT84PCAura.RemainingDuration(sim) < solarUptime {
-						return moonkin.Starfire, target
-					}
-				}
-				if canExtendMf && moonkin.ExtendingMoonfireStacks == 0 {
-					if extendTarget := moonkin.tryExtendMoonfire(sim); extendTarget != nil {
-						return moonkin.Moonfire, extendTarget
-					}
-				}
-				if (rotation.UseSmartCooldowns && solarUptime > 10*time.Second) || sim.GetRemainingDuration() < 15*time.Second {
-					moonkin.castMajorCooldown(moonkin.potionWildMagicMCD, sim, target)
-					moonkin.useTrinkets(stats.SpellCrit, sim, target)
-				}
-				if rotation.WrathUsage == proto.BalanceDruid_Rotation_RegularWrath {
-					return moonkin.Wrath, target
-				}
-			}
-		}
-		if rotation.MfUsage == proto.BalanceDruid_Rotation_BeforeLunar && lunarICD < 2*time.Second && shouldRefreshMf {
-			return moonkin.Moonfire, target
-		}
-		shouldRefreshIs := moonkin.InsectSwarm.CurDot().RemainingDuration(sim) <= 0
-		if rotation.IsUsage == proto.BalanceDruid_Rotation_BeforeSolar && solarICD < 2*time.Second && shouldRefreshIs {
-			return moonkin.InsectSwarm, target
-		}
-	}
-
-	fishingForLunar := lunarICD <= solarICD
-	if rotation.EclipsePrio == proto.BalanceDruid_Rotation_Solar {
-		fishingForLunar = lunarICD < solarICD
-	}
-
-	if canExtendMf && moonkin.ExtendingMoonfireStacks == 0 {
-		if extendTarget := moonkin.tryExtendMoonfire(sim); extendTarget != nil {
-			return moonkin.Moonfire, extendTarget
-		}
-	}
-
-	// Non-Eclipse
-	eclipseShuffle := rotation.EclipseShuffling && lunarICD == 0 && solarICD == 0
-	if eclipseShuffle && moonkin.LastCast == moonkin.Wrath && rotation.UseStarfire {
-		return moonkin.Starfire, target
-	}
-	if (fishingForLunar || eclipseShuffle) && rotation.WrathUsage != proto.BalanceDruid_Rotation_NoWrath {
-		return moonkin.Wrath, target
-	} else {
-		return moonkin.Starfire, target
-	}
-}
-
-func (moonkin *BalanceDruid) castMajorCooldown(mcd *core.MajorCooldown, sim *core.Simulation, target *core.Unit) {
-	if mcd != nil && mcd.Spell.IsReady(sim) {
-		isOffensivePotion := mcd.Spell.SameAction(core.ActionID{ItemID: 40211}) || mcd.Spell.SameAction(core.ActionID{ItemID: 40212})
-		willUseOffensivePotion := isOffensivePotion && !moonkin.potionUsed
-
-		// Use Potion if we can
-		if isOffensivePotion && moonkin.potionUsed {
-			return
-		}
-		mcd.Spell.Cast(sim, target)
-
-		if willUseOffensivePotion {
-			moonkin.potionUsed = true
-		}
-		moonkin.UpdateMajorCooldowns()
-	}
-}
-
-func (moonkin *BalanceDruid) useTrinkets(stat stats.Stat, sim *core.Simulation, target *core.Unit) {
-	if moonkin.onUseTrinket1.Stat == stat {
-		moonkin.castMajorCooldown(moonkin.onUseTrinket1.Cooldown, sim, target)
-	}
-	if moonkin.onUseTrinket2.Stat == stat {
-		moonkin.castMajorCooldown(moonkin.onUseTrinket2.Cooldown, sim, target)
-	}
-}
-
-func (moonkin *BalanceDruid) tryExtendMoonfire(sim *core.Simulation) *core.Unit {
-	if len(sim.Encounter.Targets) < 2 {
-		return nil
-	}
-	minTarget := moonkin.CurrentTarget
-	minTimer := moonkin.Moonfire.CurDot().RemainingDuration(sim)
-	for range sim.Encounter.Targets {
-		if moonkin.Moonfire.CurDot().RemainingDuration(sim) < minTimer {
-			minTarget = moonkin.CurrentTarget
-			minTimer = moonkin.Moonfire.CurDot().RemainingDuration(sim)
-		}
-		moonkin.CurrentTarget = sim.Environment.NextTargetUnit(moonkin.CurrentTarget)
-	}
-	return minTarget
-}
-
-func (moonkin *BalanceDruid) shouldSnapshotMf(sim *core.Simulation, aura *core.Aura) bool {
-	if aura.IsActive() && aura.RemainingDuration(sim) < moonkin.Moonfire.CurDot().RemainingDuration(sim) {
-		if moonkin.Moonfire.CurDot().SnapshotBaseDamage < (200 + 0.13*moonkin.Moonfire.CurDot().Spell.SpellPower()) {
-			return true
-		}
-	}
-	return false
-}
diff --git a/sim/druid/berserk.go b/sim/druid/berserk.go
index 9c189f8c2c..cd58cb4966 100644
--- a/sim/druid/berserk.go
+++ b/sim/druid/berserk.go
@@ -62,10 +62,8 @@ func (druid *Druid) registerBerserkCD() {
 		},
 	})
 
-	if druid.IsUsingAPL {
-		druid.AddMajorCooldown(core.MajorCooldown{
-			Spell: druid.Berserk.Spell,
-			Type:  core.CooldownTypeDPS,
-		})
-	}
+	druid.AddMajorCooldown(core.MajorCooldown{
+		Spell: druid.Berserk.Spell,
+		Type:  core.CooldownTypeDPS,
+	})
 }
diff --git a/sim/druid/druid.go b/sim/druid/druid.go
index c27551ed99..5bd1c29bf1 100644
--- a/sim/druid/druid.go
+++ b/sim/druid/druid.go
@@ -23,7 +23,6 @@ type Druid struct {
 	StartingForm DruidForm
 
 	RebirthUsed       bool
-	MaulRageThreshold float64
 	RebirthTiming     float64
 	BleedsActive      int
 	AssumeBleedActive bool
@@ -226,7 +225,7 @@ func (druid *Druid) RegisterFeralCatSpells() {
 	druid.registerFerociousBiteSpell()
 	druid.registerMangleBearSpell()
 	druid.registerMangleCatSpell()
-	druid.registerMaulSpell(0)
+	druid.registerMaulSpell()
 	druid.registerLacerateSpell()
 	druid.registerRakeSpell()
 	druid.registerRipSpell()
@@ -237,7 +236,7 @@ func (druid *Druid) RegisterFeralCatSpells() {
 	druid.registerTigersFurySpell()
 }
 
-func (druid *Druid) RegisterFeralTankSpells(maulRageThreshold float64) {
+func (druid *Druid) RegisterFeralTankSpells() {
 	druid.registerBarkskinCD()
 	druid.registerBerserkCD()
 	druid.registerBearFormSpell()
@@ -245,7 +244,7 @@ func (druid *Druid) RegisterFeralTankSpells(maulRageThreshold float64) {
 	druid.registerEnrageSpell()
 	druid.registerFrenziedRegenerationCD()
 	druid.registerMangleBearSpell()
-	druid.registerMaulSpell(maulRageThreshold)
+	druid.registerMaulSpell()
 	druid.registerLacerateSpell()
 	druid.registerRakeSpell()
 	druid.registerRipSpell()
diff --git a/sim/druid/feral/feral.go b/sim/druid/feral/feral.go
index 13e085a5e8..1608d06272 100644
--- a/sim/druid/feral/feral.go
+++ b/sim/druid/feral/feral.go
@@ -45,9 +45,9 @@ func NewFeralDruid(character *core.Character, options *proto.Player) *FeralDruid
 	cat.PrePopBerserk = feralOptions.Rotation.PrePopBerserk
 	cat.setupRotation(feralOptions.Rotation)
 
-	cat.EnableEnergyBar(100.0, cat.OnEnergyGain)
+	cat.EnableEnergyBar(100.0)
 
-	cat.EnableRageBar(core.RageBarOptions{RageMultiplier: 1, MHSwingSpeed: 2.5}, func(sim *core.Simulation) {})
+	cat.EnableRageBar(core.RageBarOptions{RageMultiplier: 1, MHSwingSpeed: 2.5})
 
 	cat.EnableAutoAttacks(cat, core.AutoAttackOptions{
 		// Base paw weapon.
@@ -96,22 +96,6 @@ func (cat *FeralDruid) MissChance() float64 {
 func (cat *FeralDruid) Initialize() {
 	cat.Druid.Initialize()
 	cat.RegisterFeralCatSpells()
-
-	if cat.IsUsingAPL {
-		return
-	}
-
-	if cat.prepopOoc && cat.Talents.OmenOfClarity {
-		cat.RegisterPrepullAction(-time.Second, func(sim *core.Simulation) {
-			cat.ProcOoc(sim)
-		})
-	}
-
-	if cat.PrePopBerserk && cat.Talents.Berserk {
-		cat.RegisterPrepullAction(-time.Second, func(sim *core.Simulation) {
-			cat.Berserk.Cast(sim, nil)
-		})
-	}
 }
 
 func (cat *FeralDruid) Reset(sim *core.Simulation) {
diff --git a/sim/druid/feral/rotation.go b/sim/druid/feral/rotation.go
index 5fa1b6a1df..4694a15739 100644
--- a/sim/druid/feral/rotation.go
+++ b/sim/druid/feral/rotation.go
@@ -9,30 +9,11 @@ import (
 	"github.com/wowsims/wotlk/sim/druid"
 )
 
-func (cat *FeralDruid) OnEnergyGain(sim *core.Simulation) {
-	if cat.IsUsingAPL && !cat.usingHardcodedAPL {
-		return
-	}
-
-	if sim.CurrentTime < 0 {
-		return
-	}
-
-	cat.TryUseCooldowns(sim)
-	if cat.InForm(druid.Cat) && !cat.readyToShift {
-		cat.doTigersFury(sim)
-	}
-}
-
 func (cat *FeralDruid) OnGCDReady(sim *core.Simulation) {
-	if cat.IsUsingAPL && !cat.usingHardcodedAPL {
+	if !cat.usingHardcodedAPL {
 		return
 	}
 
-	if !cat.IsUsingAPL {
-		cat.TryUseCooldowns(sim)
-	}
-
 	if !cat.GCD.IsReady(sim) {
 		return
 	}
@@ -56,33 +37,12 @@ func (cat *FeralDruid) OnGCDReady(sim *core.Simulation) {
 	// Replace gcd event with our own if we casted a spell
 	if !cat.GCD.IsReady(sim) {
 		nextGcd := cat.NextGCDAt()
-		cat.DoNothing()
 		cat.CancelGCDTimer(sim)
 
 		cat.NextRotationAction(sim, nextGcd)
 	}
 }
 
-func (cat *FeralDruid) OnAutoAttack(sim *core.Simulation, _ *core.Spell) {
-	if cat.IsUsingAPL {
-		return
-	}
-
-	if cat.InForm(druid.Humanoid) {
-		panic("auto attack out of form?")
-	}
-
-	// If the swing resulted in an Omen proc, then schedule the
-	// next player decision based on latency.
-
-	if cat.Talents.OmenOfClarity && cat.ClearcastingAura.RemainingDuration(sim) == cat.ClearcastingAura.Duration {
-		// Kick gcd loop, also need to account for any gcd 'left'
-		// otherwise it breaks gcd logic
-		kickTime := max(cat.NextGCDAt(), sim.CurrentTime+cat.latency)
-		cat.NextRotationAction(sim, kickTime)
-	}
-}
-
 func (cat *FeralDruid) NextRotationAction(sim *core.Simulation, kickAt time.Duration) {
 	if cat.rotationAction != nil {
 		cat.rotationAction.Cancel(sim)
@@ -97,62 +57,8 @@ func (cat *FeralDruid) NextRotationAction(sim *core.Simulation, kickAt time.Dura
 	sim.AddPendingAction(cat.rotationAction)
 }
 
-// Ported from https://github.com/NerdEgghead/WOTLK_cat_sim
-
 func (cat *FeralDruid) checkReplaceMaul(sim *core.Simulation, mhSwingSpell *core.Spell) *core.Spell {
-	if cat.IsUsingAPL {
-		return mhSwingSpell
-	}
-
-	// If we have enough time and Energy leeway to stay in
-	// Dire Bear Form once the GCD expires, then only Maul if we
-	// will be left with enough Rage to cast Mangle or Lacerate
-	// on that global.
-
-	ripDot := cat.Rip.CurDot()
-
-	furorCap := min(20.0*float64(cat.Talents.Furor), 85.0)
-	ripRefreshPending := ripDot.IsActive() && (ripDot.RemainingDuration(sim) < sim.GetRemainingDuration()-time.Second*10)
-	gcdTimeToRdy := cat.GCD.TimeToReady(sim)
-	energyLeeway := furorCap - 15.0 - float64((gcdTimeToRdy+cat.latency)/core.EnergyTickDuration)
-	shiftNext := cat.CurrentEnergy() > energyLeeway
-
-	if ripRefreshPending {
-		shiftNext = shiftNext || (ripDot.RemainingDuration(sim) < (gcdTimeToRdy + time.Second*3))
-	}
-
-	lacerateNext := false
-	emergencyLacerateNext := false
-	mangleNext := false
-
-	lacerateDot := cat.Lacerate.CurDot()
-	if cat.Rotation.BearweaveType == proto.FeralDruid_Rotation_Lacerate {
-		lacerateLeeway := cat.Rotation.LacerateTime + gcdTimeToRdy
-		lacerateNext = !lacerateDot.IsActive() || (lacerateDot.GetStacks() < 5) || (lacerateDot.RemainingDuration(sim) <= lacerateLeeway)
-		emergencyLeeway := gcdTimeToRdy + (3 * time.Second) + (2 * cat.latency)
-		emergencyLacerateNext = lacerateDot.IsActive() && (lacerateDot.RemainingDuration(sim) <= emergencyLeeway)
-		mangleNext = cat.MangleBear != nil && !lacerateNext && (!cat.bleedAura.IsActive() || (cat.bleedAura.RemainingDuration(sim) < gcdTimeToRdy+time.Second*3) || (sim.CurrentTime-cat.lastShift < time.Duration(1500*time.Millisecond)))
-	} else {
-		mangleNext = cat.MangleBear != nil && cat.MangleBear.TimeToReady(sim) < gcdTimeToRdy
-		lacerateNext = lacerateDot.IsActive() && (lacerateDot.GetStacks() < 5 || lacerateDot.RemainingDuration(sim) < gcdTimeToRdy+(time.Second*4))
-	}
-
-	maulRageThresh := 10.0
-	if emergencyLacerateNext {
-		maulRageThresh += cat.Lacerate.DefaultCast.Cost
-	} else if shiftNext {
-		maulRageThresh = 10.0
-	} else if mangleNext {
-		maulRageThresh += cat.MangleBear.DefaultCast.Cost
-	} else if lacerateNext {
-		maulRageThresh += cat.Lacerate.DefaultCast.Cost
-	}
-
-	if cat.CurrentRage() >= maulRageThresh {
-		return cat.Maul.Spell
-	} else {
-		return mhSwingSpell
-	}
+	return mhSwingSpell
 }
 
 func (cat *FeralDruid) shiftBearCat(sim *core.Simulation, powershift bool) bool {
diff --git a/sim/druid/hurricane.go b/sim/druid/hurricane.go
index 7ccedd3433..b13106b6d1 100644
--- a/sim/druid/hurricane.go
+++ b/sim/druid/hurricane.go
@@ -37,8 +37,7 @@ func (druid *Druid) registerHurricaneSpell() {
 		},
 		Cast: core.CastConfig{
 			DefaultCast: core.Cast{
-				GCD:         core.GCDDefault,
-				ChannelTime: time.Second * 10,
+				GCD: core.GCDDefault,
 			},
 		},
 		Dot: core.DotConfig{
diff --git a/sim/druid/maul.go b/sim/druid/maul.go
index 600d2399e4..cf82c24d2b 100644
--- a/sim/druid/maul.go
+++ b/sim/druid/maul.go
@@ -5,7 +5,7 @@ import (
 	"github.com/wowsims/wotlk/sim/core/proto"
 )
 
-func (druid *Druid) registerMaulSpell(rageThreshold float64) {
+func (druid *Druid) registerMaulSpell() {
 	flatBaseDamage := 578.0
 	if druid.Ranged().ID == 23198 { // Idol of Brutality
 		flatBaseDamage += 50
@@ -87,11 +87,6 @@ func (druid *Druid) registerMaulSpell(rageThreshold float64) {
 			druid.MaulQueueAura.Activate(sim)
 		},
 	})
-
-	druid.MaulRageThreshold = max(druid.Maul.DefaultCast.Cost, rageThreshold)
-	if druid.IsUsingAPL {
-		druid.MaulRageThreshold = 0
-	}
 }
 
 func (druid *Druid) QueueMaul(sim *core.Simulation) {
@@ -106,11 +101,6 @@ func (druid *Druid) MaulReplaceMH(sim *core.Simulation, mhSwingSpell *core.Spell
 		return mhSwingSpell
 	}
 
-	if druid.CurrentRage() < druid.MaulRageThreshold {
-		druid.MaulQueueAura.Deactivate(sim)
-		return mhSwingSpell
-	}
-
 	if !druid.Maul.Spell.CanCast(sim, druid.CurrentTarget) {
 		druid.MaulQueueAura.Deactivate(sim)
 		return mhSwingSpell
@@ -118,7 +108,3 @@ func (druid *Druid) MaulReplaceMH(sim *core.Simulation, mhSwingSpell *core.Spell
 
 	return druid.Maul.Spell
 }
-
-func (druid *Druid) ShouldQueueMaul(_ *core.Simulation) bool {
-	return druid.CurrentRage() >= druid.MaulRageThreshold
-}
diff --git a/sim/druid/talents.go b/sim/druid/talents.go
index b3d944c3c7..3a63ceadc1 100644
--- a/sim/druid/talents.go
+++ b/sim/druid/talents.go
@@ -399,7 +399,7 @@ func (druid *Druid) applyOmenOfClarity() {
 
 			// https://github.com/JamminL/wotlk-classic-bugs/issues/66#issuecomment-1182017571
 			if druid.HurricaneTickSpell.IsEqual(spell) {
-				curCastTickSpeed := spell.CurCast.ChannelTime.Seconds() / 10
+				curCastTickSpeed := spell.CurDot().TickPeriod().Seconds() / 10
 				hurricaneCoeff := 1.0 - (7.0 / 9.0)
 				spellCoeff := hurricaneCoeff * curCastTickSpeed
 				chanceToProc := ((1.5 / 60) * 3.5) * spellCoeff
diff --git a/sim/druid/tank/rotation.go b/sim/druid/tank/rotation.go
deleted file mode 100644
index f1c75829b8..0000000000
--- a/sim/druid/tank/rotation.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package tank
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-)
-
-func (bear *FeralTankDruid) OnGCDReady(sim *core.Simulation) {
-	bear.doRotation(sim)
-}
-
-func (bear *FeralTankDruid) OnAutoAttack(sim *core.Simulation, _ *core.Spell) {
-	bear.tryQueueMaul(sim)
-}
-
-func (bear *FeralTankDruid) doRotation(sim *core.Simulation) {
-	if bear.GCD.IsReady(sim) {
-		if bear.shouldSaveLacerateStacks(sim) && bear.Lacerate.CanCast(sim, bear.CurrentTarget) {
-			bear.Lacerate.Cast(sim, bear.CurrentTarget)
-		} else if bear.shouldDemoRoar(sim) {
-			bear.DemoralizingRoar.Cast(sim, bear.CurrentTarget)
-		} else if bear.Berserk.IsReady(sim) {
-			bear.Berserk.Cast(sim, nil)
-		} else if bear.MangleBear.CanCast(sim, bear.CurrentTarget) {
-			bear.MangleBear.Cast(sim, bear.CurrentTarget)
-		} else if bear.shouldFaerieFire(sim) {
-			bear.FaerieFire.Cast(sim, bear.CurrentTarget)
-		} else if bear.shouldLacerate(sim) {
-			bear.Lacerate.Cast(sim, bear.CurrentTarget)
-		} else if bear.shouldSwipe(sim) {
-			bear.SwipeBear.Cast(sim, bear.CurrentTarget)
-		}
-	}
-
-	if bear.GCD.IsReady(sim) {
-		nextAction := bear.FaerieFire.ReadyAt()
-
-		if bear.MangleBear == nil {
-			bear.WaitUntil(sim, nextAction)
-		} else if !bear.MangleBear.IsReady(sim) {
-			nextAction = max(nextAction, sim.CurrentTime)
-			nextMangle := bear.MangleBear.ReadyAt()
-
-			if nextMangle < nextAction+time.Second {
-				nextAction = nextMangle
-			}
-
-			if nextAction > sim.CurrentTime {
-				bear.WaitUntil(sim, nextAction)
-			}
-		}
-	}
-
-	bear.tryQueueMaul(sim)
-	bear.DoNothing() // means we intionally have no other action if all else fails.
-}
-
-func (bear *FeralTankDruid) shouldSaveLacerateStacks(sim *core.Simulation) bool {
-	lacerateDot := bear.Lacerate.CurDot()
-	return lacerateDot.GetStacks() == 5 &&
-		lacerateDot.RemainingDuration(sim) <= time.Millisecond*1500
-}
-
-func (bear *FeralTankDruid) shouldSwipe(sim *core.Simulation) bool {
-	return bear.SwipeBear.CanCast(sim, bear.CurrentTarget) &&
-		((bear.MangleBear == nil) || (bear.MangleBear.ReadyAt() >= sim.CurrentTime+core.GCDDefault)) &&
-		bear.CurrentRage()-bear.SwipeBear.DefaultCast.Cost >= bear.MaulRageThreshold
-}
-
-func (bear *FeralTankDruid) tryQueueMaul(sim *core.Simulation) {
-	if bear.ShouldQueueMaul(sim) {
-		bear.QueueMaul(sim)
-	}
-}
-
-func (bear *FeralTankDruid) shouldDemoRoar(sim *core.Simulation) bool {
-	return bear.ShouldDemoralizingRoar(sim, false, bear.Rotation.MaintainDemoralizingRoar)
-}
-
-func (bear *FeralTankDruid) shouldFaerieFire(sim *core.Simulation) bool {
-	return bear.FaerieFire.IsReady(sim) && ((bear.MangleBear == nil) || (bear.MangleBear.ReadyAt() >= sim.CurrentTime+time.Second))
-}
-
-func (bear *FeralTankDruid) shouldLacerate(sim *core.Simulation) bool {
-	lacerateDot := bear.Lacerate.CurDot()
-	return bear.Lacerate.CanCast(sim, bear.CurrentTarget) && ((bear.MangleBear == nil) || (bear.MangleBear.ReadyAt() >= sim.CurrentTime+core.GCDDefault)) && ((lacerateDot.GetStacks() < 5) || (lacerateDot.RemainingDuration(sim) <= time.Duration(bear.Rotation.LacerateTime*float64(time.Second))))
-}
diff --git a/sim/druid/tank/tank.go b/sim/druid/tank/tank.go
index cd8e1f895a..21fab7ceeb 100644
--- a/sim/druid/tank/tank.go
+++ b/sim/druid/tank/tank.go
@@ -28,9 +28,8 @@ func NewFeralTankDruid(character *core.Character, options *proto.Player) *FeralT
 	selfBuffs := druid.SelfBuffs{}
 
 	bear := &FeralTankDruid{
-		Druid:    druid.New(character, druid.Bear, selfBuffs, options.TalentsString),
-		Rotation: tankOptions.Rotation,
-		Options:  tankOptions.Options,
+		Druid:   druid.New(character, druid.Bear, selfBuffs, options.TalentsString),
+		Options: tankOptions.Options,
 	}
 
 	bear.SelfBuffs.InnervateTarget = &proto.UnitReference{}
@@ -64,8 +63,7 @@ func NewFeralTankDruid(character *core.Character, options *proto.Player) *FeralT
 type FeralTankDruid struct {
 	*druid.Druid
 
-	Rotation *proto.FeralTankDruid_Rotation
-	Options  *proto.FeralTankDruid_Options
+	Options *proto.FeralTankDruid_Options
 }
 
 func (bear *FeralTankDruid) GetDruid() *druid.Druid {
@@ -74,7 +72,7 @@ func (bear *FeralTankDruid) GetDruid() *druid.Druid {
 
 func (bear *FeralTankDruid) Initialize() {
 	bear.Druid.Initialize()
-	bear.RegisterFeralTankSpells(float64(bear.Rotation.MaulRageThreshold))
+	bear.RegisterFeralTankSpells()
 }
 
 func (bear *FeralTankDruid) Reset(sim *core.Simulation) {

From 3ec99cc8ef3c6eb38dd9e5a5fa11ab639980e6a9 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Thu, 4 Jan 2024 19:57:23 -0800
Subject: [PATCH 07/28] Finish druid and mage

---
 sim/deathknight/dps/TestBlood.results  |  12 +-
 sim/deathknight/dps/TestUnholy.results | 568 ++++++++++++-------------
 sim/druid/druid.go                     |   1 -
 sim/druid/feral/feral.go               |   1 -
 sim/druid/restoration/restoration.go   |   6 +-
 sim/druid/restoration/rotation.go      |  15 -
 sim/hunter/aspects.go                  |   2 +-
 sim/lib/library.go                     |   2 -
 sim/mage/TestArcane.results            |   4 +-
 sim/mage/TestFire.results              |   8 +-
 sim/mage/TestFrost.results             | 416 +++++++++---------
 sim/mage/arcane_blast.go               |   3 -
 sim/mage/arcane_missiles.go            |   3 +-
 sim/mage/blizzard.go                   |   3 +-
 sim/mage/evocation.go                  |   3 +-
 sim/mage/mage.go                       |  30 +-
 sim/mage/rotations.go                  | 155 -------
 17 files changed, 511 insertions(+), 721 deletions(-)
 delete mode 100644 sim/druid/restoration/rotation.go
 delete mode 100644 sim/mage/rotations.go

diff --git a/sim/deathknight/dps/TestBlood.results b/sim/deathknight/dps/TestBlood.results
index a4f19e677b..5102649157 100644
--- a/sim/deathknight/dps/TestBlood.results
+++ b/sim/deathknight/dps/TestBlood.results
@@ -287,8 +287,8 @@ dps_results: {
 dps_results: {
  key: "TestBlood-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 10443.90447
-  tps: 5277.80696
+  dps: 10467.28296
+  tps: 5296.53902
  }
 }
 dps_results: {
@@ -666,8 +666,8 @@ dps_results: {
 dps_results: {
  key: "TestBlood-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 10383.49361
-  tps: 5254.72979
+  dps: 10350.69235
+  tps: 5235.5789
  }
 }
 dps_results: {
@@ -778,8 +778,8 @@ dps_results: {
 dps_results: {
  key: "TestBlood-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 10379.62764
-  tps: 5242.10503
+  dps: 10317.50284
+  tps: 5211.82802
  }
 }
 dps_results: {
diff --git a/sim/deathknight/dps/TestUnholy.results b/sim/deathknight/dps/TestUnholy.results
index b00336dda8..e31fee5d05 100644
--- a/sim/deathknight/dps/TestUnholy.results
+++ b/sim/deathknight/dps/TestUnholy.results
@@ -46,984 +46,984 @@ character_stats_results: {
 dps_results: {
  key: "TestUnholy-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 9710.58503
-  tps: 5711.54761
+  dps: 9680.47429
+  tps: 5704.45855
   hps: 63.52617
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 9710.58503
-  tps: 5711.54761
+  dps: 9680.47429
+  tps: 5704.45855
   hps: 66.3402
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 10248.30721
-  tps: 5886.95782
+  dps: 10217.92511
+  tps: 5880.532
   hps: 42.71551
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 9924.17016
-  tps: 5866.99266
+  dps: 9893.9745
+  tps: 5859.94867
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 9710.59693
-  tps: 5711.60656
+  dps: 9680.48618
+  tps: 5704.5175
   hps: 134.78881
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 9710.59693
-  tps: 5711.60656
+  dps: 9680.48618
+  tps: 5704.5175
   hps: 134.78881
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 10270.83734
-  tps: 5906.78165
+  dps: 10240.42421
+  tps: 5900.33685
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Beast-tamer'sShoulders-30892"
  value: {
-  dps: 9590.63003
-  tps: 5434.59411
+  dps: 9561.27606
+  tps: 5428.86625
   hps: 40.4349
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BlessedBattlegearofUndeadSlaying"
  value: {
-  dps: 7528.9716
-  tps: 4272.7715
+  dps: 7500.6957
+  tps: 4266.20781
   hps: 34.38394
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BlessedGarboftheUndeadSlayer"
  value: {
-  dps: 7417.57124
-  tps: 4240.52367
+  dps: 7390.28759
+  tps: 4234.65809
   hps: 32.96775
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 7201.24066
-  tps: 4096.56066
+  dps: 7173.57707
+  tps: 4090.382
   hps: 32.72866
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 10248.30721
-  tps: 5769.21867
+  dps: 10217.92511
+  tps: 5762.92136
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Bryntroll,theBoneArbiter-50415"
  value: {
-  dps: 10377.44167
-  tps: 5996.15064
+  dps: 10346.91539
+  tps: 5989.61257
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Bryntroll,theBoneArbiter-50709"
  value: {
-  dps: 10377.44167
-  tps: 5996.15064
+  dps: 10346.91539
+  tps: 5989.61257
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 10384.12635
-  tps: 6001.95118
+  dps: 10353.5962
+  tps: 5995.41414
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 110.32034
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 9841.85262
-  tps: 5824.32161
+  dps: 9811.44494
+  tps: 5817.02786
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 9904.01516
-  tps: 5871.41583
+  dps: 9873.64594
+  tps: 5864.21871
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 10016.40898
-  tps: 5851.61296
+  dps: 9986.29823
+  tps: 5844.5239
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DarkrunedBattlegear"
  value: {
-  dps: 8342.40652
-  tps: 4728.74456
+  dps: 8313.25385
+  tps: 4722.11602
   hps: 38.90837
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DarkrunedPlate"
  value: {
-  dps: 7455.04498
-  tps: 4195.27998
+  dps: 7427.64393
+  tps: 4189.2183
   hps: 40.98666
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DeadlyGladiator'sSigilofStrife-42620"
  value: {
-  dps: 10428.94056
-  tps: 6026.53603
+  dps: 10398.41427
+  tps: 6019.99796
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 9814.76517
-  tps: 5800.54798
+  dps: 9784.41797
+  tps: 5793.32415
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 10244.26393
-  tps: 6019.38836
+  dps: 10212.23545
+  tps: 6011.7831
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 10336.93352
-  tps: 6134.04278
+  dps: 10304.74071
+  tps: 6126.35152
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Defender'sCode-40257"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 10275.66073
-  tps: 5910.13257
+  dps: 10245.17112
+  tps: 5903.63941
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DislodgedForeignObject-50348"
  value: {
-  dps: 10058.86423
-  tps: 5920.2823
+  dps: 10027.7374
+  tps: 5913.21833
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 10045.90755
-  tps: 5870.04449
+  dps: 10015.90074
+  tps: 5863.92416
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 10248.30721
-  tps: 5886.95782
+  dps: 10217.92511
+  tps: 5880.532
   hps: 42.71551
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 10248.30721
-  tps: 5886.95782
+  dps: 10217.92511
+  tps: 5880.532
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 10270.83734
-  tps: 5906.78165
+  dps: 10240.42421
+  tps: 5900.33685
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 10269.30196
-  tps: 5905.31005
+  dps: 10238.88528
+  tps: 5898.8624
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 9960.43305
-  tps: 5713.77531
+  dps: 9957.51073
+  tps: 5686.58021
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 44.16848
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 10248.30721
-  tps: 5886.95782
+  dps: 10217.92511
+  tps: 5880.532
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 9884.46426
-  tps: 5855.45523
+  dps: 9854.14952
+  tps: 5848.27771
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 9829.83582
-  tps: 5812.66149
+  dps: 9799.48063
+  tps: 5805.4028
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ForgeEmber-37660"
  value: {
-  dps: 9809.03194
-  tps: 5795.31409
+  dps: 9778.71591
+  tps: 5788.07856
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 10248.30721
-  tps: 5886.95782
+  dps: 10217.92511
+  tps: 5880.532
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 10248.30721
-  tps: 5886.95782
+  dps: 10217.92511
+  tps: 5880.532
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-FuriousGladiator'sSigilofStrife-42621"
  value: {
-  dps: 10439.24034
-  tps: 6032.61311
+  dps: 10408.71405
+  tps: 6026.07503
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 9935.80342
-  tps: 5878.29335
+  dps: 9905.69267
+  tps: 5871.20429
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-FuturesightRune-38763"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 9862.40069
-  tps: 5837.60588
+  dps: 9832.05026
+  tps: 5830.41915
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-HatefulGladiator'sSigilofStrife-42619"
  value: {
-  dps: 10406.95035
-  tps: 6012.39309
+  dps: 10376.42406
+  tps: 6005.85502
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 10270.83734
-  tps: 5906.78165
+  dps: 10240.42421
+  tps: 5900.33685
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 10269.30196
-  tps: 5905.31005
+  dps: 10238.88528
+  tps: 5898.8624
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-IncisorFragment-37723"
  value: {
-  dps: 9889.84971
-  tps: 5849.80123
+  dps: 9858.84411
+  tps: 5841.99589
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 10248.30721
-  tps: 5886.95782
+  dps: 10217.92511
+  tps: 5880.532
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 10280.82071
-  tps: 5909.54809
+  dps: 10250.43861
+  tps: 5903.12227
   hps: 58.71565
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-LastWord-50179"
  value: {
-  dps: 10377.44167
-  tps: 5996.15064
+  dps: 10346.91539
+  tps: 5989.61257
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-LastWord-50708"
  value: {
-  dps: 10377.44167
-  tps: 5996.15064
+  dps: 10346.91539
+  tps: 5989.61257
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 10032.0625
-  tps: 5843.61227
+  dps: 10002.66676
+  tps: 5837.61294
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 9859.78621
-  tps: 5842.11561
+  dps: 9829.70588
+  tps: 5834.98049
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 10274.62766
-  tps: 5905.24519
+  dps: 10244.24556
+  tps: 5898.81936
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 10280.82071
-  tps: 5909.54809
+  dps: 10250.43861
+  tps: 5903.12227
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 10248.30721
-  tps: 5886.95782
+  dps: 10217.92511
+  tps: 5880.532
   hps: 42.60516
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 10248.30721
-  tps: 5886.95782
+  dps: 10217.92511
+  tps: 5880.532
   hps: 42.71551
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 9785.06353
-  tps: 5770.6385
+  dps: 9754.94571
+  tps: 5763.56884
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 9792.15193
-  tps: 5776.63924
+  dps: 9762.03411
+  tps: 5769.56958
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 10377.44167
-  tps: 5996.15064
+  dps: 10346.91539
+  tps: 5989.61257
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-RelentlessGladiator'sSigilofStrife-42622"
  value: {
-  dps: 10451.25674
-  tps: 6039.70303
+  dps: 10420.73045
+  tps: 6033.16496
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 10248.30721
-  tps: 5886.95782
+  dps: 10217.92511
+  tps: 5880.532
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SavageGladiator'sSigilofStrife-42618"
  value: {
-  dps: 10403.60974
-  tps: 6010.55432
+  dps: 10373.08345
+  tps: 6004.01625
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ScourgeborneBattlegear"
  value: {
-  dps: 8029.52875
-  tps: 4566.12376
+  dps: 8000.74515
+  tps: 4559.84406
   hps: 36.48062
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ScourgebornePlate"
  value: {
-  dps: 7364.82744
-  tps: 4120.861
+  dps: 7338.02073
+  tps: 4115.32448
   hps: 38.04394
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Scourgelord'sBattlegear"
  value: {
-  dps: 9358.46371
-  tps: 5521.64302
+  dps: 9330.42072
+  tps: 5515.28971
   hps: 44.60989
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Scourgelord'sPlate"
  value: {
-  dps: 8219.42444
-  tps: 4642.08454
+  dps: 8192.96648
+  tps: 4636.93277
   hps: 48.06758
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Shadowmourne-49623"
  value: {
-  dps: 10377.44167
-  tps: 5996.15064
+  dps: 10346.91539
+  tps: 5989.61257
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigilofDeflection-45144"
  value: {
-  dps: 10377.44167
-  tps: 5996.15064
+  dps: 10346.91539
+  tps: 5989.61257
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigilofHauntedDreams-40715"
  value: {
-  dps: 10407.04323
-  tps: 6021.87725
+  dps: 10376.62308
+  tps: 6015.42194
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigilofInsolence-47672"
  value: {
-  dps: 10377.44167
-  tps: 5996.15064
+  dps: 10346.91539
+  tps: 5989.61257
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigiloftheBoneGryphon-50462"
  value: {
-  dps: 10377.44167
-  tps: 5996.15064
+  dps: 10346.91539
+  tps: 5989.61257
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigiloftheHangedMan-50459"
  value: {
-  dps: 10377.44167
-  tps: 5996.15064
+  dps: 10346.91539
+  tps: 5989.61257
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigiloftheUnfalteringKnight-40714"
  value: {
-  dps: 10377.44167
-  tps: 5996.15064
+  dps: 10346.91539
+  tps: 5989.61257
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 46.32034
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SoulPreserver-37111"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SouloftheDead-40382"
  value: {
-  dps: 9832.82416
-  tps: 5816.15563
+  dps: 9802.43285
+  tps: 5808.86704
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SparkofLife-37657"
  value: {
-  dps: 9862.05029
-  tps: 5804.30139
+  dps: 9832.06089
+  tps: 5797.8349
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 9913.75663
-  tps: 5850.97982
+  dps: 9800.6582
+  tps: 5811.52763
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-StormshroudArmor"
  value: {
-  dps: 7095.52999
-  tps: 4057.87955
+  dps: 7067.22499
+  tps: 4050.80338
   hps: 30.7975
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 10280.82071
-  tps: 5909.54809
+  dps: 10250.43861
+  tps: 5903.12227
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 10274.62766
-  tps: 5905.24519
+  dps: 10244.24556
+  tps: 5898.81936
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 10263.78983
-  tps: 5897.7151
+  dps: 10233.40773
+  tps: 5891.28927
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Thassarian'sBattlegear"
  value: {
-  dps: 8618.51653
-  tps: 4958.8001
+  dps: 8590.35604
+  tps: 4952.61627
   hps: 38.68766
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Thassarian'sPlate"
  value: {
-  dps: 7486.10663
-  tps: 4194.90667
+  dps: 7458.635
+  tps: 4188.82468
   hps: 40.30616
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TheTwinBladesofAzzinoth"
  value: {
-  dps: 9010.98248
-  tps: 5032.12724
+  dps: 8979.88721
+  tps: 5025.14863
   hps: 40.74757
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 10305.50604
-  tps: 5918.26645
+  dps: 10275.22899
+  tps: 5912.27746
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 9954.00729
-  tps: 5913.58867
+  dps: 9924.59603
+  tps: 5907.08552
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 9920.42339
-  tps: 5880.41645
+  dps: 9890.45908
+  tps: 5873.52857
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 10248.30721
-  tps: 5886.95782
+  dps: 10217.92511
+  tps: 5880.532
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 10248.30721
-  tps: 5886.95782
+  dps: 10217.92511
+  tps: 5880.532
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 9840.77218
-  tps: 5774.12915
+  dps: 9826.9966
+  tps: 5751.9898
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 10248.30721
-  tps: 5886.95782
+  dps: 10217.92511
+  tps: 5880.532
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 10248.30721
-  tps: 5886.95782
+  dps: 10217.92511
+  tps: 5880.532
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-UndeadSlayer'sBlessedArmor"
  value: {
-  dps: 7423.03446
-  tps: 4256.35867
+  dps: 7395.21167
+  tps: 4250.00861
   hps: 33.68504
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Val'anyr,HammerofAncientKings-46017"
  value: {
-  dps: 9633.49282
-  tps: 5427.26413
+  dps: 9602.82536
+  tps: 5420.6152
   hps: 42.81036
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-WingedTalisman-37844"
  value: {
-  dps: 9710.60578
-  tps: 5711.54461
+  dps: 9680.49504
+  tps: 5704.45555
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-WrathfulGladiator'sSigilofStrife-51417"
  value: {
-  dps: 10464.98978
-  tps: 6047.8058
+  dps: 10434.46349
+  tps: 6041.26773
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-Average-Default"
  value: {
-  dps: 10310.79563
-  tps: 5971.29709
+  dps: 10279.57916
+  tps: 5964.26539
   hps: 42.14164
  }
 }
@@ -1174,152 +1174,152 @@ dps_results: {
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-FullBuffs-LongMultiTarget"
  value: {
-  dps: 14312.2404
-  tps: 9287.052
+  dps: 14275.55112
+  tps: 9275.62941
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-FullBuffs-LongSingleTarget"
  value: {
-  dps: 10377.44167
-  tps: 5996.15064
+  dps: 10346.91539
+  tps: 5989.61257
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 15559.83451
-  tps: 7162.15963
+  dps: 15451.74278
+  tps: 7165.07213
   hps: 210.63484
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-NoBuffs-LongMultiTarget"
  value: {
-  dps: 6783.40918
-  tps: 4583.6299
+  dps: 6761.4395
+  tps: 4575.413
   hps: 27.7272
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-NoBuffs-LongSingleTarget"
  value: {
-  dps: 5103.61606
-  tps: 3177.62391
+  dps: 5085.97636
+  tps: 3172.95627
   hps: 27.7272
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 6757.40436
-  tps: 3489.08968
+  dps: 6701.62049
+  tps: 3491.63258
   hps: 138.636
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 59452.42669
-  tps: 61476.73056
+  dps: 59321.04606
+  tps: 61361.65332
   hps: 236.33229
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 11354.57131
-  tps: 7485.7125
+  dps: 11318.78456
+  tps: 7472.9393
   hps: 236.33229
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 16535.42503
-  tps: 8698.33633
+  dps: 16412.5451
+  tps: 8686.56163
   hps: 337.01574
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 35212.15599
-  tps: 37565.37369
+  dps: 35117.90018
+  tps: 37478.75367
   hps: 155.82686
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 5772.20815
-  tps: 4185.27192
+  dps: 5750.84606
+  tps: 4175.86894
   hps: 155.27232
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 7347.6605
-  tps: 4481.15645
+  dps: 7280.02322
+  tps: 4471.44132
   hps: 221.8176
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-FullBuffs-LongMultiTarget"
  value: {
-  dps: 36324.85779
-  tps: 43276.25499
+  dps: 36238.79517
+  tps: 43195.85148
   hps: 236.33229
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-FullBuffs-LongSingleTarget"
  value: {
-  dps: 11470.17283
-  tps: 7591.32914
+  dps: 11434.83628
+  tps: 7578.90963
   hps: 236.33229
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 16608.09173
-  tps: 8808.92846
+  dps: 16487.2343
+  tps: 8798.61633
   hps: 339.12209
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-NoBuffs-LongMultiTarget"
  value: {
-  dps: 18749.83496
-  tps: 22923.25478
+  dps: 18696.93865
+  tps: 22871.51167
   hps: 155.82686
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-NoBuffs-LongSingleTarget"
  value: {
-  dps: 5897.73441
-  tps: 4290.70565
+  dps: 5876.43297
+  tps: 4281.49641
   hps: 155.27232
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 7417.6708
-  tps: 4556.06307
+  dps: 7351.97253
+  tps: 4548.398
   hps: 221.8176
  }
 }
 dps_results: {
  key: "TestUnholy-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 9845.43373
-  tps: 5635.64895
+  dps: 9815.01951
+  tps: 5629.20041
   hps: 42.12697
  }
 }
diff --git a/sim/druid/druid.go b/sim/druid/druid.go
index 5bd1c29bf1..cab2a21f2f 100644
--- a/sim/druid/druid.go
+++ b/sim/druid/druid.go
@@ -26,7 +26,6 @@ type Druid struct {
 	RebirthTiming     float64
 	BleedsActive      int
 	AssumeBleedActive bool
-	PrePopBerserk     bool
 
 	ReplaceBearMHFunc core.ReplaceMHSwing
 
diff --git a/sim/druid/feral/feral.go b/sim/druid/feral/feral.go
index 1608d06272..d30579fe0a 100644
--- a/sim/druid/feral/feral.go
+++ b/sim/druid/feral/feral.go
@@ -42,7 +42,6 @@ func NewFeralDruid(character *core.Character, options *proto.Player) *FeralDruid
 	cat.AssumeBleedActive = feralOptions.Options.AssumeBleedActive
 	cat.maxRipTicks = cat.MaxRipTicks()
 	cat.prepopOoc = feralOptions.Rotation.PrePopOoc
-	cat.PrePopBerserk = feralOptions.Rotation.PrePopBerserk
 	cat.setupRotation(feralOptions.Rotation)
 
 	cat.EnableEnergyBar(100.0)
diff --git a/sim/druid/restoration/restoration.go b/sim/druid/restoration/restoration.go
index 428160c8d9..d0d6137a65 100644
--- a/sim/druid/restoration/restoration.go
+++ b/sim/druid/restoration/restoration.go
@@ -28,8 +28,7 @@ func NewRestorationDruid(character *core.Character, options *proto.Player) *Rest
 	selfBuffs := druid.SelfBuffs{}
 
 	resto := &RestorationDruid{
-		Druid:    druid.New(character, druid.Tree, selfBuffs, options.TalentsString),
-		Rotation: restoOptions.Rotation,
+		Druid: druid.New(character, druid.Tree, selfBuffs, options.TalentsString),
 	}
 
 	resto.SelfBuffs.InnervateTarget = &proto.UnitReference{}
@@ -37,14 +36,11 @@ func NewRestorationDruid(character *core.Character, options *proto.Player) *Rest
 		resto.SelfBuffs.InnervateTarget = restoOptions.Options.InnervateTarget
 	}
 
-	resto.EnableResumeAfterManaWait(resto.tryUseGCD)
 	return resto
 }
 
 type RestorationDruid struct {
 	*druid.Druid
-
-	Rotation *proto.RestorationDruid_Rotation
 }
 
 func (resto *RestorationDruid) GetDruid() *druid.Druid {
diff --git a/sim/druid/restoration/rotation.go b/sim/druid/restoration/rotation.go
deleted file mode 100644
index 5a59ad57f2..0000000000
--- a/sim/druid/restoration/rotation.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package restoration
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-)
-
-func (resto *RestorationDruid) OnGCDReady(sim *core.Simulation) {
-	resto.tryUseGCD(sim)
-}
-
-func (resto *RestorationDruid) tryUseGCD(sim *core.Simulation) {
-	resto.WaitUntil(sim, sim.CurrentTime+time.Second*5)
-}
diff --git a/sim/hunter/aspects.go b/sim/hunter/aspects.go
index 9788a2058c..8b5fa83e3e 100644
--- a/sim/hunter/aspects.go
+++ b/sim/hunter/aspects.go
@@ -134,7 +134,7 @@ func (hunter *Hunter) registerAspectOfTheViperSpell() {
 }
 
 func (hunter *Hunter) applySharedAspectConfig(isHawk bool, aura *core.Aura) {
-	if isHawk != (hunter.Rotation.ViperStartManaPercent >= 1) {
+	if isHawk {
 		aura.OnReset = func(aura *core.Aura, sim *core.Simulation) {
 			aura.Activate(sim)
 		}
diff --git a/sim/lib/library.go b/sim/lib/library.go
index 74b37c17fe..9539c478bd 100644
--- a/sim/lib/library.go
+++ b/sim/lib/library.go
@@ -193,8 +193,6 @@ func trySpell(act int) bool {
 
 //export doNothing
 func doNothing() bool {
-	player := _active_sim.Raid.Parties[0].Players[0]
-	player.GetCharacter().DoNothing()
 	return true
 }
 
diff --git a/sim/mage/TestArcane.results b/sim/mage/TestArcane.results
index 4a70f5b230..5f0cf2c0be 100644
--- a/sim/mage/TestArcane.results
+++ b/sim/mage/TestArcane.results
@@ -252,8 +252,8 @@ dps_results: {
 dps_results: {
  key: "TestArcane-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 10738.79708
-  tps: 6577.6561
+  dps: 10727.16841
+  tps: 6570.11619
  }
 }
 dps_results: {
diff --git a/sim/mage/TestFire.results b/sim/mage/TestFire.results
index d26760097b..b939817bf9 100644
--- a/sim/mage/TestFire.results
+++ b/sim/mage/TestFire.results
@@ -770,8 +770,8 @@ dps_results: {
 dps_results: {
  key: "TestFire-Settings-Troll-p3_fire_alliance-Fire-fire-FullBuffs-LongMultiTarget"
  value: {
-  dps: 38085.36222
-  tps: 32980.78258
+  dps: 38064.82304
+  tps: 32963.64697
  }
 }
 dps_results: {
@@ -791,8 +791,8 @@ dps_results: {
 dps_results: {
  key: "TestFire-Settings-Troll-p3_fire_alliance-Fire-fire-NoBuffs-LongMultiTarget"
  value: {
-  dps: 22222.81509
-  tps: 20575.92958
+  dps: 22173.53497
+  tps: 20541.48297
  }
 }
 dps_results: {
diff --git a/sim/mage/TestFrost.results b/sim/mage/TestFrost.results
index dc582270eb..cc1f38bac4 100644
--- a/sim/mage/TestFrost.results
+++ b/sim/mage/TestFrost.results
@@ -46,277 +46,277 @@ character_stats_results: {
 dps_results: {
  key: "TestFrost-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 7928.24208
-  tps: 6453.45112
+  dps: 7933.66277
+  tps: 6459.8041
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 7962.77382
-  tps: 6481.89515
+  dps: 7968.21694
+  tps: 6488.27328
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 7760.1538
-  tps: 6318.09095
+  dps: 7765.31653
+  tps: 6324.30567
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 7646.41564
-  tps: 6221.07535
-  hps: 92.57675
+  dps: 7652.00052
+  tps: 6227.57151
+  hps: 92.573
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 7646.41564
-  tps: 6221.07535
-  hps: 92.57675
+  dps: 7652.00052
+  tps: 6227.57151
+  hps: 92.573
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 7782.85819
-  tps: 6339.33425
+  dps: 7783.7779
+  tps: 6341.22794
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 6005.44274
-  tps: 4826.77708
+  dps: 6024.87702
+  tps: 4845.65031
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Bloodmage'sRegalia"
  value: {
-  dps: 8143.08775
-  tps: 6688.26693
+  dps: 8140.05763
+  tps: 6685.8876
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 7798.72055
-  tps: 6223.09271
+  dps: 7803.91273
+  tps: 6229.21586
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 8003.40047
-  tps: 6538.31272
+  dps: 8004.42685
+  tps: 6540.31281
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
   hps: 64
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 7721.01592
-  tps: 6287.16324
+  dps: 7727.10393
+  tps: 6294.20537
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 7731.37855
-  tps: 6303.73706
+  dps: 7734.47564
+  tps: 6306.31917
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 7684.58603
-  tps: 6266.86577
+  dps: 7690.85322
+  tps: 6274.32423
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Death'sChoice-47464"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 7678.84495
-  tps: 6246.81867
+  dps: 7685.02105
+  tps: 6253.78087
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Defender'sCode-40257"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 7783.55414
-  tps: 6340.66928
+  dps: 7784.47384
+  tps: 6342.56315
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-DislodgedForeignObject-50348"
  value: {
-  dps: 8381.41843
-  tps: 6873.3235
+  dps: 8384.30782
+  tps: 6875.40602
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 8297.80523
-  tps: 6789.83899
+  dps: 8289.8124
+  tps: 6783.32805
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 7760.1538
-  tps: 6318.09095
+  dps: 7765.31653
+  tps: 6324.30567
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 7812.27004
-  tps: 6362.69935
+  dps: 7813.94105
+  tps: 6365.39795
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 7782.89356
-  tps: 6340.06823
+  dps: 7783.81326
+  tps: 6341.9621
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 7777.85554
-  tps: 6335.57176
+  dps: 7778.77525
+  tps: 6337.46563
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 7759.64948
-  tps: 6319.89085
+  dps: 7755.40406
+  tps: 6315.42295
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 7760.1538
-  tps: 6318.09095
+  dps: 7765.31653
+  tps: 6324.30567
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 7723.41199
-  tps: 6291.61522
+  dps: 7727.1541
+  tps: 6295.38387
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 7906.80366
-  tps: 6452.50593
+  dps: 7913.07517
+  tps: 6459.71001
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 7668.23295
-  tps: 6239.78194
+  dps: 7673.31368
+  tps: 6245.89566
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 7821.50761
-  tps: 6365.5332
+  dps: 7826.85897
+  tps: 6371.80846
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ForgeEmber-37660"
  value: {
-  dps: 7824.55194
-  tps: 6374.99683
+  dps: 7831.62918
+  tps: 6382.78835
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 7798.72055
-  tps: 6349.7974
+  dps: 7803.91273
+  tps: 6356.04504
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 7791.0072
-  tps: 6343.45611
+  dps: 7796.19349
+  tps: 6349.69717
  }
 }
 dps_results: {
@@ -329,15 +329,15 @@ dps_results: {
 dps_results: {
  key: "TestFrost-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-FuturesightRune-38763"
  value: {
-  dps: 7744.89597
-  tps: 6301.18733
+  dps: 7750.19737
+  tps: 6307.40657
  }
 }
 dps_results: {
@@ -350,85 +350,85 @@ dps_results: {
 dps_results: {
  key: "TestFrost-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 7945.50795
-  tps: 6467.67314
+  dps: 7950.93986
+  tps: 6474.03869
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 7984.74857
-  tps: 6499.9959
+  dps: 7990.20596
+  tps: 6506.39003
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 7726.20314
-  tps: 6306.13936
+  dps: 7712.63873
+  tps: 6291.8092
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Heartpierce-49982"
  value: {
-  dps: 8003.40047
-  tps: 6538.31272
+  dps: 8004.42685
+  tps: 6540.31281
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Heartpierce-50641"
  value: {
-  dps: 8003.40047
-  tps: 6538.31272
+  dps: 8004.42685
+  tps: 6540.31281
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
-  dps: 7930.51116
-  tps: 6474.17485
+  dps: 7936.00618
+  tps: 6480.54928
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 7782.89356
-  tps: 6340.06823
+  dps: 7783.81326
+  tps: 6341.9621
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 7777.85554
-  tps: 6335.57176
+  dps: 7778.77525
+  tps: 6337.46563
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-IncisorFragment-37723"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 7772.45693
-  tps: 6333.79039
+  dps: 7774.27285
+  tps: 6336.45718
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 7760.1538
-  tps: 6318.09095
+  dps: 7765.31653
+  tps: 6324.30567
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Khadgar'sRegalia"
  value: {
-  dps: 6580.00377
-  tps: 5309.1505
+  dps: 6575.79129
+  tps: 5306.49837
  }
 }
 dps_results: {
@@ -441,239 +441,239 @@ dps_results: {
 dps_results: {
  key: "TestFrost-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 7647.59946
-  tps: 6219.47581
+  dps: 7652.83765
+  tps: 6225.62283
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 7691.79354
-  tps: 6256.75774
+  dps: 7698.15546
+  tps: 6263.90226
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 7847.31458
-  tps: 6384.09669
+  dps: 7848.80614
+  tps: 6384.95591
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Nibelung-49992"
  value: {
-  dps: 8003.40047
-  tps: 6538.31272
+  dps: 8004.42685
+  tps: 6540.31281
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Nibelung-50648"
  value: {
-  dps: 8003.40047
-  tps: 6538.31272
+  dps: 8004.42685
+  tps: 6540.31281
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 7760.1538
-  tps: 6318.09095
+  dps: 7765.31653
+  tps: 6324.30567
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 7760.1538
-  tps: 6318.09095
+  dps: 7765.31653
+  tps: 6324.30567
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 7646.26897
-  tps: 6221.11085
+  dps: 7651.50716
+  tps: 6227.25922
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 7760.1538
-  tps: 6318.09095
+  dps: 7765.31653
+  tps: 6324.30567
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 7760.1538
-  tps: 6318.09095
+  dps: 7765.31653
+  tps: 6324.30567
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 7998.96913
-  tps: 6538.92968
+  dps: 7998.48484
+  tps: 6538.10806
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 8048.33749
-  tps: 6583.34161
+  dps: 8047.7877
+  tps: 6582.45344
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 7978.38291
-  tps: 6514.28941
+  dps: 7984.09185
+  tps: 6521.00509
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 7760.12417
-  tps: 6316.94905
+  dps: 7765.2869
+  tps: 6323.1634
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 7895.33302
-  tps: 6429.12257
+  dps: 7900.7323
+  tps: 6435.45003
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 7926.72551
-  tps: 6455.23432
+  dps: 7932.14518
+  tps: 6461.58463
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SoulPreserver-37111"
  value: {
-  dps: 7765.00113
-  tps: 6318.98842
+  dps: 7770.31579
+  tps: 6325.22253
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SouloftheDead-40382"
  value: {
-  dps: 7719.6486
-  tps: 6289.29418
+  dps: 7725.7366
+  tps: 6296.32776
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SparkofLife-37657"
  value: {
-  dps: 7755.89541
-  tps: 6324.01321
+  dps: 7757.64893
+  tps: 6327.51453
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 7706.01629
-  tps: 6281.8047
+  dps: 7696.99765
+  tps: 6273.70353
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 7760.1538
-  tps: 6318.09095
+  dps: 7765.31653
+  tps: 6324.30567
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 7760.1538
-  tps: 6318.09095
+  dps: 7765.31653
+  tps: 6324.30567
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 7760.1538
-  tps: 6318.09095
+  dps: 7765.31653
+  tps: 6324.30567
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 7647.57925
-  tps: 6220.68724
+  dps: 7652.81744
+  tps: 6226.83399
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 7686.34035
-  tps: 6255.4996
+  dps: 7691.42109
+  tps: 6261.64718
  }
 }
 dps_results: {
@@ -686,106 +686,106 @@ dps_results: {
 dps_results: {
  key: "TestFrost-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 7647.27929
-  tps: 6222.02013
+  dps: 7652.51748
+  tps: 6228.1685
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TheTwinBladesofAzzinoth"
  value: {
-  dps: 6369.89154
-  tps: 5180.72909
+  dps: 6372.08189
+  tps: 5180.43316
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 7760.1538
-  tps: 6318.09095
+  dps: 7765.31653
+  tps: 6324.30567
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 7706.01629
-  tps: 6281.8047
+  dps: 7696.99765
+  tps: 6273.70353
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 7706.01629
-  tps: 6281.8047
+  dps: 7696.99765
+  tps: 6273.70353
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 7798.72055
-  tps: 6349.7974
+  dps: 7803.91273
+  tps: 6356.04504
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 7791.0072
-  tps: 6343.45611
+  dps: 7796.19349
+  tps: 6349.69717
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 7784.88807
-  tps: 6332.07288
+  dps: 7779.60815
+  tps: 6327.85299
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 7791.0072
-  tps: 6343.45611
+  dps: 7796.19349
+  tps: 6349.69717
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 7798.72055
-  tps: 6349.7974
+  dps: 7803.91273
+  tps: 6356.04504
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-WingedTalisman-37844"
  value: {
-  dps: 7749.91677
-  tps: 6314.13796
+  dps: 7755.11162
+  tps: 6320.25051
  }
 }
 dps_results: {
  key: "TestFrost-Average-Default"
  value: {
-  dps: 8088.27706
-  tps: 6619.63625
+  dps: 8088.78057
+  tps: 6620.05808
  }
 }
 dps_results: {
  key: "TestFrost-Settings-Troll-p3_frost_alliance-Frost-frost-FullBuffs-LongMultiTarget"
  value: {
-  dps: 8003.40047
-  tps: 6815.93838
+  dps: 8004.42685
+  tps: 6818.27786
  }
 }
 dps_results: {
  key: "TestFrost-Settings-Troll-p3_frost_alliance-Frost-frost-FullBuffs-LongSingleTarget"
  value: {
-  dps: 8003.40047
-  tps: 6538.31272
+  dps: 8004.42685
+  tps: 6540.31281
  }
 }
 dps_results: {
  key: "TestFrost-Settings-Troll-p3_frost_alliance-Frost-frost-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 10069.72226
-  tps: 8272.31852
+  dps: 10047.18185
+  tps: 8250.71555
  }
 }
 dps_results: {
@@ -848,7 +848,7 @@ dps_results: {
 dps_results: {
  key: "TestFrost-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 8003.40047
-  tps: 6538.31272
+  dps: 8004.42685
+  tps: 6540.31281
  }
 }
diff --git a/sim/mage/arcane_blast.go b/sim/mage/arcane_blast.go
index 2833bfc057..9db24bfdf9 100644
--- a/sim/mage/arcane_blast.go
+++ b/sim/mage/arcane_blast.go
@@ -23,9 +23,6 @@ func (mage *Mage) registerArcaneBlastSpell() {
 			mage.PseudoStats.SchoolDamageDealtMultiplier[stats.SchoolIndexArcane] *= newMultiplier / oldMultiplier
 			mage.ArcaneBlast.CostMultiplier += 1.75 * float64(newStacks-oldStacks)
 		},
-		OnExpire: func(aura *core.Aura, sim *core.Simulation) {
-			mage.arcaneBlastStreak = 0
-		},
 	})
 
 	actionID := core.ActionID{SpellID: 42897}
diff --git a/sim/mage/arcane_missiles.go b/sim/mage/arcane_missiles.go
index 8b2cd71ea5..f9fae720ea 100644
--- a/sim/mage/arcane_missiles.go
+++ b/sim/mage/arcane_missiles.go
@@ -49,8 +49,7 @@ func (mage *Mage) registerArcaneMissilesSpell() {
 		},
 		Cast: core.CastConfig{
 			DefaultCast: core.Cast{
-				GCD:         core.GCDDefault,
-				ChannelTime: time.Second * 5,
+				GCD: core.GCDDefault,
 			},
 		},
 		Dot: core.DotConfig{
diff --git a/sim/mage/blizzard.go b/sim/mage/blizzard.go
index 1b64af2720..f0b1831039 100644
--- a/sim/mage/blizzard.go
+++ b/sim/mage/blizzard.go
@@ -57,8 +57,7 @@ func (mage *Mage) registerBlizzardSpell() {
 		},
 		Cast: core.CastConfig{
 			DefaultCast: core.Cast{
-				GCD:         core.GCDDefault,
-				ChannelTime: time.Second * 8,
+				GCD: core.GCDDefault,
 			},
 		},
 		Dot: core.DotConfig{
diff --git a/sim/mage/evocation.go b/sim/mage/evocation.go
index de0e5f0733..b3025f9c10 100644
--- a/sim/mage/evocation.go
+++ b/sim/mage/evocation.go
@@ -18,8 +18,7 @@ func (mage *Mage) registerEvocation() {
 
 		Cast: core.CastConfig{
 			DefaultCast: core.Cast{
-				GCD:         core.GCDDefault,
-				ChannelTime: time.Duration(maxTicks) * time.Second * 2,
+				GCD: core.GCDDefault,
 			},
 			CD: core.Cooldown{
 				Timer:    mage.NewTimer(),
diff --git a/sim/mage/mage.go b/sim/mage/mage.go
index 763b09a0fe..f8daa71777 100644
--- a/sim/mage/mage.go
+++ b/sim/mage/mage.go
@@ -1,8 +1,6 @@
 package mage
 
 import (
-	"time"
-
 	"github.com/wowsims/wotlk/sim/common/wotlk"
 
 	"github.com/wowsims/wotlk/sim/core"
@@ -38,12 +36,8 @@ func RegisterMage() {
 type Mage struct {
 	core.Character
 
-	Talents  *proto.MageTalents
-	Options  *proto.Mage_Options
-	Rotation *proto.Mage_Rotation
-
-	arcaneBlastStreak int32
-	arcanePowerMCD    *core.MajorCooldown
+	Talents *proto.MageTalents
+	Options *proto.Mage_Options
 
 	waterElemental *WaterElemental
 	mirrorImage    *MirrorImage
@@ -137,20 +131,9 @@ func (mage *Mage) Initialize() {
 	mage.registerMirrorImageCD()
 	mage.registerBlastWaveSpell()
 	mage.registerDragonsBreathSpell()
-
-	mage.RegisterPrepullAction(-2000*time.Millisecond, func(sim *core.Simulation) {
-		if mirrorImageMCD := mage.GetMajorCooldownIgnoreTag(mage.MirrorImage.ActionID); !mage.IsUsingAPL && mirrorImageMCD != nil {
-			if len(mirrorImageMCD.GetTimings()) == 0 {
-				mage.MirrorImage.Cast(sim, nil)
-				mage.UpdateMajorCooldowns()
-			}
-		}
-	})
 }
 
 func (mage *Mage) Reset(sim *core.Simulation) {
-	mage.arcaneBlastStreak = 0
-	mage.arcanePowerMCD = mage.GetMajorCooldown(core.ActionID{SpellID: 12042})
 }
 
 func NewMage(character *core.Character, options *proto.Player) *Mage {
@@ -160,20 +143,11 @@ func NewMage(character *core.Character, options *proto.Player) *Mage {
 		Character: *character,
 		Talents:   &proto.MageTalents{},
 		Options:   mageOptions.Options,
-		Rotation:  mageOptions.Rotation,
 	}
 	core.FillTalentsProto(mage.Talents.ProtoReflect(), options.TalentsString, TalentTreeSizes)
 
 	mage.bonusCritDamage = .25*float64(mage.Talents.SpellPower) + .1*float64(mage.Talents.Burnout)
 	mage.EnableManaBar()
-	mage.EnableResumeAfterManaWait(mage.tryUseGCD)
-
-	if !mage.Talents.ArcaneBarrage {
-		mage.Rotation.UseArcaneBarrage = false
-	}
-	if mage.Talents.ImprovedScorch == 0 {
-		mage.Rotation.MaintainImprovedScorch = false
-	}
 
 	if mage.Options.Armor == proto.Mage_Options_MageArmor {
 		mage.PseudoStats.SpiritRegenRateCasting += .5
diff --git a/sim/mage/rotations.go b/sim/mage/rotations.go
deleted file mode 100644
index 42d2b9f248..0000000000
--- a/sim/mage/rotations.go
+++ /dev/null
@@ -1,155 +0,0 @@
-package mage
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-)
-
-func (mage *Mage) OnGCDReady(sim *core.Simulation) {
-	mage.tryUseGCD(sim)
-}
-
-func (mage *Mage) tryUseGCD(sim *core.Simulation) {
-	if mage.IsUsingAPL {
-		return
-	}
-
-	spell := mage.chooseSpell(sim)
-	if spell != nil {
-		if success := spell.Cast(sim, mage.CurrentTarget); !success {
-			mage.WaitForMana(sim, spell.CurCast.Cost)
-		}
-	}
-}
-
-func (mage *Mage) chooseSpell(sim *core.Simulation) *core.Spell {
-	if mage.Rotation.MaintainImprovedScorch && (!mage.CritDebuffCategories.Get(mage.CurrentTarget).AnyActive() || (mage.ScorchAuras.Get(mage.CurrentTarget).IsActive() && mage.ScorchAuras.Get(mage.CurrentTarget).RemainingDuration(sim) < time.Millisecond*4000)) {
-		return mage.Scorch
-	}
-
-	if mage.PrimaryTalentTree == 0 {
-		spell := mage.doArcaneRotation(sim)
-		if spell == mage.ArcaneBlast {
-			mage.arcaneBlastStreak++
-		}
-		return spell
-	} else if mage.PrimaryTalentTree == 1 {
-		return mage.doFireRotation(sim)
-	} else {
-		return mage.doFrostRotation(sim)
-	}
-}
-
-func (mage *Mage) doArcaneRotation(sim *core.Simulation) *core.Spell {
-	// AB until the end.
-	if mage.canBlast(sim) {
-		return mage.ArcaneBlast
-	}
-
-	// Extra ABs before first AP.
-	if sim.CurrentTime < time.Second*10 && !mage.ArcanePowerAura.IsActive() && mage.arcanePowerMCD != nil && mage.arcanePowerMCD.TimeToNextCast(sim) < time.Second*5 {
-		return mage.ArcaneBlast
-	}
-
-	// Extra ABs during first AP.
-	if sim.CurrentTime < time.Second*60 && mage.ArcanePowerAura.IsActive() && mage.arcaneBlastStreak < mage.Rotation.ExtraBlastsDuringFirstAp+4 {
-		return mage.ArcaneBlast
-	}
-
-	abStacks := mage.ArcaneBlastAura.GetStacks()
-	hasMissileBarrage := mage.MissileBarrageAura.IsActive() && mage.MissileBarrageAura.TimeActive(sim) > mage.ReactionTime
-
-	// AM if we have MB and below n AB stacks.
-	if hasMissileBarrage && abStacks < mage.Rotation.MissileBarrageBelowArcaneBlastStacks {
-		return mage.ArcaneMissiles
-	}
-
-	// AM if we have MB and below mana %.
-	manaPercent := mage.CurrentManaPercent()
-	if hasMissileBarrage && manaPercent < mage.Rotation.MissileBarrageBelowManaPercent {
-		return mage.ArcaneMissiles
-	}
-
-	// AM if we don't have barrage and over mana %.
-	if !hasMissileBarrage && manaPercent > mage.Rotation.BlastWithoutMissileBarrageAboveManaPercent {
-		return mage.ArcaneBlast
-	}
-
-	// If we've reached max desired stacks, use AM / ABarr. Otherwise blast.
-	maxAbStacks := int32(4)
-	if manaPercent < mage.Rotation.Only_3ArcaneBlastStacksBelowManaPercent {
-		maxAbStacks = 3
-	}
-	if abStacks < maxAbStacks {
-		return mage.ArcaneBlast
-	} else if mage.Rotation.UseArcaneBarrage && mage.ArcaneBarrage.IsReady(sim) {
-		return mage.ArcaneBarrage
-	} else {
-		return mage.ArcaneMissiles
-	}
-}
-
-func (mage *Mage) canBlast(sim *core.Simulation) bool {
-	// Save computation by assuming we can't blast for 30+ seconds.
-	remainingDur := sim.GetRemainingDuration()
-	if remainingDur > time.Second*30 {
-		return false
-	}
-
-	castTime := mage.ApplyCastSpeed(ArcaneBlastBaseCastTime)
-	manaCost := mage.ArcaneBlast.DefaultCast.Cost
-
-	stacks := float64(mage.ArcaneBlastAura.GetStacks())
-	curMana := mage.CurrentMana()
-	for curTime := time.Duration(0); curTime <= remainingDur; curTime += castTime {
-		if stacks < 4 {
-			stacks++
-		}
-		curMana -= manaCost * 1.75 * stacks
-		if curMana < 0 {
-			return false
-		}
-	}
-	return true
-}
-
-func (mage *Mage) doFireRotation(sim *core.Simulation) *core.Spell {
-	noBomb := mage.LivingBomb != nil && !mage.LivingBomb.Dot(mage.CurrentTarget).IsActive() && sim.GetRemainingDuration() > time.Second*12
-	if noBomb && mage.hotStreakCritAura.GetStacks() == 0 {
-		return mage.LivingBomb
-	}
-
-	hasHotStreak := mage.HotStreakAura.IsActive() && mage.HotStreakAura.TimeActive(sim) > mage.ReactionTime
-	if hasHotStreak && mage.Pyroblast != nil {
-		return mage.Pyroblast
-	}
-
-	if noBomb {
-		return mage.LivingBomb
-	}
-
-	if mage.Rotation.PrimaryFireSpell == proto.Mage_Rotation_Fireball {
-		return mage.Fireball
-	} else if mage.Rotation.PrimaryFireSpell == proto.Mage_Rotation_FrostfireBolt {
-		return mage.FrostfireBolt
-	} else {
-		return mage.Scorch
-	}
-}
-
-func (mage *Mage) doFrostRotation(sim *core.Simulation) *core.Spell {
-	hasBrainFreeze := mage.BrainFreezeAura.IsActive() && mage.BrainFreezeAura.TimeActive(sim) > mage.ReactionTime
-	if mage.FingersOfFrostAura.IsActive() {
-		if mage.DeepFreeze != nil && mage.DeepFreeze.IsReady(sim) {
-			return mage.DeepFreeze
-		} else if hasBrainFreeze {
-			return mage.FrostfireBolt
-		} else if mage.Rotation.UseIceLance {
-			return mage.IceLance
-		}
-	}
-
-	return mage.Frostbolt
-}

From ffd4730343dacbeeddf377cdb39676393a6dbb4b Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Thu, 4 Jan 2024 20:12:13 -0800
Subject: [PATCH 08/28] Update priest

---
 sim/common/custom_rotation.go                 |  144 --
 sim/paladin/protection/protection.go          |   44 +-
 sim/paladin/protection/rotation.go            |  209 ---
 .../retribution/TestRetribution.results       |  664 +++++-----
 sim/paladin/retribution/retribution.go        |  105 +-
 sim/paladin/retribution/rotation.go           |  355 -----
 sim/priest/healing/TestDisc.results           |    2 +-
 sim/priest/healing/TestHoly.results           |    4 +-
 sim/priest/healing/healing_priest.go          |   34 +-
 sim/priest/healing/rotation.go                |  115 --
 sim/priest/holy_fire.go                       |    4 +-
 sim/priest/hymn_of_hope.go                    |    7 +-
 sim/priest/mind_flay.go                       |   32 +-
 sim/priest/mind_sear.go                       |    4 +-
 sim/priest/penance.go                         |    3 +-
 sim/priest/priest.go                          |    6 +-
 sim/priest/shadow/TestShadow.results          |    4 +-
 sim/priest/shadow/rotation.go                 | 1173 -----------------
 sim/priest/shadow/shadow_priest.go            |   45 +-
 sim/priest/shadowfiend.go                     |    8 -
 sim/priest/smite.go                           |    8 +-
 sim/priest/smite/TestSmite.results            |  454 +++----
 sim/priest/smite/rotation.go                  |   91 --
 sim/priest/smite/smite_priest.go              |   17 +-
 sim/priest/talents.go                         |    4 +-
 25 files changed, 593 insertions(+), 2943 deletions(-)
 delete mode 100644 sim/common/custom_rotation.go
 delete mode 100644 sim/paladin/protection/rotation.go
 delete mode 100644 sim/paladin/retribution/rotation.go
 delete mode 100644 sim/priest/healing/rotation.go
 delete mode 100644 sim/priest/shadow/rotation.go
 delete mode 100644 sim/priest/smite/rotation.go

diff --git a/sim/common/custom_rotation.go b/sim/common/custom_rotation.go
deleted file mode 100644
index 89ce3b4157..0000000000
--- a/sim/common/custom_rotation.go
+++ /dev/null
@@ -1,144 +0,0 @@
-package common
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-)
-
-type CustomRotationType byte
-
-const (
-	Basic CustomRotationType = iota
-	CPM
-)
-
-// Custom condition for an action.
-type CustomCondition func(*core.Simulation) bool
-
-// Custom action based on a condition. Returns a bool and the CurCast cost.
-type CustomAction func(*core.Simulation, *core.Unit) (bool, float64)
-
-type CustomSpell struct {
-	Spell      *core.Spell // Might be nil if this is not a spell action.
-	Action     CustomAction
-	Condition  CustomCondition
-	DesiredCPM float64
-
-	casts int // Number of casts thus far in the current iteration.
-}
-
-type CustomRotation struct {
-	rotationType CustomRotationType
-	character    *core.Character
-	spells       []CustomSpell
-}
-
-func NewCustomRotation(crProto *proto.CustomRotation, character *core.Character, spellsMap map[int32]CustomSpell) *CustomRotation {
-	if crProto == nil || len(crProto.Spells) == 0 {
-		return nil
-	}
-
-	cr := &CustomRotation{
-		rotationType: Basic,
-		character:    character,
-	}
-
-	for _, customSpellProto := range crProto.Spells {
-		customSpell := spellsMap[customSpellProto.Spell]
-		customSpell.DesiredCPM = customSpellProto.CastsPerMinute
-		if customSpell.DesiredCPM > 0 {
-			cr.rotationType = CPM
-		}
-		if customSpell.Action == nil && customSpell.Spell != nil {
-			spell := customSpell.Spell
-			customSpell.Action = func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				success := spell.Cast(sim, target)
-				return success, spell.CurCast.Cost
-			}
-		}
-		if customSpell.Condition == nil {
-			spell := customSpell.Spell
-			customSpell.Condition = func(sim *core.Simulation) bool {
-				return spell.CanCast(sim, character.CurrentTarget)
-			}
-		}
-		if customSpell.Action != nil {
-			cr.spells = append(cr.spells, customSpell)
-		}
-	}
-
-	if len(cr.spells) == 0 {
-		return nil
-	} else {
-		cr.character.RegisterResetEffect(func(sim *core.Simulation) {
-			cr.reset(sim)
-		})
-		return cr
-	}
-}
-
-func (cr *CustomRotation) reset(_ *core.Simulation) {
-	for i := range cr.spells {
-		cr.spells[i].casts = 0
-	}
-}
-
-func (cr *CustomRotation) ChooseSpell(sim *core.Simulation) *CustomSpell {
-	if cr.rotationType == Basic {
-		return cr.chooseSpellBasic(sim)
-	} else {
-		return cr.chooseSpellCPM(sim)
-	}
-}
-
-func (cr *CustomRotation) chooseSpellBasic(sim *core.Simulation) *CustomSpell {
-	for _, customSpell := range cr.spells {
-		if customSpell.Condition(sim) {
-			return &customSpell
-		}
-	}
-	return nil
-}
-
-func (cr *CustomRotation) chooseSpellCPM(sim *core.Simulation) *CustomSpell {
-	for i := range cr.spells {
-		customSpell := &cr.spells[i]
-		if customSpell.CPM(sim) <= customSpell.DesiredCPM && customSpell.Condition(sim) {
-			return customSpell
-		}
-	}
-	return nil
-}
-
-func (cs *CustomSpell) CPM(sim *core.Simulation) float64 {
-	if sim.CurrentTime == 0 {
-		return 0
-	}
-	return float64(cs.casts) / (float64(sim.CurrentTime) / float64(time.Minute))
-}
-
-func (cr *CustomRotation) Cast(sim *core.Simulation) bool {
-	if cr == nil {
-		panic("Custom Rotation is empty")
-	}
-
-	spell := cr.ChooseSpell(sim)
-
-	if spell == nil {
-		cr.character.WaitUntil(sim, sim.CurrentTime+time.Millisecond*100)
-		return false
-	}
-
-	success, cost := spell.Action(sim, cr.character.CurrentTarget)
-	if success {
-		spell.casts++
-	} else {
-		if cr.character.HasManaBar() {
-			cr.character.WaitForMana(sim, cost)
-		}
-	}
-
-	return true
-}
diff --git a/sim/paladin/protection/protection.go b/sim/paladin/protection/protection.go
index ddb9f64720..befda73549 100644
--- a/sim/paladin/protection/protection.go
+++ b/sim/paladin/protection/protection.go
@@ -1,8 +1,6 @@
 package protection
 
 import (
-	"time"
-
 	"github.com/wowsims/wotlk/sim/core"
 	"github.com/wowsims/wotlk/sim/core/proto"
 	"github.com/wowsims/wotlk/sim/paladin"
@@ -29,23 +27,11 @@ func NewProtectionPaladin(character *core.Character, options *proto.Player) *Pro
 	protOptions := options.GetProtectionPaladin()
 
 	prot := &ProtectionPaladin{
-		Paladin:  paladin.NewPaladin(character, options.TalentsString),
-		Rotation: protOptions.Rotation,
-		Options:  protOptions.Options,
-		Seal:     protOptions.Options.Seal,
+		Paladin: paladin.NewPaladin(character, options.TalentsString),
+		Options: protOptions.Options,
+		Seal:    protOptions.Options.Seal,
 	}
 
-	var rotationInput = protOptions.Rotation.CustomRotation
-
-	if rotationInput != nil {
-		prot.RotationInput = make([]int32, len(rotationInput.Spells))
-		for i, customSpellProto := range rotationInput.Spells {
-			prot.RotationInput[i] = customSpellProto.Spell
-		}
-	}
-
-	prot.SelectedRotation = prot.customRotation
-
 	prot.PaladinAura = protOptions.Options.Aura
 
 	prot.HasGlyphAS = prot.HasMajorGlyph(proto.PaladinMajorGlyph_GlyphOfAvengerSShield)
@@ -68,17 +54,13 @@ func NewProtectionPaladin(character *core.Character, options *proto.Player) *Pro
 type ProtectionPaladin struct {
 	*paladin.Paladin
 
-	Rotation *proto.ProtectionPaladin_Rotation
-	Options  *proto.ProtectionPaladin_Options
+	Options *proto.ProtectionPaladin_Options
 
 	Judgement proto.PaladinJudgement
 
 	Seal proto.PaladinSeal
 
 	HasGlyphAS bool
-
-	SelectedRotation func(*core.Simulation)
-	RotationInput    []int32
 }
 
 func (prot *ProtectionPaladin) GetPaladin() *paladin.Paladin {
@@ -92,29 +74,11 @@ func (prot *ProtectionPaladin) Initialize() {
 	if prot.Options.UseAvengingWrath {
 		prot.RegisterAvengingWrathCD()
 	}
-
-	if !prot.IsUsingAPL {
-		prot.RegisterPrepullAction(-3*time.Second, func(sim *core.Simulation) {
-			prot.HolyShield.Cast(sim, nil)
-		})
-	}
-
-	if !prot.IsUsingAPL {
-		prot.RegisterPrepullAction(-1500*time.Millisecond, func(sim *core.Simulation) {
-			prot.DivinePlea.Cast(sim, nil)
-		})
-	}
 }
 
 func (prot *ProtectionPaladin) Reset(sim *core.Simulation) {
 	prot.Paladin.Reset(sim)
 
-	sim.RegisterExecutePhaseCallback(func(sim *core.Simulation, isExecute int32) {
-		if isExecute == 20 {
-			prot.OnGCDReady(sim)
-		}
-	})
-
 	switch prot.Seal {
 	case proto.PaladinSeal_Vengeance:
 		prot.CurrentSeal = prot.SealOfVengeanceAura
diff --git a/sim/paladin/protection/rotation.go b/sim/paladin/protection/rotation.go
deleted file mode 100644
index b129a3eebd..0000000000
--- a/sim/paladin/protection/rotation.go
+++ /dev/null
@@ -1,209 +0,0 @@
-package protection
-
-import (
-	"math"
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-)
-
-func (prot *ProtectionPaladin) OnGCDReady(sim *core.Simulation) {
-	if prot.IsUsingAPL {
-		return
-	}
-
-	prot.SelectedRotation(sim)
-
-	if prot.GCD.IsReady(sim) {
-		prot.DoNothing()
-	}
-}
-
-func (prot *ProtectionPaladin) customRotation(sim *core.Simulation) {
-	// Setup
-	target := prot.CurrentTarget
-
-	isExecutePhase := sim.IsExecutePhase20()
-
-	// Forced CD remaining on HotR/ShoR to cast the other. Can't be exactly 3sec or lusted consecration GCDs will desync us.
-	gapSlack := time.Millisecond * 4000
-
-	// Allowed time to wait for HotR/ShoR to come off cooldown so we can cast them on cooldown and maintain 969.
-	maxWait := time.Duration(prot.Rotation.WaitSlack) * time.Millisecond
-
-	// Helper vars since we call these repeatedly in many cases
-	nextHammer := prot.HammerOfTheRighteous.TimeToReady(sim)
-	nextShield := prot.ShieldOfRighteousness.TimeToReady(sim)
-
-	if prot.GCD.IsReady(sim) {
-
-		if !prot.Rotation.UseCustomPrio {
-
-			// Standard rotation. Enforce 6sec CDs to have 1 GCD between, filling with 9sec abilities.
-			// HammerFirst flag flips ShoR and HotR in the rotation prio order
-			if prot.Rotation.HammerFirst && prot.HammerOfTheRighteous.IsReady(sim) {
-				// Always cast HotR if ready
-				prot.HammerOfTheRighteous.Cast(sim, target)
-			} else if prot.Rotation.HammerFirst &&
-				prot.ShieldOfRighteousness.IsReady(sim) &&
-				(nextHammer < gapSlack) {
-				// Cast ShoR if ready but only if you've spent a global since HotR
-				prot.ShieldOfRighteousness.Cast(sim, target)
-			} else if !prot.Rotation.HammerFirst && prot.ShieldOfRighteousness.IsReady(sim) {
-				// Always cast ShoR if ready
-				prot.ShieldOfRighteousness.Cast(sim, target)
-			} else if !prot.Rotation.HammerFirst &&
-				prot.HammerOfTheRighteous.IsReady(sim) &&
-				(nextShield < gapSlack) {
-				// Cast HotR if ready but only if you've spent a global since ShoR
-				prot.HammerOfTheRighteous.Cast(sim, target)
-
-				// Maximum WaitSlack checking here to see if we should delay casting anything else because it will clip our 6
-				// This callback method is probably inefficient, TODO perf improvement
-			} else if (nextHammer < maxWait) && (nextShield < gapSlack-maxWait) {
-				if sim.Log != nil {
-					prot.Log(sim, "Waiting %d ms to cast HotR...", int32(nextHammer.Milliseconds()))
-				}
-				prot.waitUntilNextEvent(sim, prot.customRotation)
-			} else if (nextShield < maxWait) && (nextHammer < gapSlack-maxWait) {
-				if sim.Log != nil {
-					prot.Log(sim, "Waiting %d ms to cast ShoR...", int32(nextShield.Milliseconds()))
-				}
-				prot.waitUntilNextEvent(sim, prot.customRotation)
-
-			} else if isExecutePhase && prot.HammerOfWrath.IsReady(sim) {
-				// TODO: Prio may depend on gear; consider Glyph behavior
-				prot.HammerOfWrath.Cast(sim, target)
-			} else if prot.HolyShield.IsReady(sim) {
-				// Top priority 9 is Holy Shield
-				prot.HolyShield.Cast(sim, target)
-			} else if prot.HasGlyphAS && prot.AvengersShield.IsReady(sim) {
-				// AS prio if glyphed. This will push out Cons/Judge which may not be ideal, but assumed desired based on the glyph choice
-				prot.AvengersShield.Cast(sim, target)
-			} else if prot.Consecration.IsReady(sim) {
-				prot.Consecration.Cast(sim, target)
-			} else if prot.Rotation.SqueezeHolyWrath && prot.HolyWrath.IsReady(sim) && (prot.Consecration.TimeToReady(sim) > time.Millisecond*6850) && (target.MobType == proto.MobType_MobTypeDemon || target.MobType == proto.MobType_MobTypeUndead) {
-				// Squeeze HW in open partial global after Consecration during bloodlust against Undead/Demon
-				prot.HolyWrath.Cast(sim, target)
-			} else if prot.JudgementOfWisdom.IsReady(sim) {
-				prot.JudgementOfWisdom.Cast(sim, target)
-			}
-			// Do not ever cast Exorcism or unglyphed AS
-			// TODO: Possible to dynamically affect Judgement<>AS priority based on Libram bonus at SBV softcap?
-
-		} else {
-
-			// Custom rotation
-		rotationLoop:
-			for _, spellNumber := range prot.RotationInput {
-				// In priority order, fire the first spell which is Ready
-				// Still enforce Hammer/Shield being separated by a GCD
-				switch spellNumber {
-				case int32(proto.ProtectionPaladin_Rotation_JudgementOfWisdom):
-					if prot.JudgementOfWisdom.IsReady(sim) {
-						prot.JudgementOfWisdom.Cast(sim, target)
-						break rotationLoop
-					}
-				case int32(proto.ProtectionPaladin_Rotation_HammerOfWrath):
-					if isExecutePhase && prot.HammerOfWrath.IsReady(sim) {
-						prot.HammerOfWrath.Cast(sim, target)
-						break rotationLoop
-					}
-				case int32(proto.ProtectionPaladin_Rotation_Consecration):
-					if prot.Consecration.IsReady(sim) {
-						prot.Consecration.Cast(sim, target)
-						break rotationLoop
-					}
-				case int32(proto.ProtectionPaladin_Rotation_HolyWrath):
-					if prot.HolyWrath.IsReady(sim) {
-						prot.HolyWrath.Cast(sim, target)
-						break rotationLoop
-					}
-				case int32(proto.ProtectionPaladin_Rotation_Exorcism):
-					if prot.Exorcism.IsReady(sim) {
-						prot.Exorcism.Cast(sim, target)
-						break rotationLoop
-					}
-				case int32(proto.ProtectionPaladin_Rotation_ShieldOfRighteousness):
-					if prot.ShieldOfRighteousness.IsReady(sim) && (nextHammer < gapSlack) {
-						prot.ShieldOfRighteousness.Cast(sim, target)
-						break rotationLoop
-					} else if (nextShield < maxWait) && (nextHammer < gapSlack-maxWait) {
-						if sim.Log != nil {
-							prot.Log(sim, "Waiting %d ms to cast ShoR...", int32(nextShield.Milliseconds()))
-						}
-						prot.waitUntilNextEvent(sim, prot.customRotation)
-						break rotationLoop
-					}
-				case int32(proto.ProtectionPaladin_Rotation_AvengersShield):
-					if prot.AvengersShield.IsReady(sim) {
-						prot.AvengersShield.Cast(sim, target)
-						break rotationLoop
-					}
-				case int32(proto.ProtectionPaladin_Rotation_HammerOfTheRighteous):
-					if prot.HammerOfTheRighteous.IsReady(sim) && (nextShield < gapSlack) {
-						prot.HammerOfTheRighteous.Cast(sim, target)
-						break rotationLoop
-					} else if nextHammer < maxWait && (nextShield < gapSlack-maxWait) {
-						if sim.Log != nil {
-							prot.Log(sim, "Waiting %d ms to cast HotR...", int32(nextHammer.Milliseconds()))
-						}
-						prot.waitUntilNextEvent(sim, prot.customRotation)
-						break rotationLoop
-					}
-				case int32(proto.ProtectionPaladin_Rotation_HolyShield):
-					if prot.HolyShield.IsReady(sim) {
-						prot.HolyShield.Cast(sim, target)
-						break rotationLoop
-					}
-				}
-			}
-
-		}
-
-	}
-
-	prot.waitUntilNextEvent(sim, prot.customRotation)
-
-}
-
-// Helper function for finding the next event
-func (prot *ProtectionPaladin) waitUntilNextEvent(sim *core.Simulation, rotationCallback func(*core.Simulation)) {
-	// Find the minimum possible next event that is greater than the current time
-	nextEventAt := time.Duration(math.MaxInt64) // any event will happen before forever.
-
-	// All possible next events
-	events := []time.Duration{
-		prot.AutoAttacks.NextAttackAt(),
-		prot.GCD.ReadyAt(),
-		prot.JudgementOfWisdom.ReadyAt(),
-		prot.HammerOfWrath.ReadyAt(),
-		prot.Consecration.ReadyAt(),
-		prot.HolyWrath.ReadyAt(),
-		prot.Exorcism.ReadyAt(),
-		prot.ShieldOfRighteousness.ReadyAt(),
-		prot.AvengersShield.ReadyAt(),
-		prot.HammerOfTheRighteous.ReadyAt(),
-		prot.HolyShield.ReadyAt(),
-	}
-
-	for _, elem := range events {
-		if elem > sim.CurrentTime && elem < nextEventAt {
-			nextEventAt = elem
-		}
-	}
-	// If the next action is  the GCD, just return
-	if nextEventAt == prot.GCD.ReadyAt() {
-		return
-	}
-
-	// Otherwise add a pending action for the next time
-	pa := &core.PendingAction{
-		Priority:     core.ActionPriorityLow,
-		OnAction:     rotationCallback,
-		NextActionAt: nextEventAt,
-	}
-
-	sim.AddPendingAction(pa)
-}
diff --git a/sim/paladin/retribution/TestRetribution.results b/sim/paladin/retribution/TestRetribution.results
index f2a5eba241..80ffc5e518 100644
--- a/sim/paladin/retribution/TestRetribution.results
+++ b/sim/paladin/retribution/TestRetribution.results
@@ -46,8 +46,8 @@ character_stats_results: {
 dps_results: {
  key: "TestRetribution-AllItems-AegisBattlegear"
  value: {
-  dps: 6267.52464
-  tps: 6352.98416
+  dps: 6268.37146
+  tps: 6353.83098
   dtps: 9.92959
  }
 }
@@ -62,48 +62,48 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 6465.71452
-  tps: 6551.19894
+  dps: 6465.57257
+  tps: 6551.05699
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 6471.26908
-  tps: 6556.75349
+  dps: 6471.12751
+  tps: 6556.61193
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-AshtongueTalismanofZeal-32489"
  value: {
-  dps: 6469.44227
-  tps: 6554.92317
+  dps: 6468.86887
+  tps: 6554.34976
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 6530.10682
-  tps: 6615.59123
+  dps: 6530.15775
+  tps: 6615.64217
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 6628.55432
-  tps: 6713.89603
+  dps: 6628.55546
+  tps: 6713.89718
   dtps: 10.03858
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 6420.2583
-  tps: 6505.74271
+  dps: 6420.11322
+  tps: 6505.59764
   dtps: 9.48456
   hps: 94.7032
  }
@@ -111,8 +111,8 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 6420.2583
-  tps: 6505.74271
+  dps: 6420.11322
+  tps: 6505.59764
   dtps: 9.48456
   hps: 94.7032
  }
@@ -120,96 +120,96 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 6545.05403
-  tps: 6630.47856
+  dps: 6544.91075
+  tps: 6630.33528
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Beast-tamer'sShoulders-30892"
  value: {
-  dps: 6399.15708
-  tps: 6484.35433
+  dps: 6398.97549
+  tps: 6484.17274
   dtps: 9.23882
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-BlessedBattlegearofUndeadSlaying"
  value: {
-  dps: 5408.57284
-  tps: 5491.81912
+  dps: 5408.7184
+  tps: 5491.96468
   dtps: 9.23882
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-BlessedGarboftheUndeadSlayer"
  value: {
-  dps: 5352.64254
-  tps: 5435.94508
+  dps: 5352.69147
+  tps: 5435.99401
   dtps: 9.23882
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 5184.81786
-  tps: 5268.04033
+  dps: 5184.95794
+  tps: 5268.18041
   dtps: 9.23882
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 6536.30717
-  tps: 6491.06544
+  dps: 6536.35928
+  tps: 6491.11652
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Bryntroll,theBoneArbiter-50415"
  value: {
-  dps: 6999.6297
-  tps: 7085.11412
+  dps: 6999.49261
+  tps: 7084.97703
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Bryntroll,theBoneArbiter-50709"
  value: {
-  dps: 7031.44182
-  tps: 7116.92623
+  dps: 7031.30525
+  tps: 7116.78967
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 6663.61419
-  tps: 6749.09861
+  dps: 6663.47166
+  tps: 6748.95608
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 9.92959
   hps: 64
  }
@@ -217,216 +217,216 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 6518.42139
-  tps: 6603.90581
+  dps: 6518.45383
+  tps: 6603.93825
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 6568.56742
-  tps: 6653.91921
+  dps: 6568.11706
+  tps: 6653.46885
   dtps: 9.77043
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 6420.52064
-  tps: 6505.48174
+  dps: 6420.37555
+  tps: 6505.33665
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Death'sChoice-47464"
  value: {
-  dps: 7001.57473
-  tps: 7087.05915
+  dps: 7001.45475
+  tps: 7086.93917
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 6484.24836
-  tps: 6569.73278
+  dps: 6484.37787
+  tps: 6569.86229
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 6755.26996
-  tps: 6840.92204
+  dps: 6754.79378
+  tps: 6840.44585
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 6800.03362
-  tps: 6885.65813
+  dps: 6801.15779
+  tps: 6886.7823
   dtps: 10.29273
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Defender'sCode-40257"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 6547.98298
-  tps: 6633.4674
+  dps: 6547.8397
+  tps: 6633.32412
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-DislodgedForeignObject-50348"
  value: {
-  dps: 6641.1179
-  tps: 6727.31352
+  dps: 6640.22126
+  tps: 6726.41689
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 6609.03015
-  tps: 6694.84443
+  dps: 6607.8895
+  tps: 6693.70378
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 6530.10682
-  tps: 6615.59123
+  dps: 6530.15775
+  tps: 6615.64217
   dtps: 9.731
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 6536.30717
-  tps: 6621.76208
+  dps: 6536.35928
+  tps: 6621.8142
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 6545.05403
-  tps: 6630.53844
+  dps: 6544.91075
+  tps: 6630.39516
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 6541.64166
-  tps: 6627.12607
+  dps: 6541.49838
+  tps: 6626.98279
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 6540.15806
-  tps: 6626.17348
+  dps: 6540.37893
+  tps: 6626.39435
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 6530.10682
-  tps: 6615.59123
+  dps: 6530.15775
+  tps: 6615.64217
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 6577.62612
-  tps: 6663.13023
+  dps: 6576.99883
+  tps: 6662.50294
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 6541.07166
-  tps: 6626.55608
+  dps: 6541.10959
+  tps: 6626.59401
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 6420.52064
-  tps: 6505.86929
+  dps: 6420.37555
+  tps: 6505.7242
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 6448.5459
-  tps: 6534.03031
+  dps: 6448.40275
+  tps: 6533.88717
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ForgeEmber-37660"
  value: {
-  dps: 6500.35794
-  tps: 6585.84236
+  dps: 6500.39038
+  tps: 6585.8748
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 6536.30717
-  tps: 6621.79159
+  dps: 6536.35928
+  tps: 6621.8437
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 6535.0671
-  tps: 6620.55151
+  dps: 6535.11898
+  tps: 6620.6034
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-FuriousGladiator'sLibramofFortitude-42853"
  value: {
-  dps: 6682.35485
-  tps: 6767.83927
+  dps: 6682.2126
+  tps: 6767.69702
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-FuturesightRune-38763"
  value: {
-  dps: 6436.17439
-  tps: 6521.65881
+  dps: 6436.03038
+  tps: 6521.5148
   dtps: 9.92959
  }
 }
@@ -441,80 +441,80 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 6468.4918
-  tps: 6553.97622
+  dps: 6468.35004
+  tps: 6553.83446
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 6474.80379
-  tps: 6560.28821
+  dps: 6474.66247
+  tps: 6560.14689
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 6541.36776
-  tps: 6626.68068
+  dps: 6540.91693
+  tps: 6626.22985
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-HatefulGladiator'sLibramofFortitude-42851"
  value: {
-  dps: 6651.85055
-  tps: 6737.33497
+  dps: 6651.70785
+  tps: 6737.19227
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
-  dps: 6431.41229
-  tps: 6516.89671
+  dps: 6431.2686
+  tps: 6516.75301
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 6545.05403
-  tps: 6630.53844
+  dps: 6544.91075
+  tps: 6630.39516
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 6541.64166
-  tps: 6627.12607
+  dps: 6541.49838
+  tps: 6626.98279
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-IncisorFragment-37723"
  value: {
-  dps: 6584.77153
-  tps: 6670.25595
+  dps: 6584.63506
+  tps: 6670.11948
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 6530.10682
-  tps: 6617.19522
+  dps: 6530.15775
+  tps: 6617.24615
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 6562.40098
-  tps: 6647.8854
+  dps: 6562.45389
+  tps: 6647.93831
   dtps: 9.92959
   hps: 12.10749
  }
@@ -522,72 +522,72 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-LastWord-50179"
  value: {
-  dps: 6862.09336
-  tps: 6947.57778
+  dps: 6861.95405
+  tps: 6947.43847
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-LastWord-50708"
  value: {
-  dps: 6891.70956
-  tps: 6977.19398
+  dps: 6891.5707
+  tps: 6977.05512
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-LibramofFuriousBlows-37574"
  value: {
-  dps: 6594.26071
-  tps: 6679.74513
+  dps: 6594.19122
+  tps: 6679.67564
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-LibramofObstruction-40707"
  value: {
-  dps: 6569.9109
-  tps: 6655.39532
+  dps: 6569.76696
+  tps: 6655.25138
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-LibramofReciprocation-40706"
  value: {
-  dps: 6569.9109
-  tps: 6655.39532
+  dps: 6569.76696
+  tps: 6655.25138
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-LibramofThreeTruths-50455"
  value: {
-  dps: 6989.10988
-  tps: 7074.5943
+  dps: 6988.96862
+  tps: 7074.45304
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-LibramofValiance-47661"
  value: {
-  dps: 6956.32355
-  tps: 7041.80797
+  dps: 6956.365
+  tps: 7041.84942
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-LibramoftheSacredShield-45145"
  value: {
-  dps: 6569.9109
-  tps: 6655.39532
+  dps: 6569.76696
+  tps: 6655.25138
   dtps: 9.92959
  }
 }
@@ -602,8 +602,8 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-LightswornBattlegear"
  value: {
-  dps: 7652.37702
-  tps: 7739.88693
+  dps: 7650.81686
+  tps: 7738.32677
   dtps: 9.92959
  }
 }
@@ -618,104 +618,104 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 6554.05506
-  tps: 6639.92597
+  dps: 6553.90912
+  tps: 6639.78002
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 6479.3976
-  tps: 6564.88202
+  dps: 6479.55694
+  tps: 6565.04136
   dtps: 10.29273
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 6556.3074
-  tps: 6641.79181
+  dps: 6556.35971
+  tps: 6641.84413
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 6562.47224
-  tps: 6647.95666
+  dps: 6562.52488
+  tps: 6648.0093
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 8.19524
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 6530.10682
-  tps: 6615.59123
+  dps: 6530.15775
+  tps: 6615.64217
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 6530.10682
-  tps: 6615.59123
+  dps: 6530.15775
+  tps: 6615.64217
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-RedemptionBattlegear"
  value: {
-  dps: 5847.91647
-  tps: 5937.23562
+  dps: 5847.28087
+  tps: 5936.60002
   dtps: 9.23882
  }
 }
@@ -730,136 +730,136 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 6682.32469
-  tps: 6767.84107
+  dps: 6683.04766
+  tps: 6768.56404
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 6715.46847
-  tps: 6800.98485
+  dps: 6716.19926
+  tps: 6801.71564
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 6663.61419
-  tps: 6749.09861
+  dps: 6663.47166
+  tps: 6748.95608
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-RelentlessGladiator'sLibramofFortitude-42854"
  value: {
-  dps: 6704.21895
-  tps: 6789.70337
+  dps: 6704.07703
+  tps: 6789.56145
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 6530.10682
-  tps: 6615.03035
+  dps: 6530.15775
+  tps: 6615.08129
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SavageGladiator'sLibramofFortitude-42611"
  value: {
-  dps: 6642.57436
-  tps: 6728.05878
+  dps: 6642.43152
+  tps: 6727.91594
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Shadowmourne-49623"
  value: {
-  dps: 7904.00969
-  tps: 7989.33513
+  dps: 7904.17373
+  tps: 7989.49917
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 6.55074
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 6460.41245
-  tps: 6546.45908
+  dps: 6460.27012
+  tps: 6546.31676
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 6465.46204
-  tps: 6551.50868
+  dps: 6465.32007
+  tps: 6551.36671
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SoulPreserver-37111"
  value: {
-  dps: 6439.45662
-  tps: 6524.94104
+  dps: 6439.31285
+  tps: 6524.79727
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SouloftheDead-40382"
  value: {
-  dps: 6511.46767
-  tps: 6598.66364
+  dps: 6511.50012
+  tps: 6598.69608
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SparkofLife-37657"
  value: {
-  dps: 6505.76336
-  tps: 6587.92798
+  dps: 6506.70356
+  tps: 6588.86818
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 6560.42813
-  tps: 6645.82051
+  dps: 6560.29702
+  tps: 6645.6894
   dtps: 9.92959
  }
 }
@@ -874,48 +874,48 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 6562.47224
-  tps: 6647.95666
+  dps: 6562.52488
+  tps: 6648.0093
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 6556.3074
-  tps: 6641.79181
+  dps: 6556.35971
+  tps: 6641.84413
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 6545.51892
-  tps: 6631.00334
+  dps: 6545.57067
+  tps: 6631.05509
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 6420.52064
-  tps: 6505.53628
+  dps: 6420.37555
+  tps: 6505.39119
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 6420.52064
-  tps: 6506.00506
+  dps: 6420.37555
+  tps: 6505.85997
   dtps: 9.92959
  }
 }
@@ -930,72 +930,72 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 6578.78508
-  tps: 6664.4151
+  dps: 6578.68896
+  tps: 6664.31897
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 7024.62707
-  tps: 7110.86472
+  dps: 7023.73776
+  tps: 7109.97541
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 7123.34647
-  tps: 7209.63541
+  dps: 7123.31213
+  tps: 7209.60107
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 6536.30717
-  tps: 6621.79159
+  dps: 6536.35928
+  tps: 6621.8437
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 6535.0671
-  tps: 6620.55151
+  dps: 6535.11898
+  tps: 6620.6034
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 6536.78528
-  tps: 6622.43424
+  dps: 6535.26623
+  tps: 6620.91519
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TomeoftheLightbringer-32368"
  value: {
-  dps: 6569.9109
-  tps: 6655.39532
+  dps: 6569.76696
+  tps: 6655.25138
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 6535.0671
-  tps: 6620.55151
+  dps: 6535.11898
+  tps: 6620.6034
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 6536.30717
-  tps: 6621.79159
+  dps: 6536.35928
+  tps: 6621.8437
   dtps: 9.92959
  }
 }
@@ -1018,64 +1018,64 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-UndeadSlayer'sBlessedArmor"
  value: {
-  dps: 5397.40235
-  tps: 5480.95432
+  dps: 5397.45216
+  tps: 5481.00412
   dtps: 9.23882
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Val'anyr,HammerofAncientKings-46017"
  value: {
-  dps: 5145.63201
-  tps: 5230.49693
+  dps: 5145.39682
+  tps: 5230.26174
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-WingedTalisman-37844"
  value: {
-  dps: 6440.00643
-  tps: 6525.49085
+  dps: 6439.86531
+  tps: 6525.34973
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-WrathfulGladiator'sLibramofFortitude-51478"
  value: {
-  dps: 6729.2065
-  tps: 6814.69091
+  dps: 6729.06495
+  tps: 6814.54937
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Average-Default"
  value: {
-  dps: 6668.69511
-  tps: 6753.29762
+  dps: 6668.56873
+  tps: 6753.17123
   dtps: 13.59845
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOC-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 18965.75625
-  tps: 20677.85589
+  dps: 18965.91539
+  tps: 20678.01503
   dtps: 10.57482
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOC-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5477.65446
-  tps: 5563.291
+  dps: 5477.99131
+  tps: 5563.62785
   dtps: 11.82069
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOC-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6191.11812
-  tps: 6281.82806
+  dps: 6192.80238
+  tps: 6287.44359
   dtps: 59.10347
  }
 }
@@ -1103,24 +1103,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOR-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 16956.62716
-  tps: 18668.41273
+  dps: 16956.30594
+  tps: 18668.0915
   dtps: 10.33353
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOR-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5849.24722
-  tps: 5934.80017
+  dps: 5849.18097
+  tps: 5934.73392
   dtps: 11.82069
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOR-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6635.13703
-  tps: 6725.70508
+  dps: 6634.80579
+  tps: 6729.31363
   dtps: 59.10347
  }
 }
@@ -1148,24 +1148,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 18992.27977
-  tps: 20700.44093
+  dps: 18993.19487
+  tps: 20701.35603
   dtps: 8.80605
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6663.61419
-  tps: 6749.09861
+  dps: 6663.47166
+  tps: 6748.95608
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7382.00712
-  tps: 7472.77717
+  dps: 7381.2945
+  tps: 7473.81754
   dtps: 49.64794
  }
 }
@@ -1193,24 +1193,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOV-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 18992.27977
-  tps: 20700.44093
+  dps: 18993.19487
+  tps: 20701.35603
   dtps: 8.80605
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOV-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6663.61419
-  tps: 6749.09861
+  dps: 6663.47166
+  tps: 6748.95608
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOV-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7382.00712
-  tps: 7472.77717
+  dps: 7381.2945
+  tps: 7473.81754
   dtps: 49.64794
  }
 }
@@ -1238,24 +1238,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOC-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19088.57134
-  tps: 20780.2746
+  dps: 19088.7306
+  tps: 20780.43386
   dtps: 10.14629
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOC-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5488.35294
-  tps: 5572.98793
+  dps: 5488.69007
+  tps: 5573.32505
   dtps: 11.82069
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOC-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6195.09407
-  tps: 6285.8247
+  dps: 6196.77971
+  tps: 6291.46684
   dtps: 59.10347
  }
 }
@@ -1283,24 +1283,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOR-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 17080.40494
-  tps: 18773.12786
+  dps: 17080.08349
+  tps: 18772.80641
   dtps: 9.95262
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOR-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5863.37349
-  tps: 5947.91779
+  dps: 5863.30716
+  tps: 5947.85146
   dtps: 11.82069
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOR-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6647.69899
-  tps: 6738.28782
+  dps: 6647.36735
+  tps: 6741.92039
   dtps: 59.10347
  }
 }
@@ -1328,24 +1328,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19118.75828
-  tps: 20804.14482
+  dps: 19119.70694
+  tps: 20805.09347
   dtps: 8.48862
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6676.86616
-  tps: 6761.29993
+  dps: 6676.72376
+  tps: 6761.15752
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7391.39147
-  tps: 7482.18276
+  dps: 7390.6795
+  tps: 7483.24301
   dtps: 49.64794
  }
 }
@@ -1373,24 +1373,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOV-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19118.75828
-  tps: 20804.14482
+  dps: 19119.70694
+  tps: 20805.09347
   dtps: 8.48862
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOV-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6676.86616
-  tps: 6761.29993
+  dps: 6676.72376
+  tps: 6761.15752
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOV-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7391.39147
-  tps: 7482.18276
+  dps: 7390.6795
+  tps: 7483.24301
   dtps: 49.64794
  }
 }
@@ -1418,24 +1418,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOC-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19000.91274
-  tps: 20693.50805
+  dps: 19001.07213
+  tps: 20693.66744
   dtps: 10.14629
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOC-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5483.34399
-  tps: 5568.03751
+  dps: 5483.6814
+  tps: 5568.37492
   dtps: 11.82069
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOC-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6191.23305
-  tps: 6281.97057
+  dps: 6192.92007
+  tps: 6287.62251
   dtps: 59.10347
  }
 }
@@ -1463,24 +1463,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOR-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 16989.15945
-  tps: 18681.15785
+  dps: 16988.83779
+  tps: 18680.83618
   dtps: 9.95262
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOR-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5860.25589
-  tps: 5944.83467
+  dps: 5860.18948
+  tps: 5944.76826
   dtps: 11.82069
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOR-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6643.78035
-  tps: 6734.3761
+  dps: 6643.44831
+  tps: 6738.01641
   dtps: 59.10347
  }
 }
@@ -1508,24 +1508,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19034.21526
-  tps: 20719.28799
+  dps: 19035.16533
+  tps: 20720.23806
   dtps: 8.48862
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6675.1274
-  tps: 6759.55479
+  dps: 6674.98513
+  tps: 6759.41251
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7389.89531
-  tps: 7480.69367
+  dps: 7389.18398
+  tps: 7481.76099
   dtps: 49.64794
  }
 }
@@ -1553,24 +1553,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOV-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19034.21526
-  tps: 20719.28799
+  dps: 19035.16533
+  tps: 20720.23806
   dtps: 8.48862
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOV-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6675.1274
-  tps: 6759.55479
+  dps: 6674.98513
+  tps: 6759.41251
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOV-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7389.89531
-  tps: 7480.69367
+  dps: 7389.18398
+  tps: 7481.76099
   dtps: 49.64794
  }
 }
@@ -1598,24 +1598,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOC-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 18979.65129
-  tps: 20672.08688
+  dps: 18979.81052
+  tps: 20672.24611
   dtps: 10.14629
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOC-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5479.89294
-  tps: 5564.57868
+  dps: 5480.23
+  tps: 5564.91574
   dtps: 11.82069
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOC-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6190.09544
-  tps: 6280.82607
+  dps: 6191.78073
+  tps: 6286.46786
   dtps: 59.10347
  }
 }
@@ -1643,24 +1643,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOR-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 16969.93508
-  tps: 18661.7792
+  dps: 16969.61369
+  tps: 18661.45781
   dtps: 9.95262
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOR-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5853.50398
-  tps: 5938.07557
+  dps: 5853.43767
+  tps: 5938.00926
   dtps: 11.82069
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOR-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6637.62044
-  tps: 6728.20926
+  dps: 6637.28889
+  tps: 6731.84193
   dtps: 59.10347
  }
 }
@@ -1688,24 +1688,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19007.73945
-  tps: 20692.68658
+  dps: 19008.65557
+  tps: 20693.6027
   dtps: 8.48862
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6666.86363
-  tps: 6751.28483
+  dps: 6666.72119
+  tps: 6751.1424
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7383.71468
-  tps: 7474.50596
+  dps: 7383.00254
+  tps: 7475.56606
   dtps: 49.64794
  }
 }
@@ -1733,24 +1733,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOV-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19007.73945
-  tps: 20692.68658
+  dps: 19008.65557
+  tps: 20693.6027
   dtps: 8.48862
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOV-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6666.86363
-  tps: 6751.28483
+  dps: 6666.72119
+  tps: 6751.1424
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOV-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7383.71468
-  tps: 7474.50596
+  dps: 7383.00254
+  tps: 7475.56606
   dtps: 49.64794
  }
 }
@@ -1778,8 +1778,8 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 6317.26995
-  tps: 6402.64627
+  dps: 6317.40296
+  tps: 6402.77929
   dtps: 9.92959
  }
 }
diff --git a/sim/paladin/retribution/retribution.go b/sim/paladin/retribution/retribution.go
index c619ce0f6a..a21aca2089 100644
--- a/sim/paladin/retribution/retribution.go
+++ b/sim/paladin/retribution/retribution.go
@@ -1,8 +1,6 @@
 package retribution
 
 import (
-	"time"
-
 	"github.com/wowsims/wotlk/sim/core"
 	"github.com/wowsims/wotlk/sim/core/proto"
 	"github.com/wowsims/wotlk/sim/paladin"
@@ -31,20 +29,8 @@ func NewRetributionPaladin(character *core.Character, options *proto.Player) *Re
 	pal := paladin.NewPaladin(character, options.TalentsString)
 
 	ret := &RetributionPaladin{
-		Paladin:                             pal,
-		Rotation:                            retOptions.Rotation,
-		Judgement:                           retOptions.Options.Judgement,
-		Seal:                                retOptions.Options.Seal,
-		UseDivinePlea:                       retOptions.Rotation.UseDivinePlea,
-		AvoidClippingConsecration:           retOptions.Rotation.AvoidClippingConsecration,
-		HoldLastAvengingWrathUntilExecution: retOptions.Rotation.HoldLastAvengingWrathUntilExecution,
-		DivinePleaPercentage:                retOptions.Rotation.DivinePleaPercentage,
-		CancelChaosBane:                     retOptions.Rotation.CancelChaosBane,
-		ExoSlack:                            retOptions.Rotation.ExoSlack,
-		ConsSlack:                           retOptions.Rotation.ConsSlack,
-		HolyWrathThreshold:                  retOptions.Rotation.HolyWrathThreshold,
-		MaxSoVTargets:                       retOptions.Rotation.SovTargets,
-		HasLightswornBattlegear2Pc:          character.HasSetBonus(paladin.ItemSetLightswornBattlegear, 2),
+		Paladin: pal,
+		Seal:    retOptions.Options.Seal,
 	}
 
 	pal.AvoidClippingConsecration = retOptions.Rotation.AvoidClippingConsecration
@@ -53,55 +39,18 @@ func NewRetributionPaladin(character *core.Character, options *proto.Player) *Re
 
 	ret.PaladinAura = retOptions.Options.Aura
 
-	ret.RotatioOption = retOptions.Rotation.CustomRotation
-	if retOptions.Rotation.Type == proto.RetributionPaladin_Rotation_Standard {
-		ret.SelectedRotation = ret.mainRotation
-	} else if retOptions.Rotation.Type == proto.RetributionPaladin_Rotation_Custom {
-		ret.SelectedRotation = ret.customRotation
-	} else if retOptions.Rotation.Type == proto.RetributionPaladin_Rotation_CastSequence {
-		ret.SelectedRotation = ret.castSequenceRotation
-		ret.CastSequenceIndex = 0
-		ret.RotatioOption = retOptions.Rotation.CustomCastSequence
-	} else {
-		ret.SelectedRotation = ret.mainRotation
-	}
-
 	ret.EnableAutoAttacks(ret, core.AutoAttackOptions{
 		MainHand:       ret.WeaponFromMainHand(0), // Set crit multiplier later when we have targets.
 		AutoSwingMelee: true,
 	})
 
-	ret.EnableResumeAfterManaWait(ret.OnGCDReady)
-
 	return ret
 }
 
 type RetributionPaladin struct {
 	*paladin.Paladin
 
-	Judgement                           proto.PaladinJudgement
-	Seal                                proto.PaladinSeal
-	UseDivinePlea                       bool
-	AvoidClippingConsecration           bool
-	HoldLastAvengingWrathUntilExecution bool
-	CancelChaosBane                     bool
-
-	DivinePleaPercentage float64
-	ExoSlack             int32
-	ConsSlack            int32
-	HolyWrathThreshold   int32
-	MaxSoVTargets        int32
-
-	HasLightswornBattlegear2Pc bool
-
-	SelectedJudgement *core.Spell
-
-	SelectedRotation  func(*core.Simulation)
-	RotatioOption     *proto.CustomRotation
-	RotationInput     []*core.Spell
-	CastSequenceIndex int32
-
-	Rotation *proto.RetributionPaladin_Rotation
+	Seal proto.PaladinSeal
 }
 
 func (ret *RetributionPaladin) GetPaladin() *paladin.Paladin {
@@ -111,54 +60,11 @@ func (ret *RetributionPaladin) GetPaladin() *paladin.Paladin {
 func (ret *RetributionPaladin) Initialize() {
 	ret.Paladin.Initialize()
 	ret.RegisterAvengingWrathCD()
-
-	if ret.Seal != proto.PaladinSeal_Vengeance {
-		ret.DelayDPSCooldownsForArmorDebuffs(time.Second * 10)
-	}
 }
 
 func (ret *RetributionPaladin) Reset(sim *core.Simulation) {
 	ret.Paladin.Reset(sim)
 
-	switch ret.Judgement {
-	case proto.PaladinJudgement_JudgementOfWisdom:
-		ret.SelectedJudgement = ret.JudgementOfWisdom
-	case proto.PaladinJudgement_JudgementOfLight:
-		ret.SelectedJudgement = ret.JudgementOfLight
-	}
-
-	if ret.RotatioOption != nil {
-		ret.RotationInput = make([]*core.Spell, 0, len(ret.RotatioOption.Spells))
-		for _, customSpellProto := range ret.RotatioOption.Spells {
-			switch customSpellProto.Spell {
-			case int32(proto.RetributionPaladin_Rotation_JudgementOfWisdom):
-				ret.RotationInput = append(ret.RotationInput, ret.SelectedJudgement)
-			case int32(proto.RetributionPaladin_Rotation_DivineStorm):
-				ret.RotationInput = append(ret.RotationInput, ret.DivineStorm)
-			case int32(proto.RetributionPaladin_Rotation_HammerOfWrath):
-				ret.RotationInput = append(ret.RotationInput, ret.HammerOfWrath)
-			case int32(proto.RetributionPaladin_Rotation_Consecration):
-				ret.RotationInput = append(ret.RotationInput, ret.Consecration)
-			case int32(proto.RetributionPaladin_Rotation_HolyWrath):
-				ret.RotationInput = append(ret.RotationInput, ret.HolyWrath)
-			case int32(proto.RetributionPaladin_Rotation_CrusaderStrike):
-				ret.RotationInput = append(ret.RotationInput, ret.CrusaderStrike)
-			case int32(proto.RetributionPaladin_Rotation_Exorcism):
-				ret.RotationInput = append(ret.RotationInput, ret.Exorcism)
-			case int32(proto.RetributionPaladin_Rotation_DivinePlea):
-				ret.RotationInput = append(ret.RotationInput, ret.DivinePlea)
-			}
-		}
-	}
-
-	sim.RegisterExecutePhaseCallback(func(sim *core.Simulation, isExecute int32) {
-		if isExecute == 20 {
-			ret.OnGCDReady(sim)
-		}
-	})
-
-	ret.CastSequenceIndex = 0
-
 	switch ret.Seal {
 	case proto.PaladinSeal_Vengeance:
 		ret.CurrentSeal = ret.SealOfVengeanceAura
@@ -170,9 +76,4 @@ func (ret *RetributionPaladin) Reset(sim *core.Simulation) {
 		ret.CurrentSeal = ret.SealOfRighteousnessAura
 		ret.SealOfRighteousnessAura.Activate(sim)
 	}
-
-	if !ret.IsUsingAPL {
-		ret.DivinePleaAura.Activate(sim)
-		ret.DivinePlea.CD.Use(sim)
-	}
 }
diff --git a/sim/paladin/retribution/rotation.go b/sim/paladin/retribution/rotation.go
deleted file mode 100644
index 287010865d..0000000000
--- a/sim/paladin/retribution/rotation.go
+++ /dev/null
@@ -1,355 +0,0 @@
-package retribution
-
-import (
-	"math"
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-)
-
-func (ret *RetributionPaladin) OnAutoAttack(sim *core.Simulation, _ *core.Spell) {
-	if ret.SealOfVengeanceAura.IsActive() && min(ret.MaxSoVTargets, ret.Env.GetNumTargets()) > 1 {
-		minVengeanceDotDuration := time.Second * 15
-		var minVengeanceDotDurationTarget *core.Unit
-		minVengeanceDotStacks := int32(5)
-		var minVengeanceDotStacksTarget *core.Unit
-		for i := int32(0); i < min(ret.MaxSoVTargets, ret.Env.GetNumTargets()); i++ {
-			target := ret.Env.GetTargetUnit(i)
-			dot := ret.SovDotSpell.Dot(target)
-			remainingDuration := dot.RemainingDuration(sim)
-			stackCount := dot.GetStacks()
-
-			if remainingDuration < minVengeanceDotDuration && remainingDuration > 0 {
-				minVengeanceDotDuration = remainingDuration
-				minVengeanceDotDurationTarget = target
-			}
-
-			if stackCount < minVengeanceDotStacks {
-				minVengeanceDotStacks = stackCount
-				minVengeanceDotStacksTarget = target
-			}
-		}
-
-		if minVengeanceDotDuration < core.DurationFromSeconds(ret.AutoAttacks.MH().SwingSpeed*2) {
-			ret.CurrentTarget = minVengeanceDotDurationTarget
-		} else if ret.SovDotSpell.Dot(ret.CurrentTarget).GetStacks() == 5 && minVengeanceDotStacks < 5 {
-			ret.CurrentTarget = minVengeanceDotStacksTarget
-		} else {
-			ret.CurrentTarget = ret.Env.Encounter.TargetUnits[0]
-		}
-	}
-}
-
-func (ret *RetributionPaladin) OnGCDReady(sim *core.Simulation) {
-	if ret.IsUsingAPL {
-		return
-	}
-
-	ret.SelectedRotation(sim)
-	if ret.GCD.IsReady(sim) {
-		ret.DoNothing() // this means we had nothing to do and we are ok
-	}
-}
-
-func (ret *RetributionPaladin) customRotation(sim *core.Simulation) {
-	// Setup
-	target := ret.Env.Encounter.TargetUnits[0]
-
-	nextSwingAt := ret.AutoAttacks.NextAttackAt()
-	isExecutePhase := sim.IsExecutePhase20()
-
-	if ret.HandOfReckoning != nil && ret.HandOfReckoning.IsReady(sim) {
-		ret.HandOfReckoning.Cast(sim, ret.CurrentTarget)
-	}
-
-	if ret.GCD.IsReady(sim) {
-	rotationLoop:
-		for _, spell := range ret.RotationInput {
-			if spell == ret.HammerOfWrath && !isExecutePhase {
-				continue
-			}
-
-			if spell == ret.HammerOfWrath && isExecutePhase && ret.HoldLastAvengingWrathUntilExecution {
-				if ret.AvengingWrath.IsReady(sim) {
-					success := ret.AvengingWrath.Cast(sim, target)
-					if !success {
-						ret.WaitForMana(sim, ret.AvengingWrath.CurCast.Cost)
-					}
-				}
-			}
-
-			if spell == ret.HolyWrath {
-				// Holy Wrath isn't worth casting if it will reduce usages of CS/DS
-				if ret.CrusaderStrike.ReadyAt()-sim.CurrentTime < 500*time.Millisecond {
-					continue
-				}
-				if ret.DivineStorm.ReadyAt()-sim.CurrentTime < 500*time.Millisecond {
-					continue
-				}
-			}
-
-			if spell == ret.Consecration && !ret.checkConsecrationClipping(sim) {
-				// This is a skip, so we take the opposite of the clip check.
-				continue
-			}
-
-			if spell == ret.Exorcism && !ret.ArtOfWarInstantCast.IsActive() {
-				continue
-			}
-
-			if spell == ret.DivinePlea && ret.CurrentMana() > (ret.MaxMana()*ret.DivinePleaPercentage) {
-				continue
-			}
-
-			if spell.IsReady(sim) {
-				success := spell.Cast(sim, target)
-				if !success {
-					ret.WaitForMana(sim, spell.CurCast.Cost)
-				}
-				break rotationLoop
-			}
-		}
-	}
-
-	// All possible next events
-	events := []time.Duration{
-		nextSwingAt,
-		ret.GCD.ReadyAt(),
-		ret.SelectedJudgement.CD.ReadyAt(),
-		ret.DivineStorm.CD.ReadyAt(),
-		ret.HammerOfWrath.CD.ReadyAt(),
-		ret.HolyWrath.CD.ReadyAt(),
-		ret.CrusaderStrike.CD.ReadyAt(),
-		ret.Consecration.CD.ReadyAt(),
-		ret.Exorcism.CD.ReadyAt(),
-		ret.DivinePlea.CD.ReadyAt(),
-	}
-
-	if ret.HandOfReckoning != nil {
-		events = append(events, ret.HandOfReckoning.CD.ReadyAt())
-	}
-
-	CancelChaosBane(ret, sim)
-	ret.waitUntilNextEvent(sim, events, ret.customRotation)
-}
-
-func (ret *RetributionPaladin) castSequenceRotation(sim *core.Simulation) {
-	if len(ret.RotationInput) == 0 {
-		return
-	}
-
-	// Setup
-	target := ret.Env.Encounter.TargetUnits[0]
-	isExecutePhase := sim.IsExecutePhase20()
-
-	nextReadyAt := sim.CurrentTime
-
-	if hc := ret.Hardcast; ret.HandOfReckoning != nil && ret.HandOfReckoning.IsReady(sim) && !(hc.Expires > sim.CurrentTime) {
-		ret.HandOfReckoning.Cast(sim, ret.CurrentTarget)
-	}
-
-	if ret.GCD.IsReady(sim) {
-		if ret.UseDivinePlea && ret.DivinePlea.IsReady(sim) && ret.CurrentMana() < (ret.MaxMana()*ret.DivinePleaPercentage) {
-			ret.DivinePlea.Cast(sim, nil)
-		} else {
-			currentSpell := ret.RotationInput[ret.CastSequenceIndex]
-
-			if currentSpell == ret.HammerOfWrath && !isExecutePhase {
-				return
-			}
-
-			if currentSpell.IsReady(sim) {
-				success := currentSpell.Cast(sim, target)
-				if success {
-					ret.CastSequenceIndex = (ret.CastSequenceIndex + 1) % int32(len(ret.RotationInput))
-				} else {
-					ret.WaitForMana(sim, currentSpell.CurCast.Cost)
-				}
-			} else {
-				nextReadyAt = currentSpell.ReadyAt()
-			}
-		}
-	}
-
-	events := []time.Duration{
-		ret.GCD.ReadyAt(),
-		nextReadyAt,
-	}
-
-	if ret.HandOfReckoning != nil {
-		events = append(events, ret.HandOfReckoning.CD.ReadyAt())
-	}
-
-	CancelChaosBane(ret, sim)
-	ret.waitUntilNextEvent(sim, events, ret.castSequenceRotation)
-}
-
-func (ret *RetributionPaladin) mainRotation(sim *core.Simulation) {
-
-	// Setup
-	target := ret.Env.Encounter.TargetUnits[0]
-
-	nextSwingAt := ret.AutoAttacks.NextAttackAt()
-	isExecutePhase := sim.IsExecutePhase20()
-
-	nextPrimaryAbility := min(ret.CrusaderStrike.CD.ReadyAt(), ret.DivineStorm.CD.ReadyAt(), ret.SelectedJudgement.CD.ReadyAt())
-	nextPrimaryAbilityDelta := nextPrimaryAbility - sim.CurrentTime
-
-	if ret.HandOfReckoning != nil && ret.HandOfReckoning.IsReady(sim) {
-		ret.HandOfReckoning.Cast(sim, ret.CurrentTarget)
-	}
-
-	if ret.GCD.IsReady(sim) {
-		switch {
-		case isExecutePhase && ret.HammerOfWrath.IsReady(sim) && ret.HoldLastAvengingWrathUntilExecution:
-			if ret.AvengingWrath.IsReady(sim) {
-				success := ret.AvengingWrath.Cast(sim, target)
-				if !success {
-					ret.WaitForMana(sim, ret.AvengingWrath.CurCast.Cost)
-				}
-			}
-
-			success := ret.HammerOfWrath.Cast(sim, target)
-			if !success {
-				ret.WaitForMana(sim, ret.HammerOfWrath.CurCast.Cost)
-			}
-		case ret.SelectedJudgement.IsReady(sim):
-			success := ret.SelectedJudgement.Cast(sim, target)
-			if !success {
-				ret.WaitForMana(sim, ret.SelectedJudgement.CurCast.Cost)
-			}
-		case ret.HasLightswornBattlegear2Pc && ret.DivineStorm.IsReady(sim):
-			success := ret.DivineStorm.Cast(sim, target)
-			if !success {
-				ret.WaitForMana(sim, ret.DivineStorm.CurCast.Cost)
-			}
-		case ret.Env.GetNumTargets() > 1 && ret.Consecration.IsReady(sim):
-			success := ret.Consecration.Cast(sim, target)
-			if !success {
-				ret.WaitForMana(sim, ret.Consecration.CurCast.Cost)
-			}
-		case isExecutePhase && ret.HammerOfWrath.IsReady(sim):
-			success := ret.HammerOfWrath.Cast(sim, target)
-			if !success {
-				ret.WaitForMana(sim, ret.HammerOfWrath.CurCast.Cost)
-			}
-		case ret.DemonAndUndeadTargetCount >= ret.HolyWrathThreshold && ret.HolyWrath.IsReady(sim):
-			success := ret.HolyWrath.Cast(sim, target)
-			if !success {
-				ret.WaitForMana(sim, ret.HolyWrath.CurCast.Cost)
-			}
-		case ret.UseDivinePlea && ret.CurrentMana() < (ret.MaxMana()*ret.DivinePleaPercentage) && ret.DivinePlea.IsReady(sim):
-			ret.DivinePlea.Cast(sim, nil)
-		case ret.CrusaderStrike.IsReady(sim):
-			success := ret.CrusaderStrike.Cast(sim, target)
-			if !success {
-				ret.WaitForMana(sim, ret.CrusaderStrike.CurCast.Cost)
-			}
-		case ret.DivineStorm.IsReady(sim):
-			success := ret.DivineStorm.Cast(sim, target)
-			if !success {
-				ret.WaitForMana(sim, ret.DivineStorm.CurCast.Cost)
-			}
-		case (target.MobType == proto.MobType_MobTypeDemon || target.MobType == proto.MobType_MobTypeUndead) &&
-			nextPrimaryAbilityDelta.Milliseconds() > int64(ret.ExoSlack) && ret.Exorcism.IsReady(sim) && ret.ArtOfWarInstantCast.IsActive():
-			success := ret.Exorcism.Cast(sim, target)
-			if !success {
-				ret.WaitForMana(sim, ret.Exorcism.CurCast.Cost)
-			}
-		case nextPrimaryAbilityDelta.Milliseconds() > int64(ret.ConsSlack) && ret.Consecration.IsReady(sim) && ret.checkConsecrationClipping(sim):
-			success := ret.Consecration.Cast(sim, target)
-			if !success {
-				ret.WaitForMana(sim, ret.Consecration.CurCast.Cost)
-			}
-		case nextPrimaryAbilityDelta.Milliseconds() > int64(ret.ExoSlack) && ret.Exorcism.IsReady(sim) && ret.ArtOfWarInstantCast.IsActive():
-			success := ret.Exorcism.Cast(sim, target)
-			if !success {
-				ret.WaitForMana(sim, ret.Exorcism.CurCast.Cost)
-			}
-		case ret.DemonAndUndeadTargetCount >= 1 && ret.HolyWrath.IsReady(sim):
-			// Holy Wrath isn't worth casting if it will reduce usages of CS/DS
-			if ret.CrusaderStrike.ReadyAt()-sim.CurrentTime < 500*time.Millisecond {
-				break
-			}
-			if ret.DivineStorm.ReadyAt()-sim.CurrentTime < 500*time.Millisecond {
-				break
-			}
-			success := ret.HolyWrath.Cast(sim, target)
-			if !success {
-				ret.WaitForMana(sim, ret.HolyWrath.CurCast.Cost)
-			}
-		}
-	}
-
-	// All possible next events
-	events := []time.Duration{
-		nextSwingAt,
-		ret.GCD.ReadyAt(),
-		ret.SelectedJudgement.CD.ReadyAt(),
-		ret.DivineStorm.CD.ReadyAt(),
-		ret.HammerOfWrath.CD.ReadyAt(),
-		ret.HolyWrath.CD.ReadyAt(),
-		ret.CrusaderStrike.CD.ReadyAt(),
-		ret.Consecration.CD.ReadyAt(),
-		ret.Exorcism.CD.ReadyAt(),
-		ret.DivinePlea.CD.ReadyAt(),
-	}
-
-	if ret.HandOfReckoning != nil {
-		events = append(events, ret.HandOfReckoning.CD.ReadyAt())
-	}
-
-	CancelChaosBane(ret, sim)
-	ret.waitUntilNextEvent(sim, events, ret.mainRotation)
-}
-
-func (ret *RetributionPaladin) checkConsecrationClipping(sim *core.Simulation) bool {
-	if ret.AvoidClippingConsecration {
-		return ret.Consecration.AOEDot().TickLength*4 <= sim.GetRemainingDuration()
-	} else {
-		// If we're not configured to check, always return success.
-		return true
-	}
-}
-
-// Helper function for finding the next event
-func (ret *RetributionPaladin) waitUntilNextEvent(sim *core.Simulation, events []time.Duration, rotationCallback func(*core.Simulation)) {
-
-	// Find the minimum possible next event that is greater than the current time
-	nextEventAt := time.Duration(math.MaxInt64) // any event will happen before forever.
-	for _, elem := range events {
-		if elem > sim.CurrentTime && elem < nextEventAt {
-			nextEventAt = elem
-		}
-	}
-	// If the next action is  the GCD, just return
-	if nextEventAt == ret.GCD.ReadyAt() {
-		if ret.CancelChaosBane && ret.HasActiveAura("Chaos Bane") {
-			ret.GetAura("Chaos Bane").Deactivate(sim)
-		}
-		return
-	}
-
-	// Otherwise add a pending action for the next time
-	pa := &core.PendingAction{
-		Priority:     core.ActionPriorityLow,
-		OnAction:     rotationCallback,
-		NextActionAt: nextEventAt,
-	}
-
-	sim.AddPendingAction(pa)
-
-	if ret.CancelChaosBane && ret.HasActiveAura("Chaos Bane") {
-		ret.GetAura("Chaos Bane").Deactivate(sim)
-	}
-}
-
-func CancelChaosBane(ret *RetributionPaladin, sim *core.Simulation) {
-	if !ret.Paladin.CancelChaosBane {
-		return
-	}
-	if a := ret.Paladin.GetAura("Chaos Bane"); a != nil {
-		a.Deactivate(sim)
-	}
-}
diff --git a/sim/priest/healing/TestDisc.results b/sim/priest/healing/TestDisc.results
index a57d5dc3b3..2e1c8bb972 100644
--- a/sim/priest/healing/TestDisc.results
+++ b/sim/priest/healing/TestDisc.results
@@ -343,7 +343,7 @@ dps_results: {
 dps_results: {
  key: "TestDisc-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  tps: 19.00072
+  tps: 18.9916
   hps: 3650.23426
  }
 }
diff --git a/sim/priest/healing/TestHoly.results b/sim/priest/healing/TestHoly.results
index 91517efd66..7de62a4da8 100644
--- a/sim/priest/healing/TestHoly.results
+++ b/sim/priest/healing/TestHoly.results
@@ -172,8 +172,8 @@ dps_results: {
 dps_results: {
  key: "TestHoly-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  tps: 36.25385
-  hps: 4991.42137
+  tps: 35.9717
+  hps: 4983.05245
  }
 }
 dps_results: {
diff --git a/sim/priest/healing/healing_priest.go b/sim/priest/healing/healing_priest.go
index 743f2e3231..1150ac7b73 100644
--- a/sim/priest/healing/healing_priest.go
+++ b/sim/priest/healing/healing_priest.go
@@ -1,7 +1,6 @@
 package healing
 
 import (
-	"github.com/wowsims/wotlk/sim/common"
 	"github.com/wowsims/wotlk/sim/core"
 	"github.com/wowsims/wotlk/sim/core/proto"
 	"github.com/wowsims/wotlk/sim/priest"
@@ -27,13 +26,7 @@ func RegisterHealingPriest() {
 type HealingPriest struct {
 	*priest.Priest
 
-	rotation       *proto.HealingPriest_Rotation
-	CustomRotation *common.CustomRotation
-	Options        *proto.HealingPriest_Options
-
-	// Spells to rotate through for cyclic rotation.
-	spellCycle     []*core.Spell
-	nextCycleIndex int
+	Options *proto.HealingPriest_Options
 }
 
 func NewHealingPriest(character *core.Character, options *proto.Player) *HealingPriest {
@@ -46,9 +39,8 @@ func NewHealingPriest(character *core.Character, options *proto.Player) *Healing
 
 	basePriest := priest.New(character, selfBuffs, options.TalentsString)
 	hpriest := &HealingPriest{
-		Priest:   basePriest,
-		rotation: healingOptions.Rotation,
-		Options:  healingOptions.Options,
+		Priest:  basePriest,
+		Options: healingOptions.Options,
 	}
 
 	hpriest.SelfBuffs.PowerInfusionTarget = &proto.UnitReference{}
@@ -56,8 +48,6 @@ func NewHealingPriest(character *core.Character, options *proto.Player) *Healing
 		hpriest.SelfBuffs.PowerInfusionTarget = hpriest.Options.PowerInfusionTarget
 	}
 
-	hpriest.EnableResumeAfterManaWait(hpriest.tryUseGCD)
-
 	return hpriest
 }
 
@@ -81,26 +71,8 @@ func (hpriest *HealingPriest) Initialize() {
 
 	hpriest.ApplyRapture(hpriest.Options.RapturesPerMinute)
 	hpriest.RegisterHymnOfHopeCD()
-
-	if hpriest.rotation.Type == proto.HealingPriest_Rotation_Custom {
-		hpriest.CustomRotation = hpriest.makeCustomRotation()
-	}
-
-	if hpriest.CustomRotation == nil {
-		hpriest.rotation.Type = proto.HealingPriest_Rotation_Cycle
-		hpriest.spellCycle = []*core.Spell{
-			hpriest.GreaterHeal,
-			hpriest.FlashHeal,
-			hpriest.CircleOfHealing,
-			hpriest.BindingHeal,
-			hpriest.PrayerOfHealing,
-			hpriest.PrayerOfMending,
-			hpriest.PenanceHeal,
-		}
-	}
 }
 
 func (hpriest *HealingPriest) Reset(sim *core.Simulation) {
 	hpriest.Priest.Reset(sim)
-	hpriest.nextCycleIndex = 0
 }
diff --git a/sim/priest/healing/rotation.go b/sim/priest/healing/rotation.go
deleted file mode 100644
index 1169c21e71..0000000000
--- a/sim/priest/healing/rotation.go
+++ /dev/null
@@ -1,115 +0,0 @@
-package healing
-
-import (
-	"github.com/wowsims/wotlk/sim/common"
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-)
-
-func (hpriest *HealingPriest) OnGCDReady(sim *core.Simulation) {
-	hpriest.tryUseGCD(sim)
-}
-
-func (hpriest *HealingPriest) tryUseGCD(sim *core.Simulation) {
-	if hpriest.CustomRotation != nil {
-		hpriest.CustomRotation.Cast(sim)
-	} else {
-		spell := hpriest.chooseSpell(sim)
-
-		if success := spell.Cast(sim, hpriest.CurrentTarget); !success {
-			hpriest.WaitForMana(sim, spell.CurCast.Cost)
-		}
-	}
-}
-
-func (hpriest *HealingPriest) chooseSpell(sim *core.Simulation) *core.Spell {
-	if !hpriest.Renew.CurHot().IsActive() {
-		return hpriest.Renew
-	} else if hpriest.PowerWordShield.CanCast(sim, hpriest.CurrentTarget) {
-		return hpriest.PowerWordShield
-	} else {
-		for !hpriest.spellCycle[hpriest.nextCycleIndex].IsReady(sim) {
-			hpriest.nextCycleIndex = (hpriest.nextCycleIndex + 1) % len(hpriest.spellCycle)
-		}
-		spell := hpriest.spellCycle[hpriest.nextCycleIndex]
-		hpriest.nextCycleIndex = (hpriest.nextCycleIndex + 1) % len(hpriest.spellCycle)
-		return spell
-	}
-}
-
-func (hpriest *HealingPriest) makeCustomRotation() *common.CustomRotation {
-	return common.NewCustomRotation(hpriest.rotation.CustomRotation, hpriest.GetCharacter(), map[int32]common.CustomSpell{
-		int32(proto.HealingPriest_Rotation_GreaterHeal): {
-			Spell: hpriest.GreaterHeal,
-		},
-		int32(proto.HealingPriest_Rotation_FlashHeal): {
-			Spell: hpriest.FlashHeal,
-		},
-		int32(proto.HealingPriest_Rotation_Renew): {
-			Spell: hpriest.Renew,
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				for _, unit := range hpriest.Env.Raid.AllUnits {
-					renewHot := hpriest.Renew.Hot(unit)
-					if renewHot != nil && !renewHot.IsActive() {
-						success := hpriest.Renew.Cast(sim, unit)
-						return success, hpriest.Renew.CurCast.Cost
-					}
-				}
-				panic("No valid Renew target")
-			},
-			Condition: func(sim *core.Simulation) bool {
-				for _, unit := range hpriest.Env.Raid.AllUnits {
-					renewHot := hpriest.Renew.Hot(unit)
-					if renewHot != nil && !renewHot.IsActive() {
-						return true
-					}
-				}
-				return false
-			},
-		},
-		int32(proto.HealingPriest_Rotation_PowerWordShield): {
-			Spell: hpriest.PowerWordShield,
-			Action: func(sim *core.Simulation, target *core.Unit) (bool, float64) {
-				for _, unit := range hpriest.Env.Raid.AllUnits {
-					if hpriest.PowerWordShield.CanCast(sim, unit) {
-						success := hpriest.PowerWordShield.Cast(sim, unit)
-						return success, hpriest.PowerWordShield.CurCast.Cost
-					}
-				}
-				panic("No valid PowerWordShield target")
-			},
-			Condition: func(sim *core.Simulation) bool {
-				for _, unit := range hpriest.Env.Raid.AllUnits {
-					if hpriest.PowerWordShield.CanCast(sim, unit) {
-						return true
-					}
-				}
-				return false
-			},
-		},
-		int32(proto.HealingPriest_Rotation_CircleOfHealing): {
-			Spell: hpriest.CircleOfHealing,
-			Condition: func(sim *core.Simulation) bool {
-				return hpriest.CircleOfHealing.IsReady(sim)
-			},
-		},
-		int32(proto.HealingPriest_Rotation_PrayerOfHealing): {
-			Spell: hpriest.PrayerOfHealing,
-		},
-		int32(proto.HealingPriest_Rotation_PrayerOfMending): {
-			Spell: hpriest.PrayerOfMending,
-			Condition: func(sim *core.Simulation) bool {
-				return hpriest.PrayerOfMending.IsReady(sim)
-			},
-		},
-		int32(proto.HealingPriest_Rotation_Penance): {
-			Spell: hpriest.PenanceHeal,
-			Condition: func(sim *core.Simulation) bool {
-				return hpriest.PenanceHeal.IsReady(sim)
-			},
-		},
-		int32(proto.HealingPriest_Rotation_BindingHeal): {
-			Spell: hpriest.BindingHeal,
-		},
-	})
-}
diff --git a/sim/priest/holy_fire.go b/sim/priest/holy_fire.go
index 0490636cb6..5fbaeda9ac 100644
--- a/sim/priest/holy_fire.go
+++ b/sim/priest/holy_fire.go
@@ -7,8 +7,8 @@ import (
 	"github.com/wowsims/wotlk/sim/core/proto"
 )
 
-func (priest *Priest) RegisterHolyFireSpell(memeDream bool) {
-	hasGlyph := !memeDream && priest.HasMajorGlyph(proto.PriestMajorGlyph_GlyphOfSmite)
+func (priest *Priest) RegisterHolyFireSpell() {
+	hasGlyph := priest.HasMajorGlyph(proto.PriestMajorGlyph_GlyphOfSmite)
 
 	priest.HolyFire = priest.RegisterSpell(core.SpellConfig{
 		ActionID:    core.ActionID{SpellID: 48135},
diff --git a/sim/priest/hymn_of_hope.go b/sim/priest/hymn_of_hope.go
index 4b097acbea..f88c6ecafe 100644
--- a/sim/priest/hymn_of_hope.go
+++ b/sim/priest/hymn_of_hope.go
@@ -13,7 +13,6 @@ func (priest *Priest) RegisterHymnOfHopeCD() {
 	manaMetrics := priest.NewManaMetrics(actionID)
 
 	numTicks := 4 + core.TernaryInt32(priest.HasMajorGlyph(proto.PriestMajorGlyph_GlyphOfHymnOfHope), 1, 0)
-	channelTime := time.Duration(numTicks) * time.Second * 2
 
 	hymnOfHopeSpell := priest.RegisterSpell(core.SpellConfig{
 		ActionID: actionID,
@@ -21,8 +20,7 @@ func (priest *Priest) RegisterHymnOfHopeCD() {
 
 		Cast: core.CastConfig{
 			DefaultCast: core.Cast{
-				GCD:         core.GCDDefault,
-				ChannelTime: channelTime,
+				GCD: core.GCDDefault,
 			},
 			CD: core.Cooldown{
 				Timer:    priest.NewTimer(),
@@ -31,9 +29,8 @@ func (priest *Priest) RegisterHymnOfHopeCD() {
 		},
 
 		ApplyEffects: func(sim *core.Simulation, _ *core.Unit, spell *core.Spell) {
-			period := spell.CurCast.ChannelTime / time.Duration(numTicks)
 			core.StartPeriodicAction(sim, core.PeriodicActionOptions{
-				Period:   period,
+				Period:   time.Second * 2,
 				NumTicks: int(numTicks),
 				OnAction: func(sim *core.Simulation) {
 					// This is 3%, but it increases the target's max mana by 20% for the duration
diff --git a/sim/priest/mind_flay.go b/sim/priest/mind_flay.go
index b96cbf8d94..d25ba8649d 100644
--- a/sim/priest/mind_flay.go
+++ b/sim/priest/mind_flay.go
@@ -71,7 +71,6 @@ func (priest *Priest) newMindFlaySpell(numTicksIdx int32) *core.Spell {
 		mfReducTime = time.Millisecond * 170
 	}
 	tickLength := time.Second - mfReducTime
-	channelTime := tickLength * time.Duration(numTicks)
 
 	rolloverChance := float64(priest.Talents.PainAndSuffering) / 3.0
 	shadowFocus := 0.02 * float64(priest.Talents.ShadowFocus)
@@ -92,21 +91,7 @@ func (priest *Priest) newMindFlaySpell(numTicksIdx int32) *core.Spell {
 		},
 		Cast: core.CastConfig{
 			DefaultCast: core.Cast{
-				GCD:         core.GCDDefault,
-				ChannelTime: channelTime,
-			},
-			ModifyCast: func(sim *core.Simulation, spell *core.Spell, cast *core.Cast) {
-				if spell.Unit.IsUsingAPL || priest.Latency == 0 {
-					return
-				}
-				// if our channel is longer than GCD it will have human latency to end it because you can't queue the next spell.
-				if float64(channelTime)*priest.CastSpeed > float64(core.GCDMin) {
-					variation := priest.Latency * (0.66 + sim.RandomFloat("spriest latency")*(1.33-0.66)) // should vary from 0.66 - 1.33 of given latency
-					cast.ChannelTime += time.Duration(variation / priest.CastSpeed * float64(time.Millisecond))
-					if sim.Log != nil {
-						priest.Log(sim, "Latency: %.3f, Applied Latency: %.3f", priest.Latency, variation)
-					}
-				}
+				GCD: core.GCDDefault,
 			},
 		},
 		BonusHitRating: float64(priest.Talents.ShadowFocus) * 1 * core.SpellHitRatingPerHitChance,
@@ -154,18 +139,3 @@ func (priest *Priest) newMindFlaySpell(numTicksIdx int32) *core.Spell {
 		},
 	})
 }
-
-func (priest *Priest) MindFlayTickDuration() time.Duration {
-	return priest.ApplyCastSpeed(time.Second - core.TernaryDuration(priest.T10FourSetBonus, time.Millisecond*170, 0))
-}
-
-func (priest *Priest) AverageMindFlayLatencyDelay(numTicks int, gcd time.Duration) time.Duration {
-	wait := priest.ApplyCastSpeed(priest.MindFlay[numTicks].DefaultCast.ChannelTime)
-	if wait <= gcd || priest.Latency == 0 {
-		return 0
-	}
-
-	base := priest.Latency * 0.25
-	variation := base + 0.5*base
-	return time.Duration(variation) * time.Millisecond
-}
diff --git a/sim/priest/mind_sear.go b/sim/priest/mind_sear.go
index 4514daf3b3..fd48c35ca4 100644
--- a/sim/priest/mind_sear.go
+++ b/sim/priest/mind_sear.go
@@ -54,7 +54,6 @@ func (priest *Priest) newMindSearSpell(numTicksIdx int32) *core.Spell {
 		flags |= core.SpellFlagAPL
 	}
 
-	channelTime := time.Second * time.Duration(numTicks)
 	miseryCoeff := priest.getMindSearMiseryCoefficient()
 	mindSearTickSpell := priest.getMindSearTickSpell(numTicksIdx)
 
@@ -67,8 +66,7 @@ func (priest *Priest) newMindSearSpell(numTicksIdx int32) *core.Spell {
 	}
 	config.Cast = core.CastConfig{
 		DefaultCast: core.Cast{
-			GCD:         core.GCDDefault,
-			ChannelTime: channelTime,
+			GCD: core.GCDDefault,
 		},
 	}
 	config.Dot = core.DotConfig{
diff --git a/sim/priest/penance.go b/sim/priest/penance.go
index fb3eb199ae..a1b0432b6d 100644
--- a/sim/priest/penance.go
+++ b/sim/priest/penance.go
@@ -43,8 +43,7 @@ func (priest *Priest) makePenanceSpell(isHeal bool) *core.Spell {
 		},
 		Cast: core.CastConfig{
 			DefaultCast: core.Cast{
-				GCD:         core.GCDDefault,
-				ChannelTime: time.Second * 2,
+				GCD: core.GCDDefault,
 			},
 			CD: core.Cooldown{
 				Timer:    priest.NewTimer(),
diff --git a/sim/priest/priest.go b/sim/priest/priest.go
index 0f78a7afae..5c851c955a 100644
--- a/sim/priest/priest.go
+++ b/sim/priest/priest.go
@@ -140,10 +140,8 @@ func (priest *Priest) Initialize() {
 
 	priest.registerPowerInfusionCD()
 
-	if priest.IsUsingAPL {
-		priest.MindFlayAPL = priest.newMindFlaySpell(0)
-		priest.MindSearAPL = priest.newMindSearSpell(0)
-	}
+	priest.MindFlayAPL = priest.newMindFlaySpell(0)
+	priest.MindSearAPL = priest.newMindSearSpell(0)
 
 	priest.MindFlay = []*core.Spell{
 		nil, // So we can use # of ticks as the index
diff --git a/sim/priest/shadow/TestShadow.results b/sim/priest/shadow/TestShadow.results
index fd4adab58a..a353b97a97 100644
--- a/sim/priest/shadow/TestShadow.results
+++ b/sim/priest/shadow/TestShadow.results
@@ -798,8 +798,8 @@ dps_results: {
 dps_results: {
  key: "TestShadow-AllItems-Zabra'sRaiment"
  value: {
-  dps: 6508.31094
-  tps: 6310.96351
+  dps: 6507.98519
+  tps: 6310.63776
  }
 }
 dps_results: {
diff --git a/sim/priest/shadow/rotation.go b/sim/priest/shadow/rotation.go
deleted file mode 100644
index 064af2ef3b..0000000000
--- a/sim/priest/shadow/rotation.go
+++ /dev/null
@@ -1,1173 +0,0 @@
-package shadow
-
-import (
-	//"fmt"
-	"math"
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-)
-
-const (
-	mbIdx int = iota
-	dpIdx
-	vtIdx
-	swdIdx
-	mfIdx
-	swpIdx
-)
-
-func (spriest *ShadowPriest) OnGCDReady(sim *core.Simulation) {
-	spriest.tryUseGCD(sim)
-}
-
-func (spriest *ShadowPriest) OnManaTick(sim *core.Simulation) {
-	if spriest.FinishedWaitingForManaAndGCDReady(sim) {
-		spriest.tryUseGCD(sim)
-	}
-}
-func (spriest *ShadowPriest) tryUseGCD(sim *core.Simulation) {
-	if spriest.IsUsingAPL {
-		return
-	}
-
-	// grab all of the shadow priest spell CDs remaining durations to use in the dps calculation
-	spriest.VTCastTime = spriest.ApplyCastSpeed(time.Millisecond * 1500)
-	spriest.AllCDs = []time.Duration{
-		max(0, spriest.MindBlast.TimeToReady(sim)),
-		max(0, spriest.DevouringPlague.CurDot().RemainingDuration(sim)),
-		max(0, spriest.VampiricTouch.CurDot().RemainingDuration(sim)-spriest.VTCastTime),
-		max(0, spriest.ShadowWordDeath.TimeToReady(sim)),
-		0,
-	}
-
-	var spell *core.Spell
-	var waitTime time.Duration
-	target := spriest.CurrentTarget
-
-	switch spriest.rotation.RotationType {
-	case proto.ShadowPriest_Rotation_Ideal:
-		spell, waitTime = spriest.chooseSpellIdeal(sim)
-	case proto.ShadowPriest_Rotation_Basic:
-		spell = spriest.chooseSpellBasicOrClipping(sim, false)
-	case proto.ShadowPriest_Rotation_Clipping:
-		spell = spriest.chooseSpellBasicOrClipping(sim, true)
-	case proto.ShadowPriest_Rotation_AoE:
-		spell, target = spriest.chooseSpellAOE(sim)
-	default:
-		spell, waitTime = spriest.chooseSpellIdeal(sim)
-	}
-
-	if spell != nil {
-		if spell == spriest.MindFlay[3] && spriest.InnerFocus != nil && spriest.InnerFocus.IsReady(sim) {
-			spriest.InnerFocus.Cast(sim, nil)
-		}
-
-		if success := spell.Cast(sim, target); !success {
-			spriest.WaitForMana(sim, spell.CurCast.Cost)
-		}
-	} else if waitTime != 0 {
-		spriest.WaitUntil(sim, sim.CurrentTime+waitTime)
-	} else {
-		spriest.WaitUntil(sim, sim.CurrentTime+time.Millisecond*500)
-	}
-}
-
-func (spriest *ShadowPriest) chooseSpellAOE(sim *core.Simulation) (*core.Spell, *core.Unit) {
-	if len(sim.Encounter.Targets) >= 4 {
-		return spriest.MindSear[5], spriest.CurrentTarget
-	}
-
-	for _, t := range sim.Encounter.TargetUnits {
-		if spriest.ShadowWordPain.Dot(t).IsActive() && spriest.ShadowWordPain.Dot(t).RemainingDuration(sim).Seconds() < 3 {
-			return spriest.MindFlay[2], t
-		}
-	}
-
-	for _, t := range sim.Encounter.TargetUnits {
-		if !spriest.VampiricTouch.Dot(t).IsActive() && sim.GetRemainingDuration().Seconds() > 5 {
-			return spriest.VampiricTouch, t
-		}
-	}
-
-	for _, t := range sim.Encounter.TargetUnits {
-		if !spriest.ShadowWordPain.Dot(t).IsActive() && sim.GetRemainingDuration().Seconds() > 12 && spriest.DevouringPlague.Dot(sim.Encounter.TargetUnits[0]).IsActive() {
-			return spriest.ShadowWordPain, t
-		}
-		if spriest.ShadowWordPain.Dot(t).IsActive() && spriest.ShadowWordPain.Dot(t).RemainingDuration(sim).Seconds() < 3 {
-			return spriest.MindFlay[2], t
-		}
-	}
-
-	spell, _ := spriest.chooseSpellIdeal(sim)
-	return spell, spriest.CurrentTarget
-}
-
-func (spriest *ShadowPriest) chooseSpellBasicOrClipping(sim *core.Simulation, isClipping bool) *core.Spell {
-	if spriest.DevouringPlague.CurDot().RemainingDuration(sim) <= 0 {
-		return spriest.DevouringPlague
-	} else if spriest.Talents.VampiricTouch && spriest.VampiricTouch.CurDot().RemainingDuration(sim) <= spriest.VTCastTime {
-		return spriest.VampiricTouch
-	} else if !spriest.ShadowWordPain.CurDot().IsActive() && spriest.ShadowWeavingAura.GetStacks() >= 5 {
-		return spriest.ShadowWordPain
-	} else if spriest.MindBlast.TimeToReady(sim) == 0 {
-		return spriest.MindBlast
-	} else if spriest.ShadowWordDeath.TimeToReady(sim) == 0 {
-		return spriest.ShadowWordDeath
-	} else {
-		var numTicks int
-		gcd := spriest.SpellGCD()
-		tickLength := spriest.MindFlayTickDuration()
-		if isClipping {
-			numTicks = spriest.ClippingMindflayRotation(sim, gcd, tickLength)
-		} else {
-			//numTicks = spriest.BasicMindflayRotation(sim, gcd, tickLength)
-			numTicks = 3
-		}
-		return spriest.MindFlay[numTicks]
-	}
-}
-
-// Returns (spellToCast, waitTime). waitTime should be nonzero only if spellToCast is nil.
-func (spriest *ShadowPriest) chooseSpellIdeal(sim *core.Simulation) (*core.Spell, time.Duration) {
-	// TODO: probably do something different instead of making it global?
-	// some global variables used throughout the code
-	var currentWait time.Duration
-	var mbDamage float64
-	var dpDamage float64
-	var vtDamage float64
-	var swdDamage float64
-	var mfDamage float64
-	var overwriteDPS float64
-
-	var currDotTickSpeed float64
-
-	//if sim.CurrentTime == 0 && spriest.rotation.PrecastVt {
-	//spriest.SpendMana(sim, spriest.VampiricTouch.DefaultCast.Cost, spriest.VampiricTouch.ResourceMetrics)
-	//spriest.VampiricTouch.SkipCastAndApplyEffects(sim, spriest.CurrentTarget)
-	//}
-
-	// initialize function specific variables
-	var swStacks float64
-	var numswptickstime float64
-	var cdDpso float64
-	var cdDps float64
-	var chosenMfs int
-	var num_DP_ticks float64
-	var num_VT_ticks float64
-	var wait1 time.Duration
-	var wait2 time.Duration
-	var wait time.Duration
-	var wait3 time.Duration
-
-	// initialize helpful variables for calculations later
-	gcd := spriest.SpellGCD()
-	tickLength := spriest.MindFlayTickDuration()
-
-	dotTickSpeed := float64(spriest.ApplyCastSpeed(time.Second * 3))
-	remain_fight := float64(sim.GetRemainingDuration())
-	castMf2 := 0 // if SW stacks = 3, and we want to get SWP up at 5 stacks exactly, then we want to hard code a MF2
-	bestIdx := -1
-
-	// How much time until lust is used?
-	timeUntilBLStarts := float64(spriest.BLUsedAt) - sim.CurrentTime.Seconds()
-	if timeUntilBLStarts < 0 {
-		timeUntilBLStarts = 0
-	}
-	// How many VT ticks before lust is used?
-	numVTbeforeBL := math.Floor(timeUntilBLStarts / (dotTickSpeed * 1e-9))
-	if numVTbeforeBL < 0 {
-		numVTbeforeBL = 0
-	}
-
-	swStacks = float64(spriest.ShadowWeavingAura.GetStacks())
-
-	// Reduce number of DP/VT ticks based on remaining duration
-	num_DP_ticks = min(float64(spriest.DevouringPlague.CurDot().NumberOfTicks), math.Floor(remain_fight/dotTickSpeed))
-	num_VT_ticks = min(float64(spriest.VampiricTouch.CurDot().NumberOfTicks), math.Floor(remain_fight/dotTickSpeed))
-
-	// Spell damage numbers that are updated before each cast in order to determine the most optimal next cast based on dps over a finite window
-	// This is needed throughout the code to determine the optimal spell(s) to cast next
-	// MB dmg
-	mbDamage = 0
-	impDamage := 0
-	if spriest.options.UseMindBlast {
-		if spriest.T8FourSetBonus { //include benefit of 240 haste rating for 4 seconds. This isnt perfect because 1.6 dps per haste is an average and varies throughout the fight
-			impDamage = 1.6 * 240 * 4
-		}
-		mbDamage = spriest.MindBlast.ExpectedInitialDamage(sim, spriest.CurrentTarget) + float64(impDamage)
-	}
-
-	// DP dmg
-	dpTickDamage := spriest.DevouringPlague.ExpectedTickDamage(sim, spriest.CurrentTarget)
-	dpInit := dpTickDamage * spriest.DpInitMultiplier
-	dpDot := dpTickDamage * num_DP_ticks
-	dpDamage = dpInit + dpDot
-
-	if sim.Log != nil {
-		//spriest.Log(sim, "dpTickDamage [%d]", dpTickDamage)
-		//spriest.Log(sim, "dpInit [%d]", dpInit)
-		//spriest.Log(sim, "dpDamage[%d]", dpDamage)
-	}
-
-	// Determine number of DP ticks before BL. If there is at least 1 then it's worth using
-	numDPbeforeBL := math.Floor(timeUntilBLStarts / (dotTickSpeed * 1e-9))
-	if numDPbeforeBL < 0 {
-		numDPbeforeBL = 0
-	}
-	if (timeUntilBLStarts > gcd.Seconds() && numDPbeforeBL < 1 && sim.CurrentTime.Seconds() < float64(spriest.BLUsedAt)) || (timeUntilBLStarts <= gcd.Seconds() && timeUntilBLStarts > 0.01) {
-		dpDamage = 0
-	}
-
-	vtDamage = spriest.VampiricTouch.ExpectedTickDamage(sim, spriest.CurrentTarget) * num_VT_ticks
-
-	// If there is at least 2 VT ticks then it's worth using
-	if timeUntilBLStarts > gcd.Seconds() && numVTbeforeBL < 2 && sim.CurrentTime.Seconds() < float64(spriest.BLUsedAt) {
-		vtDamage = 0
-	}
-
-	// SWD dmg
-	swdDamage = 0
-	if spriest.options.UseShadowWordDeath {
-		swdDamage = spriest.ShadowWordDeath.ExpectedInitialDamage(sim, spriest.CurrentTarget)
-	}
-
-	mfDamage = spriest.MindFlay[3].ExpectedTickDamage(sim, spriest.CurrentTarget) * 3
-	swpTickDamage := spriest.ShadowWordPain.ExpectedTickDamage(sim, spriest.CurrentTarget)
-
-	//if spriest.rotation.RotationType == 4 {
-	//	msDamage = spriest.MindSear[5].ExpectedDamage(sim, spriest.CurrentTarget)
-	//	}
-	castSwpNow := 0 // if SW stacks = 3, and we want to get SWP up immediately becaues fight length is low enough, then this flag gets set to 1
-	if spriest.rotation.RotationType != 4 {
-		// this should be cleaned up, but essentially we want to cast SWP either 3rd or 5th in the rotation which is fight length dependent
-
-		if swStacks > 2 && swStacks < 5 && !spriest.ShadowWordPain.CurDot().IsActive() {
-			addedDmg := mbDamage*0.12 + mfDamage*0.22*2/3 + swpTickDamage*2*gcd.Seconds()/3
-			numswptickstime = addedDmg / (swpTickDamage * 0.06) * 3 //if the fight lenght is < numswptickstime then use swp 3rd.. if > then use at weaving = 5
-			//
-			if remain_fight*math.Pow(10, -9) < numswptickstime {
-				castSwpNow = 1
-			} else {
-				castMf2 = 1
-			}
-		}
-	}
-
-	var currDPS float64
-	var nextTickWait time.Duration
-	var currDPS2 float64
-	var overwriteDPS2 float64
-
-	if spriest.DevouringPlague.CurDot().IsActive() {
-		currDotTickSpeed = spriest.DevouringPlague.CurDot().TickPeriod().Seconds()
-		nextTickWait = spriest.DevouringPlague.CurDot().TimeUntilNextTick(sim)
-
-		dpDotCurr := spriest.DevouringPlague.ExpectedTickDamageFromCurrentSnapshot(sim, spriest.CurrentTarget)
-		dpInitCurr := dpDotCurr * spriest.DpInitMultiplier
-
-		cdDamage := mbDamage
-		if spriest.T10FourSetBonus || cdDamage == 0 {
-			cdDamage = mfDamage / 3 * 2
-		}
-
-		dotTickSpeednew := 3 * spriest.CastSpeed
-		currDPS = (dpInitCurr + dpDotCurr*8 + cdDamage) / (currDotTickSpeed * 8)
-		//overwriteDPS = (dpInitCurr + dpInit + dpDotCurr*1 + dpDot) / (dotTickSpeednew*8 + currDotTickSpeed*1)
-		overwriteDPS = (dpInit + dpDot) / (dotTickSpeednew * 8)
-
-		if blAura := spriest.GetActiveAuraWithTag(core.BloodlustAuraTag); blAura != nil {
-			blRemainingDur := blAura.RemainingDuration(sim)
-			if blRemainingDur < time.Second*2 && blRemainingDur > time.Millisecond*100 {
-				dpRemainTicks := 8 - float64(spriest.DevouringPlague.CurDot().NumTicksRemaining(sim))
-				overwriteDPS2 = dpInitCurr + dpRemainTicks*dpDotCurr*(1-spriest.CastSpeed)
-				currDPS2 = cdDamage
-
-				if sim.Log != nil {
-					spriest.Log(sim, "currDPS2[%d]", currDPS2)
-					spriest.Log(sim, "overwriteDPS2[%d]", overwriteDPS2)
-					spriest.Log(sim, "dpRemainTicks[%d]", dpRemainTicks)
-				}
-			}
-		}
-	}
-
-	// Make an array of DPCT per spell that will be used to find the optimal spell to cast
-	spellDPCT := []float64{
-		// MB dps
-		mbDamage / (gcd + spriest.AllCDs[mbIdx]).Seconds(),
-		// DP dps
-		dpDamage / (gcd + spriest.AllCDs[dpIdx]).Seconds(),
-		// VT dps
-		vtDamage / (gcd + spriest.AllCDs[vtIdx]).Seconds(),
-		// SWD dps
-		swdDamage / (gcd + spriest.AllCDs[swdIdx]).Seconds(),
-		// MF dps 3 ticks
-		mfDamage / (tickLength * 3).Seconds(),
-	}
-
-	//if sim.Log != nil {
-	//spriest.Log(sim, "mbDamage[%d]", mbDamage)
-	//spriest.Log(sim, "mb time[%d]", spriest.AllCDs[mbIdx])
-	//spriest.Log(sim, "mftime[%d]", float64((tickLength * 3).Seconds()))
-	//spriest.Log(sim, "gcd[%d]", gcd.Seconds())
-	//spriest.Log(sim, "CastSpeedMultiplier[%d]", spriest.PseudoStats.CastSpeedMultiplier)
-	//spriest.Log(sim, "critChance[%d]", critChance)
-	//}
-
-	// Find the maximum DPCT spell
-	bestDmg := 0.0
-	for i, v := range spellDPCT {
-		if sim.Log != nil {
-			spriest.Log(sim, "\tspellDPCT[%d]: %01.f", i, v)
-			spriest.Log(sim, "\tcdDiffs[%d]: %0.1f", i, spriest.AllCDs[i].Seconds())
-		}
-		if v > bestDmg {
-			bestIdx = i
-			bestDmg = v
-		}
-	}
-	// Find the minimum CD ability to make sure that shouldnt be cast first
-	nextCD := core.NeverExpires
-	nextIdx := -1
-	for i, v := range spriest.AllCDs[1 : len(spriest.AllCDs)-1] {
-		//	if sim.Log != nil {
-		// spriest.Log(sim, "\tspriest.AllCDs[%d]: %01.f", i, v)
-		// 	 spriest.Log(sim, "\tcdDiffs[%d]: %0.1f", i, cdDiffs[i].Seconds())
-		//}
-		if v < nextCD {
-			nextCD = v
-			nextIdx = i + 1
-		}
-	}
-	waitmin := nextCD
-
-	// Now it's possible that the wait time for the chosen spell is long, if that's the case, then it might be better to investigate the dps over a 2 spell window to see if casting something else will benefit
-	if bestIdx < mfIdx {
-		currentWait = spriest.AllCDs[bestIdx]
-	}
-
-	if spriest.AllCDs[mbIdx] < gcd && bestIdx == mfIdx && spriest.AllCDs[swdIdx] == 0 {
-		totalDps__poss := (mbDamage + swdDamage) / (gcd + gcd).Seconds()
-		totalDps__poss3 := (mbDamage + mfDamage*2/3) / (2*tickLength + gcd).Seconds()
-
-		if totalDps__poss > totalDps__poss3 {
-			bestIdx = swdIdx
-			currentWait = spriest.AllCDs[bestIdx]
-		}
-	}
-
-	if nextIdx != mfIdx && bestIdx != mfIdx && bestIdx != swpIdx && currentWait > waitmin && currentWait.Seconds() < 3 { // right now 3 might not be correct number, but we can study this to optimize
-
-		if bestIdx == vtIdx {
-			cdDpso = vtDamage / (gcd + currentWait).Seconds()
-		} else if bestIdx == mbIdx {
-			cdDpso = mbDamage / (gcd + currentWait).Seconds()
-		} else if bestIdx == swdIdx {
-			cdDpso = swdDamage / (gcd + currentWait).Seconds()
-		} else if bestIdx == dpIdx {
-			cdDpso = dpDamage / (gcd + currentWait).Seconds()
-		}
-
-		if nextIdx == vtIdx {
-			cdDps = vtDamage / (gcd + waitmin).Seconds()
-		} else if nextIdx == mbIdx {
-			cdDps = mbDamage / (gcd + waitmin).Seconds()
-		} else if nextIdx == swdIdx {
-			cdDps = swdDamage / (gcd + waitmin).Seconds()
-		} else if nextIdx == dpIdx {
-			cdDps = dpDamage / (gcd + waitmin).Seconds()
-		}
-
-		residualWait := currentWait - gcd
-		if residualWait < 0 {
-			residualWait = 0
-		}
-		totalDps__poss0 := (cdDpso * (currentWait + gcd).Seconds()) / (gcd + currentWait).Seconds()
-		totalDps__poss1 := (cdDpso*(currentWait+gcd).Seconds() + cdDps*(waitmin+gcd).Seconds()) / (waitmin + gcd + gcd + residualWait).Seconds()
-
-		totalDps__poss2 := float64(0)
-		totalDps__poss3 := float64(0)
-
-		residualMF := currentWait - 2*tickLength
-		if residualMF < 0 {
-			residualMF = 0
-		}
-		totalDps__poss2 = (cdDpso*(currentWait+gcd).Seconds() + mfDamage*2/3) / (2*tickLength + gcd + residualMF).Seconds()
-		residualMF = currentWait - 3*tickLength
-		if residualMF < 0 {
-			residualMF = 0
-		}
-		totalDps__poss3 = (cdDpso*(currentWait+gcd).Seconds() + mfDamage) / (3*tickLength + gcd + residualMF).Seconds()
-
-		//if sim.Log != nil {
-		//spriest.Log(sim, "nextIdx[%d]", nextIdx)
-		//spriest.Log(sim, "bestIdx[%d]", bestIdx)
-		//spriest.Log(sim, "currentWait[%d]", currentWait.Seconds())
-		//spriest.Log(sim, "total_dps__poss0[%d]", totalDps__poss0)
-		//spriest.Log(sim, "total_dps__poss1[%d]", totalDps__poss1)
-		//spriest.Log(sim, "total_dps__poss2[%d]", totalDps__poss2)
-		//spriest.Log(sim, "total_dps__poss3[%d]", totalDps__poss3)
-		//}
-
-		// TODO looks fishy, repeated bestIdx = mfIdx
-		if (totalDps__poss1 > totalDps__poss0) || (totalDps__poss2 > totalDps__poss0) || (totalDps__poss3 > totalDps__poss0) {
-			if totalDps__poss1 > totalDps__poss0 && totalDps__poss1 > totalDps__poss2 && totalDps__poss1 > totalDps__poss3 {
-				bestIdx = nextIdx // if choosing the minimum wait time spell first is highest dps, then change the index and current wait
-				currentWait = waitmin
-			} else if totalDps__poss2 > totalDps__poss0 && totalDps__poss2 > totalDps__poss1 && totalDps__poss2 > totalDps__poss3 {
-				//bestIdx = bestIdx // if choosing the minimum wait time spell first is highest dps, then change the index and current wait
-				//currentWait = currentWait
-				bestIdx = mfIdx
-			} else if totalDps__poss3 > totalDps__poss0 && totalDps__poss3 > totalDps__poss1 && totalDps__poss3 > totalDps__poss2 {
-				//bestIdx = bestIdx // if choosing the minimum wait time spell first is highest dps, then change the index and current wait
-				//currentWait = currentWait
-				bestIdx = mfIdx
-			} else {
-				bestIdx = mfIdx
-			}
-		}
-
-	}
-
-	// If VT isnt chosen, and reapplying DP is more dps, then overwrite it next
-	if overwriteDPS-currDPS > 1000 && bestIdx != vtIdx {
-		bestIdx = dpIdx
-		currentWait = nextTickWait
-	} else {
-		overwriteDPS = 0
-	}
-
-	// Now it's possible that the wait time is > 1 gcd and is the minimum wait time. this is unlikely in wrath given how good MF is, but still might be worth to check
-	// if chosen wait time is > 0.3*GCD (this was optimized in private sim, but might want to reoptimize with procs/ect) then check if it's more dps to add a mf sequence
-	if bestIdx != mfIdx && currentWait.Seconds() > 0.3*gcd.Seconds() {
-
-		if bestIdx == vtIdx {
-			cdDpso = vtDamage
-		} else if bestIdx == mbIdx {
-			cdDpso = mbDamage
-		} else if bestIdx == swdIdx {
-			cdDpso = swdDamage
-		} else if bestIdx == dpIdx {
-			cdDpso = dpDamage
-		}
-
-		addedgcd := max(gcd, time.Duration(2)*tickLength)
-		addedgcdtime := addedgcd - time.Duration(2)*tickLength
-
-		deltaMf1 := currentWait - gcd
-		if deltaMf1 < 0 {
-			deltaMf1 = 0
-		}
-		deltaMf2 := currentWait - (tickLength*2 + addedgcdtime)
-		if deltaMf2 < 0 {
-			deltaMf2 = 0
-		}
-		deltaMf3 := currentWait - tickLength*3
-		if deltaMf3 < 0 {
-			deltaMf3 = 0
-		}
-
-		dpsPossibleshort := []float64{
-			(cdDpso) / (gcd + currentWait).Seconds(),
-			(cdDpso + mfDamage/3) / (deltaMf1 + gcd + gcd).Seconds(),
-			(cdDpso + mfDamage/3*2) / (deltaMf2 + tickLength*2 + addedgcdtime + gcd).Seconds(),
-			(cdDpso + mfDamage) / (deltaMf3 + tickLength*3 + gcd).Seconds(),
-		}
-
-		// Find the highest possible dps and its index
-		highestPossibleIdx := 0
-		highestPossibleDmg := 0.0
-		for i, v := range dpsPossibleshort {
-			if v >= highestPossibleDmg {
-				//if sim.Log != nil {
-				//	spriest.Log(sim, "\thighestPossibleDmg[%d]: %01.f", i, v)
-				//}
-				highestPossibleIdx = i
-				highestPossibleDmg = v
-			}
-		}
-		mfAddIdx := highestPossibleIdx
-
-		if mfAddIdx == 0 {
-			chosenMfs = 0
-		} else if mfAddIdx == 1 {
-			chosenMfs = 1
-		} else if mfAddIdx == 2 {
-			chosenMfs = 2
-		} else if mfAddIdx == 3 {
-			chosenMfs = 3
-		}
-		if chosenMfs > 0 {
-			if spriest.AllCDs[mbIdx].Seconds() < currentWait.Seconds() && spriest.AllCDs[mbIdx].Seconds() == 0 && (mfAddIdx == 2 && spellDPCT[mbIdx] > spellDPCT[mfIdx]/3*2) || (mfAddIdx == 3 && spellDPCT[mbIdx] > spellDPCT[mfIdx]) {
-				bestIdx = mbIdx
-				currentWait = spriest.AllCDs[mbIdx]
-			} else if tickLength*3 <= gcd {
-				bestIdx = mfIdx // TODO looks fishy, repeated bestIdx = mfIdx
-			} else {
-				bestIdx = mfIdx
-			}
-		}
-	}
-
-	if bestIdx == vtIdx && spriest.AllCDs[mbIdx].Seconds() < currentWait.Seconds() && currentWait.Seconds() > 0.4 && spriest.options.UseMindBlast {
-		bestIdx = mbIdx
-		currentWait = spriest.AllCDs[mbIdx]
-	}
-
-	// if current spell is SWD and mf2 is less than GCD, and is more dps than SWD then use instead
-	if bestIdx == swdIdx && tickLength*2 <= gcd {
-		if spellDPCT[swdIdx] < spellDPCT[mfIdx]*2/3 {
-			bestIdx = mfIdx
-		}
-	}
-
-	// if MF1 is chosen, and SWD is off CD and isn't 0 dmg, then use SWD unless mf2 is < gcd
-	if chosenMfs == 1 && spriest.AllCDs[swdIdx] == 0 && swdDamage != 0 {
-		if tickLength*2 <= gcd {
-			bestIdx = mfIdx
-		} else {
-			bestIdx = swdIdx
-			currentWait = 0
-		}
-	}
-
-	if (overwriteDPS-currDPS > 1000 && (currentWait < gcd/2 || float64(currentWait) >= currDotTickSpeed*0.9)) && bestIdx != vtIdx {
-		bestIdx = dpIdx
-		currentWait = 0
-	}
-
-	if overwriteDPS-currDPS > 1000 && currentWait <= gcd && currentWait >= gcd/2 && spriest.AllCDs[swdIdx] == 0 {
-		if tickLength*2 <= gcd {
-			bestIdx = mfIdx
-		} else {
-			bestIdx = swdIdx
-			currentWait = 0
-		}
-	}
-
-	// if MF2 is chosen in order to get to 5 weaving stacks, then make sure that VT/DP are already up first
-	if castMf2 > 0 {
-		if !spriest.DevouringPlague.CurDot().IsActive() && swStacks >= 4 && dpDamage != 0 {
-			bestIdx = dpIdx
-		} else if !spriest.VampiricTouch.CurDot().IsActive() && swStacks >= 4 && spriest.DevouringPlague.CurDot().IsActive() && vtDamage != 0 {
-			bestIdx = vtIdx
-		} else {
-			bestIdx = mfIdx
-		}
-	}
-	// if at 5 SW stacks and SWP is not up, then cast unless VT/DP are down
-	if swStacks == 5 && !spriest.ShadowWordPain.CurDot().IsActive() {
-		if !spriest.DevouringPlague.CurDot().IsActive() && swStacks >= 4 && dpDamage != 0 {
-			bestIdx = dpIdx
-		} else if !spriest.VampiricTouch.CurDot().IsActive() && swStacks >= 4 && spriest.DevouringPlague.CurDot().IsActive() && vtDamage != 0 {
-			bestIdx = vtIdx
-		} else {
-			bestIdx = swpIdx
-		}
-	}
-	// cast SWP 3rd for short fights
-	if castSwpNow > 0 {
-		bestIdx = swpIdx
-	}
-	// Snap shot BL on DP
-	if overwriteDPS2-currDPS2 > 2500 && bestIdx != vtIdx { //Seems to be a dps loss to overwrite a DP to snap shot
-		bestIdx = dpIdx
-		currentWait = 0
-	}
-
-	//if timeUntilBLStarts < float64((time.Duration(3)*tickLength).Seconds()) && timeUntilBLStarts > 0.2 && bestIdx != swpIdx {
-	//	bestIdx = mfIdx
-	//	castMf2 = 1
-	//}
-	// If BL is almost up and VT is not active, then use VT
-	if timeUntilBLStarts <= gcd.Seconds() && !spriest.VampiricTouch.CurDot().IsActive() && timeUntilBLStarts > 0 {
-		bestIdx = vtIdx
-	}
-	// If BL is up in <0.3 seconds and greater than 10ms, then wait for it to be active
-	if timeUntilBLStarts <= 0.3 && timeUntilBLStarts > 0.01 {
-		bestIdx = dpIdx
-		currentWait = time.Millisecond * time.Duration(math.Round(timeUntilBLStarts*1010))
-	}
-
-	//if sim.Log != nil {
-	//spriest.Log(sim, "spriest.BLUsedAt %d", currentWait)
-	//spriest.Log(sim, "dpDamage %d", dpDamage)
-	//spriest.Log(sim, "currentWait %d", currentWait)
-	//}
-	if spriest.PrevTicks == 4 {
-		castMf2 = 1
-		bestIdx = mfIdx
-		spriest.PrevTicks = 0
-	}
-
-	if castMf2 == 1 && spriest.AllCDs[mbIdx].Seconds() == 0 && spriest.options.UseMindBlast {
-		return spriest.MindBlast, 0
-	}
-
-	if remain_fight*math.Pow(10, -9) < 2*float64(gcd.Seconds()) && !spriest.options.UseMindBlast {
-		bestIdx = dpIdx
-	}
-
-	if currentWait <= gcd/6 && currentWait > 0 && bestIdx != swpIdx && bestIdx != mfIdx {
-		return nil, currentWait
-	}
-
-	if currentWait >= gcd/6 && bestIdx != swpIdx && bestIdx != mfIdx {
-		bestIdx = mfIdx
-	}
-
-	if bestIdx == mbIdx {
-		return spriest.MindBlast, 0
-	} else if bestIdx == dpIdx {
-		return spriest.DevouringPlague, 0
-	} else if bestIdx == vtIdx {
-		return spriest.VampiricTouch, 0
-	} else if bestIdx == swdIdx {
-		return spriest.ShadowWordDeath, 0
-	} else if bestIdx == swpIdx {
-		return spriest.ShadowWordPain, 0 // once swp is cast need a way for talents to refresh the duration
-	} else if bestIdx == mfIdx {
-		var numTicks int
-
-		if chosenMfs == 1 {
-			numTicks = 1 // determiend above that it's more dps to add MF1, need if it's not better to enter ideal rotation instead
-		} else if (castMf2 == 1 && spriest.DevouringPlague.CurDot().IsActive() && spriest.VampiricTouch.CurDot().IsActive()) || (timeUntilBLStarts < (time.Duration(3)*tickLength).Seconds() && timeUntilBLStarts > 0.2) {
-			if float64(spriest.MindFlayTickDuration().Seconds()*3) < float64(gcd.Seconds()*1.3) {
-				numTicks = 3
-			} else {
-				numTicks = 2
-			}
-		} else {
-			numTicks = spriest.IdealMindflayRotation(sim, gcd, tickLength, currentWait, mfDamage, mbDamage, dpDamage, vtDamage, swdDamage, overwriteDPS) //enter the mf optimizaiton routine to optimze mf clips and for next optimal spell
-		}
-
-		if numTicks == 3 && spriest.ShadowWordPain.CurDot().IsActive() && spriest.ShadowWordPain.CurDot().RemainingDuration(sim).Seconds() < tickLength.Seconds()*12 && float64(tickLength.Seconds()*3) >= gcd.Seconds() {
-			numTicks = 2
-		}
-
-		if numTicks == 0 {
-			// Means we'd rather wait for next CD (swp, vt, etc) than start a MF cast.
-			nextCD := core.NeverExpires
-			for _, v := range spriest.AllCDs[1 : len(spriest.AllCDs)-1] {
-				if v < nextCD {
-					nextCD = v
-				}
-			}
-			return nil, nextCD
-		} else if numTicks == 2 && spriest.AllCDs[mbIdx].Seconds() == 0 && spriest.options.UseMindBlast {
-			return spriest.MindBlast, 0
-		} else {
-			//numTicks = 3
-			if spriest.rotation.RotationType == 4 && len(sim.Encounter.Targets) >= 3 {
-				return spriest.MindSear[numTicks], 0
-			} else {
-				return spriest.MindFlay[numTicks], 0
-			}
-		}
-	} else {
-		mbcd := spriest.MindBlast.TimeToReady(sim)
-		swdcd := spriest.ShadowWordDeath.TimeToReady(sim)
-		vtidx := spriest.VampiricTouch.CurDot().RemainingDuration(sim) - spriest.VTCastTime
-		swpidx := spriest.ShadowWordPain.CurDot().RemainingDuration(sim)
-		dpidx := spriest.DevouringPlague.CurDot().RemainingDuration(sim)
-		wait1 = min(mbcd, swdcd)
-		wait2 = min(dpidx, wait1)
-		wait3 = min(vtidx, swpidx)
-		wait = min(wait3, wait2)
-		if wait <= 0 {
-			wait = time.Millisecond * 500
-		}
-		return nil, wait
-	}
-}
-
-// Returns the number of MF ticks to use, or 0 to wait for next CD.
-func (spriest *ShadowPriest) BasicMindflayRotation(_ *core.Simulation, gcd time.Duration, _ time.Duration) int {
-	// just do MF3, never clipping
-	nextCD := core.NeverExpires
-	for _, v := range spriest.AllCDs {
-		if v < nextCD {
-			nextCD = v
-		}
-	}
-	// But don't start a MF if we can't get a single tick off.
-	if nextCD < gcd {
-		return 0
-	} else {
-		return 3
-	}
-}
-
-// Returns the number of MF ticks to use, or 0 to wait for next CD.
-func (spriest *ShadowPriest) IdealMindflayRotation(sim *core.Simulation, gcd time.Duration, tickLength time.Duration,
-	currentWait time.Duration, mfDamage, mbDamage, dpDamage, vtDamage, swdDamage, overwriteDPS float64) int {
-	nextCD := core.NeverExpires
-	nextIdx := -1
-
-	newCDs := []time.Duration{
-		max(0, spriest.AllCDs[1]),
-		max(0, spriest.AllCDs[2]),
-	}
-
-	if mbDamage != 0 {
-		newCDs = []time.Duration{
-			max(0, spriest.AllCDs[0]),
-			max(0, spriest.AllCDs[1]),
-			max(0, spriest.AllCDs[2]),
-		}
-	}
-
-	for i, v := range newCDs {
-		if v < nextCD {
-			nextCD = v
-			nextIdx = i
-		}
-	}
-
-	if currentWait != 0 {
-		nextCD = currentWait
-	}
-
-	var numTicks int
-	numTicks_Base := 0.0
-	numTicks_floored := 0.0
-	if nextCD < gcd/2 {
-		numTicks = 0
-	} else {
-		numTicks_Base = nextCD.Seconds() / tickLength.Seconds()
-		numTicks_floored = math.Floor(nextCD.Seconds() / tickLength.Seconds())
-		numTicks = int(numTicks_Base)
-	}
-
-	AlmostAnotherTick := numTicks_Base - numTicks_floored
-
-	if AlmostAnotherTick >= 0.25 {
-		numTicks += 1
-	}
-
-	mfTickDamage := mfDamage * 0.3333
-
-	if tickLength.Seconds() < gcd.Seconds()/2.9 {
-		numTicks = 3
-		return numTicks
-	}
-
-	if sim.Log != nil {
-		//spriest.Log(sim, "AlmostAnotherTick %d", AlmostAnotherTick)
-		//spriest.Log(sim, "numTicks %d", numTicks)
-		//spriest.Log(sim, "tickLength %d", tickLength.Seconds())
-		//spriest.Log(sim, "nextCD %d", nextCD.Seconds())
-		//spriest.Log(sim, "numTicks_Base %d", numTicks_Base)
-		//spriest.Log(sim, "numTicks_floored %d", numTicks_floored)
-	}
-
-	if numTicks < 100 && overwriteDPS == 0 { // if the code entered this loop because mf is the higest dps spell, and the number of ticks that can fit in the remaining cd time is < 1, then just cast a mf3 as it essentially fits perfectly
-		// TODO: Should spriest latency be added to the second option here?
-
-		mfTime := max(gcd, time.Duration(numTicks)*tickLength)
-		if numTicks == 0 {
-			mfTime = max(gcd, time.Duration(numTicks)*tickLength)
-		}
-
-		if sim.Log != nil {
-			//spriest.Log(sim, "mfTime %d", mfTime.Seconds())
-			//spriest.Log(sim, "spriest.AllCDs %d", spriest.AllCDs[0].Seconds())
-			//spriest.Log(sim, "mf3Time %d", float64(time.Duration(3*tickLength).Seconds()))
-		}
-		// Amount of gap time after casting mind flay, but before each CD is available.
-
-		cdDiffs := []time.Duration{
-			max(0, spriest.AllCDs[0]-mfTime),
-			max(0, spriest.AllCDs[1]-mfTime),
-			max(0, spriest.AllCDs[2]-mfTime),
-			max(0, spriest.AllCDs[3]-mfTime),
-			0,
-		}
-
-		mfspdmg := 0.0
-		if numTicks != 0 {
-			mfspdmg = mfTickDamage * float64(numTicks) / (time.Duration(numTicks) * tickLength).Seconds()
-		} else if numTicks > 3 {
-			mfspdmg = mfTickDamage * float64(3) / (time.Duration(3) * tickLength).Seconds()
-		}
-		if sim.Log != nil {
-			//spriest.Log(sim, "mfspdmg %d", mfspdmg)
-		}
-		spellDamages := []float64{
-			// MB dps
-			mbDamage / (gcd + cdDiffs[mbIdx]).Seconds(),
-			// DP dps
-			dpDamage / (gcd + cdDiffs[dpIdx]).Seconds(),
-			// VT dps
-			vtDamage / (gcd + cdDiffs[vtIdx]).Seconds(),
-			// SWD dps
-			swdDamage / (gcd + cdDiffs[swdIdx]).Seconds(),
-
-			mfspdmg,
-		}
-
-		bestIdx := 0
-		bestDmg := 0.0
-		for i, v := range spellDamages {
-			if sim.Log != nil {
-				//spriest.Log(sim, "\tspellDamages[%d]: %01.f", i, v)
-			}
-			if v > bestDmg {
-				bestIdx = i
-				bestDmg = v
-			}
-		}
-
-		//if numTicks < 1 && bestIdx == 4 {
-		//	numTicks = 3
-		//return numTicks
-		//}
-
-		if sim.Log != nil {
-			//spriest.Log(sim, "bestIdx %d", bestIdx)
-			//spriest.Log(sim, "nextIdx %d", nextIdx)
-			//spriest.Log(sim, "spellDamages[bestIdx]  %d", spellDamages[bestIdx])
-			//spriest.Log(sim, "spellDamages[nextIdx]  %d", spellDamages[nextIdx])
-			//spriest.Log(sim, "numTicks %d", numTicks)
-		}
-
-		if bestIdx != nextIdx && spellDamages[nextIdx] < spellDamages[bestIdx] && bestIdx != 4 {
-			numTicks_Base = spriest.AllCDs[bestIdx].Seconds() / tickLength.Seconds()
-			numTicks_floored = math.Floor(spriest.AllCDs[bestIdx].Seconds() / tickLength.Seconds())
-			numTicks = int(numTicks_Base)
-			if sim.Log != nil {
-				//spriest.Log(sim, "numTicks2 %d", numTicks)
-			}
-			AlmostAnotherTick := numTicks_Base - numTicks_floored
-
-			if AlmostAnotherTick >= 0.2 {
-				numTicks += 1
-			}
-
-			mfTime = max(gcd, time.Duration(numTicks)*tickLength)
-			if numTicks > 3 && numTicks < 5 {
-				addedgcd := max(gcd, time.Duration(2)*tickLength)
-				addedgcdtime := addedgcd - time.Duration(2)*tickLength
-				mfTime = max(gcd, time.Duration(numTicks)*tickLength+2*addedgcdtime)
-			}
-			deltaTime := spriest.AllCDs[bestIdx] - mfTime
-			cdDiffs = []time.Duration{
-				max(0, spriest.AllCDs[0]-mfTime),
-				max(0, spriest.AllCDs[1]-mfTime),
-				max(0, spriest.AllCDs[2]-mfTime),
-				max(0, spriest.AllCDs[3]-mfTime),
-				0,
-			}
-			if deltaTime.Seconds() < -1 {
-				numTicks -= 1
-				cdDiffs[bestIdx] += tickLength
-			}
-		}
-
-		if numTicks < 0 {
-			numTicks = 0
-		}
-
-		chosenWait := cdDiffs[bestIdx]
-
-		//if sim.Log != nil {
-		//spriest.Log(sim, "numTicks %d", numTicks)
-		//spriest.Log(sim, "mfTime %d", mfTime.Seconds())
-		//spriest.Log(sim, "chosenWait %d", chosenWait.Seconds())
-		//}
-
-		var newInd int
-		if chosenWait > gcd {
-			check_CDs := cdDiffs
-			check_CDs[bestIdx] = time.Second * 15
-			// need to find a way to sort the cdDiffs and find the next highest dps cast with lower wait time
-			for i, v := range check_CDs {
-				if v < nextCD {
-					//nextCDc = v
-					newInd = i
-				}
-			}
-		}
-		skipNext := 0
-		var totalWaitCurr float64
-		var numTicksAvail float64
-		var remainTime1 float64
-		var remainTime2 float64
-		var remainTime3 float64
-		var addTime1 float64
-		var addTime2 float64
-		var addTime3 float64
-
-		if chosenWait.Seconds() > gcd.Seconds() && bestIdx != newInd && newInd > -1 {
-
-			tick_var := float64(numTicks)
-			if numTicks == 1 {
-				totalWaitCurr = chosenWait.Seconds() - tick_var*gcd.Seconds()
-			} else {
-				totalWaitCurr = chosenWait.Seconds() - tick_var*tickLength.Seconds()
-			}
-
-			if totalWaitCurr-gcd.Seconds() <= gcd.Seconds() {
-				if totalWaitCurr > tickLength.Seconds() {
-					numTicksAvail = math.Floor((totalWaitCurr - gcd.Seconds()) / tickLength.Seconds())
-				} else {
-					numTicksAvail = math.Floor((totalWaitCurr - gcd.Seconds()) / gcd.Seconds())
-				}
-			} else {
-				numTicksAvail = math.Floor((totalWaitCurr - gcd.Seconds()) / tickLength.Seconds())
-			}
-
-			if numTicksAvail < 0 {
-				numTicksAvail = 0
-			}
-
-			// TODO looks fishy, remainTime1 and remainTime2 are equal
-			remainTime1 = totalWaitCurr - tickLength.Seconds()*numTicksAvail - gcd.Seconds()
-			remainTime2 = totalWaitCurr - 1*tickLength.Seconds()*numTicksAvail - gcd.Seconds()
-			remainTime3 = totalWaitCurr - 2*tickLength.Seconds()*numTicksAvail - gcd.Seconds()
-
-			if remainTime1 > 0 {
-				addTime1 = remainTime1
-			} else {
-				addTime1 = 0
-			}
-
-			if remainTime2 > 0 {
-				addTime2 = remainTime2
-			} else {
-				addTime2 = 0
-			}
-
-			if remainTime3 > 0 {
-				addTime3 = remainTime3
-			} else {
-				addTime3 = 0
-			}
-
-			dpsPossible0 := []float64{
-				0,
-				0,
-				0,
-			}
-
-			cd_dpsb := spellDamages[bestIdx]
-			cd_dpsn := spellDamages[newInd]
-
-			dpsPossible0[0] = (numTicksAvail*mfTickDamage + cd_dpsb*gcd.Seconds() + cd_dpsn*gcd.Seconds()) / (numTicksAvail*tickLength.Seconds() + 2*gcd.Seconds() + addTime1)
-			dpsPossible0[1] = (tick_var*mfTickDamage + cd_dpsb*(cdDiffs[bestIdx].Seconds()+gcd.Seconds()) + cd_dpsn*(cdDiffs[newInd].Seconds())) / (tick_var*tickLength.Seconds() + (cdDiffs[bestIdx].Seconds() + gcd.Seconds()) + (cdDiffs[newInd].Seconds() + addTime2))
-			dpsPossible0[2] = ((tick_var+1)*mfTickDamage + cd_dpsb*(cdDiffs[len(cdDiffs)-1-1].Seconds()+gcd.Seconds()) + cd_dpsn*(cdDiffs[len(cdDiffs)-1].Seconds()-tickLength.Seconds())) / ((tick_var+1)*tickLength.Seconds() + (cdDiffs[bestIdx].Seconds() + gcd.Seconds()) + (cdDiffs[newInd].Seconds() + addTime3))
-
-			highestPossibleDmg := 0.0
-			highestPossibleIdx := -1
-			// TODO looks fishy, this branch is never taken
-			if highestPossibleIdx == 0 {
-				for i, v := range dpsPossible0 {
-
-					if v >= highestPossibleDmg {
-						highestPossibleIdx = i
-						highestPossibleDmg = v
-					}
-				}
-			}
-			if highestPossibleIdx > 0 {
-				numTicks = highestPossibleIdx + 1
-			} else {
-				numTicks = int(numTicksAvail)
-				skipNext = 1
-			}
-		}
-
-		if sim.Log != nil {
-			//spriest.Log(sim, "numTicks3 %d", numTicks)
-		}
-
-		if numTicks > 3 {
-			if (spriest.AllCDs[bestIdx] - time.Duration(numTicks-1)*tickLength - gcd) >= 0 {
-				//if (spriest.AllCDs[3]-time.Duration(numTicks-1)*tickLength <= 0) || (spriest.AllCDs[0]-time.Duration(numTicks-1)*tickLength <= 0) { \\might need to readd this for later phases
-				if spriest.AllCDs[3]-time.Duration(numTicks-1)*tickLength <= 0 {
-					numTicks = 3
-					return numTicks
-				}
-			}
-		}
-
-		if skipNext == 0 {
-			finalMFStart := math.Mod(float64(numTicks), 3) // Base ticks before adding additional
-			dpsPossible := []float64{
-				bestDmg, // dps with no tick and just wait
-				0,
-				0,
-				0,
-			}
-			dpsDuration := (chosenWait + gcd).Seconds()
-
-			highestPossibleIdx := 0
-			// TODO: Modified this slightly to expand time window, but it still doesn't change dps for any tests.
-			// Probably can remove this entirely (and then also the if highestPossibleIdx == 0 right after)
-			if (finalMFStart == 2) && (chosenWait <= tickLength && chosenWait > (tickLength-time.Millisecond*15)) {
-				highestPossibleIdx = 1 // if the wait time is equal to an extra mf tick, and there are already 2 ticks, then just add 1
-			}
-
-			if highestPossibleIdx == 0 {
-				switch finalMFStart {
-				case 0:
-					// this means that the extra ticks will be relative to starting a new mf cast entirely
-					dpsPossible[1] = (bestDmg*dpsDuration + mfDamage*1/3) / (gcd.Seconds() + gcd.Seconds())          // new damage for 1 extra tick
-					dpsPossible[2] = (bestDmg*dpsDuration + mfDamage*2/3) / (2*tickLength.Seconds() + gcd.Seconds()) // new damage for 2 extra tick
-					dpsPossible[3] = (bestDmg*dpsDuration + mfDamage) / (3*tickLength.Seconds() + gcd.Seconds())     // new damage for 3 extra tick
-
-				case 1:
-					total_check_time := 2 * tickLength
-
-					if total_check_time < gcd {
-						newDuration := (gcd + gcd).Seconds()
-						dpsPossible[1] = (bestDmg*dpsDuration + (mfDamage * 1 / 3 * (finalMFStart + 1))) / newDuration
-					} else {
-						newDuration := ((total_check_time - gcd) + gcd).Seconds()
-						dpsPossible[1] = (bestDmg*dpsDuration + (mfDamage * 1 / 3 * (finalMFStart + 1))) / newDuration
-					}
-					// % check add 2
-					total_check_time2 := 2 * tickLength.Seconds()
-					if total_check_time2 < gcd.Seconds() {
-						dpsPossible[2] = (bestDmg*dpsDuration + (mfDamage * 1 / 3 * (finalMFStart + 2))) / (gcd.Seconds() + gcd.Seconds())
-					} else {
-						dpsPossible[2] = (bestDmg*dpsDuration + (mfDamage * 1 / 3 * (finalMFStart + 2))) / (total_check_time2 + gcd.Seconds())
-					}
-				case 2:
-					// % check add 1
-					total_check_time := tickLength
-					newDuration := (total_check_time + gcd).Seconds()
-					dpsPossible[1] = (bestDmg*dpsDuration + mfDamage*1/3) / newDuration
-
-				default:
-					dpsPossible[1] = (bestDmg*dpsDuration + mfDamage*1/3) / (gcd.Seconds() + gcd.Seconds())
-					if tickLength*2 > gcd {
-						dpsPossible[2] = (bestDmg*dpsDuration + mfDamage*2/3) / (2*tickLength.Seconds() + gcd.Seconds())
-					} else {
-						dpsPossible[2] = (bestDmg*dpsDuration + mfDamage*2/3) / (gcd.Seconds() + gcd.Seconds())
-					}
-					dpsPossible[3] = (bestDmg*dpsDuration + mfDamage) / (3*tickLength.Seconds() + gcd.Seconds())
-				}
-			}
-
-			// Find the highest possible dps and its index
-			// highestPossibleIdx := 0
-			highestPossibleDmg := 0.0
-			if highestPossibleIdx == 0 {
-				for i, v := range dpsPossible {
-					if sim.Log != nil {
-						//spriest.Log(sim, "\tdpsPossible[%d]: %01.f", i, v)
-					}
-					if v >= highestPossibleDmg {
-						highestPossibleIdx = i
-						highestPossibleDmg = v
-					}
-				}
-			}
-
-			numTicks += highestPossibleIdx
-			if sim.Log != nil {
-				//spriest.Log(sim, "numTicks4 %d", numTicks)
-			}
-			// if sim.Log != nil {
-			// 	spriest.Log(sim, "final_ticks %d", numTicks)
-			// }
-			if numTicks == 1 && tickLength*3 <= time.Duration(float64(gcd)*1.05) {
-				numTicks += 2
-			}
-			if numTicks == 1 && tickLength*2 <= gcd {
-				numTicks += 1
-			}
-			//  Now that the number of optimal ticks has been determined to optimize dps
-			//  Now optimize mf2s and mf3s
-
-			//if numTicks == 0 {
-			//return numTicks
-			//}
-
-			if numTicks == 1 {
-				numTicks = 1
-			} else if numTicks == 2 || numTicks == 4 {
-				if numTicks == 4 {
-					spriest.PrevTicks = 4
-				}
-
-				if numTicks == 4 {
-					if sim.Log != nil {
-						spriest.Log(sim, "numTicks[%d]", numTicks)
-						spriest.Log(sim, "bestIdx %d", bestIdx)
-						spriest.Log(sim, "nextCD %d", nextCD.Seconds())
-						spriest.Log(sim, "tickLength %d", tickLength.Seconds())
-						spriest.Log(sim, "chosenWait %d", chosenWait.Seconds())
-					}
-				}
-
-				numTicks = 2
-
-			} else if numTicks == 0 {
-				numTicks = 2
-			} else {
-				numTicks = 3
-			}
-		}
-	} else {
-		numTicks = int(nextCD / tickLength)
-		if nextCD-max(gcd, time.Duration(2)*tickLength) < 0 && numTicks != 0 {
-			numTicks -= 1
-		}
-		// if sim.Log != nil {
-		// 	spriest.Log(sim, "c_ticks %d", numTicks)
-		// 	spriest.Log(sim, "nextCD %d", nextCD)
-		// 	spriest.Log(sim, "tickLength %d", tickLength)
-		// }
-		if numTicks == 0 {
-			// if sim.Log != nil {
-			//   spriest.Log(sim, "zero ticks %d", numTicks)
-			// }
-			numTicks = 2
-		}
-		if numTicks >= 3 {
-			numTicks = 3
-		}
-	}
-	if sim.Log != nil {
-		//spriest.Log(sim, "c_ticks %d", numTicks)
-		//spriest.Log(sim, "nextCD %d", nextCD)
-		//spriest.Log(sim, "tickLength %d", tickLength)
-	}
-	if numTicks >= 3 {
-		numTicks = 3
-	}
-
-	return numTicks
-}
-
-func (spriest *ShadowPriest) ClippingMindflayRotation(_ *core.Simulation, gcd time.Duration, tickLength time.Duration) int {
-	nextCD := core.NeverExpires
-	for _, v := range spriest.AllCDs[1 : len(spriest.AllCDs)-1] {
-		if v < nextCD {
-			nextCD = v
-		}
-	}
-
-	// if sim.Log != nil {
-	// 	spriest.Log(sim, "<spriest> NextCD: %0.2f", nextCD.Seconds())
-	// }
-
-	// This means a CD is coming up before we could cast a single MF
-	if nextCD < gcd {
-		return 0
-	}
-
-	// How many ticks we have time for.
-	numTicks := int((nextCD - time.Duration(spriest.Latency)) / tickLength)
-	if numTicks == 1 {
-		return 1
-	} else if numTicks == 2 || numTicks == 4 {
-		return 2
-	} else {
-		return 3
-	}
-}
diff --git a/sim/priest/shadow/shadow_priest.go b/sim/priest/shadow/shadow_priest.go
index 0562a657f1..36a2ae9c6e 100644
--- a/sim/priest/shadow/shadow_priest.go
+++ b/sim/priest/shadow/shadow_priest.go
@@ -1,8 +1,6 @@
 package shadow
 
 import (
-	"time"
-
 	"github.com/wowsims/wotlk/sim/common/wotlk"
 	"github.com/wowsims/wotlk/sim/core"
 	"github.com/wowsims/wotlk/sim/core/proto"
@@ -37,9 +35,8 @@ func NewShadowPriest(character *core.Character, options *proto.Player) *ShadowPr
 	basePriest := priest.New(character, selfBuffs, options.TalentsString)
 	basePriest.Latency = float64(basePriest.ChannelClipDelay.Milliseconds())
 	spriest := &ShadowPriest{
-		Priest:   basePriest,
-		rotation: shadowOptions.Rotation,
-		options:  shadowOptions.Options,
+		Priest:  basePriest,
+		options: shadowOptions.Options,
 	}
 
 	spriest.SelfBuffs.PowerInfusionTarget = &proto.UnitReference{}
@@ -47,25 +44,14 @@ func NewShadowPriest(character *core.Character, options *proto.Player) *ShadowPr
 		spriest.SelfBuffs.PowerInfusionTarget = shadowOptions.Options.PowerInfusionTarget
 	}
 
-	spriest.EnableResumeAfterManaWait(spriest.tryUseGCD)
-	spriest.CanRolloverSWP = spriest.Talents.MindFlay && spriest.Talents.PainAndSuffering > 0
 	wotlk.ConstructValkyrPets(&spriest.Character)
 
 	return spriest
 }
 
 type ShadowPriest struct {
-	PrevTicks float64
-
 	*priest.Priest
-	rotation *proto.ShadowPriest_Rotation
-	options  *proto.ShadowPriest_Options
-
-	VTCastTime time.Duration
-	AllCDs     []time.Duration
-	BLUsedAt   time.Duration
-
-	CanRolloverSWP bool
+	options *proto.ShadowPriest_Options
 }
 
 func (spriest *ShadowPriest) GetPriest() *priest.Priest {
@@ -74,33 +60,8 @@ func (spriest *ShadowPriest) GetPriest() *priest.Priest {
 
 func (spriest *ShadowPriest) Initialize() {
 	spriest.Priest.Initialize()
-
-	if !spriest.IsUsingAPL && spriest.rotation.PrecastType > 0 {
-		precastSpell := spriest.VampiricTouch
-		if spriest.rotation.PrecastType == 2 {
-			precastSpell = spriest.MindBlast
-		}
-
-		// Do this post-finalize so cast speed is updated with new stats
-		spriest.Env.RegisterPostFinalizeEffect(func() {
-			precastSpellAt := -spriest.ApplyCastSpeedForSpell(precastSpell.DefaultCast.CastTime, precastSpell)
-
-			spriest.RegisterPrepullAction(precastSpellAt, func(sim *core.Simulation) {
-				precastSpell.Cast(sim, spriest.CurrentTarget)
-			})
-		})
-	}
 }
 
 func (spriest *ShadowPriest) Reset(sim *core.Simulation) {
 	spriest.Priest.Reset(sim)
-
-	// Save info related to blood lust timing
-	spriest.BLUsedAt = 0
-	if bloodlustMCD := spriest.GetMajorCooldownIgnoreTag(core.BloodlustActionID); bloodlustMCD != nil {
-		timings := bloodlustMCD.GetTimings()
-		if len(timings) > 0 {
-			spriest.BLUsedAt = timings[0]
-		}
-	}
 }
diff --git a/sim/priest/shadowfiend.go b/sim/priest/shadowfiend.go
index be5fea8825..dcbb95f5a7 100644
--- a/sim/priest/shadowfiend.go
+++ b/sim/priest/shadowfiend.go
@@ -41,12 +41,4 @@ func (priest *Priest) registerShadowfiendSpell() {
 			priest.ShadowfiendAura.Activate(sim)
 		},
 	})
-
-	if !priest.IsUsingAPL {
-		priest.AddMajorCooldown(core.MajorCooldown{
-			Spell:    priest.Shadowfiend,
-			Priority: 1,
-			Type:     core.CooldownTypeMana,
-		})
-	}
 }
diff --git a/sim/priest/smite.go b/sim/priest/smite.go
index 0cc95b84ca..ac9d8264e0 100644
--- a/sim/priest/smite.go
+++ b/sim/priest/smite.go
@@ -6,7 +6,7 @@ import (
 	"github.com/wowsims/wotlk/sim/core"
 )
 
-func (priest *Priest) RegisterSmiteSpell(memeDream bool) {
+func (priest *Priest) RegisterSmiteSpell() {
 	priest.Smite = priest.RegisterSpell(core.SpellConfig{
 		ActionID:    core.ActionID{SpellID: 48123},
 		SpellSchool: core.SpellSchoolHoly,
@@ -23,10 +23,8 @@ func (priest *Priest) RegisterSmiteSpell(memeDream bool) {
 			},
 		},
 
-		BonusCritRating: float64(priest.Talents.HolySpecialization) * 1 * core.CritRatingPerCritChance,
-		DamageMultiplier: 1 *
-			(1 + 0.05*float64(priest.Talents.SearingLight)) *
-			core.TernaryFloat64(memeDream, 1.2, 1),
+		BonusCritRating:  float64(priest.Talents.HolySpecialization) * 1 * core.CritRatingPerCritChance,
+		DamageMultiplier: 1 + 0.05*float64(priest.Talents.SearingLight),
 		CritMultiplier:   priest.DefaultSpellCritMultiplier(),
 		ThreatMultiplier: 1 - []float64{0, .07, .14, .20}[priest.Talents.SilentResolve],
 
diff --git a/sim/priest/smite/TestSmite.results b/sim/priest/smite/TestSmite.results
index c37dd30657..110aae8059 100644
--- a/sim/priest/smite/TestSmite.results
+++ b/sim/priest/smite/TestSmite.results
@@ -46,739 +46,739 @@ character_stats_results: {
 dps_results: {
  key: "TestSmite-AllItems-AbsolutionRegalia"
  value: {
-  dps: 1995.65978
-  tps: 1685.19061
+  dps: 1995.47614
+  tps: 1685.05564
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 2894.04696
-  tps: 2431.07532
+  dps: 2893.83018
+  tps: 2430.91688
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 2907.685
-  tps: 2442.39619
+  dps: 2907.46733
+  tps: 2442.2371
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 2844.67304
-  tps: 2388.45874
+  dps: 2844.46107
+  tps: 2388.30346
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 2784.21341
-  tps: 2340.78415
+  dps: 2783.94305
+  tps: 2340.58703
   hps: 90.33849
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 2784.21341
-  tps: 2340.78415
+  dps: 2783.94305
+  tps: 2340.58703
   hps: 90.33849
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 2896.6066
-  tps: 2430.4724
+  dps: 2896.39463
+  tps: 2430.31712
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 2214.0364
-  tps: 1868.07834
+  dps: 2213.89453
+  tps: 1867.97361
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 2859.94481
-  tps: 2353.70915
+  dps: 2859.73182
+  tps: 2353.55626
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 2874.02992
-  tps: 2411.10571
+  dps: 2873.81795
+  tps: 2410.95042
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
   hps: 64
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-CrimsonAcolyte'sRaiment"
  value: {
-  dps: 3656.43315
-  tps: 3094.38378
+  dps: 3656.275
+  tps: 3094.26738
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-CrimsonAcolyte'sRegalia"
  value: {
-  dps: 3403.97761
-  tps: 2858.95096
+  dps: 3403.63853
+  tps: 2858.7041
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 2809.61022
-  tps: 2358.63454
+  dps: 2809.40067
+  tps: 2358.48127
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 2856.17338
-  tps: 2406.6212
+  dps: 2855.92826
+  tps: 2406.4309
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 2988.88506
-  tps: 2512.16435
+  dps: 2988.6755
+  tps: 2512.01108
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Death'sChoice-47464"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 2792.70508
-  tps: 2346.01678
+  dps: 2792.49553
+  tps: 2345.86351
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Defender'sCode-40257"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 2848.80173
-  tps: 2390.98808
+  dps: 2848.58975
+  tps: 2390.83279
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-DislodgedForeignObject-50348"
  value: {
-  dps: 2892.00829
-  tps: 2422.7308
+  dps: 2891.88465
+  tps: 2422.64278
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 2878.27734
-  tps: 2413.69279
+  dps: 2878.07976
+  tps: 2413.54911
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 2844.67304
-  tps: 2388.45874
+  dps: 2844.46107
+  tps: 2388.30346
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 2901.60191
-  tps: 2436.99942
+  dps: 2901.38893
+  tps: 2436.84342
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 2846.44341
-  tps: 2389.0365
+  dps: 2846.23143
+  tps: 2388.88121
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 2846.03224
-  tps: 2389.54878
+  dps: 2845.82027
+  tps: 2389.3935
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 2775.36116
-  tps: 2330.40091
+  dps: 2775.20299
+  tps: 2330.28527
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 2844.67304
-  tps: 2388.45874
+  dps: 2844.46107
+  tps: 2388.30346
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 2847.15813
-  tps: 2398.41544
+  dps: 2846.94857
+  tps: 2398.26217
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 2886.63813
-  tps: 2423.12085
+  dps: 2886.44236
+  tps: 2422.97869
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 2927.69669
-  tps: 2463.26545
+  dps: 2927.48714
+  tps: 2463.11218
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 2851.89303
-  tps: 2396.08356
+  dps: 2851.67899
+  tps: 2395.92708
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ForgeEmber-37660"
  value: {
-  dps: 2851.36407
-  tps: 2394.13159
+  dps: 2851.15451
+  tps: 2393.97832
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 2859.94481
-  tps: 2401.12839
+  dps: 2859.73182
+  tps: 2400.97239
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 2856.89045
-  tps: 2398.59446
+  dps: 2856.67767
+  tps: 2398.4386
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-FuturesightRune-38763"
  value: {
-  dps: 2865.78098
-  tps: 2408.01177
+  dps: 2865.56665
+  tps: 2407.85508
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-GarbofFaith"
  value: {
-  dps: 2622.90525
-  tps: 2209.44725
+  dps: 2622.70384
+  tps: 2209.29954
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Gladiator'sInvestiture"
  value: {
-  dps: 3097.07074
-  tps: 2621.61262
+  dps: 3096.91139
+  tps: 2621.49536
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Gladiator'sRaiment"
  value: {
-  dps: 2778.12313
-  tps: 2340.70258
+  dps: 2777.84271
+  tps: 2340.49807
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 2900.86598
-  tps: 2436.73575
+  dps: 2900.64875
+  tps: 2436.57699
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 2916.36375
-  tps: 2449.60037
+  dps: 2916.14552
+  tps: 2449.44089
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 2850.05052
-  tps: 2398.02263
+  dps: 2850.13779
+  tps: 2398.08766
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Heartpierce-49982"
  value: {
-  dps: 2901.60191
-  tps: 2436.99942
+  dps: 2901.38893
+  tps: 2436.84342
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Heartpierce-50641"
  value: {
-  dps: 2901.60191
-  tps: 2436.99942
+  dps: 2901.38893
+  tps: 2436.84342
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 2846.44341
-  tps: 2389.0365
+  dps: 2846.23143
+  tps: 2388.88121
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 2846.03224
-  tps: 2389.54878
+  dps: 2845.82027
+  tps: 2389.3935
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-IncisorFragment-37723"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 2985.93267
-  tps: 2511.63616
+  dps: 2985.7207
+  tps: 2511.48088
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 2844.67304
-  tps: 2388.45874
+  dps: 2844.46107
+  tps: 2388.30346
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-LastWord-50179"
  value: {
-  dps: 2901.60191
-  tps: 2436.99942
+  dps: 2901.38893
+  tps: 2436.84342
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-LastWord-50708"
  value: {
-  dps: 2901.60191
-  tps: 2436.99942
+  dps: 2901.38893
+  tps: 2436.84342
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 2955.91562
-  tps: 2482.50483
+  dps: 2955.70512
+  tps: 2482.3508
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 2799.28132
-  tps: 2351.15048
+  dps: 2799.07176
+  tps: 2350.99721
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 2863.61187
-  tps: 2405.67062
+  dps: 2863.27632
+  tps: 2405.42676
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Nibelung-49992"
  value: {
-  dps: 2901.60191
-  tps: 2436.99942
+  dps: 2901.38893
+  tps: 2436.84342
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Nibelung-50648"
  value: {
-  dps: 2901.60191
-  tps: 2436.99942
+  dps: 2901.38893
+  tps: 2436.84342
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 2844.67304
-  tps: 2388.45874
+  dps: 2844.46107
+  tps: 2388.30346
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 2844.67304
-  tps: 2388.45874
+  dps: 2844.46107
+  tps: 2388.30346
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 2844.67304
-  tps: 2388.45874
+  dps: 2844.46107
+  tps: 2388.30346
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 2844.67304
-  tps: 2388.45874
+  dps: 2844.46107
+  tps: 2388.30346
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-RegaliaofFaith"
  value: {
-  dps: 2639.07517
-  tps: 2233.62531
+  dps: 2638.93021
+  tps: 2233.51836
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 2965.6255
-  tps: 2506.39096
+  dps: 2965.40989
+  tps: 2506.23335
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 2988.30259
-  tps: 2527.17565
+  dps: 2988.08626
+  tps: 2527.01752
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 2872.02031
-  tps: 2410.33656
+  dps: 2871.80834
+  tps: 2410.18128
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 2869.30107
-  tps: 2408.39573
+  dps: 2869.0891
+  tps: 2408.24045
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SanctificationGarb"
  value: {
-  dps: 2766.23464
-  tps: 2331.88086
+  dps: 2766.03738
+  tps: 2331.73868
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SanctificationRegalia"
  value: {
-  dps: 2799.71504
-  tps: 2362.65649
+  dps: 2799.56623
+  tps: 2362.54679
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 3004.25046
-  tps: 2532.71111
+  dps: 3004.03453
+  tps: 2532.55327
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 3024.45542
-  tps: 2551.52189
+  dps: 3024.23868
+  tps: 2551.36347
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SoulPreserver-37111"
  value: {
-  dps: 2829.57624
-  tps: 2377.55851
+  dps: 2829.36365
+  tps: 2377.40307
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SouloftheDead-40382"
  value: {
-  dps: 2942.10685
-  tps: 2478.39753
+  dps: 2941.89729
+  tps: 2478.24426
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SparkofLife-37657"
  value: {
-  dps: 2879.02312
-  tps: 2419.68116
+  dps: 2878.81356
+  tps: 2419.52789
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 2809.43043
-  tps: 2361.09267
+  dps: 2809.22087
+  tps: 2360.9394
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 2844.67304
-  tps: 2388.45874
+  dps: 2844.46107
+  tps: 2388.30346
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 2844.67304
-  tps: 2388.45874
+  dps: 2844.46107
+  tps: 2388.30346
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 2844.67304
-  tps: 2388.45874
+  dps: 2844.46107
+  tps: 2388.30346
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 2857.32763
-  tps: 2399.73234
+  dps: 2857.11717
+  tps: 2399.57842
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 2918.1954
-  tps: 2453.9325
+  dps: 2917.98584
+  tps: 2453.77923
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 2783.08293
-  tps: 2338.96466
+  dps: 2782.87337
+  tps: 2338.81139
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 2844.67304
-  tps: 2388.45874
+  dps: 2844.46107
+  tps: 2388.30346
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 2809.43043
-  tps: 2361.09267
+  dps: 2809.22087
+  tps: 2360.9394
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 2809.43043
-  tps: 2361.09267
+  dps: 2809.22087
+  tps: 2360.9394
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 2859.94481
-  tps: 2401.12839
+  dps: 2859.73182
+  tps: 2400.97239
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 2856.89045
-  tps: 2398.59446
+  dps: 2856.67767
+  tps: 2398.4386
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 2822.59725
-  tps: 2369.91019
+  dps: 2822.4374
+  tps: 2369.79338
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 2856.89045
-  tps: 2398.59446
+  dps: 2856.67767
+  tps: 2398.4386
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 2859.94481
-  tps: 2401.12839
+  dps: 2859.73182
+  tps: 2400.97239
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Val'anyr,HammerofAncientKings-46017"
  value: {
-  dps: 2971.98183
-  tps: 2487.99516
+  dps: 2971.76409
+  tps: 2487.83574
  }
 }
 dps_results: {
@@ -791,50 +791,50 @@ dps_results: {
 dps_results: {
  key: "TestSmite-AllItems-WingedTalisman-37844"
  value: {
-  dps: 2835.27316
-  tps: 2382.3622
+  dps: 2835.04964
+  tps: 2382.19892
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Zabra'sRaiment"
  value: {
-  dps: 2815.72712
-  tps: 2378.09865
+  dps: 2815.57936
+  tps: 2377.9897
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Zabra'sRegalia"
  value: {
-  dps: 2931.27671
-  tps: 2466.27309
+  dps: 2931.07129
+  tps: 2466.1225
  }
 }
 dps_results: {
  key: "TestSmite-Average-Default"
  value: {
-  dps: 2892.01638
-  tps: 2428.52981
+  dps: 2892.02467
+  tps: 2428.5357
  }
 }
 dps_results: {
  key: "TestSmite-Settings-Undead-p1-Basic-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 2901.60191
-  tps: 3015.73285
+  dps: 2901.38893
+  tps: 3015.57684
  }
 }
 dps_results: {
  key: "TestSmite-Settings-Undead-p1-Basic-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 2901.60191
-  tps: 2436.99942
+  dps: 2901.38893
+  tps: 2436.84342
  }
 }
 dps_results: {
  key: "TestSmite-Settings-Undead-p1-Basic-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 4836.07031
-  tps: 4107.85388
+  dps: 4835.0054
+  tps: 4107.07385
  }
 }
 dps_results: {
@@ -855,13 +855,13 @@ dps_results: {
  key: "TestSmite-Settings-Undead-p1-Basic-default-NoBuffs-ShortSingleTarget"
  value: {
   dps: 2520.9093
-  tps: 2134.74022
+  tps: 2126.72097
  }
 }
 dps_results: {
  key: "TestSmite-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 2901.60191
-  tps: 2436.99942
+  dps: 2901.38893
+  tps: 2436.84342
  }
 }
diff --git a/sim/priest/smite/rotation.go b/sim/priest/smite/rotation.go
deleted file mode 100644
index b37fcca221..0000000000
--- a/sim/priest/smite/rotation.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package smite
-
-import (
-	"github.com/wowsims/wotlk/sim/core"
-)
-
-func (spriest *SmitePriest) OnGCDReady(sim *core.Simulation) {
-	spriest.tryUseGCD(sim)
-}
-
-func (spriest *SmitePriest) tryUseGCD(sim *core.Simulation) {
-	var spell *core.Spell
-
-	if spriest.rotation.MemeDream {
-		spell = spriest.chooseSpellMemeDream(sim)
-	} else {
-		spell = spriest.chooseSpell(sim)
-	}
-
-	if spell == nil {
-		// nil means wait for HF.
-		spriest.WaitUntil(sim, spriest.HolyFire.ReadyAt())
-		return
-	}
-
-	if success := spell.Cast(sim, spriest.CurrentTarget); !success {
-		spriest.WaitForMana(sim, spell.CurCast.Cost)
-	}
-}
-
-func (spriest *SmitePriest) chooseSpell(sim *core.Simulation) *core.Spell {
-	if spriest.holyFireDotWillBeUp(sim) {
-		if spriest.InnerFocus != nil && spriest.InnerFocus.IsReady(sim) {
-			spriest.InnerFocus.Cast(sim, nil)
-		}
-
-		// Make sure we spam smite while dot is active.
-		return spriest.Smite
-	} else if spriest.rotation.UseDevouringPlague && !spriest.DevouringPlague.CurDot().IsActive() {
-		return spriest.DevouringPlague
-	} else if !spriest.ShadowWordPain.CurDot().IsActive() {
-		return spriest.ShadowWordPain
-	} else if spriest.HolyFire.IsReady(sim) {
-		return spriest.HolyFire
-	} else if spriest.HolyFire.TimeToReady(sim) <= spriest.allowedHFDelay {
-		return nil
-	} else if spriest.ImprovedSpiritTap.IsActive() {
-		return spriest.Smite
-	} else if spriest.Penance != nil && spriest.Penance.IsReady(sim) {
-		return spriest.Penance
-	} else if spriest.rotation.UseShadowWordDeath && spriest.ShadowWordDeath.IsReady(sim) {
-		return spriest.ShadowWordDeath
-	} else if spriest.rotation.UseMindBlast && spriest.MindBlast.IsReady(sim) {
-		return spriest.MindBlast
-	} else if spriest.Talents.MindFlay {
-		mfTickLength := spriest.MindFlayTickDuration()
-		hfTimeToReady := spriest.HolyFire.TimeToReady(sim)
-		numTicks := min(3, int(hfTimeToReady/mfTickLength+1))
-		return spriest.MindFlay[numTicks]
-	} else {
-		return spriest.Smite
-	}
-}
-
-func (spriest *SmitePriest) chooseSpellMemeDream(sim *core.Simulation) *core.Spell {
-	if spriest.rotation.UseDevouringPlague && !spriest.DevouringPlague.CurDot().IsActive() {
-		return spriest.DevouringPlague
-	} else if !spriest.ShadowWordPain.CurDot().IsActive() {
-		return spriest.ShadowWordPain
-	} else if spriest.HolyFire.IsReady(sim) {
-		return spriest.HolyFire
-	} else if spriest.HolyFire.TimeToReady(sim) <= spriest.allowedHFDelay {
-		return nil
-	} else {
-		if spriest.InnerFocus != nil && spriest.InnerFocus.IsReady(sim) {
-			spriest.InnerFocus.Cast(sim, nil)
-		}
-
-		return spriest.Smite
-	}
-}
-
-// Returns whether a Smite cast starting now would complete while Holy Fire is active.
-func (spriest *SmitePriest) holyFireDotWillBeUp(sim *core.Simulation) bool {
-	if !spriest.HolyFire.CurDot().IsActive() {
-		return false
-	}
-
-	smiteCastTime := spriest.ApplyCastSpeedForSpell(spriest.Smite.DefaultCast.CastTime, spriest.Smite)
-	return smiteCastTime <= spriest.HolyFire.CurDot().RemainingDuration(sim)
-}
diff --git a/sim/priest/smite/smite_priest.go b/sim/priest/smite/smite_priest.go
index ea967c83bb..1c11d6c834 100644
--- a/sim/priest/smite/smite_priest.go
+++ b/sim/priest/smite/smite_priest.go
@@ -1,8 +1,6 @@
 package smite
 
 import (
-	"time"
-
 	"github.com/wowsims/wotlk/sim/core"
 	"github.com/wowsims/wotlk/sim/core/proto"
 	"github.com/wowsims/wotlk/sim/priest"
@@ -35,10 +33,7 @@ func NewSmitePriest(character *core.Character, options *proto.Player) *SmitePrie
 
 	basePriest := priest.New(character, selfBuffs, options.TalentsString)
 	spriest := &SmitePriest{
-		Priest:   basePriest,
-		rotation: smiteOptions.Rotation,
-
-		allowedHFDelay: time.Millisecond * time.Duration(smiteOptions.Rotation.AllowedHolyFireDelayMs),
+		Priest: basePriest,
 	}
 
 	spriest.SelfBuffs.PowerInfusionTarget = &proto.UnitReference{}
@@ -46,17 +41,11 @@ func NewSmitePriest(character *core.Character, options *proto.Player) *SmitePrie
 		spriest.SelfBuffs.PowerInfusionTarget = smiteOptions.Options.PowerInfusionTarget
 	}
 
-	spriest.EnableResumeAfterManaWait(spriest.tryUseGCD)
-
 	return spriest
 }
 
 type SmitePriest struct {
 	*priest.Priest
-
-	rotation *proto.SmitePriest_Rotation
-
-	allowedHFDelay time.Duration
 }
 
 func (spriest *SmitePriest) GetPriest() *priest.Priest {
@@ -66,8 +55,8 @@ func (spriest *SmitePriest) GetPriest() *priest.Priest {
 func (spriest *SmitePriest) Initialize() {
 	spriest.Priest.Initialize()
 
-	spriest.RegisterHolyFireSpell(spriest.rotation.MemeDream)
-	spriest.RegisterSmiteSpell(spriest.rotation.MemeDream)
+	spriest.RegisterHolyFireSpell()
+	spriest.RegisterSmiteSpell()
 	spriest.RegisterPenanceSpell()
 	spriest.RegisterHymnOfHopeCD()
 }
diff --git a/sim/priest/talents.go b/sim/priest/talents.go
index 3b4cae890b..3a4aeee251 100644
--- a/sim/priest/talents.go
+++ b/sim/priest/talents.go
@@ -397,9 +397,7 @@ func (priest *Priest) applyMisery() {
 			priest.VampiricTouch.RelatedAuras = append(priest.VampiricTouch.RelatedAuras, miseryAuras)
 		}
 		if priest.MindFlay[1] != nil {
-			if priest.IsUsingAPL {
-				priest.MindFlayAPL.RelatedAuras = append(priest.MindFlayAPL.RelatedAuras, miseryAuras)
-			}
+			priest.MindFlayAPL.RelatedAuras = append(priest.MindFlayAPL.RelatedAuras, miseryAuras)
 			priest.MindFlay[1].RelatedAuras = append(priest.MindFlay[1].RelatedAuras, miseryAuras)
 			priest.MindFlay[2].RelatedAuras = append(priest.MindFlay[2].RelatedAuras, miseryAuras)
 			priest.MindFlay[3].RelatedAuras = append(priest.MindFlay[3].RelatedAuras, miseryAuras)

From 37e1b78c1ec31c461dabfc2dd339bc00169ab00b Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Thu, 4 Jan 2024 21:19:12 -0800
Subject: [PATCH 09/28] Commit before fixing rogue tests

---
 sim/rogue/feint.go                  |  13 -
 sim/rogue/rogue.go                  |  53 +---
 sim/rogue/rogue_test.go             |  36 +--
 sim/rogue/rotation.go               |  77 -----
 sim/rogue/rotation_assassination.go | 374 -----------------------
 sim/rogue/rotation_combat.go        | 454 ----------------------------
 sim/rogue/rotation_generic.go       | 430 --------------------------
 sim/rogue/rotation_multi.go         | 405 -------------------------
 sim/rogue/rotation_subtlety.go      | 340 ---------------------
 sim/rogue/talents.go                |  12 -
 sim/rogue/tricks_of_the_trade.go    |  16 -
 sim/rogue/vanish.go                 |  30 +-
 12 files changed, 18 insertions(+), 2222 deletions(-)
 delete mode 100644 sim/rogue/rotation.go
 delete mode 100644 sim/rogue/rotation_assassination.go
 delete mode 100644 sim/rogue/rotation_combat.go
 delete mode 100644 sim/rogue/rotation_generic.go
 delete mode 100644 sim/rogue/rotation_multi.go
 delete mode 100644 sim/rogue/rotation_subtlety.go

diff --git a/sim/rogue/feint.go b/sim/rogue/feint.go
index 459902376c..363328ebb3 100644
--- a/sim/rogue/feint.go
+++ b/sim/rogue/feint.go
@@ -36,17 +36,4 @@ func (rogue *Rogue) registerFeintSpell() {
 			spell.CalcAndDealOutcome(sim, target, spell.OutcomeMeleeSpecialHit)
 		},
 	})
-	// Feint
-	if rogue.Rotation.UseFeint {
-		rogue.AddMajorCooldown(core.MajorCooldown{
-			Spell:    rogue.Feint,
-			Priority: core.CooldownPriorityDefault,
-			Type:     core.CooldownTypeDPS,
-			//don't feint if you're gonna waste energy by using the gcd
-			ShouldActivate: func(sim *core.Simulation, character *core.Character) bool {
-				thresh := 55.0 //55 simmed best with standard settings for now 3/12/2023, will refine with the rotational refinements. 55 was definitely best for combat, didn't make a difference for muti
-				return rogue.CurrentEnergy() <= thresh
-			},
-		})
-	}
 }
diff --git a/sim/rogue/rogue.go b/sim/rogue/rogue.go
index f41e5375ba..5804c9374e 100644
--- a/sim/rogue/rogue.go
+++ b/sim/rogue/rogue.go
@@ -1,7 +1,6 @@
 package rogue
 
 import (
-	"math"
 	"time"
 
 	"github.com/wowsims/wotlk/sim/core"
@@ -39,21 +38,14 @@ const RogueBleedTag = "RogueBleed"
 type Rogue struct {
 	core.Character
 
-	Talents  *proto.RogueTalents
-	Options  *proto.Rogue_Options
-	Rotation *proto.Rogue_Rotation
-
-	rotation rotation
+	Talents *proto.RogueTalents
+	Options *proto.Rogue_Options
 
 	bleedCategory *core.ExclusiveCategory
 
 	sliceAndDiceDurations [6]time.Duration
 	exposeArmorDurations  [6]time.Duration
 
-	allMCDsDisabled bool
-
-	maxEnergy float64
-
 	Backstab         *core.Spell
 	BladeFlurry      *core.Spell
 	DeadlyPoison     *core.Spell
@@ -185,13 +177,6 @@ func (rogue *Rogue) Initialize() {
 	rogue.registerVanishSpell()
 
 	rogue.finishingMoveEffectApplier = rogue.makeFinishingMoveEffectApplier()
-
-	if !rogue.IsUsingAPL && rogue.Rotation.TricksOfTheTradeFrequency != proto.Rogue_Rotation_Never && !rogue.HasSetBonus(Tier10, 2) {
-		rogue.RegisterPrepullAction(-10*time.Second, func(sim *core.Simulation) {
-			rogue.TricksOfTheTrade.Cast(sim, nil)
-			rogue.UpdateMajorCooldowns()
-		})
-	}
 }
 
 func (rogue *Rogue) ApplyEnergyTickMultiplier(multiplier float64) {
@@ -202,30 +187,6 @@ func (rogue *Rogue) Reset(sim *core.Simulation) {
 	for _, mcd := range rogue.GetMajorCooldowns() {
 		mcd.Disable()
 	}
-	rogue.allMCDsDisabled = true
-
-	if !rogue.IsUsingAPL {
-		// Stealth triggered effects (Overkill and Master of Subtlety) pre-pull activation
-		if rogue.Rotation.OpenWithGarrote || rogue.Rotation.OpenWithPremeditation {
-			rogue.AutoAttacks.CancelAutoSwing(sim)
-			rogue.StealthAura.Activate(sim)
-		} else {
-			if rogue.Options.StartingOverkillDuration > 0 {
-				if rogue.Talents.Overkill {
-					duration := time.Second * time.Duration(math.Min(float64(rogue.Options.StartingOverkillDuration), 20))
-					rogue.OverkillAura.Activate(sim)
-					rogue.OverkillAura.UpdateExpires(duration)
-				}
-				if rogue.Talents.MasterOfSubtlety > 0 {
-					duration := time.Second * time.Duration(math.Min(float64(rogue.Options.StartingOverkillDuration), 6))
-					rogue.MasterOfSubtletyAura.Activate(sim)
-					rogue.MasterOfSubtletyAura.UpdateExpires(duration)
-				}
-			}
-		}
-	}
-
-	rogue.setupRotation(sim)
 }
 
 func (rogue *Rogue) MeleeCritMultiplier(applyLethality bool) float64 {
@@ -248,13 +209,13 @@ func NewRogue(character *core.Character, options *proto.Player) *Rogue {
 		Character: *character,
 		Talents:   &proto.RogueTalents{},
 		Options:   rogueOptions.Options,
-		Rotation:  rogueOptions.Rotation,
 	}
 	core.FillTalentsProto(rogue.Talents.ProtoReflect(), options.TalentsString, TalentTreeSizes)
 
 	// Passive rogue threat reduction: https://wotlk.wowhead.com/spell=21184/rogue-passive-dnd
 	rogue.PseudoStats.ThreatMultiplier *= 0.71
 	rogue.PseudoStats.CanParry = true
+
 	maxEnergy := 100.0
 	if rogue.Talents.Vigor {
 		maxEnergy += 10
@@ -265,8 +226,7 @@ func NewRogue(character *core.Character, options *proto.Player) *Rogue {
 	if rogue.HasSetBonus(Arena, 4) {
 		maxEnergy += 10
 	}
-	rogue.maxEnergy = maxEnergy
-	rogue.EnableEnergyBar(maxEnergy, rogue.OnEnergyGain)
+	rogue.EnableEnergyBar(maxEnergy)
 	rogue.ApplyEnergyTickMultiplier([]float64{0, 0.08, 0.16, 0.25}[rogue.Talents.Vitality])
 
 	rogue.EnableAutoAttacks(rogue, core.AutoAttackOptions{
@@ -303,11 +263,6 @@ func (rogue *Rogue) BreakStealth(sim *core.Simulation) {
 	}
 }
 
-// Can the rogue fulfil the weapon equipped requirement for Mutilate?
-func (rogue *Rogue) CanMutilate() bool {
-	return rogue.Talents.Mutilate && rogue.HasDagger(core.MainHand) && rogue.HasDagger(core.OffHand)
-}
-
 // Does the rogue have a dagger equipped in the specified hand (main or offhand)?
 func (rogue *Rogue) HasDagger(hand core.Hand) bool {
 	if hand == core.MainHand {
diff --git a/sim/rogue/rogue_test.go b/sim/rogue/rogue_test.go
index 2edf03bf44..9fdd9220c1 100644
--- a/sim/rogue/rogue_test.go
+++ b/sim/rogue/rogue_test.go
@@ -238,79 +238,67 @@ var SubtletyGlyphs = &proto.Glyphs{
 
 var PlayerOptionsCombatDI = &proto.Player_Rogue{
 	Rogue: &proto.Rogue{
-		Options:  DeadlyInstant,
-		Rotation: &proto.Rogue_Rotation{},
+		Options: DeadlyInstant,
 	},
 }
 var PlayerOptionsCombatDD = &proto.Player_Rogue{
 	Rogue: &proto.Rogue{
-		Options:  DeadlyDeadly,
-		Rotation: &proto.Rogue_Rotation{},
+		Options: DeadlyDeadly,
 	},
 }
 var PlayerOptionsCombatID = &proto.Player_Rogue{
 	Rogue: &proto.Rogue{
-		Options:  InstantDeadly,
-		Rotation: &proto.Rogue_Rotation{},
+		Options: InstantDeadly,
 	},
 }
 var PlayerOptionsCombatII = &proto.Player_Rogue{
 	Rogue: &proto.Rogue{
-		Options:  InstantInstant,
-		Rotation: &proto.Rogue_Rotation{},
+		Options: InstantInstant,
 	},
 }
 
 var PlayerOptionsNoLethality = &proto.Player_Rogue{
 	Rogue: &proto.Rogue{
-		Options:  DeadlyInstant,
-		Rotation: &proto.Rogue_Rotation{},
+		Options: DeadlyInstant,
 	},
 }
 
 var PlayerOptionsNoPotW = &proto.Player_Rogue{
 	Rogue: &proto.Rogue{
-		Options:  DeadlyInstant,
-		Rotation: &proto.Rogue_Rotation{},
+		Options: DeadlyInstant,
 	},
 }
 
 var PlayerOptionsNoLethalityNoPotW = &proto.Player_Rogue{
 	Rogue: &proto.Rogue{
-		Options:  DeadlyInstant,
-		Rotation: &proto.Rogue_Rotation{},
+		Options: DeadlyInstant,
 	},
 }
 
 var PlayerOptionsAssassinationDI = &proto.Player_Rogue{
 	Rogue: &proto.Rogue{
-		Options:  DeadlyInstant,
-		Rotation: &proto.Rogue_Rotation{},
+		Options: DeadlyInstant,
 	},
 }
 var PlayerOptionsAssassinationDD = &proto.Player_Rogue{
 	Rogue: &proto.Rogue{
-		Options:  DeadlyDeadly,
-		Rotation: &proto.Rogue_Rotation{},
+		Options: DeadlyDeadly,
 	},
 }
 var PlayerOptionsAssassinationID = &proto.Player_Rogue{
 	Rogue: &proto.Rogue{
-		Options:  InstantDeadly,
-		Rotation: &proto.Rogue_Rotation{},
+		Options: InstantDeadly,
 	},
 }
 var PlayerOptionsAssassinationII = &proto.Player_Rogue{
 	Rogue: &proto.Rogue{
-		Options:  InstantInstant,
-		Rotation: &proto.Rogue_Rotation{},
+		Options: InstantInstant,
 	},
 }
 
 var PlayerOptionsSubtletyID = &proto.Player_Rogue{
 	Rogue: &proto.Rogue{
-		Options:  InstantDeadly,
-		Rotation: &proto.Rogue_Rotation{},
+		Options: InstantDeadly,
 	},
 }
 
diff --git a/sim/rogue/rotation.go b/sim/rogue/rotation.go
deleted file mode 100644
index 95190e3799..0000000000
--- a/sim/rogue/rotation.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package rogue
-
-import (
-	"github.com/wowsims/wotlk/sim/core"
-)
-
-type rotation interface {
-	setup(sim *core.Simulation, rogue *Rogue)
-	run(sim *core.Simulation, rogue *Rogue)
-}
-
-type PriorityAction int32
-
-const (
-	Skip PriorityAction = iota
-	Build
-	Cast
-	Wait
-	Once
-)
-
-type prio struct {
-	check func(sim *core.Simulation, rogue *Rogue) PriorityAction
-	cast  func(sim *core.Simulation, rogue *Rogue) bool
-	cost  float64
-}
-
-func (rogue *Rogue) OnEnergyGain(sim *core.Simulation) {
-	if rogue.IsUsingAPL {
-		return
-	}
-
-	if sim.CurrentTime < 0 {
-		return
-	}
-
-	rogue.TryUseCooldowns(sim)
-
-	if !rogue.GCD.IsReady(sim) {
-		return
-	}
-
-	rogue.rotation.run(sim, rogue)
-}
-
-func (rogue *Rogue) OnGCDReady(sim *core.Simulation) {
-	if rogue.IsUsingAPL {
-		return
-	}
-	rogue.TryUseCooldowns(sim)
-
-	if rogue.IsWaitingForEnergy() {
-		rogue.DoNothing()
-		return
-	}
-
-	rogue.rotation.run(sim, rogue)
-}
-
-func (rogue *Rogue) setupRotation(sim *core.Simulation) {
-	if rogue.IsUsingAPL {
-		return
-	}
-	switch {
-	case rogue.Env.GetNumTargets() >= 3:
-		rogue.rotation = &rotation_multi{} // rotation multi will soon be removed
-	case rogue.CanMutilate():
-		rogue.rotation = &rotation_assassination{}
-	case rogue.Talents.CombatPotency > 0:
-		rogue.rotation = &rotation_combat{}
-	case rogue.Talents.HonorAmongThieves > 0:
-		rogue.rotation = &rotation_subtlety{}
-	default:
-		rogue.rotation = &rotation_generic{}
-	}
-	rogue.rotation.setup(sim, rogue)
-}
diff --git a/sim/rogue/rotation_assassination.go b/sim/rogue/rotation_assassination.go
deleted file mode 100644
index a4a98bc23d..0000000000
--- a/sim/rogue/rotation_assassination.go
+++ /dev/null
@@ -1,374 +0,0 @@
-package rogue
-
-import (
-	"log"
-	"slices"
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-)
-
-type rotation_assassination struct {
-	prios []prio
-}
-
-func (x *rotation_assassination) setup(sim *core.Simulation, rogue *Rogue) {
-	rogue.bleedCategory = rogue.CurrentTarget.GetExclusiveEffectCategory(core.BleedEffectCategory)
-
-	x.prios = x.prios[:0]
-
-	mutiCost := rogue.Mutilate.DefaultCast.Cost
-	rupCost := rogue.Rupture.DefaultCast.Cost
-	envCost := rogue.Envenom.DefaultCast.Cost
-
-	// estimate of energy per second while nothing is cast
-	energyPerSecond := func() float64 {
-		if rogue.Talents.FocusedAttacks == 0 {
-			return 10 * rogue.EnergyTickMultiplier
-		}
-
-		getCritChance := func(spell *core.Spell) float64 {
-			at := rogue.AttackTables[rogue.CurrentTarget.UnitIndex]
-
-			critCap := 1.0 - at.BaseGlanceChance
-			if miss := at.BaseMissChance + 0.19 - spell.PhysicalHitChance(at); miss > 0 {
-				critCap -= miss
-			}
-			if dodge := at.BaseDodgeChance - spell.ExpertisePercentage() - rogue.PseudoStats.DodgeReduction; dodge > 0 {
-				critCap -= dodge
-			}
-
-			critChance := spell.PhysicalCritChance(at)
-			if critChance > critCap {
-				critChance = critCap
-			}
-			return critChance
-		}
-
-		critsPerSecond := getCritChance(rogue.AutoAttacks.MHAuto())/rogue.AutoAttacks.MainhandSwingSpeed().Seconds() +
-			getCritChance(rogue.AutoAttacks.OHAuto())/rogue.AutoAttacks.OffhandSwingSpeed().Seconds()
-		procChance := []float64{0, 0.33, 0.66, 1}[rogue.Talents.FocusedAttacks]
-
-		return 10*rogue.EnergyTickMultiplier + critsPerSecond*procChance*2
-	}
-
-	// Garrote
-	if rogue.Rotation.OpenWithGarrote && !rogue.PseudoStats.InFrontOfTarget && rogue.IsStealthed() {
-		x.prios = append(x.prios, prio{
-			func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-				if rogue.CurrentEnergy() > rogue.Garrote.DefaultCast.Cost && rogue.IsStealthed() {
-					return Once
-				}
-				return Wait
-			},
-			func(sim *core.Simulation, rogue *Rogue) bool {
-				return rogue.Garrote.Cast(sim, rogue.CurrentTarget)
-			},
-			rogue.Garrote.DefaultCast.Cost,
-		})
-	}
-
-	// Slice And Dice
-	x.prios = append(x.prios, prio{
-		func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-			if rogue.SliceAndDiceAura.IsActive() {
-				return Skip
-			}
-			if rogue.ComboPoints() > 0 && rogue.CurrentEnergy() > rogue.SliceAndDice.DefaultCast.Cost {
-				return Cast
-			}
-			if rogue.ComboPoints() < 1 && rogue.CurrentEnergy() > mutiCost {
-				return Build
-			}
-			return Wait
-		},
-		func(sim *core.Simulation, rogue *Rogue) bool {
-			return rogue.SliceAndDice.Cast(sim, rogue.CurrentTarget)
-		},
-		rogue.SliceAndDice.DefaultCast.Cost,
-	})
-
-	// Hunger while planning
-	if rogue.Talents.HungerForBlood {
-		x.prios = append(x.prios, prio{
-			func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-
-				prioExpose := rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Once ||
-					rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Maintain
-				if prioExpose && !rogue.ExposeArmorAuras.Get(rogue.CurrentTarget).IsActive() {
-					return Skip
-				}
-
-				if rogue.HungerForBloodAura.IsActive() {
-					return Skip
-				}
-
-				if !x.targetHasBleed(sim, rogue) {
-					return Skip
-				}
-
-				if x.targetHasBleed(sim, rogue) && rogue.CurrentEnergy() > rogue.HungerForBlood.DefaultCast.Cost {
-					return Cast
-				}
-				return Wait
-			},
-			func(sim *core.Simulation, rogue *Rogue) bool {
-				return rogue.HungerForBlood.Cast(sim, rogue.CurrentTarget)
-			},
-			rogue.HungerForBlood.DefaultCast.Cost,
-		})
-	}
-
-	// Expose armor
-	if rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Once || rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Maintain {
-		hasCastExpose := false
-		x.prios = append(x.prios, prio{
-			func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-				if hasCastExpose && rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Once {
-					return Skip
-				}
-				timeLeft := rogue.ExposeArmorAuras.Get(rogue.CurrentTarget).RemainingDuration(sim)
-				minPoints := max(1, min(rogue.Rotation.MinimumComboPointsExposeArmor, 5))
-				if rogue.Rotation.ExposeArmorFrequency != proto.Rogue_Rotation_Once {
-					minPoints = 1
-				}
-				if timeLeft <= 0 {
-					if rogue.ComboPoints() < minPoints {
-						if rogue.CurrentEnergy() >= mutiCost {
-							return Build
-						} else {
-							return Wait
-						}
-					} else {
-						if rogue.CurrentEnergy() >= rogue.ExposeArmor.DefaultCast.Cost {
-							return Cast
-						} else {
-							return Wait
-						}
-					}
-				} else {
-					energyGained := energyPerSecond() * timeLeft.Seconds()
-					cpGenerated := energyGained / mutiCost
-					currentCp := float64(rogue.ComboPoints())
-					if currentCp+cpGenerated > 5 {
-						return Skip
-					} else {
-						if currentCp < 5 {
-							if rogue.CurrentEnergy() >= mutiCost {
-								return Build
-							}
-						}
-						return Wait
-					}
-				}
-			},
-			func(sim *core.Simulation, rogue *Rogue) bool {
-				casted := rogue.ExposeArmor.Cast(sim, rogue.CurrentTarget)
-				if casted {
-					hasCastExpose = true
-				}
-				return casted
-			},
-			rogue.ExposeArmor.DefaultCast.Cost,
-		})
-	}
-
-	// Rupture for Bleed
-	if rogue.Rotation.RuptureForBleed {
-		x.prios = append(x.prios, prio{
-			func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-				if x.targetHasBleed(sim, rogue) {
-					return Skip
-				}
-				if rogue.HungerForBloodAura.IsActive() {
-					return Skip
-				}
-				if rogue.ComboPoints() > 0 && rogue.CurrentEnergy() >= rupCost {
-					return Cast
-				}
-				if rogue.ComboPoints() < 1 && rogue.CurrentEnergy() >= mutiCost {
-					return Build
-				}
-				return Wait
-			},
-			func(sim *core.Simulation, rogue *Rogue) bool {
-				return rogue.Rupture.Cast(sim, rogue.CurrentTarget)
-			},
-			rupCost,
-		})
-	}
-
-	// Hunger for Blood
-	if rogue.Talents.HungerForBlood {
-		x.prios = append(x.prios, prio{
-			func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-				if rogue.HungerForBloodAura.IsActive() {
-					return Skip
-				}
-
-				if !x.targetHasBleed(sim, rogue) {
-					return Skip
-				}
-
-				if x.targetHasBleed(sim, rogue) && rogue.CurrentEnergy() >= rogue.HungerForBlood.DefaultCast.Cost {
-					return Cast
-				}
-				return Wait
-			},
-			func(s *core.Simulation, r *Rogue) bool {
-				return rogue.HungerForBlood.Cast(sim, rogue.CurrentTarget)
-			},
-			rogue.HungerForBlood.DefaultCast.Cost,
-		})
-	}
-
-	// Enable CDs
-	x.prios = append(x.prios, prio{
-		func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-			for _, mcd := range rogue.GetMajorCooldowns() {
-				if mcd.Spell != rogue.ColdBlood {
-					mcd.Enable()
-				}
-			}
-			return Once
-		},
-		func(s *core.Simulation, r *Rogue) bool {
-			return true
-		},
-		0,
-	})
-
-	// Rupture
-	x.prios = append(x.prios, prio{
-		func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-			cp, e := rogue.ComboPoints(), rogue.CurrentEnergy()
-
-			if rogue.Rotation.AssassinationFinisherPriority == proto.Rogue_Rotation_EnvenomRupture {
-				if rogue.Rupture.CurDot().IsActive() || sim.GetRemainingDuration() < rogue.RuptureDuration(4) {
-					return Skip
-				}
-				if !rogue.EnvenomAura.IsActive() || cp < 4 || rogue.Talents.Ruthlessness < 3 {
-					return Skip
-				}
-
-				// use Rupture if you can re-cast Envenom with minimal delay, hoping for a Ruthlessness proc ;)
-				avail := e + rogue.EnvenomAura.RemainingDuration(sim).Seconds()*energyPerSecond()
-				cost := rupCost + mutiCost + envCost
-				if avail >= cost {
-					return Cast
-				}
-				return Skip
-
-			} else {
-				if rogue.Rupture.CurDot().IsActive() || sim.GetRemainingDuration() < time.Second*18 {
-					return Skip
-				}
-				if cp >= 4 && e >= rupCost {
-					return Cast
-				}
-				if cp < 4 && e >= mutiCost {
-					return Build
-				}
-				return Wait
-			}
-		},
-		func(sim *core.Simulation, rogue *Rogue) bool {
-			return rogue.Rupture.Cast(sim, rogue.CurrentTarget)
-		},
-		rupCost,
-	})
-
-	// Envenom
-	x.prios = append(x.prios, prio{
-		func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-			e, cp := rogue.CurrentEnergy(), rogue.ComboPoints()
-
-			// end of combat handling - possibly use low CP Envenoms instead of doing nothing
-			if dur := sim.GetRemainingDuration(); dur <= 10*time.Second {
-				avail := e + dur.Seconds()*energyPerSecond()
-
-				if cp == 3 && avail < mutiCost+envCost && e >= envCost {
-					return Cast
-				}
-
-				if cp >= 1 && avail < mutiCost && e >= envCost {
-					return Cast
-				}
-			}
-
-			if cp >= 4 {
-				eps := energyPerSecond()
-
-				if rogue.EnvenomAura.IsActive() {
-					// don't clip Envenom, unless you'd energy cap
-					if e < rogue.maxEnergy-eps && sim.GetRemainingDuration() >= rogue.EnvenomDuration(5) {
-						return Wait
-					}
-					return Cast
-				}
-
-				// pool, so two Mutilate casts fit into the next uptime; this is a very minor DPS gain, and primarily for lower gear levels
-				cost := envCost + mutiCost + mutiCost
-				if cp == 5 && rogue.Talents.RelentlessStrikes == 5 {
-					cost -= 25
-				}
-				avail := e + rogue.EnvenomDuration(cp).Seconds()*eps
-				if avail < cost {
-					return Wait
-				}
-				return Cast
-			}
-
-			if e >= mutiCost {
-				return Build
-			}
-			return Wait
-		},
-		func(sim *core.Simulation, rogue *Rogue) bool {
-			if rogue.ColdBlood.IsReady(sim) && rogue.ComboPoints() == 5 {
-				rogue.ColdBlood.Cast(sim, rogue.CurrentTarget)
-			}
-			return rogue.Envenom.Cast(sim, rogue.CurrentTarget)
-		},
-		envCost,
-	})
-}
-
-func (x *rotation_assassination) run(sim *core.Simulation, rogue *Rogue) {
-	for i := 0; i < len(x.prios); i++ {
-		switch p := x.prios[i]; p.check(sim, rogue) {
-		case Skip:
-			continue
-		case Build:
-			if !rogue.Mutilate.Cast(sim, rogue.CurrentTarget) {
-				rogue.WaitForEnergy(sim, rogue.Mutilate.DefaultCast.Cost)
-				return
-			}
-		case Cast:
-			if !p.cast(sim, rogue) {
-				rogue.WaitForEnergy(sim, p.cost)
-				return
-			}
-		case Once:
-			if !p.cast(sim, rogue) {
-				rogue.WaitForEnergy(sim, p.cost)
-				return
-			}
-			x.prios = slices.Delete(x.prios, i, i+1)
-			i--
-		case Wait:
-			rogue.DoNothing()
-			return
-		}
-
-		if !rogue.GCD.IsReady(sim) {
-			return
-		}
-	}
-	log.Panic("skipped all prios")
-}
-
-func (x *rotation_assassination) targetHasBleed(_ *core.Simulation, rogue *Rogue) bool {
-	return rogue.bleedCategory.AnyActive() || rogue.CurrentTarget.HasActiveAuraWithTag(RogueBleedTag) || rogue.Options.AssumeBleedActive
-}
diff --git a/sim/rogue/rotation_combat.go b/sim/rogue/rotation_combat.go
deleted file mode 100644
index a38113ba66..0000000000
--- a/sim/rogue/rotation_combat.go
+++ /dev/null
@@ -1,454 +0,0 @@
-package rogue
-
-import (
-	"log"
-	"slices"
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-)
-
-type rotation_combat struct {
-	prios []prio
-
-	builder *core.Spell
-}
-
-func (x *rotation_combat) setup(_ *core.Simulation, rogue *Rogue) {
-	x.prios = x.prios[:0]
-
-	x.builder = rogue.SinisterStrike
-	if rogue.Rotation.CombatBuilder == proto.Rogue_Rotation_Backstab && rogue.HasDagger(core.MainHand) && !rogue.PseudoStats.InFrontOfTarget {
-		x.builder = rogue.Backstab
-	}
-	if rogue.Talents.Hemorrhage && rogue.Rotation.CombatBuilder == proto.Rogue_Rotation_HemorrhageCombat {
-		x.builder = rogue.Hemorrhage
-	}
-
-	bldCost := x.builder.DefaultCast.Cost
-	sndCost := rogue.SliceAndDice.DefaultCast.Cost
-	rupCost := rogue.Rupture.DefaultCast.Cost
-	evisCost := rogue.Eviscerate.DefaultCast.Cost
-
-	baseEps := 10 * rogue.EnergyTickMultiplier
-	maxPool := rogue.maxEnergy - 3*float64(rogue.Talents.CombatPotency)
-
-	ruthCp := 0.2 * float64(rogue.Talents.Ruthlessness)
-
-	// estimate of energy per second while nothing is cast
-	energyPerSecond := func() float64 {
-		if rogue.Talents.CombatPotency == 0 {
-			return 10 * rogue.EnergyTickMultiplier
-		}
-
-		spell := rogue.AutoAttacks.OHAuto()
-		at := rogue.AttackTables[rogue.CurrentTarget.UnitIndex]
-
-		landChance := 1.0
-		if miss := at.BaseMissChance + 0.19 - spell.PhysicalHitChance(at); miss > 0 {
-			landChance -= miss
-		}
-		if dodge := at.BaseDodgeChance - spell.ExpertisePercentage() - spell.Unit.PseudoStats.DodgeReduction; dodge > 0 {
-			landChance -= dodge
-		}
-
-		landsPerSecond := landChance / rogue.AutoAttacks.OffhandSwingSpeed().Seconds()
-
-		return 10*rogue.EnergyTickMultiplier + landsPerSecond*0.2*float64(rogue.Talents.CombatPotency)*3
-	}
-
-	// Glyph of Backstab support
-	var bonusDuration float64
-	rupRemaining := func(sim *core.Simulation) time.Duration {
-		if dot := rogue.Rupture.CurDot(); dot.IsActive() {
-			return dot.RemainingDuration(sim)
-		}
-		return 0
-	}
-
-	if x.builder == rogue.Backstab && rogue.HasMajorGlyph(proto.RogueMajorGlyph_GlyphOfBackstab) {
-		bonusDuration = 6
-		rupRemaining = func(sim *core.Simulation) time.Duration {
-			if dot := rogue.Rupture.CurDot(); dot.IsActive() {
-				dur := dot.RemainingDuration(sim)
-				dur += dot.TickLength * time.Duration(dot.MaxStacks+3-dot.NumberOfTicks)
-				return dur
-			}
-			return 0
-		}
-	}
-
-	// Garrote
-	if rogue.Rotation.OpenWithGarrote && !rogue.PseudoStats.InFrontOfTarget && rogue.IsStealthed() {
-		x.prios = append(x.prios, prio{
-			func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-				if rogue.CurrentEnergy() > rogue.Garrote.DefaultCast.Cost && rogue.IsStealthed() {
-					return Once
-				}
-				return Wait
-			},
-			func(sim *core.Simulation, rogue *Rogue) bool {
-				return rogue.Garrote.Cast(sim, rogue.CurrentTarget)
-			},
-			rogue.Garrote.DefaultCast.Cost,
-		})
-	}
-
-	// Slice And Dice
-	x.prios = append(x.prios, prio{
-		func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-			cp, e := rogue.ComboPoints(), rogue.CurrentEnergy()
-
-			if sndDur := rogue.SliceAndDiceAura.RemainingDuration(sim); sndDur > 0 {
-				if cp == 5 { // pool for snd if pooling for rupture fails
-					rupDur := rupRemaining(sim)
-					if e+rupDur.Seconds()*energyPerSecond() > maxPool {
-						if e+sndDur.Seconds()*energyPerSecond() <= maxPool {
-							return Wait
-						}
-					}
-					return Skip
-				}
-
-				if cp >= 1 { // don't build if it reduces uptime
-					if e+sndDur.Seconds()*energyPerSecond() < sndCost+bldCost || sndDur < time.Second {
-						return Wait
-					}
-				}
-				return Skip
-			}
-
-			// end of fight - heuristically, 2s of snd beat a 3 CP eviscerate for DPE, and 3s are close to a 5 CP one.
-			if cp >= 3 && sim.GetRemainingDuration() < time.Duration(2000+600*cp)*time.Millisecond {
-				return Skip
-			}
-
-			if cp >= 1 && e >= sndCost {
-				return Cast
-			}
-			if cp < 1 && e >= bldCost {
-				return Build
-			}
-			return Wait
-		},
-		func(sim *core.Simulation, rogue *Rogue) bool {
-			return rogue.SliceAndDice.Cast(sim, rogue.CurrentTarget)
-		},
-		sndCost,
-	})
-
-	// Expose armor - update this as well
-	if rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Once || rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Maintain {
-		hasCastExpose := false
-		x.prios = append(x.prios, prio{
-			func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-				if hasCastExpose && rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Once {
-					return Skip
-				}
-				timeLeft := rogue.ExposeArmorAuras.Get(rogue.CurrentTarget).RemainingDuration(sim)
-				minPoints := max(1, min(rogue.Rotation.MinimumComboPointsExposeArmor, 5))
-				if rogue.Rotation.ExposeArmorFrequency != proto.Rogue_Rotation_Once {
-					minPoints = 1
-				}
-				if timeLeft <= 0 {
-					if rogue.ComboPoints() < minPoints {
-						if rogue.CurrentEnergy() >= bldCost {
-							return Build
-						} else {
-							return Wait
-						}
-					} else {
-						if rogue.CurrentEnergy() >= rogue.ExposeArmor.DefaultCast.Cost {
-							return Cast
-						} else {
-							return Wait
-						}
-					}
-				} else {
-					energyGained := energyPerSecond() * timeLeft.Seconds()
-					cpGenerated := energyGained / bldCost
-					currentCp := float64(rogue.ComboPoints())
-					if currentCp+cpGenerated > 5 {
-						return Skip
-					} else {
-						if currentCp < 5 {
-							if rogue.CurrentEnergy() >= bldCost {
-								return Build
-							}
-						}
-						return Wait
-					}
-				}
-			},
-			func(sim *core.Simulation, rogue *Rogue) bool {
-				casted := rogue.ExposeArmor.Cast(sim, rogue.CurrentTarget)
-				if casted {
-					hasCastExpose = true
-				}
-				return casted
-			},
-			rogue.ExposeArmor.DefaultCast.Cost,
-		})
-	}
-
-	// Enable CDs
-	x.prios = append(x.prios, prio{
-		func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-			for _, mcd := range rogue.GetMajorCooldowns() {
-				mcd.Enable()
-			}
-			return Once
-		},
-		func(s *core.Simulation, r *Rogue) bool {
-			return true
-		},
-		0,
-	})
-
-	if rogue.Rotation.CombatFinisherPriority == proto.Rogue_Rotation_EviscerateRupture {
-		// this is the pre-3.3.3 "rupture-less" rotation, essentially
-		x.prios = append(x.prios, prio{
-			func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-				e, cp := rogue.CurrentEnergy(), rogue.ComboPoints()
-
-				if dur := sim.GetRemainingDuration(); dur <= 10*time.Second {
-					// end of fight handling - build towards a 3+ cp eviscerate, or just sinister strike
-					switch cp {
-					case 5:
-						if e >= evisCost {
-							return Cast
-						}
-						return Wait
-					default:
-						if e+dur.Seconds()*energyPerSecond() >= bldCost+evisCost {
-							return Build
-						}
-						if cp >= 3 && e >= evisCost {
-							return Cast
-						}
-						if cp < 3 && e >= bldCost {
-							return Build
-						}
-					}
-					return Wait
-				}
-
-				if cp >= 5 {
-					sndDur := rogue.SliceAndDiceAura.RemainingDuration(sim)
-					// correcting with ruthCp seems to be a loss, so we just use bldCost directly
-					if e+sndDur.Seconds()*energyPerSecond() >= evisCost+bldCost+sndCost {
-						return Cast
-					}
-					return Wait
-				}
-				return Build
-			},
-			func(sim *core.Simulation, rogue *Rogue) bool {
-				return rogue.Eviscerate.Cast(sim, rogue.CurrentTarget)
-			},
-			evisCost,
-		})
-
-		return
-	}
-
-	const ruptureMinDuration = time.Second * 10 // heuristically, 4-5 rupture ticks are better DPE than eviscerate
-
-	// seconds a 5 cp rupture can be delayed to match a 4 cp rupture's dps. for rup4to5 and rup3to4, this delay is < 2s,
-	// which also means that clipping 3 or 4 cp ruptures is usually a dps loss
-	rup4to5 := (rogue.RuptureDuration(4).Seconds() + bonusDuration) * (1 - rogue.RuptureDamage(4)/rogue.RuptureDamage(5))
-	rup3to4 := (rogue.RuptureDuration(3).Seconds() + bonusDuration) * (1 - rogue.RuptureDamage(3)/rogue.RuptureDamage(4))
-
-	// Rupture
-	x.prios = append(x.prios, prio{
-		func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-			cp, e := rogue.ComboPoints(), rogue.CurrentEnergy()
-
-			if sim.GetRemainingDuration() < ruptureMinDuration {
-				return Skip
-			}
-
-			rupDot := rogue.Rupture.CurDot()
-			if !rupDot.IsActive() {
-				if cp == 5 && e >= rupCost {
-					return Cast
-				}
-				if cp == 4 && e+rup4to5*energyPerSecond() < bldCost+rupCost {
-					return Cast
-				}
-				if cp == 3 && e+rup3to4*energyPerSecond() < bldCost+rupCost {
-					return Cast
-				}
-				if e >= bldCost {
-					return Build
-				}
-				return Wait
-			}
-
-			// there's ample time to rebuild, simply skip
-			dur := rupRemaining(sim).Seconds()
-			if e+dur*baseEps > maxPool {
-				return Skip
-			}
-
-			if cp == 5 {
-				if e+dur*energyPerSecond() > maxPool {
-					return Skip // can't pool any longer, maybe we can fit in Eviscerate
-				}
-				return Wait
-			}
-			if cp == 4 && e+(dur+rup4to5)*energyPerSecond() < bldCost+rupCost {
-				return Wait
-			}
-			if cp == 3 && e+(dur+rup3to4)*energyPerSecond() < bldCost+rupCost {
-				return Wait
-			}
-			if e >= bldCost {
-				return Build
-			}
-			return Wait
-		},
-		func(sim *core.Simulation, rogue *Rogue) bool {
-			return rogue.Rupture.Cast(sim, rogue.CurrentTarget)
-		},
-		rupCost,
-	})
-
-	bldPerCp := 1.0
-	if x.builder == rogue.SinisterStrike && rogue.HasMajorGlyph(proto.RogueMajorGlyph_GlyphOfSinisterStrike) {
-		attackTable := rogue.AttackTables[rogue.CurrentTarget.UnitIndex]
-		crit := rogue.SinisterStrike.PhysicalCritChance(attackTable)
-		bldPerCp = 1 / (1 + crit*(0.5+0.2*float64(rogue.Talents.SealFate)))
-	}
-	if x.builder == rogue.Backstab && rogue.Talents.SealFate > 0 {
-		attackTable := rogue.AttackTables[rogue.CurrentTarget.UnitIndex]
-		crit := rogue.Backstab.PhysicalCritChance(attackTable)
-		bldPerCp = 1 / (1 + crit*(0.2*float64(rogue.Talents.SealFate)))
-	}
-
-	// Eviscerate
-	x.prios = append(x.prios, prio{
-		func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-			e, cp := rogue.CurrentEnergy(), rogue.ComboPoints()
-
-			if dur := sim.GetRemainingDuration(); dur <= ruptureMinDuration {
-				// end of fight handling - build towards a 3+ cp eviscerate, or just sinister strike
-				switch cp {
-				case 5:
-					if e >= evisCost {
-						return Cast
-					}
-					return Wait
-				default:
-					if e+dur.Seconds()*energyPerSecond() >= bldCost+evisCost {
-						return Build
-					}
-					if cp >= 3 && e >= evisCost {
-						return Cast
-					}
-					if cp < 3 && e >= bldCost {
-						return Build
-					}
-				}
-				return Wait
-			}
-
-			// we only get here if there's ample time left on rupture, or rupture pooling failed: in these cases, we
-			// can try to fill in a 5 cp eviscerate, if it's not too disruptive. lower cp eviscerates aren't worth it,
-			// since sinister spam isn't all that much worse
-			if cp <= 4 {
-				return Build
-			}
-
-			cost := evisCost + (4-ruthCp)*bldCost*bldPerCp + rupCost
-
-			rupDur := rupRemaining(sim)
-			sndDur := rogue.SliceAndDiceAura.RemainingDuration(sim)
-			if sndDur < rupDur {
-				cost += sndCost + (1-ruthCp)*bldCost*bldPerCp
-			}
-
-			if avail := e + rupDur.Seconds()*energyPerSecond(); avail >= cost {
-				return Cast
-			}
-
-			// we'd lose a CP here, so we just wait...
-			if e <= maxPool {
-				return Wait
-			}
-
-			// ... and if that doesn't work, allow to clip snd
-			if sndDur < rogue.sliceAndDiceDurations[2]-rogue.sliceAndDiceDurations[1] {
-				rogue.SliceAndDice.Cast(sim, rogue.CurrentTarget)
-				return Wait
-			}
-
-			return Build
-		},
-		func(sim *core.Simulation, rogue *Rogue) bool {
-			return rogue.Eviscerate.Cast(sim, rogue.CurrentTarget)
-		},
-		evisCost,
-	})
-}
-
-func (x *rotation_combat) run(sim *core.Simulation, rogue *Rogue) {
-	for i := 0; i < len(x.prios); i++ {
-		switch p := x.prios[i]; p.check(sim, rogue) {
-		case Skip:
-			continue
-		case Build:
-			//Handle Ghostly Strike. This is badly copy-pasted code, and is not considered in a regular raid setting.
-			if rogue.Talents.GhostlyStrike && rogue.Rotation.UseGhostlyStrike && rogue.GhostlyStrike.IsReady(sim) {
-				x.builder = rogue.GhostlyStrike
-
-				if !x.builder.Cast(sim, rogue.CurrentTarget) {
-					rogue.WaitForEnergy(sim, x.builder.DefaultCast.Cost)
-
-					x.builder = rogue.SinisterStrike
-					if rogue.Rotation.CombatBuilder == proto.Rogue_Rotation_Backstab && rogue.HasDagger(core.MainHand) && !rogue.PseudoStats.InFrontOfTarget {
-						x.builder = rogue.Backstab
-					}
-					if rogue.Talents.Hemorrhage && rogue.Rotation.CombatBuilder == proto.Rogue_Rotation_HemorrhageCombat {
-						x.builder = rogue.Hemorrhage
-					}
-
-					return
-				}
-
-				x.builder = rogue.SinisterStrike
-				if rogue.Rotation.CombatBuilder == proto.Rogue_Rotation_Backstab && rogue.HasDagger(core.MainHand) && !rogue.PseudoStats.InFrontOfTarget {
-					x.builder = rogue.Backstab
-				}
-				if rogue.Talents.Hemorrhage && rogue.Rotation.CombatBuilder == proto.Rogue_Rotation_HemorrhageCombat {
-					x.builder = rogue.Hemorrhage
-				}
-				//Done with Ghostly Strike
-			} else if !x.builder.Cast(sim, rogue.CurrentTarget) {
-				rogue.WaitForEnergy(sim, x.builder.DefaultCast.Cost)
-				return
-			}
-		case Cast:
-			if !p.cast(sim, rogue) {
-				rogue.WaitForEnergy(sim, p.cost)
-				return
-			}
-		case Once:
-			if !p.cast(sim, rogue) {
-				rogue.WaitForEnergy(sim, p.cost)
-				return
-			}
-			x.prios = slices.Delete(x.prios, i, i+1)
-			i--
-		case Wait:
-			rogue.DoNothing()
-			return
-		}
-
-		if !rogue.GCD.IsReady(sim) {
-			return
-		}
-	}
-	log.Panic("skipped all prios")
-}
diff --git a/sim/rogue/rotation_generic.go b/sim/rogue/rotation_generic.go
deleted file mode 100644
index a3ad5da4f7..0000000000
--- a/sim/rogue/rotation_generic.go
+++ /dev/null
@@ -1,430 +0,0 @@
-package rogue
-
-import (
-	"log"
-	"slices"
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-)
-
-type rotation_generic struct {
-	prios []prio
-
-	builder *core.Spell
-}
-
-func (x *rotation_generic) setup(_ *core.Simulation, rogue *Rogue) {
-	x.prios = x.prios[:0]
-
-	x.builder = rogue.SinisterStrike
-	if rogue.HasDagger(core.MainHand) && !rogue.PseudoStats.InFrontOfTarget {
-		x.builder = rogue.Backstab
-	}
-	if rogue.CanMutilate() {
-		x.builder = rogue.Mutilate
-	}
-	if rogue.Talents.Hemorrhage {
-		x.builder = rogue.Hemorrhage
-	}
-
-	bldCost := x.builder.DefaultCast.Cost
-	sndCost := rogue.SliceAndDice.DefaultCast.Cost
-	rupCost := rogue.Rupture.DefaultCast.Cost
-
-	baseEps := 10 * rogue.EnergyTickMultiplier
-	maxPool := rogue.maxEnergy - 3*float64(rogue.Talents.CombatPotency) - 2*float64(rogue.Talents.FocusedAttacks)/3.0
-
-	ruthCp := 0.2 * float64(rogue.Talents.Ruthlessness)
-	rsPerCp := float64(rogue.Talents.RelentlessStrikes)
-
-	// estimate of energy per second while nothing is cast
-	energyPerSecond := func() float64 {
-		var eps float64
-		if rogue.Talents.CombatPotency > 0 {
-			spell := rogue.AutoAttacks.OHAuto()
-			at := rogue.AttackTables[rogue.CurrentTarget.UnitIndex]
-
-			landChance := 1.0
-			if miss := at.BaseMissChance + 0.19 - spell.PhysicalHitChance(at); miss > 0 {
-				landChance -= miss
-			}
-			if dodge := at.BaseDodgeChance - spell.ExpertisePercentage() - spell.Unit.PseudoStats.DodgeReduction; dodge > 0 {
-				landChance -= dodge
-			}
-
-			landsPerSecond := landChance / rogue.AutoAttacks.OffhandSwingSpeed().Seconds()
-
-			eps += landsPerSecond * 0.2 * float64(rogue.Talents.CombatPotency) * 3
-		}
-		if rogue.Talents.FocusedAttacks > 0 {
-			getCritChance := func(spell *core.Spell) float64 {
-				at := rogue.AttackTables[rogue.CurrentTarget.UnitIndex]
-
-				critCap := 1.0 - at.BaseGlanceChance
-				if miss := at.BaseMissChance + 0.19 - spell.PhysicalHitChance(at); miss > 0 {
-					critCap -= miss
-				}
-				if dodge := at.BaseDodgeChance - spell.ExpertisePercentage() - rogue.PseudoStats.DodgeReduction; dodge > 0 {
-					critCap -= dodge
-				}
-
-				critChance := spell.PhysicalCritChance(at)
-				if critChance > critCap {
-					critChance = critCap
-				}
-				return critChance
-			}
-
-			critsPerSecond := getCritChance(rogue.AutoAttacks.MHAuto())/rogue.AutoAttacks.MainhandSwingSpeed().Seconds() +
-				getCritChance(rogue.AutoAttacks.OHAuto())/rogue.AutoAttacks.OffhandSwingSpeed().Seconds()
-			procChance := []float64{0, 0.33, 0.66, 1}[rogue.Talents.FocusedAttacks]
-
-			eps += critsPerSecond * procChance * 2
-		}
-		return 10*rogue.EnergyTickMultiplier + eps
-	}
-
-	// Glyph of Backstab support
-	var bonusDuration float64
-	rupRemaining := func(sim *core.Simulation) time.Duration {
-		if dot := rogue.Rupture.CurDot(); dot.IsActive() {
-			return dot.RemainingDuration(sim)
-		}
-		return 0
-	}
-
-	if x.builder == rogue.Backstab && rogue.HasMajorGlyph(proto.RogueMajorGlyph_GlyphOfBackstab) {
-		bonusDuration = 6
-		rupRemaining = func(sim *core.Simulation) time.Duration {
-			if dot := rogue.Rupture.CurDot(); dot.IsActive() {
-				dur := dot.RemainingDuration(sim)
-				dur += dot.TickLength * time.Duration(dot.MaxStacks+3-dot.NumberOfTicks)
-				return dur
-			}
-			return 0
-		}
-	}
-
-	// Garrote
-	if rogue.Rotation.OpenWithGarrote && !rogue.PseudoStats.InFrontOfTarget {
-		x.prios = append(x.prios, prio{
-			func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-				if rogue.CurrentEnergy() > rogue.Garrote.DefaultCast.Cost {
-					return Once
-				}
-				return Wait
-			},
-			func(sim *core.Simulation, rogue *Rogue) bool {
-				return rogue.Garrote.Cast(sim, rogue.CurrentTarget)
-			},
-			rogue.Garrote.DefaultCast.Cost,
-		})
-	}
-
-	// Slice And Dice
-	x.prios = append(x.prios, prio{
-		func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-			cp, e := rogue.ComboPoints(), rogue.CurrentEnergy()
-
-			if sndDur := rogue.SliceAndDiceAura.RemainingDuration(sim); sndDur > 0 {
-				if cp == 5 { // pool for snd if pooling for rupture fails
-					rupDur := rupRemaining(sim)
-					if e+rupDur.Seconds()*energyPerSecond() > maxPool {
-						if e+sndDur.Seconds()*energyPerSecond() <= maxPool {
-							return Wait
-						}
-					}
-					return Skip
-				}
-
-				if cp >= 1 { // don't build if it reduces uptime
-					if e+sndDur.Seconds()*energyPerSecond() < sndCost+bldCost || sndDur < time.Second {
-						return Wait
-					}
-				}
-				return Skip
-			}
-
-			// end of fight - heuristically, 2s of snd beat a 3 CP eviscerate for DPE, and 3s are close to a 5 CP one.
-			if cp >= 3 && sim.GetRemainingDuration() < time.Duration(2000+600*cp)*time.Millisecond {
-				return Skip
-			}
-
-			if cp >= 1 && e >= sndCost {
-				return Cast
-			}
-			if cp < 1 && e >= bldCost {
-				return Build
-			}
-			return Wait
-		},
-		func(sim *core.Simulation, rogue *Rogue) bool {
-			return rogue.SliceAndDice.Cast(sim, rogue.CurrentTarget)
-		},
-		sndCost,
-	})
-
-	// Expose armor - update this as well
-	if rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Once || rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Maintain {
-		hasCastExpose := false
-		x.prios = append(x.prios, prio{
-			func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-				if hasCastExpose && rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Once {
-					return Skip
-				}
-				timeLeft := rogue.ExposeArmorAuras.Get(rogue.CurrentTarget).RemainingDuration(sim)
-				minPoints := max(1, min(rogue.Rotation.MinimumComboPointsExposeArmor, 5))
-				if rogue.Rotation.ExposeArmorFrequency != proto.Rogue_Rotation_Once {
-					minPoints = 1
-				}
-				if timeLeft <= 0 {
-					if rogue.ComboPoints() < minPoints {
-						if rogue.CurrentEnergy() >= bldCost {
-							return Build
-						} else {
-							return Wait
-						}
-					} else {
-						if rogue.CurrentEnergy() >= rogue.ExposeArmor.DefaultCast.Cost {
-							return Cast
-						} else {
-							return Wait
-						}
-					}
-				} else {
-					energyGained := energyPerSecond() * timeLeft.Seconds()
-					cpGenerated := energyGained / bldCost
-					currentCp := float64(rogue.ComboPoints())
-					if currentCp+cpGenerated > 5 {
-						return Skip
-					} else {
-						if currentCp < 5 {
-							if rogue.CurrentEnergy() >= bldCost {
-								return Build
-							}
-						}
-						return Wait
-					}
-				}
-			},
-			func(sim *core.Simulation, rogue *Rogue) bool {
-				casted := rogue.ExposeArmor.Cast(sim, rogue.CurrentTarget)
-				if casted {
-					hasCastExpose = true
-				}
-				return casted
-			},
-			rogue.ExposeArmor.DefaultCast.Cost,
-		})
-	}
-
-	// Enable CDs
-	x.prios = append(x.prios, prio{
-		func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-			for _, mcd := range rogue.GetMajorCooldowns() {
-				mcd.Enable()
-			}
-			return Once
-		},
-		func(s *core.Simulation, r *Rogue) bool {
-			return true
-		},
-		0,
-	})
-
-	const ruptureMinDuration = time.Second * 10 // heuristically, 4-5 rupture ticks are better DPE than eviscerate
-
-	// seconds a 5 cp rupture can be delayed to match a 4 cp rupture's dps. for rup4to5 and rup3to4, this delay is < 2s,
-	// which also means that clipping 3 or 4 cp ruptures is usually a dps loss
-	rup4to5 := (rogue.RuptureDuration(4).Seconds() + bonusDuration) * (1 - rogue.RuptureDamage(4)/rogue.RuptureDamage(5))
-	rup3to4 := (rogue.RuptureDuration(3).Seconds() + bonusDuration) * (1 - rogue.RuptureDamage(3)/rogue.RuptureDamage(4))
-
-	// Rupture
-	x.prios = append(x.prios, prio{
-		func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-			cp, e := rogue.ComboPoints(), rogue.CurrentEnergy()
-
-			if sim.GetRemainingDuration() < ruptureMinDuration {
-				return Skip
-			}
-
-			rupDot := rogue.Rupture.CurDot()
-			if !rupDot.IsActive() {
-				if cp == 5 && e >= rupCost {
-					return Cast
-				}
-				if cp == 4 && e+rup4to5*energyPerSecond() < bldCost+rupCost {
-					return Cast
-				}
-				if cp == 3 && e+rup3to4*energyPerSecond() < bldCost+rupCost {
-					return Cast
-				}
-				if e >= bldCost {
-					return Build
-				}
-				return Wait
-			}
-
-			// there's ample time to rebuild, simply skip
-			dur := rupRemaining(sim).Seconds()
-			if e+dur*baseEps > maxPool {
-				return Skip
-			}
-
-			if cp == 5 {
-				if e+dur*energyPerSecond() > maxPool {
-					return Skip // can't pool any longer, maybe we can fit in Eviscerate
-				}
-				return Wait
-			}
-			if cp == 4 && e+(dur+rup4to5)*energyPerSecond() < bldCost+rupCost {
-				return Wait
-			}
-			if cp == 3 && e+(dur+rup3to4)*energyPerSecond() < bldCost+rupCost {
-				return Wait
-			}
-			if e >= bldCost {
-				return Build
-			}
-			return Wait
-		},
-		func(sim *core.Simulation, rogue *Rogue) bool {
-			return rogue.Rupture.Cast(sim, rogue.CurrentTarget)
-		},
-		rupCost,
-	})
-
-	bldPerCp := 1.0
-	if x.builder == rogue.SinisterStrike {
-		attackTable := rogue.AttackTables[rogue.CurrentTarget.UnitIndex]
-		crit := rogue.SinisterStrike.PhysicalCritChance(attackTable)
-		var extraChance float64
-		if rogue.HasMajorGlyph(proto.RogueMajorGlyph_GlyphOfSinisterStrike) {
-			extraChance = 0.5
-		}
-		bldPerCp = 1 / (1 + crit*(extraChance+0.2*float64(rogue.Talents.SealFate)))
-	}
-	if x.builder == rogue.Backstab {
-		attackTable := rogue.AttackTables[rogue.CurrentTarget.UnitIndex]
-		crit := rogue.Backstab.PhysicalCritChance(attackTable)
-		bldPerCp = 1 / (1 + crit*(0.2*float64(rogue.Talents.SealFate)))
-	}
-	if x.builder == rogue.Hemorrhage {
-		attackTable := rogue.AttackTables[rogue.CurrentTarget.UnitIndex]
-		crit := rogue.Hemorrhage.PhysicalCritChance(attackTable)
-		bldPerCp = 1 / (1 + crit*(0.2*float64(rogue.Talents.SealFate)))
-	}
-	if x.builder == rogue.Mutilate {
-		attackTable := rogue.AttackTables[rogue.CurrentTarget.UnitIndex]
-		critMH := rogue.MutilateMH.PhysicalCritChance(attackTable)
-		critOH := rogue.MutilateOH.PhysicalCritChance(attackTable)
-		crit := 1 - (1-critMH)*(1-critOH)
-		bldPerCp = 1 / (2 + crit*(0.2*float64(rogue.Talents.SealFate)))
-	}
-
-	// direct damage finisher (Eviscerate/Envenom)
-	finisher := rogue.Eviscerate
-	if rogue.Talents.MasterPoisoner > 0 {
-		finisher = rogue.Envenom
-	}
-	finisherCost := finisher.DefaultCast.Cost
-	x.prios = append(x.prios, prio{
-		func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-			e, cp := rogue.CurrentEnergy(), rogue.ComboPoints()
-
-			if dur := sim.GetRemainingDuration(); dur <= ruptureMinDuration {
-				// end of fight handling - build towards a 3+ cp finisher, or just spam the builder
-				switch cp {
-				case 5:
-					if e >= finisherCost {
-						return Cast
-					}
-					return Wait
-				default:
-					if e+dur.Seconds()*energyPerSecond() >= bldCost+finisherCost {
-						return Build
-					}
-					if cp >= 3 && e >= finisherCost {
-						return Cast
-					}
-					if cp < 3 && e >= bldCost {
-						return Build
-					}
-				}
-				return Wait
-			}
-
-			// we only get here if there's ample time left on rupture, or rupture pooling failed: in these cases, we
-			// can try to fill in a 5 cp finisher, if it's not too disruptive. lower cp finishers aren't worth it,
-			// since builder spam isn't all that much worse
-			if cp <= 4 {
-				return Build
-			}
-
-			cost := finisherCost - 5*rsPerCp + (4-ruthCp)*bldCost*bldPerCp + rupCost
-
-			rupDur := rupRemaining(sim)
-			sndDur := rogue.SliceAndDiceAura.RemainingDuration(sim)
-			if sndDur < rupDur {
-				cost += sndCost - 1*rsPerCp + (1-ruthCp)*bldCost*bldPerCp
-			}
-
-			if avail := e + rupDur.Seconds()*energyPerSecond(); avail >= cost {
-				return Cast
-			}
-
-			// we'd lose a CP here, so we just wait...
-			if e <= maxPool {
-				return Wait
-			}
-
-			// ... and if that doesn't work, allow to clip snd
-			if sndDur < rogue.sliceAndDiceDurations[2]-rogue.sliceAndDiceDurations[1] {
-				rogue.SliceAndDice.Cast(sim, rogue.CurrentTarget)
-				return Wait
-			}
-
-			return Build
-		},
-		func(sim *core.Simulation, rogue *Rogue) bool {
-			return finisher.Cast(sim, rogue.CurrentTarget)
-		},
-		finisherCost,
-	})
-}
-
-func (x *rotation_generic) run(sim *core.Simulation, rogue *Rogue) {
-	for i := 0; i < len(x.prios); i++ {
-		switch p := x.prios[i]; p.check(sim, rogue) {
-		case Skip:
-			continue
-		case Build:
-			if !x.builder.Cast(sim, rogue.CurrentTarget) {
-				rogue.WaitForEnergy(sim, x.builder.DefaultCast.Cost)
-				return
-			}
-		case Cast:
-			if !p.cast(sim, rogue) {
-				rogue.WaitForEnergy(sim, p.cost)
-				return
-			}
-		case Once:
-			if !p.cast(sim, rogue) {
-				rogue.WaitForEnergy(sim, p.cost)
-				return
-			}
-			x.prios = slices.Delete(x.prios, i, i+1)
-			i--
-		case Wait:
-			rogue.DoNothing()
-			return
-		}
-
-		if !rogue.GCD.IsReady(sim) {
-			return
-		}
-	}
-	log.Panic("skipped all prios")
-}
diff --git a/sim/rogue/rotation_multi.go b/sim/rogue/rotation_multi.go
deleted file mode 100644
index b9000428f8..0000000000
--- a/sim/rogue/rotation_multi.go
+++ /dev/null
@@ -1,405 +0,0 @@
-package rogue
-
-import (
-	"math"
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-)
-
-type rogueRotationItem struct {
-	ExpiresAt            time.Duration
-	MinimumBuildDuration time.Duration
-	MaximumBuildDuration time.Duration
-	PrioIndex            int
-}
-
-type roguePriorityItem struct {
-	Aura               *core.Aura
-	CastCount          int32
-	EnergyCost         float64
-	PoolAmount         float64
-	GetDuration        func(*Rogue, int32) time.Duration
-	GetSpell           func(*Rogue, int32) *core.Spell
-	IsFiller           bool
-	MaximumComboPoints int32
-	MaxCasts           int32
-	MinimumComboPoints int32
-}
-
-type shouldCastRotationItemResult int32
-
-const (
-	ShouldNotCast shouldCastRotationItemResult = iota
-	ShouldBuild
-	ShouldCast
-	ShouldWait
-)
-
-type rotation_multi struct {
-	priorityItems []roguePriorityItem
-	rotationItems []rogueRotationItem
-
-	builder       *core.Spell
-	builderPoints int32
-}
-
-func (x *rotation_multi) setup(sim *core.Simulation, rogue *Rogue) {
-	x.builder = rogue.SinisterStrike
-	x.builderPoints = 1
-
-	if rogue.CanMutilate() {
-		x.builder = rogue.Mutilate
-		x.builderPoints = 2
-	}
-
-	if rogue.Talents.Hemorrhage {
-		x.builder = rogue.Hemorrhage
-		x.builderPoints = 1
-	}
-
-	if rogue.Talents.SlaughterFromTheShadows > 0 && !rogue.Rotation.HemoWithDagger && !rogue.PseudoStats.InFrontOfTarget && rogue.HasDagger(core.MainHand) {
-		x.builder = rogue.Backstab
-		x.builderPoints = 1
-	}
-
-	// Slice and Dice
-	x.priorityItems = x.priorityItems[:0]
-
-	sliceAndDice := roguePriorityItem{
-		MinimumComboPoints: 1,
-		MaximumComboPoints: 5,
-		Aura:               rogue.SliceAndDiceAura,
-		EnergyCost:         rogue.SliceAndDice.DefaultCast.Cost,
-		GetDuration: func(rogue *Rogue, cp int32) time.Duration {
-			return rogue.sliceAndDiceDurations[cp]
-		},
-		GetSpell: func(rogue *Rogue, cp int32) *core.Spell {
-			return rogue.SliceAndDice
-		},
-	}
-	if rogue.Rotation.MultiTargetSliceFrequency != proto.Rogue_Rotation_Never {
-		sliceAndDice.MinimumComboPoints = max(1, rogue.Rotation.MinimumComboPointsMultiTargetSlice)
-		if rogue.Rotation.MultiTargetSliceFrequency == proto.Rogue_Rotation_Once {
-			sliceAndDice.MaxCasts = 1
-		}
-		x.priorityItems = append(x.priorityItems, sliceAndDice)
-	}
-
-	// Expose Armor
-	if rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Maintain ||
-		rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Once {
-		minPoints := int32(1)
-		maxCasts := int32(0)
-		if rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Once {
-			minPoints = rogue.Rotation.MinimumComboPointsExposeArmor
-			maxCasts = 1
-		}
-		x.priorityItems = append(x.priorityItems, roguePriorityItem{
-			MaxCasts:           maxCasts,
-			MaximumComboPoints: 5,
-			MinimumComboPoints: minPoints,
-			Aura:               rogue.ExposeArmorAuras.Get(rogue.CurrentTarget),
-			EnergyCost:         rogue.ExposeArmor.DefaultCast.Cost,
-			GetDuration: func(rogue *Rogue, cp int32) time.Duration {
-				return rogue.exposeArmorDurations[cp]
-			},
-			GetSpell: func(rogue *Rogue, cp int32) *core.Spell {
-				return rogue.ExposeArmor
-			},
-		})
-	}
-
-	// Hunger for Blood
-	if rogue.Talents.HungerForBlood {
-		x.priorityItems = append(x.priorityItems, roguePriorityItem{
-			MaximumComboPoints: 0,
-			Aura:               rogue.HungerForBloodAura,
-			EnergyCost:         rogue.HungerForBlood.DefaultCast.Cost,
-			GetDuration: func(rogue *Rogue, cp int32) time.Duration {
-				return rogue.HungerForBloodAura.Duration
-			},
-			GetSpell: func(rogue *Rogue, cp int32) *core.Spell {
-				return rogue.HungerForBlood
-			},
-		})
-	}
-
-	// Dummy priority to enable CDs
-	x.priorityItems = append(x.priorityItems, roguePriorityItem{
-		MaxCasts:           1,
-		MaximumComboPoints: 0,
-		GetDuration: func(rogue *Rogue, cp int32) time.Duration {
-			return 0
-		},
-		GetSpell: func(rogue *Rogue, cp int32) *core.Spell {
-			if rogue.allMCDsDisabled {
-				for _, mcd := range rogue.GetMajorCooldowns() {
-					mcd.Enable()
-				}
-				rogue.allMCDsDisabled = false
-			}
-			return nil
-		},
-	})
-
-	x.priorityItems = append(x.priorityItems, roguePriorityItem{
-		MaximumComboPoints: 0,
-		EnergyCost:         rogue.FanOfKnives.DefaultCast.Cost,
-		GetSpell: func(rogue *Rogue, i int32) *core.Spell {
-			return rogue.FanOfKnives
-		},
-	})
-	x.rotationItems = x.planRotation(sim, rogue)
-}
-
-func (x *rotation_multi) run(sim *core.Simulation, rogue *Rogue) {
-	if len(x.rotationItems) < 1 {
-		panic("Rotation is empty")
-	}
-	eps := x.getExpectedEnergyPerSecond(rogue)
-	shouldCast := x.shouldCastNextRotationItem(sim, rogue, eps)
-	item := x.rotationItems[0]
-	prio := x.priorityItems[item.PrioIndex]
-
-	switch shouldCast {
-	case ShouldNotCast:
-		x.rotationItems = x.rotationItems[1:]
-		x.run(sim, rogue)
-	case ShouldBuild:
-		spell := x.builder
-		if spell == nil || spell.Cast(sim, rogue.CurrentTarget) {
-			if rogue.GCD.IsReady(sim) {
-				x.run(sim, rogue)
-			}
-		} else {
-			panic("Unexpected builder cast failure")
-		}
-	case ShouldCast:
-		spell := prio.GetSpell(rogue, rogue.ComboPoints())
-		if spell == nil || spell.Cast(sim, rogue.CurrentTarget) {
-			x.priorityItems[item.PrioIndex].CastCount += 1
-			x.rotationItems = x.planRotation(sim, rogue)
-			if rogue.GCD.IsReady(sim) {
-				x.run(sim, rogue)
-			}
-		} else {
-			panic("Unexpected cast failure")
-		}
-	case ShouldWait:
-		desiredEnergy := 100.0
-		if rogue.ComboPoints() == 5 {
-			desiredEnergy = prio.EnergyCost
-		} else {
-			if rogue.CurrentEnergy() < prio.EnergyCost && rogue.ComboPoints() >= prio.MinimumComboPoints {
-				desiredEnergy = prio.EnergyCost
-			} else if rogue.ComboPoints() < 5 {
-				desiredEnergy = x.builder.DefaultCast.Cost
-			}
-		}
-		cdAvailableTime := time.Second * 10
-		if sim.CurrentTime > cdAvailableTime {
-			cdAvailableTime = core.NeverExpires
-		}
-		nextExpiration := cdAvailableTime
-		for _, otherItem := range x.rotationItems {
-			if otherItem.ExpiresAt < nextExpiration {
-				nextExpiration = otherItem.ExpiresAt
-			}
-		}
-		neededEnergy := desiredEnergy - rogue.CurrentEnergy()
-		energyAvailableTime := time.Second*time.Duration(neededEnergy/eps) + 1*time.Second
-		energyAt := sim.CurrentTime + energyAvailableTime
-		if energyAt < nextExpiration {
-			rogue.WaitForEnergy(sim, desiredEnergy)
-		} else if nextExpiration > sim.CurrentTime {
-			rogue.WaitUntil(sim, nextExpiration)
-		} else {
-			rogue.DoNothing()
-		}
-	}
-}
-
-func (x *rotation_multi) energyToBuild(points int32) float64 {
-	costPerBuilder := x.builder.DefaultCast.Cost
-
-	buildersNeeded := math.Ceil(float64(points) / float64(x.builderPoints))
-	return buildersNeeded * costPerBuilder
-}
-
-func (x *rotation_multi) timeToBuild(points int32, builderPoints int32, eps float64, finisherCost float64) time.Duration {
-	energyNeeded := x.energyToBuild(points) + finisherCost
-	secondsNeeded := energyNeeded / eps
-	globalsNeeded := math.Ceil(float64(points)/float64(builderPoints)) + 1
-	// Return greater of the time it takes to use the globals and the time it takes to build the energy
-	return max(time.Second*time.Duration(secondsNeeded), time.Second*time.Duration(globalsNeeded))
-}
-
-func (x *rotation_multi) shouldCastNextRotationItem(sim *core.Simulation, rogue *Rogue, eps float64) shouldCastRotationItemResult {
-	if len(x.rotationItems) == 0 {
-		panic("Empty rotation")
-	}
-	currentEnergy := rogue.CurrentEnergy()
-	comboPoints := rogue.ComboPoints()
-	currentTime := sim.CurrentTime
-	item := x.rotationItems[0]
-	prio := x.priorityItems[item.PrioIndex]
-	tte := item.ExpiresAt - currentTime
-	clippingThreshold := time.Second * 2
-	timeUntilNextGCD := rogue.GCD.TimeToReady(sim)
-
-	// Check to see if a higher prio item will expire
-	if len(x.rotationItems) >= 2 {
-		timeElapsed := time.Second * 1
-		for _, nextItem := range x.rotationItems[1:] {
-			if nextItem.ExpiresAt <= currentTime+timeElapsed {
-				return ShouldNotCast
-			}
-			timeElapsed += nextItem.MinimumBuildDuration
-		}
-	}
-
-	// Expires before next GCD
-	if tte <= timeUntilNextGCD {
-		if comboPoints >= prio.MinimumComboPoints && currentEnergy >= (prio.EnergyCost+prio.PoolAmount) {
-			return ShouldCast
-		} else if comboPoints < prio.MinimumComboPoints && currentEnergy >= x.builder.DefaultCast.Cost {
-			return ShouldBuild
-		} else {
-			return ShouldWait
-		}
-	}
-	if comboPoints >= prio.MaximumComboPoints { // Don't need CP
-		// Cast
-		if tte <= clippingThreshold && currentEnergy >= (prio.EnergyCost+prio.PoolAmount) {
-			return ShouldCast
-		}
-		// Pool energy
-		if tte <= clippingThreshold && currentEnergy < (prio.EnergyCost+prio.PoolAmount) {
-			return ShouldWait
-		}
-		// We have time to squeeze in another spell
-		if tte > item.MinimumBuildDuration {
-			// Find the first lower prio item that can be cast and use it
-			for lpi, lowerPrio := range x.priorityItems[item.PrioIndex+1:] {
-				if comboPoints > lowerPrio.MinimumComboPoints && currentEnergy > lowerPrio.EnergyCost && lowerPrio.MaxCasts == 0 {
-					x.rotationItems = append([]rogueRotationItem{
-						{ExpiresAt: currentTime, PrioIndex: lpi + item.PrioIndex + 1},
-					}, x.rotationItems...)
-					return ShouldCast
-				}
-			}
-		}
-		// Overcap CP with builder
-		if x.timeToBuild(1, x.builderPoints, eps, prio.EnergyCost+prio.PoolAmount) <= tte && currentEnergy >= x.builder.DefaultCast.Cost {
-			return ShouldBuild
-		}
-	} else if comboPoints < prio.MinimumComboPoints { // Need CP
-		if currentEnergy >= x.builder.DefaultCast.Cost {
-			return ShouldBuild
-		} else {
-			return ShouldWait
-		}
-	} else { // Between MinimumComboPoints and MaximumComboPoints
-		if currentEnergy >= prio.EnergyCost+prio.PoolAmount && tte <= timeUntilNextGCD {
-			return ShouldCast
-		}
-		ttb := x.timeToBuild(1, 2, eps, prio.EnergyCost+prio.PoolAmount-currentEnergy)
-		if currentEnergy >= x.builder.DefaultCast.Cost && tte > ttb {
-			return ShouldBuild
-		}
-	}
-	return ShouldWait
-}
-
-func (x *rotation_multi) getExpectedEnergyPerSecond(rogue *Rogue) float64 {
-	const finishersPerSecond = 1.0 / 6
-	const averageComboPointsSpendOnFinisher = 4.0
-	bonusEnergyPerSecond := float64(rogue.Talents.CombatPotency) * 3 * 0.2 * 1.0 / (rogue.AutoAttacks.OH().SwingSpeed / 1.4)
-	bonusEnergyPerSecond += float64(rogue.Talents.FocusedAttacks)
-	bonusEnergyPerSecond += float64(rogue.Talents.RelentlessStrikes) * 0.04 * 25 * finishersPerSecond * averageComboPointsSpendOnFinisher
-	return (core.EnergyPerTick*rogue.EnergyTickMultiplier)/core.EnergyTickDuration.Seconds() + bonusEnergyPerSecond
-}
-
-func (x *rotation_multi) planRotation(sim *core.Simulation, rogue *Rogue) []rogueRotationItem {
-	var rotationItems []rogueRotationItem
-	eps := x.getExpectedEnergyPerSecond(rogue)
-	for pi, prio := range x.priorityItems {
-		if prio.MaxCasts > 0 && prio.CastCount >= prio.MaxCasts {
-			continue
-		}
-		maxCP := prio.MaximumComboPoints
-		for maxCP > 0 && prio.GetDuration(rogue, maxCP)+sim.CurrentTime > sim.Duration {
-			maxCP--
-		}
-		var expiresAt time.Duration
-		if prio.Aura != nil {
-			expiresAt = prio.Aura.ExpiresAt()
-		} else if prio.MaxCasts == 1 {
-			expiresAt = sim.CurrentTime // TODO looks fishy, repeated expiresAt = sim.CurrentTime
-		} else {
-			expiresAt = sim.CurrentTime
-		}
-		minimumBuildDuration := x.timeToBuild(prio.MinimumComboPoints, x.builderPoints, eps, prio.EnergyCost)
-		maximumBuildDuration := x.timeToBuild(maxCP, x.builderPoints, eps, prio.EnergyCost)
-		rotationItems = append(rotationItems, rogueRotationItem{
-			ExpiresAt:            expiresAt,
-			MaximumBuildDuration: maximumBuildDuration,
-			MinimumBuildDuration: minimumBuildDuration,
-			PrioIndex:            pi,
-		})
-	}
-
-	currentTime := sim.CurrentTime
-	comboPoints := rogue.ComboPoints()
-	currentEnergy := rogue.CurrentEnergy()
-
-	var prioStack []rogueRotationItem
-	for _, item := range rotationItems {
-		if item.ExpiresAt >= sim.Duration {
-			continue
-		}
-		prio := x.priorityItems[item.PrioIndex]
-		maxBuildAt := item.ExpiresAt - item.MaximumBuildDuration
-		if prio.Aura == nil {
-			timeValueOfResources := time.Duration((float64(comboPoints)*x.builder.DefaultCast.Cost/float64(x.builderPoints) + currentEnergy) / eps)
-			maxBuildAt = currentTime - item.MaximumBuildDuration - timeValueOfResources
-		}
-		if currentTime < maxBuildAt {
-			// Put it on the to cast stack
-			prioStack = append(prioStack, item)
-			if prio.MinimumComboPoints > 0 {
-				comboPoints = 0
-			}
-			currentTime += item.MaximumBuildDuration
-		} else {
-			cpUsed := max(0, prio.MinimumComboPoints-comboPoints)
-			energyUsed := max(0, prio.EnergyCost-currentEnergy)
-			minBuildTime := x.timeToBuild(cpUsed, x.builderPoints, eps, energyUsed)
-			if currentTime+minBuildTime <= item.ExpiresAt || !prio.IsFiller {
-				prioStack = append(prioStack, item)
-				currentTime = max(item.ExpiresAt, currentTime+minBuildTime)
-				currentEnergy = 0
-				if prio.MinimumComboPoints > 0 {
-					comboPoints = 0
-				}
-			} else if len(prioStack) < 1 || (prio.Aura != nil && !prio.Aura.IsActive() && !prio.IsFiller) || prio.MaxCasts == 1 {
-				// Plan to cast it as soon as possible
-				prioStack = append(prioStack, item)
-				currentTime += item.MinimumBuildDuration
-				currentEnergy = 0
-				if prio.MinimumComboPoints > 0 {
-					comboPoints = 0
-				}
-			}
-		}
-	}
-
-	// Reverse
-	for i, j := 0, len(prioStack)-1; i < j; i, j = i+1, j-1 {
-		prioStack[i], prioStack[j] = prioStack[j], prioStack[i]
-	}
-
-	return prioStack
-}
diff --git a/sim/rogue/rotation_subtlety.go b/sim/rogue/rotation_subtlety.go
deleted file mode 100644
index d1d5a51224..0000000000
--- a/sim/rogue/rotation_subtlety.go
+++ /dev/null
@@ -1,340 +0,0 @@
-package rogue
-
-import (
-	"log"
-	"slices"
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-)
-
-type rotation_subtlety struct {
-	prios []prio
-
-	builder *core.Spell
-}
-
-func (x *rotation_subtlety) setup(sim *core.Simulation, rogue *Rogue) {
-	x.setSubtletyBuilder(sim, rogue)
-
-	x.prios = x.prios[:0]
-
-	secondsPerComboPoint := func() float64 {
-		honorAmongThievesChance := []float64{0, 0.33, 0.66, 1.0}[rogue.Talents.HonorAmongThieves]
-		return 1 + 1/(float64(rogue.Options.HonorOfThievesCritRate+100)/100*honorAmongThievesChance)
-	}
-
-	comboPointsPerSecond := func() float64 {
-		return 1 / secondsPerComboPoint()
-	}
-
-	energyPerSecond := func() float64 {
-		return 10 * rogue.EnergyTickMultiplier
-	}
-
-	if rogue.Rotation.OpenWithPremeditation && rogue.Talents.Premeditation && rogue.IsStealthed() {
-		x.prios = append(x.prios, prio{
-			func(s *core.Simulation, r *Rogue) PriorityAction {
-				if rogue.Premeditation.CanCast(s, r.CurrentTarget) {
-					return Once
-				}
-				return Wait
-			},
-			func(s *core.Simulation, r *Rogue) bool {
-				return r.Premeditation.Cast(s, r.CurrentTarget)
-			},
-			rogue.Premeditation.DefaultCast.Cost,
-		})
-	}
-
-	if rogue.Rotation.OpenWithShadowstep && rogue.Talents.Shadowstep {
-		x.prios = append(x.prios, prio{
-			func(s *core.Simulation, r *Rogue) PriorityAction {
-				if rogue.CurrentEnergy() > rogue.Shadowstep.DefaultCast.Cost {
-					return Once
-				}
-				return Wait
-			},
-			func(s *core.Simulation, r *Rogue) bool {
-				return rogue.Shadowstep.Cast(sim, rogue.CurrentTarget)
-			},
-			rogue.Shadowstep.DefaultCast.Cost,
-		})
-	}
-
-	// Garrote
-	if rogue.Rotation.OpenWithGarrote && !rogue.PseudoStats.InFrontOfTarget && rogue.IsStealthed() {
-		x.prios = append(x.prios, prio{
-			func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-				if rogue.CurrentEnergy() > rogue.Garrote.DefaultCast.Cost && rogue.IsStealthed() {
-					return Once
-				}
-				return Wait
-			},
-			func(sim *core.Simulation, rogue *Rogue) bool {
-				return rogue.Garrote.Cast(sim, rogue.CurrentTarget)
-			},
-			rogue.Garrote.DefaultCast.Cost,
-		})
-	}
-
-	// Slice and Dice
-	x.prios = append(x.prios, prio{
-		func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-			if rogue.SliceAndDiceAura.IsActive() {
-				return Skip
-			}
-			// end of combat handling - prefer Eviscerate over a mostly wasted SnD
-			if rogue.ComboPoints() >= 2 && rogue.sliceAndDiceDurations[rogue.ComboPoints()] >= 2*sim.GetRemainingDuration() {
-				return Skip
-			}
-			if rogue.ComboPoints() >= 1 && rogue.CurrentEnergy() > rogue.SliceAndDice.DefaultCast.Cost {
-				return Cast
-			}
-			if rogue.ComboPoints() < 1 && rogue.CurrentEnergy() > x.builder.DefaultCast.Cost && comboPointsPerSecond() >= 0.7 {
-				return Wait
-			}
-			if rogue.ComboPoints() < 1 && rogue.CurrentEnergy() > x.builder.DefaultCast.Cost {
-				return Build
-			}
-			return Wait
-		},
-		func(sim *core.Simulation, rogue *Rogue) bool {
-			return rogue.SliceAndDice.Cast(sim, rogue.CurrentTarget)
-		},
-		rogue.SliceAndDice.DefaultCast.Cost,
-	})
-
-	// Expose armor
-	if rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Once || rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Maintain {
-		hasCastExpose := false
-		x.prios = append(x.prios, prio{
-			func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-				if hasCastExpose && rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Once {
-					return Skip
-				}
-				timeLeft := rogue.ExposeArmorAuras.Get(rogue.CurrentTarget).RemainingDuration(sim)
-				minPoints := max(1, min(rogue.Rotation.MinimumComboPointsExposeArmor, 5))
-				if rogue.Rotation.ExposeArmorFrequency != proto.Rogue_Rotation_Once {
-					minPoints = 1
-				}
-				if timeLeft <= 0 {
-					if rogue.ComboPoints() < minPoints {
-						if rogue.CurrentEnergy() >= x.builder.DefaultCast.Cost && comboPointsPerSecond() < 1 {
-							return Build
-						} else {
-							return Wait
-						}
-					} else {
-						if rogue.CurrentEnergy() >= rogue.ExposeArmor.DefaultCast.Cost {
-							return Cast
-						} else {
-							return Wait
-						}
-					}
-				} else {
-					energyGained := energyPerSecond() * timeLeft.Seconds()
-					comboGained := comboPointsPerSecond() * timeLeft.Seconds()
-					cpGenerated := energyGained/x.builder.DefaultCast.Cost + comboGained
-					currentCP := float64(rogue.ComboPoints())
-					if currentCP+cpGenerated > 5 {
-						return Skip
-					} else {
-						if currentCP < 5 {
-							if rogue.CurrentEnergy() >= x.builder.DefaultCast.Cost {
-								return Build
-							}
-						}
-						return Wait
-					}
-				}
-			},
-			func(sim *core.Simulation, rogue *Rogue) bool {
-				casted := rogue.ExposeArmor.Cast(sim, rogue.CurrentTarget)
-				if casted {
-					hasCastExpose = true
-				}
-				return casted
-			},
-			rogue.ExposeArmor.DefaultCast.Cost,
-		})
-	}
-
-	// Enable CDS
-	x.prios = append(x.prios, prio{
-		func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-			for _, mcd := range rogue.GetMajorCooldowns() {
-				mcd.Enable()
-			}
-			return Once
-		},
-		func(_ *core.Simulation, _ *Rogue) bool {
-			return true
-		},
-		0,
-	})
-
-	//Shadowstep
-	if rogue.Talents.Shadowstep {
-		x.prios = append(x.prios, prio{
-			func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-				if rogue.Shadowstep.IsReady(sim) {
-					// Can we cast Rupture now?
-					if !rogue.Rupture.CurDot().IsActive() && rogue.ComboPoints() >= 5 && rogue.CurrentEnergy() >= rogue.Rupture.DefaultCast.Cost+rogue.Shadowstep.DefaultCast.Cost {
-						return Cast
-					} else {
-						return Skip
-					}
-				}
-				return Skip
-			},
-			func(sim *core.Simulation, rogue *Rogue) bool {
-				return rogue.Shadowstep.Cast(sim, rogue.CurrentTarget)
-			},
-			rogue.Shadowstep.DefaultCast.Cost,
-		})
-	}
-
-	const ruptureMinDuration = time.Second * 8 // heuristically, 3-4 Rupture ticks are better DPE than Eviscerate or Envenom
-
-	// Rupture
-	x.prios = append(x.prios, prio{
-		func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-			if rogue.Rupture.CurDot().IsActive() || sim.GetRemainingDuration() < ruptureMinDuration {
-				return Skip
-			}
-			if rogue.ComboPoints() >= 5 && rogue.CurrentEnergy() >= rogue.Rupture.DefaultCast.Cost {
-				return Cast
-			}
-			// don't explicitly wait here, to shorten downtime
-			if rogue.ComboPoints() < 5 && rogue.CurrentEnergy() >= x.builder.DefaultCast.Cost+rogue.Rupture.DefaultCast.Cost {
-				return Build
-			}
-			return Wait
-		},
-		func(sim *core.Simulation, rogue *Rogue) bool {
-			return rogue.Rupture.Cast(sim, rogue.CurrentTarget)
-		},
-		rogue.Rupture.DefaultCast.Cost,
-	})
-
-	//Envenom
-	if rogue.Rotation.SubtletyFinisherPriority == proto.Rogue_Rotation_SubtletyEnvenom {
-		x.prios = append(x.prios, prio{
-			func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-				if !rogue.DeadlyPoison.CurDot().Aura.IsActive() {
-					return Skip
-				}
-				if rogue.EnvenomAura.IsActive() {
-					return Skip
-				}
-				if rogue.ComboPoints() >= 5 && rogue.CurrentEnergy() >= rogue.Envenom.DefaultCast.Cost {
-					return Cast
-				}
-				if rogue.ComboPoints() < 5 && rogue.CurrentEnergy() >= x.builder.DefaultCast.Cost+rogue.Envenom.DefaultCast.Cost {
-					return Build
-				}
-				return Wait
-			},
-			func(sim *core.Simulation, rogue *Rogue) bool {
-				return rogue.Envenom.Cast(sim, rogue.CurrentTarget)
-			},
-			rogue.Envenom.DefaultCast.Cost,
-		})
-	}
-
-	// Eviscerate
-	x.prios = append(x.prios, prio{
-		func(sim *core.Simulation, rogue *Rogue) PriorityAction {
-			// end of combat handling - prefer Eviscerate over Builder, heuristically
-			if sim.GetRemainingDuration().Seconds() < secondsPerComboPoint() && rogue.ComboPoints() >= 2 && rogue.CurrentEnergy() >= rogue.Eviscerate.DefaultCast.Cost {
-				return Cast
-			}
-			if rogue.ComboPoints() >= 5 && rogue.CurrentEnergy() >= rogue.Eviscerate.DefaultCast.Cost {
-				return Cast
-			}
-			if rogue.ComboPoints() < 5 && rogue.CurrentEnergy() >= x.builder.DefaultCast.Cost+rogue.Eviscerate.DefaultCast.Cost {
-				return Build
-			}
-			return Wait
-		},
-		func(sim *core.Simulation, rogue *Rogue) bool {
-			return rogue.Eviscerate.Cast(sim, rogue.CurrentTarget)
-		},
-		rogue.Eviscerate.DefaultCast.Cost,
-	})
-}
-
-func (x *rotation_subtlety) run(sim *core.Simulation, rogue *Rogue) {
-	for i := 0; i < len(x.prios); i++ {
-		switch p := x.prios[i]; p.check(sim, rogue) {
-		case Skip:
-			continue
-		case Build:
-			if rogue.ComboPoints() == 4 && rogue.CurrentEnergy() <= rogue.maxEnergy-10 {
-				// just wait for HaT proc - if it happens, a finisher will follow and often cost effectively 0 energy,
-				// so we add another GCD worth of energy headroom
-				rogue.DoNothing()
-				return
-			}
-
-			x.setSubtletyBuilder(sim, rogue)
-			if !x.builder.Cast(sim, rogue.CurrentTarget) {
-				rogue.WaitForEnergy(sim, x.builder.DefaultCast.Cost)
-				return
-			}
-		case Cast:
-			if !p.cast(sim, rogue) {
-				rogue.WaitForEnergy(sim, p.cost)
-				return
-			}
-		case Once:
-			if !p.cast(sim, rogue) {
-				rogue.WaitForEnergy(sim, p.cost)
-				return
-			}
-			x.prios = slices.Delete(x.prios, i, i+1)
-			i--
-		case Wait:
-			rogue.DoNothing()
-			return
-		}
-
-		if !rogue.GCD.IsReady(sim) {
-			return
-		}
-	}
-	log.Panic("skipped all prios")
-}
-
-func (x *rotation_subtlety) setSubtletyBuilder(sim *core.Simulation, rogue *Rogue) {
-	// Garrote
-	if !rogue.Garrote.CurDot().Aura.IsActive() && rogue.IsStealthed() && !rogue.PseudoStats.InFrontOfTarget {
-		x.builder = rogue.Garrote
-		return
-	}
-	// Ambush
-	if rogue.IsStealthed() && !rogue.PseudoStats.InFrontOfTarget && rogue.HasDagger(core.MainHand) {
-		x.builder = rogue.Ambush
-		return
-	}
-	// Backstab
-	if rogue.Rotation.SubtletyBuilder == proto.Rogue_Rotation_BackstabSub && !rogue.PseudoStats.InFrontOfTarget && rogue.HasDagger(core.MainHand) {
-		x.builder = rogue.Backstab
-		return
-	}
-	// Ghostly Strike -- should only be considered when glyphed
-	if rogue.Talents.GhostlyStrike && rogue.Rotation.UseGhostlyStrike && rogue.GhostlyStrike.IsReady(sim) {
-		x.builder = rogue.GhostlyStrike
-		return
-	}
-	// Hemorrhage
-	if rogue.Talents.Hemorrhage {
-		x.builder = rogue.Hemorrhage
-		return
-	}
-
-	// Sinister Strike
-	x.builder = rogue.SinisterStrike
-}
diff --git a/sim/rogue/talents.go b/sim/rogue/talents.go
index e45f63e03e..d2bf6b5479 100644
--- a/sim/rogue/talents.go
+++ b/sim/rogue/talents.go
@@ -510,12 +510,6 @@ func (rogue *Rogue) registerBladeFlurryCD() {
 		Type:     core.CooldownTypeDPS,
 		Priority: core.CooldownPriorityDefault,
 		ShouldActivate: func(sim *core.Simulation, character *core.Character) bool {
-
-			if rogue.Rotation.MultiTargetSliceFrequency == proto.Rogue_Rotation_Never {
-				// Well let's just cast BF now, no need to optimize around slices that will never be cast
-				return true
-			}
-
 			if sim.GetRemainingDuration() > cooldownDur+dur {
 				// We'll have enough time to cast another BF, so use it immediately to make sure we get the 2nd one.
 				return true
@@ -543,16 +537,10 @@ func (rogue *Rogue) registerAdrenalineRushCD() {
 		OnGain: func(aura *core.Aura, sim *core.Simulation) {
 			rogue.ResetEnergyTick(sim)
 			rogue.ApplyEnergyTickMultiplier(1.0)
-			if r, ok := rogue.rotation.(*rotation_multi); ok {
-				r.planRotation(sim, rogue)
-			}
 		},
 		OnExpire: func(aura *core.Aura, sim *core.Simulation) {
 			rogue.ResetEnergyTick(sim)
 			rogue.ApplyEnergyTickMultiplier(-1.0)
-			if r, ok := rogue.rotation.(*rotation_multi); ok {
-				r.planRotation(sim, rogue)
-			}
 		},
 	})
 
diff --git a/sim/rogue/tricks_of_the_trade.go b/sim/rogue/tricks_of_the_trade.go
index 60c87bb983..13116fc0b2 100644
--- a/sim/rogue/tricks_of_the_trade.go
+++ b/sim/rogue/tricks_of_the_trade.go
@@ -72,20 +72,4 @@ func (rogue *Rogue) registerTricksOfTheTradeSpell() {
 			}
 		},
 	})
-
-	if rogue.Rotation.TricksOfTheTradeFrequency != proto.Rogue_Rotation_Never {
-		// TODO: Support Rogue_Rotation_Once
-		rogue.AddMajorCooldown(core.MajorCooldown{
-			Spell:    rogue.TricksOfTheTrade,
-			Priority: core.CooldownPriorityBloodlust,
-			Type:     core.CooldownTypeDPS,
-			ShouldActivate: func(sim *core.Simulation, character *core.Character) bool {
-				if hasShadowblades {
-					return rogue.CurrentEnergy() <= rogue.maxEnergy-15-rogue.EnergyTickMultiplier*10
-				} else {
-					return true
-				}
-			},
-		})
-	}
 }
diff --git a/sim/rogue/vanish.go b/sim/rogue/vanish.go
index a3cc28cfbf..c22f370cbb 100644
--- a/sim/rogue/vanish.go
+++ b/sim/rogue/vanish.go
@@ -27,38 +27,12 @@ func (rogue *Rogue) registerVanishSpell() {
 			rogue.AutoAttacks.CancelAutoSwing(sim)
 			// Apply stealth
 			rogue.StealthAura.Activate(sim)
-
-			if !rogue.IsUsingAPL {
-				// Master of Subtlety
-				if rogue.Talents.MasterOfSubtlety > 0 {
-					_, premedCPs := checkPremediation(sim, rogue)
-					_, garroteCPs := checkGarrote(sim, rogue)
-
-					if premedCPs > 0 && rogue.ComboPoints()+premedCPs+garroteCPs <= 5 {
-						rogue.Premeditation.Cast(sim, target)
-					}
-
-					if garroteCPs > 0 {
-						rogue.Garrote.Cast(sim, target)
-					}
-				}
-
-				// Break the Stealth effect automatically after a dely with an auto swing
-				pa := &core.PendingAction{
-					NextActionAt: sim.CurrentTime + time.Second * time.Duration(rogue.Options.VanishBreakTime),
-					Priority: core.ActionPriorityAuto,
-				}
-				pa.OnAction = func(sim *core.Simulation) {
-					rogue.BreakStealth(sim)
-					rogue.AutoAttacks.EnableAutoSwing(sim)
-				}
-			}
 		},
 	})
 
 	rogue.AddMajorCooldown(core.MajorCooldown{
-		Spell: rogue.Vanish,
-		Type:  core.CooldownTypeDPS,
+		Spell:    rogue.Vanish,
+		Type:     core.CooldownTypeDPS,
 		Priority: core.CooldownPriorityDrums,
 
 		ShouldActivate: func(s *core.Simulation, c *core.Character) bool {

From c097de661132a1a9947b1c39ba3430a98cc5a057 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Thu, 4 Jan 2024 21:32:32 -0800
Subject: [PATCH 10/28] Finish rogue

---
 sim/core/debuffs.go                           |  21 +-
 sim/mage/TestArcane.results                   |   4 +-
 sim/mage/TestFire.results                     |   8 +-
 sim/mage/TestFrost.results                    | 416 +++++------
 .../retribution/TestRetribution.results       | 664 +++++++++---------
 sim/priest/healing/TestDisc.results           |   2 +-
 sim/priest/healing/TestHoly.results           |   4 +-
 sim/priest/shadow/TestShadow.results          |   4 +-
 sim/priest/smite/TestSmite.results            | 454 ++++++------
 sim/rogue/TestCombat.results                  | 192 ++---
 10 files changed, 877 insertions(+), 892 deletions(-)

diff --git a/sim/core/debuffs.go b/sim/core/debuffs.go
index 7242de9ea7..ee7223233e 100644
--- a/sim/core/debuffs.go
+++ b/sim/core/debuffs.go
@@ -191,24 +191,9 @@ func applyDebuffEffects(target *Unit, targetIdx int, debuffs *proto.Debuffs, rai
 }
 
 func ScheduledMajorArmorAura(aura *Aura, options PeriodicActionOptions, raid *proto.Raid) {
-	// Individual rogue sim rotation option messes with these debuff options,
-	// so it has to be handled separately.
-	allRogues := RaidPlayersWithClass(raid, proto.Class_ClassRogue)
-	singleExposeDelay := len(allRogues) == 1 &&
-		allRogues[0].Spec.(*proto.Player_Rogue).Rogue.Rotation.ExposeArmorFrequency == proto.Rogue_Rotation_Once
-
-	if singleExposeDelay {
-		target := aura.Unit
-		exposeArmorAura := ExposeArmorAura(target, false)
-		exposeArmorAura.ApplyOnExpire(func(_ *Aura, sim *Simulation) {
-			aura.Duration = NeverExpires
-			StartPeriodicAction(sim, options)
-		})
-	} else {
-		aura.OnReset = func(aura *Aura, sim *Simulation) {
-			aura.Duration = NeverExpires
-			StartPeriodicAction(sim, options)
-		}
+	aura.OnReset = func(aura *Aura, sim *Simulation) {
+		aura.Duration = NeverExpires
+		StartPeriodicAction(sim, options)
 	}
 }
 
diff --git a/sim/mage/TestArcane.results b/sim/mage/TestArcane.results
index 5f0cf2c0be..4a70f5b230 100644
--- a/sim/mage/TestArcane.results
+++ b/sim/mage/TestArcane.results
@@ -252,8 +252,8 @@ dps_results: {
 dps_results: {
  key: "TestArcane-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 10727.16841
-  tps: 6570.11619
+  dps: 10738.79708
+  tps: 6577.6561
  }
 }
 dps_results: {
diff --git a/sim/mage/TestFire.results b/sim/mage/TestFire.results
index b939817bf9..d26760097b 100644
--- a/sim/mage/TestFire.results
+++ b/sim/mage/TestFire.results
@@ -770,8 +770,8 @@ dps_results: {
 dps_results: {
  key: "TestFire-Settings-Troll-p3_fire_alliance-Fire-fire-FullBuffs-LongMultiTarget"
  value: {
-  dps: 38064.82304
-  tps: 32963.64697
+  dps: 38085.36222
+  tps: 32980.78258
  }
 }
 dps_results: {
@@ -791,8 +791,8 @@ dps_results: {
 dps_results: {
  key: "TestFire-Settings-Troll-p3_fire_alliance-Fire-fire-NoBuffs-LongMultiTarget"
  value: {
-  dps: 22173.53497
-  tps: 20541.48297
+  dps: 22222.81509
+  tps: 20575.92958
  }
 }
 dps_results: {
diff --git a/sim/mage/TestFrost.results b/sim/mage/TestFrost.results
index cc1f38bac4..dc582270eb 100644
--- a/sim/mage/TestFrost.results
+++ b/sim/mage/TestFrost.results
@@ -46,277 +46,277 @@ character_stats_results: {
 dps_results: {
  key: "TestFrost-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 7933.66277
-  tps: 6459.8041
+  dps: 7928.24208
+  tps: 6453.45112
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 7968.21694
-  tps: 6488.27328
+  dps: 7962.77382
+  tps: 6481.89515
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 7765.31653
-  tps: 6324.30567
+  dps: 7760.1538
+  tps: 6318.09095
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 7652.00052
-  tps: 6227.57151
-  hps: 92.573
+  dps: 7646.41564
+  tps: 6221.07535
+  hps: 92.57675
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 7652.00052
-  tps: 6227.57151
-  hps: 92.573
+  dps: 7646.41564
+  tps: 6221.07535
+  hps: 92.57675
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 7783.7779
-  tps: 6341.22794
+  dps: 7782.85819
+  tps: 6339.33425
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 6024.87702
-  tps: 4845.65031
+  dps: 6005.44274
+  tps: 4826.77708
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Bloodmage'sRegalia"
  value: {
-  dps: 8140.05763
-  tps: 6685.8876
+  dps: 8143.08775
+  tps: 6688.26693
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 7803.91273
-  tps: 6229.21586
+  dps: 7798.72055
+  tps: 6223.09271
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 8004.42685
-  tps: 6540.31281
+  dps: 8003.40047
+  tps: 6538.31272
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
   hps: 64
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 7727.10393
-  tps: 6294.20537
+  dps: 7721.01592
+  tps: 6287.16324
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 7734.47564
-  tps: 6306.31917
+  dps: 7731.37855
+  tps: 6303.73706
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 7690.85322
-  tps: 6274.32423
+  dps: 7684.58603
+  tps: 6266.86577
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Death'sChoice-47464"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 7685.02105
-  tps: 6253.78087
+  dps: 7678.84495
+  tps: 6246.81867
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Defender'sCode-40257"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 7784.47384
-  tps: 6342.56315
+  dps: 7783.55414
+  tps: 6340.66928
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-DislodgedForeignObject-50348"
  value: {
-  dps: 8384.30782
-  tps: 6875.40602
+  dps: 8381.41843
+  tps: 6873.3235
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 8289.8124
-  tps: 6783.32805
+  dps: 8297.80523
+  tps: 6789.83899
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 7765.31653
-  tps: 6324.30567
+  dps: 7760.1538
+  tps: 6318.09095
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 7813.94105
-  tps: 6365.39795
+  dps: 7812.27004
+  tps: 6362.69935
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 7783.81326
-  tps: 6341.9621
+  dps: 7782.89356
+  tps: 6340.06823
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 7778.77525
-  tps: 6337.46563
+  dps: 7777.85554
+  tps: 6335.57176
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 7755.40406
-  tps: 6315.42295
+  dps: 7759.64948
+  tps: 6319.89085
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 7765.31653
-  tps: 6324.30567
+  dps: 7760.1538
+  tps: 6318.09095
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 7727.1541
-  tps: 6295.38387
+  dps: 7723.41199
+  tps: 6291.61522
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 7913.07517
-  tps: 6459.71001
+  dps: 7906.80366
+  tps: 6452.50593
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 7673.31368
-  tps: 6245.89566
+  dps: 7668.23295
+  tps: 6239.78194
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 7826.85897
-  tps: 6371.80846
+  dps: 7821.50761
+  tps: 6365.5332
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ForgeEmber-37660"
  value: {
-  dps: 7831.62918
-  tps: 6382.78835
+  dps: 7824.55194
+  tps: 6374.99683
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 7803.91273
-  tps: 6356.04504
+  dps: 7798.72055
+  tps: 6349.7974
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 7796.19349
-  tps: 6349.69717
+  dps: 7791.0072
+  tps: 6343.45611
  }
 }
 dps_results: {
@@ -329,15 +329,15 @@ dps_results: {
 dps_results: {
  key: "TestFrost-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-FuturesightRune-38763"
  value: {
-  dps: 7750.19737
-  tps: 6307.40657
+  dps: 7744.89597
+  tps: 6301.18733
  }
 }
 dps_results: {
@@ -350,85 +350,85 @@ dps_results: {
 dps_results: {
  key: "TestFrost-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 7950.93986
-  tps: 6474.03869
+  dps: 7945.50795
+  tps: 6467.67314
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 7990.20596
-  tps: 6506.39003
+  dps: 7984.74857
+  tps: 6499.9959
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 7712.63873
-  tps: 6291.8092
+  dps: 7726.20314
+  tps: 6306.13936
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Heartpierce-49982"
  value: {
-  dps: 8004.42685
-  tps: 6540.31281
+  dps: 8003.40047
+  tps: 6538.31272
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Heartpierce-50641"
  value: {
-  dps: 8004.42685
-  tps: 6540.31281
+  dps: 8003.40047
+  tps: 6538.31272
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
-  dps: 7936.00618
-  tps: 6480.54928
+  dps: 7930.51116
+  tps: 6474.17485
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 7783.81326
-  tps: 6341.9621
+  dps: 7782.89356
+  tps: 6340.06823
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 7778.77525
-  tps: 6337.46563
+  dps: 7777.85554
+  tps: 6335.57176
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-IncisorFragment-37723"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 7774.27285
-  tps: 6336.45718
+  dps: 7772.45693
+  tps: 6333.79039
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 7765.31653
-  tps: 6324.30567
+  dps: 7760.1538
+  tps: 6318.09095
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Khadgar'sRegalia"
  value: {
-  dps: 6575.79129
-  tps: 5306.49837
+  dps: 6580.00377
+  tps: 5309.1505
  }
 }
 dps_results: {
@@ -441,239 +441,239 @@ dps_results: {
 dps_results: {
  key: "TestFrost-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 7652.83765
-  tps: 6225.62283
+  dps: 7647.59946
+  tps: 6219.47581
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 7698.15546
-  tps: 6263.90226
+  dps: 7691.79354
+  tps: 6256.75774
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 7848.80614
-  tps: 6384.95591
+  dps: 7847.31458
+  tps: 6384.09669
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Nibelung-49992"
  value: {
-  dps: 8004.42685
-  tps: 6540.31281
+  dps: 8003.40047
+  tps: 6538.31272
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Nibelung-50648"
  value: {
-  dps: 8004.42685
-  tps: 6540.31281
+  dps: 8003.40047
+  tps: 6538.31272
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 7765.31653
-  tps: 6324.30567
+  dps: 7760.1538
+  tps: 6318.09095
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 7765.31653
-  tps: 6324.30567
+  dps: 7760.1538
+  tps: 6318.09095
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 7651.50716
-  tps: 6227.25922
+  dps: 7646.26897
+  tps: 6221.11085
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 7765.31653
-  tps: 6324.30567
+  dps: 7760.1538
+  tps: 6318.09095
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 7765.31653
-  tps: 6324.30567
+  dps: 7760.1538
+  tps: 6318.09095
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 7998.48484
-  tps: 6538.10806
+  dps: 7998.96913
+  tps: 6538.92968
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 8047.7877
-  tps: 6582.45344
+  dps: 8048.33749
+  tps: 6583.34161
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 7984.09185
-  tps: 6521.00509
+  dps: 7978.38291
+  tps: 6514.28941
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 7765.2869
-  tps: 6323.1634
+  dps: 7760.12417
+  tps: 6316.94905
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 7900.7323
-  tps: 6435.45003
+  dps: 7895.33302
+  tps: 6429.12257
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 7932.14518
-  tps: 6461.58463
+  dps: 7926.72551
+  tps: 6455.23432
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SoulPreserver-37111"
  value: {
-  dps: 7770.31579
-  tps: 6325.22253
+  dps: 7765.00113
+  tps: 6318.98842
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SouloftheDead-40382"
  value: {
-  dps: 7725.7366
-  tps: 6296.32776
+  dps: 7719.6486
+  tps: 6289.29418
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SparkofLife-37657"
  value: {
-  dps: 7757.64893
-  tps: 6327.51453
+  dps: 7755.89541
+  tps: 6324.01321
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 7696.99765
-  tps: 6273.70353
+  dps: 7706.01629
+  tps: 6281.8047
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 7765.31653
-  tps: 6324.30567
+  dps: 7760.1538
+  tps: 6318.09095
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 7765.31653
-  tps: 6324.30567
+  dps: 7760.1538
+  tps: 6318.09095
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 7765.31653
-  tps: 6324.30567
+  dps: 7760.1538
+  tps: 6318.09095
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 7652.81744
-  tps: 6226.83399
+  dps: 7647.57925
+  tps: 6220.68724
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 7691.42109
-  tps: 6261.64718
+  dps: 7686.34035
+  tps: 6255.4996
  }
 }
 dps_results: {
@@ -686,106 +686,106 @@ dps_results: {
 dps_results: {
  key: "TestFrost-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 7652.51748
-  tps: 6228.1685
+  dps: 7647.27929
+  tps: 6222.02013
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TheTwinBladesofAzzinoth"
  value: {
-  dps: 6372.08189
-  tps: 5180.43316
+  dps: 6369.89154
+  tps: 5180.72909
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 7765.31653
-  tps: 6324.30567
+  dps: 7760.1538
+  tps: 6318.09095
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 7696.99765
-  tps: 6273.70353
+  dps: 7706.01629
+  tps: 6281.8047
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 7696.99765
-  tps: 6273.70353
+  dps: 7706.01629
+  tps: 6281.8047
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 7803.91273
-  tps: 6356.04504
+  dps: 7798.72055
+  tps: 6349.7974
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 7796.19349
-  tps: 6349.69717
+  dps: 7791.0072
+  tps: 6343.45611
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 7779.60815
-  tps: 6327.85299
+  dps: 7784.88807
+  tps: 6332.07288
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 7796.19349
-  tps: 6349.69717
+  dps: 7791.0072
+  tps: 6343.45611
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 7803.91273
-  tps: 6356.04504
+  dps: 7798.72055
+  tps: 6349.7974
  }
 }
 dps_results: {
  key: "TestFrost-AllItems-WingedTalisman-37844"
  value: {
-  dps: 7755.11162
-  tps: 6320.25051
+  dps: 7749.91677
+  tps: 6314.13796
  }
 }
 dps_results: {
  key: "TestFrost-Average-Default"
  value: {
-  dps: 8088.78057
-  tps: 6620.05808
+  dps: 8088.27706
+  tps: 6619.63625
  }
 }
 dps_results: {
  key: "TestFrost-Settings-Troll-p3_frost_alliance-Frost-frost-FullBuffs-LongMultiTarget"
  value: {
-  dps: 8004.42685
-  tps: 6818.27786
+  dps: 8003.40047
+  tps: 6815.93838
  }
 }
 dps_results: {
  key: "TestFrost-Settings-Troll-p3_frost_alliance-Frost-frost-FullBuffs-LongSingleTarget"
  value: {
-  dps: 8004.42685
-  tps: 6540.31281
+  dps: 8003.40047
+  tps: 6538.31272
  }
 }
 dps_results: {
  key: "TestFrost-Settings-Troll-p3_frost_alliance-Frost-frost-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 10047.18185
-  tps: 8250.71555
+  dps: 10069.72226
+  tps: 8272.31852
  }
 }
 dps_results: {
@@ -848,7 +848,7 @@ dps_results: {
 dps_results: {
  key: "TestFrost-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 8004.42685
-  tps: 6540.31281
+  dps: 8003.40047
+  tps: 6538.31272
  }
 }
diff --git a/sim/paladin/retribution/TestRetribution.results b/sim/paladin/retribution/TestRetribution.results
index 80ffc5e518..f2a5eba241 100644
--- a/sim/paladin/retribution/TestRetribution.results
+++ b/sim/paladin/retribution/TestRetribution.results
@@ -46,8 +46,8 @@ character_stats_results: {
 dps_results: {
  key: "TestRetribution-AllItems-AegisBattlegear"
  value: {
-  dps: 6268.37146
-  tps: 6353.83098
+  dps: 6267.52464
+  tps: 6352.98416
   dtps: 9.92959
  }
 }
@@ -62,48 +62,48 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 6465.57257
-  tps: 6551.05699
+  dps: 6465.71452
+  tps: 6551.19894
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 6471.12751
-  tps: 6556.61193
+  dps: 6471.26908
+  tps: 6556.75349
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-AshtongueTalismanofZeal-32489"
  value: {
-  dps: 6468.86887
-  tps: 6554.34976
+  dps: 6469.44227
+  tps: 6554.92317
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 6530.15775
-  tps: 6615.64217
+  dps: 6530.10682
+  tps: 6615.59123
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 6628.55546
-  tps: 6713.89718
+  dps: 6628.55432
+  tps: 6713.89603
   dtps: 10.03858
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 6420.11322
-  tps: 6505.59764
+  dps: 6420.2583
+  tps: 6505.74271
   dtps: 9.48456
   hps: 94.7032
  }
@@ -111,8 +111,8 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 6420.11322
-  tps: 6505.59764
+  dps: 6420.2583
+  tps: 6505.74271
   dtps: 9.48456
   hps: 94.7032
  }
@@ -120,96 +120,96 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 6544.91075
-  tps: 6630.33528
+  dps: 6545.05403
+  tps: 6630.47856
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Beast-tamer'sShoulders-30892"
  value: {
-  dps: 6398.97549
-  tps: 6484.17274
+  dps: 6399.15708
+  tps: 6484.35433
   dtps: 9.23882
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-BlessedBattlegearofUndeadSlaying"
  value: {
-  dps: 5408.7184
-  tps: 5491.96468
+  dps: 5408.57284
+  tps: 5491.81912
   dtps: 9.23882
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-BlessedGarboftheUndeadSlayer"
  value: {
-  dps: 5352.69147
-  tps: 5435.99401
+  dps: 5352.64254
+  tps: 5435.94508
   dtps: 9.23882
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 5184.95794
-  tps: 5268.18041
+  dps: 5184.81786
+  tps: 5268.04033
   dtps: 9.23882
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 6536.35928
-  tps: 6491.11652
+  dps: 6536.30717
+  tps: 6491.06544
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Bryntroll,theBoneArbiter-50415"
  value: {
-  dps: 6999.49261
-  tps: 7084.97703
+  dps: 6999.6297
+  tps: 7085.11412
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Bryntroll,theBoneArbiter-50709"
  value: {
-  dps: 7031.30525
-  tps: 7116.78967
+  dps: 7031.44182
+  tps: 7116.92623
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 6663.47166
-  tps: 6748.95608
+  dps: 6663.61419
+  tps: 6749.09861
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 9.92959
   hps: 64
  }
@@ -217,216 +217,216 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 6518.45383
-  tps: 6603.93825
+  dps: 6518.42139
+  tps: 6603.90581
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 6568.11706
-  tps: 6653.46885
+  dps: 6568.56742
+  tps: 6653.91921
   dtps: 9.77043
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 6420.37555
-  tps: 6505.33665
+  dps: 6420.52064
+  tps: 6505.48174
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Death'sChoice-47464"
  value: {
-  dps: 7001.45475
-  tps: 7086.93917
+  dps: 7001.57473
+  tps: 7087.05915
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 6484.37787
-  tps: 6569.86229
+  dps: 6484.24836
+  tps: 6569.73278
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 6754.79378
-  tps: 6840.44585
+  dps: 6755.26996
+  tps: 6840.92204
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 6801.15779
-  tps: 6886.7823
+  dps: 6800.03362
+  tps: 6885.65813
   dtps: 10.29273
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Defender'sCode-40257"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 6547.8397
-  tps: 6633.32412
+  dps: 6547.98298
+  tps: 6633.4674
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-DislodgedForeignObject-50348"
  value: {
-  dps: 6640.22126
-  tps: 6726.41689
+  dps: 6641.1179
+  tps: 6727.31352
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 6607.8895
-  tps: 6693.70378
+  dps: 6609.03015
+  tps: 6694.84443
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 6530.15775
-  tps: 6615.64217
+  dps: 6530.10682
+  tps: 6615.59123
   dtps: 9.731
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 6536.35928
-  tps: 6621.8142
+  dps: 6536.30717
+  tps: 6621.76208
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 6544.91075
-  tps: 6630.39516
+  dps: 6545.05403
+  tps: 6630.53844
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 6541.49838
-  tps: 6626.98279
+  dps: 6541.64166
+  tps: 6627.12607
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 6540.37893
-  tps: 6626.39435
+  dps: 6540.15806
+  tps: 6626.17348
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 6530.15775
-  tps: 6615.64217
+  dps: 6530.10682
+  tps: 6615.59123
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 6576.99883
-  tps: 6662.50294
+  dps: 6577.62612
+  tps: 6663.13023
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 6541.10959
-  tps: 6626.59401
+  dps: 6541.07166
+  tps: 6626.55608
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 6420.37555
-  tps: 6505.7242
+  dps: 6420.52064
+  tps: 6505.86929
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 6448.40275
-  tps: 6533.88717
+  dps: 6448.5459
+  tps: 6534.03031
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ForgeEmber-37660"
  value: {
-  dps: 6500.39038
-  tps: 6585.8748
+  dps: 6500.35794
+  tps: 6585.84236
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 6536.35928
-  tps: 6621.8437
+  dps: 6536.30717
+  tps: 6621.79159
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 6535.11898
-  tps: 6620.6034
+  dps: 6535.0671
+  tps: 6620.55151
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-FuriousGladiator'sLibramofFortitude-42853"
  value: {
-  dps: 6682.2126
-  tps: 6767.69702
+  dps: 6682.35485
+  tps: 6767.83927
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-FuturesightRune-38763"
  value: {
-  dps: 6436.03038
-  tps: 6521.5148
+  dps: 6436.17439
+  tps: 6521.65881
   dtps: 9.92959
  }
 }
@@ -441,80 +441,80 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 6468.35004
-  tps: 6553.83446
+  dps: 6468.4918
+  tps: 6553.97622
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 6474.66247
-  tps: 6560.14689
+  dps: 6474.80379
+  tps: 6560.28821
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 6540.91693
-  tps: 6626.22985
+  dps: 6541.36776
+  tps: 6626.68068
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-HatefulGladiator'sLibramofFortitude-42851"
  value: {
-  dps: 6651.70785
-  tps: 6737.19227
+  dps: 6651.85055
+  tps: 6737.33497
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
-  dps: 6431.2686
-  tps: 6516.75301
+  dps: 6431.41229
+  tps: 6516.89671
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 6544.91075
-  tps: 6630.39516
+  dps: 6545.05403
+  tps: 6630.53844
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 6541.49838
-  tps: 6626.98279
+  dps: 6541.64166
+  tps: 6627.12607
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-IncisorFragment-37723"
  value: {
-  dps: 6584.63506
-  tps: 6670.11948
+  dps: 6584.77153
+  tps: 6670.25595
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 6530.15775
-  tps: 6617.24615
+  dps: 6530.10682
+  tps: 6617.19522
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 6562.45389
-  tps: 6647.93831
+  dps: 6562.40098
+  tps: 6647.8854
   dtps: 9.92959
   hps: 12.10749
  }
@@ -522,72 +522,72 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-LastWord-50179"
  value: {
-  dps: 6861.95405
-  tps: 6947.43847
+  dps: 6862.09336
+  tps: 6947.57778
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-LastWord-50708"
  value: {
-  dps: 6891.5707
-  tps: 6977.05512
+  dps: 6891.70956
+  tps: 6977.19398
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-LibramofFuriousBlows-37574"
  value: {
-  dps: 6594.19122
-  tps: 6679.67564
+  dps: 6594.26071
+  tps: 6679.74513
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-LibramofObstruction-40707"
  value: {
-  dps: 6569.76696
-  tps: 6655.25138
+  dps: 6569.9109
+  tps: 6655.39532
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-LibramofReciprocation-40706"
  value: {
-  dps: 6569.76696
-  tps: 6655.25138
+  dps: 6569.9109
+  tps: 6655.39532
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-LibramofThreeTruths-50455"
  value: {
-  dps: 6988.96862
-  tps: 7074.45304
+  dps: 6989.10988
+  tps: 7074.5943
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-LibramofValiance-47661"
  value: {
-  dps: 6956.365
-  tps: 7041.84942
+  dps: 6956.32355
+  tps: 7041.80797
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-LibramoftheSacredShield-45145"
  value: {
-  dps: 6569.76696
-  tps: 6655.25138
+  dps: 6569.9109
+  tps: 6655.39532
   dtps: 9.92959
  }
 }
@@ -602,8 +602,8 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-LightswornBattlegear"
  value: {
-  dps: 7650.81686
-  tps: 7738.32677
+  dps: 7652.37702
+  tps: 7739.88693
   dtps: 9.92959
  }
 }
@@ -618,104 +618,104 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 6553.90912
-  tps: 6639.78002
+  dps: 6554.05506
+  tps: 6639.92597
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 6479.55694
-  tps: 6565.04136
+  dps: 6479.3976
+  tps: 6564.88202
   dtps: 10.29273
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 6556.35971
-  tps: 6641.84413
+  dps: 6556.3074
+  tps: 6641.79181
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 6562.52488
-  tps: 6648.0093
+  dps: 6562.47224
+  tps: 6647.95666
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 8.19524
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 6530.15775
-  tps: 6615.64217
+  dps: 6530.10682
+  tps: 6615.59123
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 6530.15775
-  tps: 6615.64217
+  dps: 6530.10682
+  tps: 6615.59123
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-RedemptionBattlegear"
  value: {
-  dps: 5847.28087
-  tps: 5936.60002
+  dps: 5847.91647
+  tps: 5937.23562
   dtps: 9.23882
  }
 }
@@ -730,136 +730,136 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 6683.04766
-  tps: 6768.56404
+  dps: 6682.32469
+  tps: 6767.84107
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 6716.19926
-  tps: 6801.71564
+  dps: 6715.46847
+  tps: 6800.98485
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 6663.47166
-  tps: 6748.95608
+  dps: 6663.61419
+  tps: 6749.09861
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-RelentlessGladiator'sLibramofFortitude-42854"
  value: {
-  dps: 6704.07703
-  tps: 6789.56145
+  dps: 6704.21895
+  tps: 6789.70337
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 6530.15775
-  tps: 6615.08129
+  dps: 6530.10682
+  tps: 6615.03035
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SavageGladiator'sLibramofFortitude-42611"
  value: {
-  dps: 6642.43152
-  tps: 6727.91594
+  dps: 6642.57436
+  tps: 6728.05878
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Shadowmourne-49623"
  value: {
-  dps: 7904.17373
-  tps: 7989.49917
+  dps: 7904.00969
+  tps: 7989.33513
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 6.55074
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 6460.27012
-  tps: 6546.31676
+  dps: 6460.41245
+  tps: 6546.45908
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 6465.32007
-  tps: 6551.36671
+  dps: 6465.46204
+  tps: 6551.50868
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SoulPreserver-37111"
  value: {
-  dps: 6439.31285
-  tps: 6524.79727
+  dps: 6439.45662
+  tps: 6524.94104
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SouloftheDead-40382"
  value: {
-  dps: 6511.50012
-  tps: 6598.69608
+  dps: 6511.46767
+  tps: 6598.66364
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SparkofLife-37657"
  value: {
-  dps: 6506.70356
-  tps: 6588.86818
+  dps: 6505.76336
+  tps: 6587.92798
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 6560.29702
-  tps: 6645.6894
+  dps: 6560.42813
+  tps: 6645.82051
   dtps: 9.92959
  }
 }
@@ -874,48 +874,48 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 6562.52488
-  tps: 6648.0093
+  dps: 6562.47224
+  tps: 6647.95666
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 6556.35971
-  tps: 6641.84413
+  dps: 6556.3074
+  tps: 6641.79181
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 6545.57067
-  tps: 6631.05509
+  dps: 6545.51892
+  tps: 6631.00334
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 6420.37555
-  tps: 6505.39119
+  dps: 6420.52064
+  tps: 6505.53628
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 6420.37555
-  tps: 6505.85997
+  dps: 6420.52064
+  tps: 6506.00506
   dtps: 9.92959
  }
 }
@@ -930,72 +930,72 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 6578.68896
-  tps: 6664.31897
+  dps: 6578.78508
+  tps: 6664.4151
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 7023.73776
-  tps: 7109.97541
+  dps: 7024.62707
+  tps: 7110.86472
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 7123.31213
-  tps: 7209.60107
+  dps: 7123.34647
+  tps: 7209.63541
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 6536.35928
-  tps: 6621.8437
+  dps: 6536.30717
+  tps: 6621.79159
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 6535.11898
-  tps: 6620.6034
+  dps: 6535.0671
+  tps: 6620.55151
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 6535.26623
-  tps: 6620.91519
+  dps: 6536.78528
+  tps: 6622.43424
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TomeoftheLightbringer-32368"
  value: {
-  dps: 6569.76696
-  tps: 6655.25138
+  dps: 6569.9109
+  tps: 6655.39532
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 6535.11898
-  tps: 6620.6034
+  dps: 6535.0671
+  tps: 6620.55151
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 6536.35928
-  tps: 6621.8437
+  dps: 6536.30717
+  tps: 6621.79159
   dtps: 9.92959
  }
 }
@@ -1018,64 +1018,64 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-AllItems-UndeadSlayer'sBlessedArmor"
  value: {
-  dps: 5397.45216
-  tps: 5481.00412
+  dps: 5397.40235
+  tps: 5480.95432
   dtps: 9.23882
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-Val'anyr,HammerofAncientKings-46017"
  value: {
-  dps: 5145.39682
-  tps: 5230.26174
+  dps: 5145.63201
+  tps: 5230.49693
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-WingedTalisman-37844"
  value: {
-  dps: 6439.86531
-  tps: 6525.34973
+  dps: 6440.00643
+  tps: 6525.49085
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-AllItems-WrathfulGladiator'sLibramofFortitude-51478"
  value: {
-  dps: 6729.06495
-  tps: 6814.54937
+  dps: 6729.2065
+  tps: 6814.69091
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Average-Default"
  value: {
-  dps: 6668.56873
-  tps: 6753.17123
+  dps: 6668.69511
+  tps: 6753.29762
   dtps: 13.59845
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOC-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 18965.91539
-  tps: 20678.01503
+  dps: 18965.75625
+  tps: 20677.85589
   dtps: 10.57482
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOC-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5477.99131
-  tps: 5563.62785
+  dps: 5477.65446
+  tps: 5563.291
   dtps: 11.82069
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOC-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6192.80238
-  tps: 6287.44359
+  dps: 6191.11812
+  tps: 6281.82806
   dtps: 59.10347
  }
 }
@@ -1103,24 +1103,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOR-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 16956.30594
-  tps: 18668.0915
+  dps: 16956.62716
+  tps: 18668.41273
   dtps: 10.33353
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOR-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5849.18097
-  tps: 5934.73392
+  dps: 5849.24722
+  tps: 5934.80017
   dtps: 11.82069
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOR-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6634.80579
-  tps: 6729.31363
+  dps: 6635.13703
+  tps: 6725.70508
   dtps: 59.10347
  }
 }
@@ -1148,24 +1148,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 18993.19487
-  tps: 20701.35603
+  dps: 18992.27977
+  tps: 20700.44093
   dtps: 8.80605
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6663.47166
-  tps: 6748.95608
+  dps: 6663.61419
+  tps: 6749.09861
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7381.2945
-  tps: 7473.81754
+  dps: 7382.00712
+  tps: 7472.77717
   dtps: 49.64794
  }
 }
@@ -1193,24 +1193,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOV-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 18993.19487
-  tps: 20701.35603
+  dps: 18992.27977
+  tps: 20700.44093
   dtps: 8.80605
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOV-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6663.47166
-  tps: 6748.95608
+  dps: 6663.61419
+  tps: 6749.09861
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-BloodElf-p1-Retribution Paladin SOV-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7381.2945
-  tps: 7473.81754
+  dps: 7382.00712
+  tps: 7472.77717
   dtps: 49.64794
  }
 }
@@ -1238,24 +1238,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOC-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19088.7306
-  tps: 20780.43386
+  dps: 19088.57134
+  tps: 20780.2746
   dtps: 10.14629
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOC-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5488.69007
-  tps: 5573.32505
+  dps: 5488.35294
+  tps: 5572.98793
   dtps: 11.82069
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOC-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6196.77971
-  tps: 6291.46684
+  dps: 6195.09407
+  tps: 6285.8247
   dtps: 59.10347
  }
 }
@@ -1283,24 +1283,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOR-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 17080.08349
-  tps: 18772.80641
+  dps: 17080.40494
+  tps: 18773.12786
   dtps: 9.95262
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOR-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5863.30716
-  tps: 5947.85146
+  dps: 5863.37349
+  tps: 5947.91779
   dtps: 11.82069
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOR-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6647.36735
-  tps: 6741.92039
+  dps: 6647.69899
+  tps: 6738.28782
   dtps: 59.10347
  }
 }
@@ -1328,24 +1328,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19119.70694
-  tps: 20805.09347
+  dps: 19118.75828
+  tps: 20804.14482
   dtps: 8.48862
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6676.72376
-  tps: 6761.15752
+  dps: 6676.86616
+  tps: 6761.29993
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7390.6795
-  tps: 7483.24301
+  dps: 7391.39147
+  tps: 7482.18276
   dtps: 49.64794
  }
 }
@@ -1373,24 +1373,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOV-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19119.70694
-  tps: 20805.09347
+  dps: 19118.75828
+  tps: 20804.14482
   dtps: 8.48862
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOV-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6676.72376
-  tps: 6761.15752
+  dps: 6676.86616
+  tps: 6761.29993
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Draenei-p1-Retribution Paladin SOV-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7390.6795
-  tps: 7483.24301
+  dps: 7391.39147
+  tps: 7482.18276
   dtps: 49.64794
  }
 }
@@ -1418,24 +1418,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOC-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19001.07213
-  tps: 20693.66744
+  dps: 19000.91274
+  tps: 20693.50805
   dtps: 10.14629
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOC-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5483.6814
-  tps: 5568.37492
+  dps: 5483.34399
+  tps: 5568.03751
   dtps: 11.82069
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOC-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6192.92007
-  tps: 6287.62251
+  dps: 6191.23305
+  tps: 6281.97057
   dtps: 59.10347
  }
 }
@@ -1463,24 +1463,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOR-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 16988.83779
-  tps: 18680.83618
+  dps: 16989.15945
+  tps: 18681.15785
   dtps: 9.95262
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOR-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5860.18948
-  tps: 5944.76826
+  dps: 5860.25589
+  tps: 5944.83467
   dtps: 11.82069
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOR-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6643.44831
-  tps: 6738.01641
+  dps: 6643.78035
+  tps: 6734.3761
   dtps: 59.10347
  }
 }
@@ -1508,24 +1508,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19035.16533
-  tps: 20720.23806
+  dps: 19034.21526
+  tps: 20719.28799
   dtps: 8.48862
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6674.98513
-  tps: 6759.41251
+  dps: 6675.1274
+  tps: 6759.55479
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7389.18398
-  tps: 7481.76099
+  dps: 7389.89531
+  tps: 7480.69367
   dtps: 49.64794
  }
 }
@@ -1553,24 +1553,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOV-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19035.16533
-  tps: 20720.23806
+  dps: 19034.21526
+  tps: 20719.28799
   dtps: 8.48862
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOV-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6674.98513
-  tps: 6759.41251
+  dps: 6675.1274
+  tps: 6759.55479
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Dwarf-p1-Retribution Paladin SOV-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7389.18398
-  tps: 7481.76099
+  dps: 7389.89531
+  tps: 7480.69367
   dtps: 49.64794
  }
 }
@@ -1598,24 +1598,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOC-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 18979.81052
-  tps: 20672.24611
+  dps: 18979.65129
+  tps: 20672.08688
   dtps: 10.14629
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOC-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5480.23
-  tps: 5564.91574
+  dps: 5479.89294
+  tps: 5564.57868
   dtps: 11.82069
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOC-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6191.78073
-  tps: 6286.46786
+  dps: 6190.09544
+  tps: 6280.82607
   dtps: 59.10347
  }
 }
@@ -1643,24 +1643,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOR-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 16969.61369
-  tps: 18661.45781
+  dps: 16969.93508
+  tps: 18661.7792
   dtps: 9.95262
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOR-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5853.43767
-  tps: 5938.00926
+  dps: 5853.50398
+  tps: 5938.07557
   dtps: 11.82069
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOR-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6637.28889
-  tps: 6731.84193
+  dps: 6637.62044
+  tps: 6728.20926
   dtps: 59.10347
  }
 }
@@ -1688,24 +1688,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19008.65557
-  tps: 20693.6027
+  dps: 19007.73945
+  tps: 20692.68658
   dtps: 8.48862
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6666.72119
-  tps: 6751.1424
+  dps: 6666.86363
+  tps: 6751.28483
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOV 2 Target Swapping-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7383.00254
-  tps: 7475.56606
+  dps: 7383.71468
+  tps: 7474.50596
   dtps: 49.64794
  }
 }
@@ -1733,24 +1733,24 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOV-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19008.65557
-  tps: 20693.6027
+  dps: 19007.73945
+  tps: 20692.68658
   dtps: 8.48862
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOV-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6666.72119
-  tps: 6751.1424
+  dps: 6666.86363
+  tps: 6751.28483
   dtps: 9.92959
  }
 }
 dps_results: {
  key: "TestRetribution-Settings-Human-p1-Retribution Paladin SOV-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7383.00254
-  tps: 7475.56606
+  dps: 7383.71468
+  tps: 7474.50596
   dtps: 49.64794
  }
 }
@@ -1778,8 +1778,8 @@ dps_results: {
 dps_results: {
  key: "TestRetribution-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 6317.40296
-  tps: 6402.77929
+  dps: 6317.26995
+  tps: 6402.64627
   dtps: 9.92959
  }
 }
diff --git a/sim/priest/healing/TestDisc.results b/sim/priest/healing/TestDisc.results
index 2e1c8bb972..a57d5dc3b3 100644
--- a/sim/priest/healing/TestDisc.results
+++ b/sim/priest/healing/TestDisc.results
@@ -343,7 +343,7 @@ dps_results: {
 dps_results: {
  key: "TestDisc-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  tps: 18.9916
+  tps: 19.00072
   hps: 3650.23426
  }
 }
diff --git a/sim/priest/healing/TestHoly.results b/sim/priest/healing/TestHoly.results
index 7de62a4da8..91517efd66 100644
--- a/sim/priest/healing/TestHoly.results
+++ b/sim/priest/healing/TestHoly.results
@@ -172,8 +172,8 @@ dps_results: {
 dps_results: {
  key: "TestHoly-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  tps: 35.9717
-  hps: 4983.05245
+  tps: 36.25385
+  hps: 4991.42137
  }
 }
 dps_results: {
diff --git a/sim/priest/shadow/TestShadow.results b/sim/priest/shadow/TestShadow.results
index a353b97a97..fd4adab58a 100644
--- a/sim/priest/shadow/TestShadow.results
+++ b/sim/priest/shadow/TestShadow.results
@@ -798,8 +798,8 @@ dps_results: {
 dps_results: {
  key: "TestShadow-AllItems-Zabra'sRaiment"
  value: {
-  dps: 6507.98519
-  tps: 6310.63776
+  dps: 6508.31094
+  tps: 6310.96351
  }
 }
 dps_results: {
diff --git a/sim/priest/smite/TestSmite.results b/sim/priest/smite/TestSmite.results
index 110aae8059..c37dd30657 100644
--- a/sim/priest/smite/TestSmite.results
+++ b/sim/priest/smite/TestSmite.results
@@ -46,739 +46,739 @@ character_stats_results: {
 dps_results: {
  key: "TestSmite-AllItems-AbsolutionRegalia"
  value: {
-  dps: 1995.47614
-  tps: 1685.05564
+  dps: 1995.65978
+  tps: 1685.19061
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 2893.83018
-  tps: 2430.91688
+  dps: 2894.04696
+  tps: 2431.07532
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 2907.46733
-  tps: 2442.2371
+  dps: 2907.685
+  tps: 2442.39619
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 2844.46107
-  tps: 2388.30346
+  dps: 2844.67304
+  tps: 2388.45874
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 2783.94305
-  tps: 2340.58703
+  dps: 2784.21341
+  tps: 2340.78415
   hps: 90.33849
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 2783.94305
-  tps: 2340.58703
+  dps: 2784.21341
+  tps: 2340.78415
   hps: 90.33849
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 2896.39463
-  tps: 2430.31712
+  dps: 2896.6066
+  tps: 2430.4724
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 2213.89453
-  tps: 1867.97361
+  dps: 2214.0364
+  tps: 1868.07834
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 2859.73182
-  tps: 2353.55626
+  dps: 2859.94481
+  tps: 2353.70915
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 2873.81795
-  tps: 2410.95042
+  dps: 2874.02992
+  tps: 2411.10571
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
   hps: 64
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-CrimsonAcolyte'sRaiment"
  value: {
-  dps: 3656.275
-  tps: 3094.26738
+  dps: 3656.43315
+  tps: 3094.38378
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-CrimsonAcolyte'sRegalia"
  value: {
-  dps: 3403.63853
-  tps: 2858.7041
+  dps: 3403.97761
+  tps: 2858.95096
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 2809.40067
-  tps: 2358.48127
+  dps: 2809.61022
+  tps: 2358.63454
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 2855.92826
-  tps: 2406.4309
+  dps: 2856.17338
+  tps: 2406.6212
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 2988.6755
-  tps: 2512.01108
+  dps: 2988.88506
+  tps: 2512.16435
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Death'sChoice-47464"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 2792.49553
-  tps: 2345.86351
+  dps: 2792.70508
+  tps: 2346.01678
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Defender'sCode-40257"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 2848.58975
-  tps: 2390.83279
+  dps: 2848.80173
+  tps: 2390.98808
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-DislodgedForeignObject-50348"
  value: {
-  dps: 2891.88465
-  tps: 2422.64278
+  dps: 2892.00829
+  tps: 2422.7308
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 2878.07976
-  tps: 2413.54911
+  dps: 2878.27734
+  tps: 2413.69279
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 2844.46107
-  tps: 2388.30346
+  dps: 2844.67304
+  tps: 2388.45874
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 2901.38893
-  tps: 2436.84342
+  dps: 2901.60191
+  tps: 2436.99942
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 2846.23143
-  tps: 2388.88121
+  dps: 2846.44341
+  tps: 2389.0365
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 2845.82027
-  tps: 2389.3935
+  dps: 2846.03224
+  tps: 2389.54878
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 2775.20299
-  tps: 2330.28527
+  dps: 2775.36116
+  tps: 2330.40091
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 2844.46107
-  tps: 2388.30346
+  dps: 2844.67304
+  tps: 2388.45874
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 2846.94857
-  tps: 2398.26217
+  dps: 2847.15813
+  tps: 2398.41544
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 2886.44236
-  tps: 2422.97869
+  dps: 2886.63813
+  tps: 2423.12085
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 2927.48714
-  tps: 2463.11218
+  dps: 2927.69669
+  tps: 2463.26545
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 2851.67899
-  tps: 2395.92708
+  dps: 2851.89303
+  tps: 2396.08356
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ForgeEmber-37660"
  value: {
-  dps: 2851.15451
-  tps: 2393.97832
+  dps: 2851.36407
+  tps: 2394.13159
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 2859.73182
-  tps: 2400.97239
+  dps: 2859.94481
+  tps: 2401.12839
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 2856.67767
-  tps: 2398.4386
+  dps: 2856.89045
+  tps: 2398.59446
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-FuturesightRune-38763"
  value: {
-  dps: 2865.56665
-  tps: 2407.85508
+  dps: 2865.78098
+  tps: 2408.01177
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-GarbofFaith"
  value: {
-  dps: 2622.70384
-  tps: 2209.29954
+  dps: 2622.90525
+  tps: 2209.44725
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Gladiator'sInvestiture"
  value: {
-  dps: 3096.91139
-  tps: 2621.49536
+  dps: 3097.07074
+  tps: 2621.61262
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Gladiator'sRaiment"
  value: {
-  dps: 2777.84271
-  tps: 2340.49807
+  dps: 2778.12313
+  tps: 2340.70258
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 2900.64875
-  tps: 2436.57699
+  dps: 2900.86598
+  tps: 2436.73575
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 2916.14552
-  tps: 2449.44089
+  dps: 2916.36375
+  tps: 2449.60037
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 2850.13779
-  tps: 2398.08766
+  dps: 2850.05052
+  tps: 2398.02263
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Heartpierce-49982"
  value: {
-  dps: 2901.38893
-  tps: 2436.84342
+  dps: 2901.60191
+  tps: 2436.99942
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Heartpierce-50641"
  value: {
-  dps: 2901.38893
-  tps: 2436.84342
+  dps: 2901.60191
+  tps: 2436.99942
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 2846.23143
-  tps: 2388.88121
+  dps: 2846.44341
+  tps: 2389.0365
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 2845.82027
-  tps: 2389.3935
+  dps: 2846.03224
+  tps: 2389.54878
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-IncisorFragment-37723"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 2985.7207
-  tps: 2511.48088
+  dps: 2985.93267
+  tps: 2511.63616
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 2844.46107
-  tps: 2388.30346
+  dps: 2844.67304
+  tps: 2388.45874
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-LastWord-50179"
  value: {
-  dps: 2901.38893
-  tps: 2436.84342
+  dps: 2901.60191
+  tps: 2436.99942
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-LastWord-50708"
  value: {
-  dps: 2901.38893
-  tps: 2436.84342
+  dps: 2901.60191
+  tps: 2436.99942
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 2955.70512
-  tps: 2482.3508
+  dps: 2955.91562
+  tps: 2482.50483
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 2799.07176
-  tps: 2350.99721
+  dps: 2799.28132
+  tps: 2351.15048
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 2863.27632
-  tps: 2405.42676
+  dps: 2863.61187
+  tps: 2405.67062
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Nibelung-49992"
  value: {
-  dps: 2901.38893
-  tps: 2436.84342
+  dps: 2901.60191
+  tps: 2436.99942
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Nibelung-50648"
  value: {
-  dps: 2901.38893
-  tps: 2436.84342
+  dps: 2901.60191
+  tps: 2436.99942
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 2844.46107
-  tps: 2388.30346
+  dps: 2844.67304
+  tps: 2388.45874
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 2844.46107
-  tps: 2388.30346
+  dps: 2844.67304
+  tps: 2388.45874
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 2844.46107
-  tps: 2388.30346
+  dps: 2844.67304
+  tps: 2388.45874
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 2844.46107
-  tps: 2388.30346
+  dps: 2844.67304
+  tps: 2388.45874
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-RegaliaofFaith"
  value: {
-  dps: 2638.93021
-  tps: 2233.51836
+  dps: 2639.07517
+  tps: 2233.62531
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 2965.40989
-  tps: 2506.23335
+  dps: 2965.6255
+  tps: 2506.39096
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 2988.08626
-  tps: 2527.01752
+  dps: 2988.30259
+  tps: 2527.17565
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 2871.80834
-  tps: 2410.18128
+  dps: 2872.02031
+  tps: 2410.33656
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 2869.0891
-  tps: 2408.24045
+  dps: 2869.30107
+  tps: 2408.39573
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SanctificationGarb"
  value: {
-  dps: 2766.03738
-  tps: 2331.73868
+  dps: 2766.23464
+  tps: 2331.88086
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SanctificationRegalia"
  value: {
-  dps: 2799.56623
-  tps: 2362.54679
+  dps: 2799.71504
+  tps: 2362.65649
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 3004.03453
-  tps: 2532.55327
+  dps: 3004.25046
+  tps: 2532.71111
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 3024.23868
-  tps: 2551.36347
+  dps: 3024.45542
+  tps: 2551.52189
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SoulPreserver-37111"
  value: {
-  dps: 2829.36365
-  tps: 2377.40307
+  dps: 2829.57624
+  tps: 2377.55851
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SouloftheDead-40382"
  value: {
-  dps: 2941.89729
-  tps: 2478.24426
+  dps: 2942.10685
+  tps: 2478.39753
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SparkofLife-37657"
  value: {
-  dps: 2878.81356
-  tps: 2419.52789
+  dps: 2879.02312
+  tps: 2419.68116
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 2809.22087
-  tps: 2360.9394
+  dps: 2809.43043
+  tps: 2361.09267
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 2844.46107
-  tps: 2388.30346
+  dps: 2844.67304
+  tps: 2388.45874
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 2844.46107
-  tps: 2388.30346
+  dps: 2844.67304
+  tps: 2388.45874
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 2844.46107
-  tps: 2388.30346
+  dps: 2844.67304
+  tps: 2388.45874
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 2857.11717
-  tps: 2399.57842
+  dps: 2857.32763
+  tps: 2399.73234
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 2917.98584
-  tps: 2453.77923
+  dps: 2918.1954
+  tps: 2453.9325
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 2782.87337
-  tps: 2338.81139
+  dps: 2783.08293
+  tps: 2338.96466
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 2844.46107
-  tps: 2388.30346
+  dps: 2844.67304
+  tps: 2388.45874
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 2809.22087
-  tps: 2360.9394
+  dps: 2809.43043
+  tps: 2361.09267
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 2809.22087
-  tps: 2360.9394
+  dps: 2809.43043
+  tps: 2361.09267
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 2859.73182
-  tps: 2400.97239
+  dps: 2859.94481
+  tps: 2401.12839
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 2856.67767
-  tps: 2398.4386
+  dps: 2856.89045
+  tps: 2398.59446
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 2822.4374
-  tps: 2369.79338
+  dps: 2822.59725
+  tps: 2369.91019
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 2856.67767
-  tps: 2398.4386
+  dps: 2856.89045
+  tps: 2398.59446
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 2859.73182
-  tps: 2400.97239
+  dps: 2859.94481
+  tps: 2401.12839
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Val'anyr,HammerofAncientKings-46017"
  value: {
-  dps: 2971.76409
-  tps: 2487.83574
+  dps: 2971.98183
+  tps: 2487.99516
  }
 }
 dps_results: {
@@ -791,50 +791,50 @@ dps_results: {
 dps_results: {
  key: "TestSmite-AllItems-WingedTalisman-37844"
  value: {
-  dps: 2835.04964
-  tps: 2382.19892
+  dps: 2835.27316
+  tps: 2382.3622
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Zabra'sRaiment"
  value: {
-  dps: 2815.57936
-  tps: 2377.9897
+  dps: 2815.72712
+  tps: 2378.09865
  }
 }
 dps_results: {
  key: "TestSmite-AllItems-Zabra'sRegalia"
  value: {
-  dps: 2931.07129
-  tps: 2466.1225
+  dps: 2931.27671
+  tps: 2466.27309
  }
 }
 dps_results: {
  key: "TestSmite-Average-Default"
  value: {
-  dps: 2892.02467
-  tps: 2428.5357
+  dps: 2892.01638
+  tps: 2428.52981
  }
 }
 dps_results: {
  key: "TestSmite-Settings-Undead-p1-Basic-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 2901.38893
-  tps: 3015.57684
+  dps: 2901.60191
+  tps: 3015.73285
  }
 }
 dps_results: {
  key: "TestSmite-Settings-Undead-p1-Basic-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 2901.38893
-  tps: 2436.84342
+  dps: 2901.60191
+  tps: 2436.99942
  }
 }
 dps_results: {
  key: "TestSmite-Settings-Undead-p1-Basic-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 4835.0054
-  tps: 4107.07385
+  dps: 4836.07031
+  tps: 4107.85388
  }
 }
 dps_results: {
@@ -855,13 +855,13 @@ dps_results: {
  key: "TestSmite-Settings-Undead-p1-Basic-default-NoBuffs-ShortSingleTarget"
  value: {
   dps: 2520.9093
-  tps: 2126.72097
+  tps: 2134.74022
  }
 }
 dps_results: {
  key: "TestSmite-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 2901.38893
-  tps: 2436.84342
+  dps: 2901.60191
+  tps: 2436.99942
  }
 }
diff --git a/sim/rogue/TestCombat.results b/sim/rogue/TestCombat.results
index 094e9c81fc..78ab4bff78 100644
--- a/sim/rogue/TestCombat.results
+++ b/sim/rogue/TestCombat.results
@@ -981,43 +981,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 21435.04268
-  tps: 15218.8803
+  dps: 21132.20952
+  tps: 15003.86876
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 3539.13966
-  tps: 2512.78916
+  dps: 3516.89637
+  tps: 2496.99642
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 4297.57812
-  tps: 3051.28047
+  dps: 4109.75712
+  tps: 2917.92755
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 13206.06312
-  tps: 9376.30481
+  dps: 13003.58802
+  tps: 9232.54749
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 1785.96568
-  tps: 1268.03563
+  dps: 1778.52693
+  tps: 1262.75412
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 1898.48085
-  tps: 1347.9214
+  dps: 1863.43428
+  tps: 1323.03834
  }
 }
 dps_results: {
@@ -1149,43 +1149,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 23026.48078
-  tps: 16348.80135
+  dps: 22695.82072
+  tps: 16114.03271
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 4633.5605
-  tps: 3289.82796
+  dps: 4601.62498
+  tps: 3267.15373
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 5540.589
-  tps: 3933.81819
+  dps: 5306.8537
+  tps: 3767.86612
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 13454.08262
-  tps: 9552.39866
+  dps: 13260.84412
+  tps: 9415.19933
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2312.07404
-  tps: 1641.57257
+  dps: 2301.87343
+  tps: 1634.33013
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2393.64181
-  tps: 1699.48569
+  dps: 2337.44435
+  tps: 1659.58549
  }
 }
 dps_results: {
@@ -1317,43 +1317,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 25576.22706
-  tps: 18159.12122
+  dps: 25256.98021
+  tps: 17932.45595
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5051.3607
-  tps: 3586.46609
+  dps: 5018.34623
+  tps: 3563.02583
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6092.44116
-  tps: 4325.63322
+  dps: 5809.23206
+  tps: 4124.55476
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 15002.54589
-  tps: 10651.80759
+  dps: 14807.07114
+  tps: 10513.02051
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2529.97284
-  tps: 1796.28072
+  dps: 2519.14739
+  tps: 1788.59464
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2644.86445
-  tps: 1877.85376
+  dps: 2588.00612
+  tps: 1837.48434
  }
 }
 dps_results: {
@@ -1485,43 +1485,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19109.11172
-  tps: 13567.46932
+  dps: 18824.30249
+  tps: 13365.25477
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 4217.58491
-  tps: 2994.48528
+  dps: 4189.47287
+  tps: 2974.52573
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 5274.9979
-  tps: 3745.24851
+  dps: 5004.60982
+  tps: 3553.27297
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 11025.5797
-  tps: 7828.16159
+  dps: 10831.81586
+  tps: 7690.58926
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 1993.74101
-  tps: 1415.55612
+  dps: 1983.7099
+  tps: 1408.43403
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2156.33219
-  tps: 1530.99585
+  dps: 2101.37058
+  tps: 1491.97311
  }
 }
 dps_results: {
@@ -1653,43 +1653,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 21615.92487
-  tps: 15347.30666
+  dps: 21290.63515
+  tps: 15116.35096
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 3565.29603
-  tps: 2531.36018
+  dps: 3540.60866
+  tps: 2513.83215
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 4362.48163
-  tps: 3097.36196
+  dps: 4172.47671
+  tps: 2962.45847
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 13327.72899
-  tps: 9462.68758
+  dps: 13113.07646
+  tps: 9310.28429
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 1801.49013
-  tps: 1279.05799
+  dps: 1793.27032
+  tps: 1273.22193
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 1933.6019
-  tps: 1372.85735
+  dps: 1900.39941
+  tps: 1349.28358
  }
 }
 dps_results: {
@@ -1821,43 +1821,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 23204.3601
-  tps: 16475.09567
+  dps: 22855.23185
+  tps: 16227.21462
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 4667.4418
-  tps: 3313.88368
+  dps: 4631.68558
+  tps: 3288.49676
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 5622.83334
-  tps: 3992.21167
+  dps: 5384.15189
+  tps: 3822.74784
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 13586.68098
-  tps: 9646.54349
+  dps: 13404.07154
+  tps: 9516.8908
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2332.01436
-  tps: 1655.7302
+  dps: 2322.07483
+  tps: 1648.67313
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2435.28497
-  tps: 1729.05233
+  dps: 2383.73941
+  tps: 1692.45498
  }
 }
 dps_results: {
@@ -1989,43 +1989,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 25775.50672
-  tps: 18300.60977
+  dps: 25439.18557
+  tps: 18061.82176
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5087.70125
-  tps: 3612.26789
+  dps: 5052.03546
+  tps: 3586.94518
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6179.0402
-  tps: 4387.11854
+  dps: 5894.33138
+  tps: 4184.97528
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 15142.98192
-  tps: 10751.51716
+  dps: 14939.32565
+  tps: 10606.92121
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2550.98882
-  tps: 1811.20206
+  dps: 2539.17634
+  tps: 1802.8152
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2689.5647
-  tps: 1909.59093
+  dps: 2635.71107
+  tps: 1871.35486
  }
 }
 dps_results: {
@@ -2157,43 +2157,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19281.73013
-  tps: 13690.02839
+  dps: 18971.67938
+  tps: 13469.89236
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 4255.39217
-  tps: 3021.32844
+  dps: 4220.37505
+  tps: 2996.46629
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 5352.90194
-  tps: 3800.56038
+  dps: 5084.05186
+  tps: 3609.67682
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 11144.86205
-  tps: 7912.85206
+  dps: 10943.42878
+  tps: 7769.83444
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2007.95517
-  tps: 1425.64817
+  dps: 1997.50981
+  tps: 1418.23196
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2196.75486
-  tps: 1559.69595
+  dps: 2147.90528
+  tps: 1525.01275
  }
 }
 dps_results: {

From f75c707da58a6e283082c25a941fea387267e007 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Sat, 6 Jan 2024 10:05:08 -0800
Subject: [PATCH 11/28] Commit before making another fix in master

---
 sim/shaman/bloodlust.go                     |  13 +-
 sim/shaman/elemental/elemental.go           |  30 +-
 sim/shaman/elemental/elemental_test.go      |   4 +-
 sim/shaman/elemental/rotation.go            | 311 ---------------
 sim/shaman/enhancement/enhancement.go       |  84 ----
 sim/shaman/enhancement/enhancement_test.go  |   6 +-
 sim/shaman/enhancement/priority_rotation.go | 411 --------------------
 sim/shaman/enhancement/rotation.go          |  31 --
 sim/shaman/fire_elemental_pet.go            |   1 -
 sim/shaman/fire_elemental_totem.go          |  39 +-
 sim/shaman/fire_totems.go                   |  59 +--
 sim/shaman/restoration/restoration.go       |  20 +-
 sim/shaman/restoration/rotation.go          |  58 ---
 sim/shaman/shaman.go                        | 133 +------
 sim/shaman/shamanistic_rage.go              |  30 +-
 sim/shaman/totems.go                        |  41 --
 ui/elemental_shaman/apls/advanced.apl.json  |   1 -
 17 files changed, 22 insertions(+), 1250 deletions(-)
 delete mode 100644 sim/shaman/elemental/rotation.go
 delete mode 100644 sim/shaman/enhancement/priority_rotation.go
 delete mode 100644 sim/shaman/enhancement/rotation.go
 delete mode 100644 sim/shaman/restoration/rotation.go

diff --git a/sim/shaman/bloodlust.go b/sim/shaman/bloodlust.go
index 3e63f43d90..73dcebdf2d 100644
--- a/sim/shaman/bloodlust.go
+++ b/sim/shaman/bloodlust.go
@@ -12,9 +12,6 @@ func (shaman *Shaman) BloodlustActionID() core.ActionID {
 }
 
 func (shaman *Shaman) registerBloodlustCD() {
-	if !shaman.SelfBuffs.Bloodlust && !shaman.IsUsingAPL {
-		return
-	}
 	actionID := shaman.BloodlustActionID()
 
 	blAuras := []*core.Aura{}
@@ -24,7 +21,7 @@ func (shaman *Shaman) registerBloodlustCD() {
 		}
 	}
 
-	bloodlustSpell := shaman.RegisterSpell(core.SpellConfig{
+	shaman.RegisterSpell(core.SpellConfig{
 		ActionID: actionID,
 		Flags:    core.SpellFlagAPL,
 
@@ -72,12 +69,4 @@ func (shaman *Shaman) registerBloodlustCD() {
 			}
 		},
 	})
-
-	if !shaman.IsUsingAPL {
-		shaman.AddMajorCooldown(core.MajorCooldown{
-			Spell:    bloodlustSpell,
-			Priority: core.CooldownPriorityBloodlust,
-			Type:     core.CooldownTypeDPS,
-		})
-	}
 }
diff --git a/sim/shaman/elemental/elemental.go b/sim/shaman/elemental/elemental.go
index 3e734f02d6..aa4a5e1bfd 100644
--- a/sim/shaman/elemental/elemental.go
+++ b/sim/shaman/elemental/elemental.go
@@ -32,37 +32,16 @@ func NewElementalShaman(character *core.Character, options *proto.Player) *Eleme
 		Shield:    eleShamOptions.Options.Shield,
 	}
 
-	if eleShamOptions.Rotation.Bloodlust != proto.ElementalShaman_Rotation_UnsetBloodlust {
-		selfBuffs.Bloodlust = eleShamOptions.Rotation.Bloodlust == proto.ElementalShaman_Rotation_UseBloodlust
-	}
-
 	totems := &proto.ShamanTotems{}
 	if eleShamOptions.Options.Totems != nil {
 		totems = eleShamOptions.Options.Totems
 		totems.UseFireMcd = true // Control fire totems as MCD.
 	}
 
-	var rotation Rotation
-
-	switch eleShamOptions.Rotation.Type {
-	case proto.ElementalShaman_Rotation_Adaptive:
-		rotation = NewAdaptiveRotation(eleShamOptions.Rotation)
-	case proto.ElementalShaman_Rotation_Manual:
-		rotation = NewManualRotation(eleShamOptions.Rotation)
-	default:
-		rotation = NewAdaptiveRotation(eleShamOptions.Rotation)
-	}
-
-	inRange := eleShamOptions.Rotation.InThunderstormRange
-	if eleShamOptions.Options.ThunderstormRange != proto.ElementalShaman_Options_UnsetTSRange {
-		inRange = eleShamOptions.Options.ThunderstormRange == proto.ElementalShaman_Options_TSInRange
-	}
+	inRange := eleShamOptions.Options.ThunderstormRange == proto.ElementalShaman_Options_TSInRange
 	ele := &ElementalShaman{
-		Shaman:   shaman.NewShaman(character, options.TalentsString, totems, selfBuffs, inRange),
-		rotation: rotation,
-		has4pT6:  character.HasSetBonus(shaman.ItemSetSkyshatterRegalia, 4),
+		Shaman: shaman.NewShaman(character, options.TalentsString, totems, selfBuffs, inRange),
 	}
-	ele.EnableResumeAfterManaWait(ele.tryUseGCD)
 
 	if mh := ele.GetMHWeapon(); mh != nil {
 		ele.ApplyFlametongueImbueToItem(mh, false)
@@ -90,10 +69,6 @@ func NewElementalShaman(character *core.Character, options *proto.Player) *Eleme
 
 type ElementalShaman struct {
 	*shaman.Shaman
-
-	rotation Rotation
-
-	has4pT6 bool
 }
 
 func (eleShaman *ElementalShaman) GetShaman() *shaman.Shaman {
@@ -102,5 +77,4 @@ func (eleShaman *ElementalShaman) GetShaman() *shaman.Shaman {
 
 func (eleShaman *ElementalShaman) Reset(sim *core.Simulation) {
 	eleShaman.Shaman.Reset(sim)
-	eleShaman.rotation.Reset(eleShaman, sim)
 }
diff --git a/sim/shaman/elemental/elemental_test.go b/sim/shaman/elemental/elemental_test.go
index fe06b3efc2..5a20559d19 100644
--- a/sim/shaman/elemental/elemental_test.go
+++ b/sim/shaman/elemental/elemental_test.go
@@ -113,9 +113,8 @@ var PlayerOptionsAdaptive = &proto.Player_ElementalShaman{
 		Options: &proto.ElementalShaman_Options{
 			Shield:    proto.ShamanShield_WaterShield,
 			Bloodlust: true,
-			Totems:    BasicTotems,
+			Totems:    FireElementalBasicTotems,
 		},
-		Rotation: &proto.ElementalShaman_Rotation{},
 	},
 }
 
@@ -126,7 +125,6 @@ var PlayerOptionsAdaptiveFireElemental = &proto.Player_ElementalShaman{
 			Bloodlust: true,
 			Totems:    FireElementalBasicTotems,
 		},
-		Rotation: &proto.ElementalShaman_Rotation{},
 	},
 }
 
diff --git a/sim/shaman/elemental/rotation.go b/sim/shaman/elemental/rotation.go
deleted file mode 100644
index 3847fdbd7e..0000000000
--- a/sim/shaman/elemental/rotation.go
+++ /dev/null
@@ -1,311 +0,0 @@
-package elemental
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-)
-
-// func (eleShaman *ElementalShaman) GetPresimOptions(_ proto.Player) *core.PresimOptions {
-// 	return eleShaman.rotation.GetPresimOptions()
-// }
-
-func (eleShaman *ElementalShaman) OnGCDReady(sim *core.Simulation) {
-	if eleShaman.IsUsingAPL {
-		return
-	}
-
-	eleShaman.tryUseGCD(sim)
-}
-
-func (eleShaman *ElementalShaman) tryUseGCD(sim *core.Simulation) {
-	if eleShaman.TryDropTotems(sim) {
-		return
-	}
-
-	eleShaman.rotation.DoAction(eleShaman, sim)
-}
-
-// Picks which attacks / abilities the Shaman does.
-type Rotation interface {
-	// GetPresimOptions() *core.PresimOptions
-
-	// Returns the action this rotation would like to take next.
-	DoAction(*ElementalShaman, *core.Simulation)
-
-	// Returns this rotation to its initial state. Called before each Sim iteration.
-	Reset(*ElementalShaman, *core.Simulation)
-}
-
-// ################################################################
-//
-//	ADAPTIVE
-//
-// ################################################################
-type AdaptiveRotation struct {
-	fnmm      float64
-	clmm      float64
-	lvbFSWait time.Duration
-}
-
-func (rotation *AdaptiveRotation) DoAction(eleShaman *ElementalShaman, sim *core.Simulation) {
-	target := eleShaman.CurrentTarget
-
-	shouldTS := false
-	cmp := eleShaman.CurrentManaPercent()
-	percent := sim.GetRemainingDurationPercent() - 0.1
-	if eleShaman.Env.GetNumTargets() > 1 {
-		percent = 0.9 // single target we need less mana.
-	}
-	if cmp < percent {
-		shouldTS = true
-	}
-	if shouldTS && eleShaman.Thunderstorm.IsReady(sim) {
-		eleShaman.Thunderstorm.Cast(sim, target)
-		return
-	}
-
-	fsTime := eleShaman.FlameShock.CurDot().RemainingDuration(sim)
-	lvTime := eleShaman.LavaBurst.CD.TimeToReady(sim)
-	lvCastTime := eleShaman.ApplyCastSpeed(eleShaman.LavaBurst.DefaultCast.CastTime)
-	if fsTime <= 0 && eleShaman.FlameShock.IsReady(sim) {
-		if !eleShaman.FlameShock.Cast(sim, target) {
-			eleShaman.WaitForMana(sim, eleShaman.FlameShock.CurCast.Cost)
-		}
-		return
-	} else if fsTime > lvCastTime {
-		if lvTime <= 0 {
-			if !eleShaman.LavaBurst.Cast(sim, target) {
-				eleShaman.WaitForMana(sim, eleShaman.LavaBurst.CurCast.Cost)
-			}
-			return
-		} else if lvTime <= rotation.lvbFSWait && fsTime > lvCastTime+lvTime {
-			// If we have enough time to wait lvbFSWait and still have FS up, we should just wait to cast LvB.
-			eleShaman.WaitUntil(sim, sim.CurrentTime+lvTime)
-			return
-		}
-	}
-
-	fsCD := eleShaman.FlameShock.CD.TimeToReady(sim)
-	if fsCD > fsTime {
-		fsTime = fsCD
-	}
-	// If FS will be needed and is ready in < lvbFSWait time, delay.
-	if fsTime <= rotation.lvbFSWait {
-		eleShaman.WaitUntil(sim, sim.CurrentTime+fsTime)
-		return
-	}
-
-	if cmp > rotation.clmm && eleShaman.ChainLightning.IsReady(sim) {
-		clTime := eleShaman.ApplyCastSpeed(eleShaman.ChainLightning.DefaultCast.CastTime)
-		// Only CL if cast time > 1 second than CL or there is more than 1 target.
-		if clTime > core.GCDMin || eleShaman.Env.GetNumTargets() > 1 {
-			if !eleShaman.ChainLightning.Cast(sim, target) {
-				eleShaman.WaitForMana(sim, eleShaman.ChainLightning.CurCast.Cost)
-			}
-			return
-		}
-	} else if cmp > rotation.fnmm && eleShaman.FireNova.IsReady(sim) {
-		if !eleShaman.FireNova.Cast(sim, target) {
-			eleShaman.WaitForMana(sim, eleShaman.FireNova.CurCast.Cost)
-		}
-		return
-	}
-
-	if !eleShaman.LightningBolt.Cast(sim, target) {
-		if sim.Log != nil {
-			eleShaman.Log(sim, "Failed to cast LB, cost: %0.1f, current mana: %0.1f\n")
-		}
-		eleShaman.WaitForMana(sim, eleShaman.LightningBolt.CurCast.Cost)
-	}
-}
-
-func (rotation *AdaptiveRotation) Reset(_ *ElementalShaman, sim *core.Simulation) {
-	rotation.fnmm = 1.0
-	rotation.clmm = 1.0
-	if len(sim.Encounter.Targets) > 4 {
-		// 5+ targets FN is better
-		rotation.fnmm = 0.33
-		// Allow CL as long as you have decent mana (leaving most mana for FN)
-		rotation.clmm = 0.5
-	} else if len(sim.Encounter.Targets) == 4 {
-		// 4 targets, enable both similar prio, prob looking at real AoE now (short fight)
-		rotation.clmm = 0.33
-		rotation.fnmm = 0.33
-	} else if len(sim.Encounter.Targets) == 3 {
-		// 3 targets, enable both, but prio CL (more efficient)
-		//  Still trying to be very mana efficient as 3 targets
-		//  is still often a "boss fight" and could be long.
-		rotation.clmm = 0.33
-		rotation.fnmm = 0.66
-	} else if len(sim.Encounter.Targets) == 2 {
-		// enable CL with 2
-		rotation.clmm = 0.33
-	}
-}
-
-// func (rotation *AdaptiveRotation) GetPresimOptions() *core.PresimOptions {
-// 	return &core.PresimOptions{
-// 		SetPresimPlayerOptions: func(player *proto.Player) {
-// 		},
-
-// 		OnPresimResult: func(presimResult proto.UnitMetrics, iterations int32, duration time.Duration) bool {
-// 			return true
-// 		},
-// 	}
-// }
-
-func NewAdaptiveRotation(options *proto.ElementalShaman_Rotation) *AdaptiveRotation {
-	if options.LvbFsWaitMs == 0 {
-		options.LvbFsWaitMs = 175
-	}
-	return &AdaptiveRotation{
-		lvbFSWait: time.Duration(options.LvbFsWaitMs) * time.Millisecond,
-	}
-}
-
-// ################################################################
-//
-//	MANUAL
-//
-// ################################################################
-type ManualRotation struct {
-	options *proto.ElementalShaman_Rotation
-}
-
-func (rotation *ManualRotation) DoAction(eleShaman *ElementalShaman, sim *core.Simulation) {
-	target := eleShaman.CurrentTarget
-	lvbFSWait := time.Duration(rotation.options.LvbFsWaitMs) * time.Millisecond
-	shouldTS := false
-	cmp := eleShaman.CurrentManaPercent()
-
-	// TODO: expose these percents to let user tweak
-	percent := sim.GetRemainingDurationPercent() - 0.1
-	if eleShaman.Env.GetNumTargets() > 1 {
-		percent = 0.9
-	}
-	if cmp < percent {
-		shouldTS = true
-	}
-	if shouldTS && rotation.options.UseThunderstorm && eleShaman.Thunderstorm.IsReady(sim) {
-		eleShaman.Thunderstorm.Cast(sim, target)
-		return
-	}
-
-	fsRemain := eleShaman.FlameShock.CurDot().RemainingDuration(sim)
-	needFS := fsRemain <= 0
-	// Only overwrite if lvb is ready right now.
-	if !needFS && rotation.options.OverwriteFlameshock && eleShaman.LavaBurst.CD.TimeToReady(sim) <= core.GCDDefault {
-		lvbTime := max(eleShaman.ApplyCastSpeed(eleShaman.LavaBurst.DefaultCast.CastTime), core.GCDMin)
-		if fsRemain < lvbTime {
-			needFS = true
-		}
-	}
-
-	allowLvB := true
-	if rotation.options.AlwaysCritLvb {
-		lvbTime := max(eleShaman.ApplyCastSpeed(eleShaman.LavaBurst.DefaultCast.CastTime), core.GCDMin)
-		if fsRemain <= lvbTime {
-			allowLvB = false
-		}
-	}
-
-	shouldCL := rotation.options.UseChainLightning && cmp > (rotation.options.ClMinManaPer/100) && eleShaman.ChainLightning.IsReady(sim)
-	clTime := eleShaman.ApplyCastSpeed(eleShaman.ChainLightning.DefaultCast.CastTime)
-
-	// Never cast CL if single target and cast time <= 1 second
-	// This is effecively a waste of haste (that is probably temporary.)
-	if clTime <= time.Second && eleShaman.Env.GetNumTargets() == 1 {
-		shouldCL = false
-	}
-	lvbCD := eleShaman.LavaBurst.CD.TimeToReady(sim)
-	if shouldCL && rotation.options.UseClOnlyGap {
-		shouldCL = false
-		clCast := max(eleShaman.ApplyCastSpeed(eleShaman.ChainLightning.DefaultCast.CastTime), core.GCDMin)
-		// If LvB CD < CL cast time, we should use CL to pass the time until then.
-		// Or if FS is about to expire and we didn't cast LvB.
-		if fsRemain <= clCast || (lvbCD <= clCast) {
-			shouldCL = true
-		}
-	}
-
-	fsCD := eleShaman.FlameShock.CD.TimeToReady(sim)
-	if fsCD > fsRemain {
-		fsRemain = fsCD
-	}
-
-	// If FS will be needed and is ready in < lvbFSWait time, delay.
-	if fsRemain <= lvbFSWait && fsRemain > 0 {
-		eleShaman.WaitUntil(sim, sim.CurrentTime+fsRemain)
-		return
-	}
-
-	// If LvB will be ready in < lvbFSWait time, delay
-	if lvbCD <= lvbFSWait && lvbCD > 0 {
-		eleShaman.WaitUntil(sim, sim.CurrentTime+lvbCD)
-		return
-	}
-
-	if needFS && eleShaman.FlameShock.IsReady(sim) {
-		if !eleShaman.FlameShock.Cast(sim, target) {
-			eleShaman.WaitForMana(sim, eleShaman.FlameShock.CurCast.Cost)
-		}
-		return
-	} else if allowLvB && eleShaman.LavaBurst.IsReady(sim) {
-		if !eleShaman.LavaBurst.Cast(sim, target) {
-			eleShaman.WaitForMana(sim, eleShaman.LavaBurst.CurCast.Cost)
-		}
-		return
-	} else if shouldCL {
-		if !eleShaman.ChainLightning.Cast(sim, target) {
-			eleShaman.WaitForMana(sim, eleShaman.ChainLightning.CurCast.Cost)
-		}
-		return
-	} else if rotation.options.UseFireNova && cmp > (rotation.options.FnMinManaPer/100) && eleShaman.FireNova.IsReady(sim) {
-		if !eleShaman.FireNova.Cast(sim, target) {
-			eleShaman.WaitForMana(sim, eleShaman.FireNova.CurCast.Cost)
-		}
-		return
-	}
-
-	if !eleShaman.LightningBolt.Cast(sim, target) {
-		if sim.Log != nil {
-			eleShaman.Log(sim, "Failed to cast LB, cost: %0.1f, current mana: %0.1f\n")
-		}
-		eleShaman.WaitForMana(sim, eleShaman.LightningBolt.CurCast.Cost)
-	}
-}
-
-func (rotation *ManualRotation) Reset(_ *ElementalShaman, _ *core.Simulation) {
-}
-
-// func (rotation *ManualRotation) GetPresimOptions() *core.PresimOptions {
-// 	return &core.PresimOptions{
-// 		SetPresimPlayerOptions: func(player *proto.Player) {
-// 		},
-
-// 		OnPresimResult: func(presimResult proto.UnitMetrics, iterations int32, duration time.Duration) bool {
-// 			return true
-// 		},
-// 	}
-// }
-
-func NewManualRotation(options *proto.ElementalShaman_Rotation) *ManualRotation {
-	return &ManualRotation{
-		options: options,
-	}
-}
-
-// A single action that an Agent can take.
-type AgentAction interface {
-	GetActionID() core.ActionID
-
-	// TODO: Maybe change this to 'ResourceCost'
-	// Amount of mana required to perform the action.
-	GetManaCost() float64
-
-	// Do the action. Returns whether the action was successful. An unsuccessful
-	// action indicates that the prerequisites, like resource cost, were not met.
-	Cast(sim *core.Simulation) bool
-}
diff --git a/sim/shaman/enhancement/enhancement.go b/sim/shaman/enhancement/enhancement.go
index 00d3ef34e8..e1653492f2 100644
--- a/sim/shaman/enhancement/enhancement.go
+++ b/sim/shaman/enhancement/enhancement.go
@@ -3,7 +3,6 @@ package enhancement
 import (
 	"time"
 
-	"github.com/wowsims/wotlk/sim/common"
 	"github.com/wowsims/wotlk/sim/core"
 	"github.com/wowsims/wotlk/sim/core/proto"
 	"github.com/wowsims/wotlk/sim/shaman"
@@ -36,11 +35,6 @@ func NewEnhancementShaman(character *core.Character, options *proto.Player) *Enh
 		ImbueOH:   enhOptions.Options.ImbueOh,
 	}
 
-	// Override with new rotation option bloodlust.
-	if enhOptions.Rotation.Bloodlust != proto.EnhancementShaman_Rotation_UnsetBloodlust {
-		selfBuffs.Bloodlust = enhOptions.Rotation.Bloodlust == proto.EnhancementShaman_Rotation_UseBloodlust
-	}
-
 	totems := &proto.ShamanTotems{}
 	if enhOptions.Options.Totems != nil {
 		totems = enhOptions.Options.Totems
@@ -50,9 +44,6 @@ func NewEnhancementShaman(character *core.Character, options *proto.Player) *Enh
 		Shaman: shaman.NewShaman(character, options.TalentsString, totems, selfBuffs, true),
 	}
 
-	enh.EnableResumeAfterManaWait(enh.OnGCDReady)
-	enh.rotation = NewPriorityRotation(enh, enhOptions.Rotation)
-
 	// Enable Auto Attacks for this spec
 	enh.EnableAutoAttacks(enh, core.AutoAttackOptions{
 		MainHand:       enh.WeaponFromMainHand(enh.DefaultMeleeCritMultiplier()),
@@ -64,12 +55,6 @@ func NewEnhancementShaman(character *core.Character, options *proto.Player) *Enh
 	enh.ApplyFlametongueImbue(enh.getImbueProcMask(proto.ShamanImbue_FlametongueWeapon), false)
 	enh.ApplyFlametongueImbue(enh.getImbueProcMask(proto.ShamanImbue_FlametongueWeaponDownrank), true)
 
-	if enhOptions.Rotation.LightningboltWeave {
-		enh.maelstromWeaponMinStack = enhOptions.Rotation.MaelstromweaponMinStack
-	} else {
-		enh.maelstromWeaponMinStack = 5
-	}
-
 	if !enh.HasMHWeapon() {
 		enh.SelfBuffs.ImbueMH = proto.ShamanImbue_NoImbue
 	}
@@ -83,8 +68,6 @@ func NewEnhancementShaman(character *core.Character, options *proto.Player) *Enh
 		SpiritWolf2: enh.NewSpiritWolf(2),
 	}
 
-	enh.ShamanisticRageManaThreshold = enhOptions.Rotation.ShamanisticRageManaThreshold
-
 	return enh
 }
 
@@ -102,13 +85,8 @@ func (enh *EnhancementShaman) getImbueProcMask(imbue proto.ShamanImbue) core.Pro
 type EnhancementShaman struct {
 	*shaman.Shaman
 
-	rotation                Rotation
-	maelstromWeaponMinStack int32
-
 	// for weaving Lava Burst or Lightning Bolt
 	previousSwingAt time.Duration
-
-	scheduler common.GCDScheduler
 }
 
 func (enh *EnhancementShaman) GetShaman() *shaman.Shaman {
@@ -132,13 +110,6 @@ func (enh *EnhancementShaman) Initialize() {
 			enh.ApplySyncType(proto.ShamanSyncType_Auto)
 		})
 	}
-	enh.DelayDPSCooldowns(3 * time.Second)
-
-	if !enh.IsUsingAPL {
-		enh.RegisterPrepullAction(-time.Second, func(sim *core.Simulation) {
-			enh.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand, proto.ItemSlot_ItemSlotOffHand})
-		})
-	}
 }
 
 func (enh *EnhancementShaman) Reset(sim *core.Simulation) {
@@ -183,58 +154,3 @@ func (enh *EnhancementShaman) ApplySyncType(syncType proto.ShamanSyncType) {
 		enh.AutoAttacks.SetReplaceMHSwing(nil)
 	}
 }
-
-func (enh *EnhancementShaman) CastLightningBoltWeave(sim *core.Simulation, reactionTime time.Duration) bool {
-	previousAttack := sim.CurrentTime - enh.previousSwingAt
-	reactionTime = core.TernaryDuration(previousAttack < reactionTime, reactionTime-previousAttack, 0)
-
-	//calculate cast times for weaving
-	lbCastTime := enh.ApplyCastSpeed(enh.LightningBolt.DefaultCast.CastTime-(time.Millisecond*time.Duration(500*enh.MaelstromWeaponAura.GetStacks()))) + reactionTime
-	//calculate swing times for weaving
-	timeUntilSwing := enh.AutoAttacks.NextAttackAt() - sim.CurrentTime
-
-	if lbCastTime < timeUntilSwing {
-		if reactionTime > 0 {
-			reactionTime += sim.CurrentTime
-
-			enh.HardcastWaitUntil(sim, reactionTime, func(_ *core.Simulation, _ *core.Unit) {
-				enh.GCD.Reset()
-				enh.LightningBolt.Cast(sim, enh.CurrentTarget)
-			})
-
-			enh.WaitUntil(sim, reactionTime)
-			return true
-		}
-		return enh.LightningBolt.Cast(sim, enh.CurrentTarget)
-	}
-
-	return false
-}
-
-func (enh *EnhancementShaman) CastLavaBurstWeave(sim *core.Simulation, reactionTime time.Duration) bool {
-	previousAttack := sim.CurrentTime - enh.previousSwingAt
-	reactionTime = core.TernaryDuration(previousAttack < reactionTime, reactionTime-previousAttack, 0)
-
-	//calculate cast times for weaving
-	lvbCastTime := enh.ApplyCastSpeed(enh.LavaBurst.DefaultCast.CastTime) + reactionTime
-	//calculate swing times for weaving
-	timeUntilSwing := enh.AutoAttacks.NextAttackAt() - sim.CurrentTime
-
-	if lvbCastTime < timeUntilSwing {
-		if reactionTime > 0 {
-			reactionTime += sim.CurrentTime
-
-			enh.HardcastWaitUntil(sim, reactionTime, func(_ *core.Simulation, _ *core.Unit) {
-				enh.GCD.Reset()
-				enh.LavaBurst.Cast(sim, enh.CurrentTarget)
-			})
-
-			enh.WaitUntil(sim, reactionTime)
-			return true
-		}
-
-		return enh.LavaBurst.Cast(sim, enh.CurrentTarget)
-	}
-
-	return false
-}
diff --git a/sim/shaman/enhancement/enhancement_test.go b/sim/shaman/enhancement/enhancement_test.go
index 33d9f3dd8b..14da520aef 100644
--- a/sim/shaman/enhancement/enhancement_test.go
+++ b/sim/shaman/enhancement/enhancement_test.go
@@ -87,15 +87,13 @@ var StandardGlyphs = &proto.Glyphs{
 
 var PlayerOptionsWFWF = &proto.Player_EnhancementShaman{
 	EnhancementShaman: &proto.EnhancementShaman{
-		Options:  enhShamWFWF,
-		Rotation: &proto.EnhancementShaman_Rotation{},
+		Options: enhShamWFWF,
 	},
 }
 
 var PlayerOptionsFTFT = &proto.Player_EnhancementShaman{
 	EnhancementShaman: &proto.EnhancementShaman{
-		Options:  enhShamFTFT,
-		Rotation: &proto.EnhancementShaman_Rotation{},
+		Options: enhShamFTFT,
 	},
 }
 
diff --git a/sim/shaman/enhancement/priority_rotation.go b/sim/shaman/enhancement/priority_rotation.go
deleted file mode 100644
index 8a1900dfba..0000000000
--- a/sim/shaman/enhancement/priority_rotation.go
+++ /dev/null
@@ -1,411 +0,0 @@
-package enhancement
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-)
-
-// Default Priority Order
-const (
-	LightningBolt = iota
-	StormstrikeApplyDebuff
-	WeaveLavaBurst
-	WeaveLightningBolt
-	MagmaTotem
-	Stormstrike
-	FlameShock
-	EarthShock
-	FrostShock
-	FireNova
-	DelayedWeave
-	LightningShield
-	DropAllTotems
-	LavaLash
-	NumberSpells // Used to get the max number of spells in the prio list, keep at bottoom
-)
-
-type PriorityRotation struct {
-	options       *proto.EnhancementShaman_Rotation
-	spellPriority []Spell
-}
-
-type Cast func(sim *core.Simulation, target *core.Unit) bool
-type Condition func(sim *core.Simulation, target *core.Unit) bool
-type ReadyAt func() time.Duration
-
-// Holds all the spell info we need to make decisions
-type Spell struct {
-	readyAt ReadyAt
-	cast    Cast
-	// Must pass this check to cast or use readyAt, a special condition to be met
-	condition Condition
-}
-
-func NewPriorityRotation(enh *EnhancementShaman, options *proto.EnhancementShaman_Rotation) *PriorityRotation {
-	rotation := &PriorityRotation{
-		options: options,
-	}
-
-	rotation.buildPriorityRotation(enh)
-
-	return rotation
-}
-
-func (rotation *PriorityRotation) buildPriorityRotation(enh *EnhancementShaman) {
-	stormstrikeApplyDebuff := Spell{
-		condition: func(sim *core.Simulation, target *core.Unit) bool {
-			return !enh.StormstrikeDebuffAura(target).IsActive()
-		},
-		cast: func(sim *core.Simulation, target *core.Unit) bool {
-			return enh.Stormstrike.IsReady(sim) && enh.Stormstrike.Cast(sim, target)
-		},
-		readyAt: func() time.Duration {
-			return enh.Stormstrike.ReadyAt()
-		},
-	}
-
-	instantLightningBolt := Spell{
-		condition: func(sim *core.Simulation, target *core.Unit) bool {
-			return enh.MaelstromWeaponAura.GetStacks() == 5
-		},
-		cast: func(sim *core.Simulation, target *core.Unit) bool {
-			return enh.LightningBolt.Cast(sim, enh.CurrentTarget)
-		},
-		readyAt: func() time.Duration {
-			return 0
-		},
-	}
-
-	chainLightning := Spell{
-		condition: func(sim *core.Simulation, target *core.Unit) bool {
-			return enh.MaelstromWeaponAura.GetStacks() == 5 && enh.ChainLightning.IsReady(sim)
-		},
-		cast: func(sim *core.Simulation, target *core.Unit) bool {
-			return enh.ChainLightning.Cast(sim, enh.CurrentTarget)
-		},
-		readyAt: func() time.Duration {
-			return 0
-		},
-	}
-
-	stormstrike := Spell{
-		condition: func(sim *core.Simulation, target *core.Unit) bool {
-			//Checking if we learned the spell, ie untalented
-			return enh.Stormstrike != nil
-		},
-		cast: func(sim *core.Simulation, target *core.Unit) bool {
-			//TODO add in SS delay so we don't lose flametongues, if Last attack = current time
-			return enh.Stormstrike.IsReady(sim) && enh.Stormstrike.Cast(sim, target)
-		},
-		readyAt: func() time.Duration {
-			return enh.Stormstrike.ReadyAt()
-		},
-	}
-
-	weaveLightningBolt := Spell{
-		condition: func(sim *core.Simulation, target *core.Unit) bool {
-			return rotation.options.LightningboltWeave && enh.MaelstromWeaponAura.GetStacks() >= rotation.options.MaelstromweaponMinStack && enh.CurrentMana() >= enh.LightningBolt.DefaultCast.Cost
-		},
-		cast: func(sim *core.Simulation, target *core.Unit) bool {
-			reactionTime := time.Millisecond * time.Duration(rotation.options.AutoWeaveDelay)
-			return enh.CastLightningBoltWeave(sim, reactionTime)
-		},
-		readyAt: func() time.Duration {
-			return 0
-		},
-	}
-
-	weaveLavaBurst := Spell{
-		condition: func(sim *core.Simulation, target *core.Unit) bool {
-			if enh.CurrentMana() < enh.LavaBurst.CurCast.Cost {
-				return false
-			}
-
-			return rotation.options.LavaburstWeave && enh.MaelstromWeaponAura.GetStacks() >= rotation.options.MaelstromweaponMinStack
-		},
-		cast: func(sim *core.Simulation, target *core.Unit) bool {
-			reactionTime := time.Millisecond * time.Duration(rotation.options.AutoWeaveDelay)
-			return rotation.options.LavaburstWeave && enh.LavaBurst.IsReady(sim) && enh.CastLavaBurstWeave(sim, reactionTime)
-		},
-		readyAt: func() time.Duration {
-			return 0
-		},
-	}
-
-	flameShock := Spell{
-		condition: func(sim *core.Simulation, target *core.Unit) bool {
-			fsDot := enh.FlameShock.Dot(target)
-			return rotation.options.WeaveFlameShock && fsDot.RemainingDuration(sim) <= time.Duration(rotation.options.FlameShockClipTicks)*fsDot.TickLength
-		},
-		cast: func(sim *core.Simulation, target *core.Unit) bool {
-			return enh.FlameShock.IsReady(sim) && enh.FlameShock.Cast(sim, target)
-		},
-		readyAt: func() time.Duration {
-			return enh.FlameShock.ReadyAt()
-		},
-	}
-
-	earthShock := Spell{
-		condition: func(sim *core.Simulation, target *core.Unit) bool {
-			return rotation.options.PrimaryShock == proto.EnhancementShaman_Rotation_Earth
-		},
-		cast: func(sim *core.Simulation, target *core.Unit) bool {
-			return enh.EarthShock.IsReady(sim) && enh.EarthShock.Cast(sim, target)
-		},
-		readyAt: func() time.Duration {
-			return enh.EarthShock.ReadyAt()
-		},
-	}
-
-	frostShock := Spell{
-		condition: func(sim *core.Simulation, target *core.Unit) bool {
-			return rotation.options.PrimaryShock == proto.EnhancementShaman_Rotation_Frost
-		},
-		cast: func(sim *core.Simulation, target *core.Unit) bool {
-			return enh.FrostShock.IsReady(sim) && enh.FrostShock.Cast(sim, target)
-		},
-		readyAt: func() time.Duration {
-			return enh.EarthShock.ReadyAt()
-		},
-	}
-
-	lightningShield := Spell{
-		condition: func(sim *core.Simulation, target *core.Unit) bool {
-			return enh.LightningShield != nil && !enh.LightningShieldAura.IsActive()
-		},
-		cast: func(sim *core.Simulation, _ *core.Unit) bool {
-			return enh.LightningShield.Cast(sim, nil)
-		},
-		readyAt: func() time.Duration {
-			return 0
-		},
-	}
-
-	fireNova := Spell{
-		condition: func(sim *core.Simulation, target *core.Unit) bool {
-			if enh.Totems.Fire == proto.FireTotem_NoFireTotem {
-				return false
-			}
-
-			return enh.NextTotemDrops[2] > sim.CurrentTime && enh.CurrentMana() > rotation.options.FirenovaManaThreshold
-		},
-		cast: func(sim *core.Simulation, target *core.Unit) bool {
-			return enh.FireNova.IsReady(sim) && enh.FireNova.Cast(sim, target)
-		},
-		readyAt: func() time.Duration {
-			return enh.FireNova.ReadyAt()
-		},
-	}
-
-	lavaLash := Spell{
-		condition: func(sim *core.Simulation, target *core.Unit) bool {
-			//Checking if we learned the spell, ie untalented
-			return enh.LavaLash != nil && enh.AutoAttacks.IsDualWielding
-		},
-		cast: func(sim *core.Simulation, target *core.Unit) bool {
-			//TODO add in LL delay so we don't lose flametongues, if Last attack = current time
-			return enh.LavaLash.IsReady(sim) && enh.LavaLash.Cast(sim, target)
-		},
-		readyAt: func() time.Duration {
-			return enh.LavaLash.ReadyAt()
-		},
-	}
-
-	magmaTotem := Spell{
-		condition: func(sim *core.Simulation, target *core.Unit) bool {
-			//TODO : rework totems to make them easier to work with.
-			return enh.Totems.Fire == proto.FireTotem_MagmaTotem && enh.NextTotemDrops[2] <= sim.CurrentTime
-		},
-		cast: func(sim *core.Simulation, target *core.Unit) bool {
-			return enh.MagmaTotem.Cast(sim, target)
-		},
-		readyAt: func() time.Duration {
-			return 0
-		},
-	}
-
-	dropAllTotems := Spell{
-		condition: func(sim *core.Simulation, target *core.Unit) bool {
-			return true
-		},
-		cast: func(sim *core.Simulation, target *core.Unit) bool {
-			// TODO : totems need to be dropped all at once.
-			return enh.TryDropTotems(sim)
-		},
-		readyAt: func() time.Duration {
-			return 0
-		},
-	}
-
-	delayedWeave := Spell{
-		condition: func(sim *core.Simulation, target *core.Unit) bool {
-			if enh.CurrentMana() < enh.LightningBolt.DefaultCast.Cost {
-				return false
-			}
-
-			return rotation.options.LightningboltWeave && rotation.options.DelayGcdWeave > 0 && enh.MaelstromWeaponAura.GetStacks() >= rotation.options.MaelstromweaponMinStack
-		},
-		cast: func(sim *core.Simulation, target *core.Unit) bool {
-			timeUntilSwing := enh.AutoAttacks.NextAttackAt() - sim.CurrentTime
-			if timeUntilSwing <= time.Millisecond*time.Duration(rotation.options.DelayGcdWeave) && timeUntilSwing != 0 {
-				delay := enh.AutoAttacks.NextAttackAt() + time.Millisecond*100
-				if delay < sim.CurrentTime {
-					return false
-				}
-
-				enh.HardcastWaitUntil(sim, delay, func(_ *core.Simulation, _ *core.Unit) {
-					enh.GCD.Reset()
-					enh.CastLightningBoltWeave(sim, 0)
-				})
-
-				enh.WaitUntil(sim, delay)
-				return true
-			}
-
-			return false
-		},
-		readyAt: func() time.Duration {
-			return 0
-		},
-	}
-
-	//Normal Priority Rotation
-	var spellPriority []Spell
-	if rotation.options.RotationType == proto.EnhancementShaman_Rotation_Priority {
-		spellPriority = make([]Spell, NumberSpells)
-		spellPriority[StormstrikeApplyDebuff] = stormstrikeApplyDebuff
-		spellPriority[LightningBolt] = instantLightningBolt
-		spellPriority[Stormstrike] = stormstrike
-		spellPriority[FlameShock] = flameShock
-		spellPriority[EarthShock] = earthShock
-		spellPriority[LightningShield] = lightningShield
-		spellPriority[FireNova] = fireNova
-		spellPriority[LavaLash] = lavaLash
-		spellPriority[WeaveLightningBolt] = weaveLightningBolt
-		spellPriority[FrostShock] = frostShock
-		spellPriority[WeaveLavaBurst] = weaveLavaBurst
-		spellPriority[MagmaTotem] = magmaTotem
-		spellPriority[DropAllTotems] = dropAllTotems
-		spellPriority[DelayedWeave] = delayedWeave
-	}
-
-	//Custom Priority Rotation
-	if rotation.options.RotationType == proto.EnhancementShaman_Rotation_Custom && rotation.options.CustomRotation != nil {
-		spellPriority := make([]Spell, 0, len(rotation.options.CustomRotation.Spells))
-
-		// Turn weaving off, will enable them if they have been added.
-		rotation.options.LightningboltWeave = false
-		rotation.options.LavaburstWeave = false
-		rotation.options.WeaveFlameShock = false
-		rotation.options.PrimaryShock = proto.EnhancementShaman_Rotation_None
-		for _, customSpellProto := range rotation.options.CustomRotation.Spells {
-			switch customSpellProto.Spell {
-			case int32(proto.EnhancementShaman_Rotation_StormstrikeDebuffMissing):
-				spellPriority = append(spellPriority, stormstrikeApplyDebuff)
-			case int32(proto.EnhancementShaman_Rotation_LightningBolt):
-				spellPriority = append(spellPriority, instantLightningBolt)
-			case int32(proto.EnhancementShaman_Rotation_ChainLightning):
-				spellPriority = append(spellPriority, chainLightning)
-			case int32(proto.EnhancementShaman_Rotation_LightningBoltWeave):
-				rotation.options.LightningboltWeave = true
-				spellPriority = append(spellPriority, weaveLightningBolt)
-			case int32(proto.EnhancementShaman_Rotation_LightningBoltDelayedWeave):
-				rotation.options.LightningboltWeave = true
-				spellPriority = append(spellPriority, delayedWeave)
-			case int32(proto.EnhancementShaman_Rotation_Stormstrike):
-				spellPriority = append(spellPriority, stormstrike)
-			case int32(proto.EnhancementShaman_Rotation_FlameShock):
-				rotation.options.WeaveFlameShock = true
-				spellPriority = append(spellPriority, flameShock)
-			case int32(proto.EnhancementShaman_Rotation_FireNova):
-				spellPriority = append(spellPriority, fireNova)
-			case int32(proto.EnhancementShaman_Rotation_LavaLash):
-				spellPriority = append(spellPriority, lavaLash)
-			case int32(proto.EnhancementShaman_Rotation_EarthShock):
-				if rotation.options.PrimaryShock == proto.EnhancementShaman_Rotation_None {
-					rotation.options.PrimaryShock = proto.EnhancementShaman_Rotation_Earth
-					spellPriority = append(spellPriority, earthShock)
-				}
-			case int32(proto.EnhancementShaman_Rotation_LightningShield):
-				spellPriority = append(spellPriority, lightningShield)
-			case int32(proto.EnhancementShaman_Rotation_FrostShock):
-				if rotation.options.PrimaryShock == proto.EnhancementShaman_Rotation_None {
-					rotation.options.PrimaryShock = proto.EnhancementShaman_Rotation_Frost
-					spellPriority = append(spellPriority, frostShock)
-				}
-			case int32(proto.EnhancementShaman_Rotation_LavaBurst):
-				rotation.options.LavaburstWeave = true
-				spellPriority = append(spellPriority, weaveLavaBurst)
-			case int32(proto.EnhancementShaman_Rotation_MagmaTotem):
-				spellPriority = append(spellPriority, magmaTotem)
-			}
-		}
-
-		rotation.spellPriority = spellPriority
-		return
-	}
-
-	rotation.spellPriority = spellPriority
-}
-
-func (rotation *PriorityRotation) DoAction(enh *EnhancementShaman, sim *core.Simulation) {
-	target := enh.CurrentTarget
-
-	cheapestSpell := enh.LightningBolt.CurCast.Cost
-	if enh.LavaLash != nil {
-		cheapestSpell = enh.LavaLash.CurCast.Cost
-	}
-
-	// Incase the array is empty
-	if len(rotation.spellPriority) == 0 {
-		enh.DoNothing()
-	}
-
-	//Mana guard, our cheapest spell.
-	if enh.CurrentMana() < cheapestSpell {
-		// Lets top off lightning shield stacks before waiting for mana.
-		if enh.LightningShield != nil && enh.LightningShieldAura.GetStacks() < 3 {
-			enh.LightningShield.Cast(sim, nil)
-		}
-		enh.WaitForMana(sim, cheapestSpell)
-		return
-	}
-
-	// We could choose to not wait for auto attacks if we don't have any MW stacks,
-	// this would reduce the amount of DoAction calls by a little bit; might not be a issue though.
-	upcomingCD := enh.AutoAttacks.NextAttackAt()
-	var cast Cast
-	for _, spell := range rotation.spellPriority {
-		if !spell.condition(sim, target) {
-			continue
-		}
-
-		if spell.cast(sim, target) {
-			return
-		}
-
-		readyAt := spell.readyAt()
-		if readyAt > sim.CurrentTime && (upcomingCD > readyAt || upcomingCD < sim.CurrentTime) {
-			upcomingCD = readyAt
-			cast = spell.cast
-		}
-	}
-
-	//Lets wait on a upcoming CD or AutoAttack
-	enh.WaitUntil(sim, upcomingCD)
-
-	//Incase the next auto is our best CD then there are no spells to cast.
-	if cast != nil {
-		//We have a upcoming CD and we know what to cast lets just do that.
-		enh.HardcastWaitUntil(sim, upcomingCD, func(sim *core.Simulation, target *core.Unit) {
-			enh.GCD.Reset()
-			cast(sim, target)
-		})
-	}
-}
-
-func (rotation *PriorityRotation) Reset(_ *EnhancementShaman, _ *core.Simulation) {
-
-}
diff --git a/sim/shaman/enhancement/rotation.go b/sim/shaman/enhancement/rotation.go
deleted file mode 100644
index 53476c86d5..0000000000
--- a/sim/shaman/enhancement/rotation.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package enhancement
-
-import (
-	"github.com/wowsims/wotlk/sim/core"
-)
-
-func (enh *EnhancementShaman) OnAutoAttack(sim *core.Simulation, _ *core.Spell) {
-	enh.previousSwingAt = sim.CurrentTime
-}
-
-func (enh *EnhancementShaman) OnGCDReady(sim *core.Simulation) {
-	// TODO move this into the rotation, also this uses waitForMana if it was unable to cast the totem
-	// that will need to be pulled out so we are not waiting for a magma totem mana cost
-	enh.rotation.DoAction(enh, sim)
-}
-
-type Rotation interface {
-	DoAction(*EnhancementShaman, *core.Simulation)
-	Reset(*EnhancementShaman, *core.Simulation)
-}
-
-//	CUSTOM ROTATION (advanced) (also WIP).
-//
-// TODO: figure out how to do this (probably too complicated to copy hunters)
-type AgentAction interface {
-	GetActionID() core.ActionID
-
-	GetManaCost() float64
-
-	Cast(sim *core.Simulation) bool
-}
diff --git a/sim/shaman/fire_elemental_pet.go b/sim/shaman/fire_elemental_pet.go
index 255277e8df..879a5e56dc 100644
--- a/sim/shaman/fire_elemental_pet.go
+++ b/sim/shaman/fire_elemental_pet.go
@@ -99,7 +99,6 @@ func (fireElemental *FireElemental) ExecuteCustomRotation(sim *core.Simulation)
 	fireNovaCasts := fireElemental.FireNova.SpellMetrics[0].Casts
 
 	if fireBlastCasts == maxFireBlastCasts && fireNovaCasts == maxFireNovaCasts {
-		fireElemental.DoNothing()
 		return
 	}
 
diff --git a/sim/shaman/fire_elemental_totem.go b/sim/shaman/fire_elemental_totem.go
index fe82987ad5..cebf9fed61 100644
--- a/sim/shaman/fire_elemental_totem.go
+++ b/sim/shaman/fire_elemental_totem.go
@@ -10,7 +10,7 @@ import (
 const fireTotemDuration time.Duration = time.Second * 120
 
 func (shaman *Shaman) registerFireElementalTotem() {
-	if !shaman.Totems.UseFireElemental && !shaman.IsUsingAPL {
+	if !shaman.Totems.UseFireElemental {
 		return
 	}
 
@@ -55,50 +55,13 @@ func (shaman *Shaman) registerFireElementalTotem() {
 
 			shaman.FireElemental.EnableWithTimeout(sim, shaman.FireElemental, fireTotemDuration)
 
-			//TODO handle more then one swap if the fight is greater then 5 mins, for now will just do the one.
-			if !shaman.IsUsingAPL && shaman.FireElementalTotem.SpellMetrics[target.Index].Casts == 1 {
-				shaman.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand, proto.ItemSlot_ItemSlotOffHand})
-			}
-
 			// Add a dummy aura to show in metrics
 			fireElementalAura.Activate(sim)
 		},
 	})
 
-	//Enh has 1.5seconds GCD also, so just going to wait the normal 1.5 instead of using the dynamic Spell GCD
-	var castWindow = 1550 * time.Millisecond
-
-	enhTier10Aura := shaman.GetAura("Maelstrom Power")
-
 	shaman.AddMajorCooldown(core.MajorCooldown{
 		Spell: shaman.FireElementalTotem,
 		Type:  core.CooldownTypeUnknown,
-		ShouldActivate: func(sim *core.Simulation, character *core.Character) bool {
-			success := false
-			if enhTier10Aura != nil && shaman.Totems.EnhTierTenBonus && shaman.fireElementalSnapShot != nil {
-				if enhTier10Aura.IsActive() {
-					success = shaman.fireElementalSnapShot.CanSnapShot(sim, castWindow)
-				} else if sim.CurrentTime+fireTotemDuration > sim.Encounter.Duration {
-					success = true
-				}
-			} else if sim.CurrentTime > 1*time.Second && shaman.fireElementalSnapShot == nil {
-				success = true
-			} else if sim.Encounter.Duration <= 120*time.Second && sim.CurrentTime >= 10*time.Second {
-				success = true
-			} else if sim.Encounter.Duration > 120*time.Second && sim.CurrentTime >= 20*time.Second {
-				success = true
-			} else if shaman.fireElementalSnapShot != nil {
-				success = shaman.fireElementalSnapShot.CanSnapShot(sim, castWindow)
-			}
-
-			if success && shaman.fireElementalSnapShot != nil {
-				shaman.castFireElemental = true
-				shaman.fireElementalSnapShot.ActivateMajorCooldowns(sim)
-				shaman.fireElementalSnapShot.ResetProcTrackers()
-				shaman.castFireElemental = false
-			}
-
-			return success
-		},
 	})
 }
diff --git a/sim/shaman/fire_totems.go b/sim/shaman/fire_totems.go
index 43f88bd94e..689a92328b 100644
--- a/sim/shaman/fire_totems.go
+++ b/sim/shaman/fire_totems.go
@@ -4,23 +4,9 @@ import (
 	"time"
 
 	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
 )
 
 func (shaman *Shaman) registerSearingTotemSpell() {
-	var extraCastCondition core.CanCastCondition
-	if !shaman.IsUsingAPL && shaman.Totems.Fire == proto.FireTotem_SearingTotem && shaman.Totems.UseFireMcd {
-		extraCastCondition = func(sim *core.Simulation, target *core.Unit) bool {
-			if shaman.Totems.Fire != proto.FireTotem_SearingTotem {
-				return false
-			}
-			if shaman.SearingTotem.Dot(shaman.CurrentTarget).IsActive() || shaman.FireElemental.IsEnabled() || shaman.FireElementalTotem.IsReady(sim) {
-				return false
-			}
-			return true
-		}
-	}
-
 	shaman.SearingTotem = shaman.RegisterSpell(core.SpellConfig{
 		ActionID:    core.ActionID{SpellID: 58704},
 		SpellSchool: core.SpellSchoolFire,
@@ -38,7 +24,6 @@ func (shaman *Shaman) registerSearingTotemSpell() {
 				GCD: time.Second,
 			},
 		},
-		ExtraCastCondition: extraCastCondition,
 
 		BonusHitRating:   float64(shaman.Talents.ElementalPrecision) * core.SpellHitRatingPerHitChance,
 		DamageMultiplier: 1 + float64(shaman.Talents.CallOfFlame)*0.05,
@@ -66,37 +51,13 @@ func (shaman *Shaman) registerSearingTotemSpell() {
 			shaman.MagmaTotem.AOEDot().Cancel(sim)
 			shaman.FireElemental.Disable(sim)
 			spell.Dot(sim.GetTargetUnit(0)).Apply(sim)
-			if !shaman.Totems.UseFireMcd {
-				// +1 needed because of rounding issues with totem tick time.
-				shaman.NextTotemDrops[FireTotem] = sim.CurrentTime + time.Second*60 + 1
-			}
+			// +1 needed because of rounding issues with totem tick time.
+			shaman.NextTotemDrops[FireTotem] = sim.CurrentTime + time.Second*60 + 1
 		},
 	})
-
-	if extraCastCondition == nil {
-		return
-	}
-	shaman.AddMajorCooldown(core.MajorCooldown{
-		Spell:    shaman.SearingTotem,
-		Priority: core.CooldownPriorityDefault, // TODO needs to be altered due to snap shotting.
-		Type:     core.CooldownTypeDPS,
-	})
 }
 
 func (shaman *Shaman) registerMagmaTotemSpell() {
-	var extraCastCondition core.CanCastCondition
-	if !shaman.IsUsingAPL && shaman.Totems.Fire == proto.FireTotem_MagmaTotem && shaman.Totems.UseFireMcd {
-		extraCastCondition = func(sim *core.Simulation, target *core.Unit) bool {
-			if shaman.Totems.Fire != proto.FireTotem_MagmaTotem {
-				return false
-			}
-			if shaman.MagmaTotem.AOEDot().IsActive() || shaman.FireElemental.IsEnabled() || shaman.FireElementalTotem.IsReady(sim) {
-				return false
-			}
-			return true
-		}
-	}
-
 	shaman.MagmaTotem = shaman.RegisterSpell(core.SpellConfig{
 		ActionID:    core.ActionID{SpellID: 58734},
 		SpellSchool: core.SpellSchoolFire,
@@ -114,7 +75,6 @@ func (shaman *Shaman) registerMagmaTotemSpell() {
 				GCD: time.Second,
 			},
 		},
-		ExtraCastCondition: extraCastCondition,
 
 		BonusHitRating:   float64(shaman.Talents.ElementalPrecision) * core.SpellHitRatingPerHitChance,
 		DamageMultiplier: 1 + float64(shaman.Talents.CallOfFlame)*0.05,
@@ -141,19 +101,8 @@ func (shaman *Shaman) registerMagmaTotemSpell() {
 			shaman.SearingTotem.Dot(shaman.CurrentTarget).Cancel(sim)
 			shaman.FireElemental.Disable(sim)
 			spell.AOEDot().Apply(sim)
-			if !shaman.Totems.UseFireMcd {
-				// +1 needed because of rounding issues with totem tick time.
-				shaman.NextTotemDrops[FireTotem] = sim.CurrentTime + time.Second*20 + 1
-			}
+			// +1 needed because of rounding issues with totem tick time.
+			shaman.NextTotemDrops[FireTotem] = sim.CurrentTime + time.Second*20 + 1
 		},
 	})
-
-	if extraCastCondition == nil {
-		return
-	}
-	shaman.AddMajorCooldown(core.MajorCooldown{
-		Spell:    shaman.MagmaTotem,
-		Priority: core.CooldownPriorityDefault, // TODO needs to be altered due to snap shotting.
-		Type:     core.CooldownTypeDPS,
-	})
 }
diff --git a/sim/shaman/restoration/restoration.go b/sim/shaman/restoration/restoration.go
index 880f4c0d29..a4a2d0b706 100644
--- a/sim/shaman/restoration/restoration.go
+++ b/sim/shaman/restoration/restoration.go
@@ -1,8 +1,6 @@
 package restoration
 
 import (
-	"time"
-
 	"github.com/wowsims/wotlk/sim/core"
 	"github.com/wowsims/wotlk/sim/core/proto"
 	"github.com/wowsims/wotlk/sim/shaman"
@@ -33,26 +31,15 @@ func NewRestorationShaman(character *core.Character, options *proto.Player) *Res
 		Shield:    restoShamOptions.Options.Shield,
 	}
 
-	if restoShamOptions.Rotation.Bloodlust != proto.RestorationShaman_Rotation_UnsetBloodlust {
-		selfBuffs.Bloodlust = restoShamOptions.Rotation.Bloodlust == proto.RestorationShaman_Rotation_UseBloodlust
-	}
-
 	totems := &proto.ShamanTotems{}
 	if restoShamOptions.Options.Totems != nil {
 		totems = restoShamOptions.Options.Totems
 	}
 
 	resto := &RestorationShaman{
-		Shaman:   shaman.NewShaman(character, options.TalentsString, totems, selfBuffs, false),
-		rotation: restoShamOptions.Rotation,
+		Shaman: shaman.NewShaman(character, options.TalentsString, totems, selfBuffs, false),
 	}
 
-	// can only use earth shield if specc'd
-	resto.rotation.UseEarthShield = resto.rotation.UseEarthShield && resto.Talents.EarthShield
-	resto.earthShieldPPM = restoShamOptions.Options.EarthShieldPPM
-
-	resto.EnableResumeAfterManaWait(resto.tryUseGCD)
-
 	if resto.HasMHWeapon() {
 		resto.ApplyEarthlivingImbueToItem(resto.GetMHWeapon())
 	}
@@ -65,10 +52,6 @@ func NewRestorationShaman(character *core.Character, options *proto.Player) *Res
 
 type RestorationShaman struct {
 	*shaman.Shaman
-
-	rotation            *proto.RestorationShaman_Rotation
-	earthShieldPPM      int32
-	lastEarthShieldProc time.Duration
 }
 
 func (resto *RestorationShaman) GetShaman() *shaman.Shaman {
@@ -77,7 +60,6 @@ func (resto *RestorationShaman) GetShaman() *shaman.Shaman {
 
 func (resto *RestorationShaman) Reset(sim *core.Simulation) {
 	resto.Shaman.Reset(sim)
-	resto.lastEarthShieldProc = -core.NeverExpires
 }
 func (resto *RestorationShaman) GetMainTarget() *core.Unit {
 	// TODO: make this just grab first player that isn't self.
diff --git a/sim/shaman/restoration/rotation.go b/sim/shaman/restoration/rotation.go
deleted file mode 100644
index 08a0c52d54..0000000000
--- a/sim/shaman/restoration/rotation.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package restoration
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-)
-
-func (resto *RestorationShaman) OnGCDReady(sim *core.Simulation) {
-	resto.tryUseGCD(sim)
-}
-
-func (resto *RestorationShaman) tryUseGCD(sim *core.Simulation) {
-
-	// TODO: This could actually just be made as a PA that runs and triggers the shield instead of part of the rotation here.
-	es := resto.EarthShield.Hot(resto.CurrentTarget)
-	if es.IsActive() && resto.earthShieldPPM > 0 {
-		procTime := time.Duration(60.0 / float64(resto.earthShieldPPM) * float64(time.Second))
-		lastProc := resto.lastEarthShieldProc
-		if lastProc+procTime < sim.CurrentTime {
-			es.OnSpellHitTaken(es.Aura, sim, nil, &core.SpellResult{Outcome: core.OutcomeHit, Target: resto.CurrentTarget})
-			resto.lastEarthShieldProc = sim.CurrentTime
-		}
-	}
-
-	if resto.TryDropTotems(sim) {
-		return
-	}
-
-	var spell *core.Spell
-	switch resto.rotation.PrimaryHeal {
-	case proto.ShamanHealSpell_AutoHeal:
-		if len(resto.Party.Players) > 3 {
-			spell = resto.ChainHeal
-		} else {
-			// TODO: lots of things to consider here...
-			spell = resto.LesserHealingWave
-		}
-	case proto.ShamanHealSpell_LesserHealingWave:
-		spell = resto.LesserHealingWave
-	case proto.ShamanHealSpell_HealingWave:
-		panic("healing wave not implemented yet")
-		spell = resto.HealingWave
-	case proto.ShamanHealSpell_ChainHeal:
-		spell = resto.ChainHeal
-	}
-
-	if resto.rotation.UseEarthShield && !es.IsActive() {
-		spell = resto.EarthShield
-	} else if resto.rotation.UseRiptide && !resto.Riptide.Hot(resto.CurrentTarget).IsActive() && resto.Riptide.IsReady(sim) {
-		spell = resto.Riptide
-	}
-
-	if !spell.Cast(sim, resto.CurrentTarget) {
-		resto.WaitForMana(sim, spell.CurCast.Cost)
-	}
-}
diff --git a/sim/shaman/shaman.go b/sim/shaman/shaman.go
index 79f21578ba..a895f3acc3 100644
--- a/sim/shaman/shaman.go
+++ b/sim/shaman/shaman.go
@@ -80,8 +80,6 @@ type Shaman struct {
 
 	thunderstormInRange bool // flag if thunderstorm will be in range.
 
-	ShamanisticRageManaThreshold float64 //% of mana to use sham. rage at
-
 	Talents   *proto.ShamanTalents
 	SelfBuffs SelfBuffs
 
@@ -116,10 +114,8 @@ type Shaman struct {
 	FeralSpirit  *core.Spell
 	SpiritWolves *SpiritWolves
 
-	castFireElemental     bool
-	FireElemental         *FireElemental
-	FireElementalTotem    *core.Spell
-	fireElementalSnapShot *core.SnapshotManager
+	FireElemental      *FireElemental
+	FireElementalTotem *core.Spell
 
 	MagmaTotem           *core.Spell
 	ManaSpringTotem      *core.Spell
@@ -227,8 +223,6 @@ func (shaman *Shaman) AddPartyBuffs(partyBuffs *proto.PartyBuffs) {
 }
 
 func (shaman *Shaman) Initialize() {
-	enableSnapshot := shaman.Totems.BonusSpellpower == 0
-
 	shaman.registerChainLightningSpell()
 	shaman.registerFeralSpirit()
 	shaman.registerFireElementalTotem()
@@ -256,19 +250,6 @@ func (shaman *Shaman) Initialize() {
 	shaman.registerCallOfTheElements()
 
 	shaman.registerBloodlustCD()
-
-	if shaman.Totems.UseFireElemental && enableSnapshot {
-		shaman.fireElementalSnapShot = core.NewSnapshotManager(shaman.GetCharacter())
-		shaman.setupProcTrackers()
-	}
-
-	// Healing stream totem applies a HoT (aura) and so needs to be handled as a pre-pull action
-	// instead of during init/reset.
-	if !shaman.IsUsingAPL && shaman.Totems.Water == proto.WaterTotem_HealingStreamTotem {
-		shaman.RegisterPrepullAction(0, func(sim *core.Simulation) {
-			shaman.HealingStreamTotem.Cast(sim, &shaman.Unit)
-		})
-	}
 }
 
 func (shaman *Shaman) RegisterHealingSpells() {
@@ -301,11 +282,6 @@ func (shaman *Shaman) RegisterHealingSpells() {
 }
 
 func (shaman *Shaman) Reset(sim *core.Simulation) {
-	if shaman.Totems.UseFireElemental {
-		shaman.setupFireElementalCooldowns()
-		shaman.castFireElemental = false
-	}
-
 	// Check to see if we are casting a totem to set its expire time.
 	for i := range shaman.NextTotemDrops {
 		shaman.NextTotemDrops[i] = core.NeverExpires
@@ -344,111 +320,6 @@ func (shaman *Shaman) Reset(sim *core.Simulation) {
 	shaman.FlameShock.CD.Reset()
 }
 
-func (shaman *Shaman) setupProcTrackers() {
-	snapshotManager := shaman.fireElementalSnapShot
-
-	snapshotManager.AddProc(40212, "Potion of Wild Magic", true)
-	snapshotManager.AddProc(33697, "Blood Fury", true)
-	snapshotManager.AddProc(59620, "Berserking MH Proc", false)
-	snapshotManager.AddProc(59620, "Berserking OH Proc", false)
-
-	//AP Ring Procs
-	snapshotManager.AddProc(44308, "Signet of Edward the Odd Proc", false)
-	snapshotManager.AddProc(50401, "Ashen Band of Unmatched Vengeance Proc", false)
-	snapshotManager.AddProc(50402, "Ashen Band of Endless Vengeance Proc", false)
-	snapshotManager.AddProc(52571, "Ashen Band of Unmatched Might Proc", false)
-	snapshotManager.AddProc(52572, "Ashen Band of Endless Might Proc", false)
-
-	//SP Trinket Procs
-	snapshotManager.AddProc(40255, "Dying Curse Proc", false)
-	snapshotManager.AddProc(40682, "Sundial of the Exiled Proc", false)
-	snapshotManager.AddProc(37660, "Forge Ember Proc", false)
-	snapshotManager.AddProc(45518, "Flare of the Heavens Proc", false)
-	snapshotManager.AddProc(54572, "Charred Twilight Scale Proc", false)
-	snapshotManager.AddProc(54588, "Charred Twilight Scale H Proc", false)
-	snapshotManager.AddProc(47213, "Abyssal Rune Proc", false)
-	snapshotManager.AddProc(45490, "Pandora's Plea Proc", false)
-	snapshotManager.AddProc(50348, "Dislodged Foreign Object H", false)
-	snapshotManager.AddProc(50353, "Dislodged Foreign Object", false)
-	snapshotManager.AddProc(50360, "Phylactery of the Nameless Lich Proc", false)
-	snapshotManager.AddProc(50365, "Phylactery of the Nameless Lich H Proc", false)
-	snapshotManager.AddProc(50345, "Muradin's Spyglass H Proc", false)
-	snapshotManager.AddProc(50340, "Muradin's Spyglass Proc", false)
-
-	// SP Ring Procs
-	snapshotManager.AddProc(50398, "Ashen Band of Endless Destruction", false)
-
-	//AP Trinket Procs
-	snapshotManager.AddProc(40684, "Mirror of Truth Proc", false)
-	snapshotManager.AddProc(45522, "Blood of the Old God Proc", false)
-	snapshotManager.AddProc(40767, "Sonic Booster Proc", false)
-	snapshotManager.AddProc(44914, "Anvil of Titans Proc", false)
-	snapshotManager.AddProc(45286, "Pyrite Infuser Proc", false)
-	snapshotManager.AddProc(47214, "Banner of Victory Proc", false)
-	snapshotManager.AddProc(49074, "Coren's Chromium Coaster Proc", false)
-	snapshotManager.AddProc(50342, "Whispering Fanged Skull Proc", false)
-	snapshotManager.AddProc(50343, "Whispering Fanged Skull H Proc", false)
-	snapshotManager.AddProc(54569, "Sharpened Twilight Scale Proc", false)
-	snapshotManager.AddProc(54590, "Sharpened Twilight Scale H Proc", false)
-	snapshotManager.AddProc(47115, "Deaths Verdict Agility Proc", false)
-	snapshotManager.AddProc(47131, "Deaths Verdict H Agility Proc", false)
-	snapshotManager.AddProc(47303, "Deaths Choice Agility Proc", false)
-	snapshotManager.AddProc(47464, "Deaths Choice H Agility Proc", false)
-	snapshotManager.AddProc(71492, "Deathbringer's Will Strength Proc", false)
-	snapshotManager.AddProc(71561, "Deathbringer's Will H Strength Proc", false)
-	snapshotManager.AddProc(71492, "Deathbringer's Will Agility Proc", false)
-	snapshotManager.AddProc(71561, "Deathbringer's Will H Agility Proc", false)
-	snapshotManager.AddProc(71492, "Deathbringer's Will AP Proc", false)
-	snapshotManager.AddProc(71561, "Deathbringer's Will H AP Proc", false)
-
-	// Tier Bonus
-	snapshotManager.AddProc(70831, "Maelstrom Power", false)
-}
-
-func (shaman *Shaman) setupFireElementalCooldowns() {
-	if shaman.fireElementalSnapShot == nil {
-		return
-	}
-
-	shaman.fireElementalSnapShot.ClearMajorCooldowns()
-
-	// blood fury (orc)
-	shaman.fireElementalCooldownSync(core.ActionID{SpellID: 33697}, false)
-
-	// potion of Wild Magic
-	shaman.fireElementalCooldownSync(core.ActionID{ItemID: 40212}, true)
-
-	//active sp trinkets
-	shaman.fireElementalCooldownSync(core.ActionID{ItemID: 37873}, false)
-	shaman.fireElementalCooldownSync(core.ActionID{ItemID: 45148}, false)
-	shaman.fireElementalCooldownSync(core.ActionID{ItemID: 48724}, false)
-	shaman.fireElementalCooldownSync(core.ActionID{ItemID: 50357}, false)
-
-	// active ap trinkets
-	shaman.fireElementalCooldownSync(core.ActionID{ItemID: 35937}, false)
-	shaman.fireElementalCooldownSync(core.ActionID{ItemID: 36871}, false)
-	shaman.fireElementalCooldownSync(core.ActionID{ItemID: 37166}, false)
-	shaman.fireElementalCooldownSync(core.ActionID{ItemID: 37556}, false)
-	shaman.fireElementalCooldownSync(core.ActionID{ItemID: 37557}, false)
-	shaman.fireElementalCooldownSync(core.ActionID{ItemID: 38080}, false)
-	shaman.fireElementalCooldownSync(core.ActionID{ItemID: 38081}, false)
-	shaman.fireElementalCooldownSync(core.ActionID{ItemID: 38761}, false)
-	shaman.fireElementalCooldownSync(core.ActionID{ItemID: 39257}, false)
-	shaman.fireElementalCooldownSync(core.ActionID{ItemID: 45263}, false)
-	shaman.fireElementalCooldownSync(core.ActionID{ItemID: 46086}, false)
-	shaman.fireElementalCooldownSync(core.ActionID{ItemID: 47734}, false)
-}
-
-func (shaman *Shaman) fireElementalCooldownSync(actionID core.ActionID, isPotion bool) {
-	if majorCd := shaman.Character.GetMajorCooldown(actionID); majorCd != nil {
-		majorCd.ShouldActivate = func(sim *core.Simulation, character *core.Character) bool {
-			return shaman.castFireElemental || (shaman.FireElementalTotem.CD.TimeToReady(sim) > majorCd.Spell.CD.Duration && !isPotion) || shaman.FireElementalTotem.CD.ReadyAt() > shaman.Env.Encounter.Duration
-		}
-
-		shaman.fireElementalSnapShot.AddMajorCooldown(majorCd)
-	}
-}
-
 func (shaman *Shaman) ElementalCritMultiplier(secondary float64) float64 {
 	critBonus := 0.2*float64(shaman.Talents.ElementalFury) + secondary
 	return shaman.SpellCritMultiplier(1, critBonus)
diff --git a/sim/shaman/shamanistic_rage.go b/sim/shaman/shamanistic_rage.go
index 42fcde55ef..d62b95fe91 100644
--- a/sim/shaman/shamanistic_rage.go
+++ b/sim/shaman/shamanistic_rage.go
@@ -12,10 +12,7 @@ func (shaman *Shaman) registerShamanisticRageCD() {
 		return
 	}
 
-	t10Bonus := false
-	if shaman.HasSetBonus(ItemSetFrostWitchBattlegear, 2) {
-		t10Bonus = true
-	}
+	t10Bonus := shaman.HasSetBonus(ItemSetFrostWitchBattlegear, 2)
 
 	actionID := core.ActionID{SpellID: 30823}
 	ppmm := shaman.AutoAttacks.NewPPMManager(15, core.ProcMaskMelee)
@@ -66,22 +63,11 @@ func (shaman *Shaman) registerShamanisticRageCD() {
 		},
 	})
 
-	if shaman.IsUsingAPL {
-		shaman.AddMajorCooldown(core.MajorCooldown{
-			Spell: spell,
-			Type:  core.CooldownTypeMana,
-			ShouldActivate: func(sim *core.Simulation, character *core.Character) bool {
-				return character.CurrentManaPercent() <= 0.2
-			},
-		})
-	} else {
-		shaman.AddMajorCooldown(core.MajorCooldown{
-			Spell: spell,
-			Type:  core.CooldownTypeMana,
-			ShouldActivate: func(sim *core.Simulation, character *core.Character) bool {
-				manaReserve := shaman.ShamanisticRageManaThreshold / 100 * shaman.MaxMana()
-				return character.CurrentMana() <= manaReserve
-			},
-		})
-	}
+	shaman.AddMajorCooldown(core.MajorCooldown{
+		Spell: spell,
+		Type:  core.CooldownTypeMana,
+		ShouldActivate: func(sim *core.Simulation, character *core.Character) bool {
+			return character.CurrentManaPercent() <= 0.2
+		},
+	})
 }
diff --git a/sim/shaman/totems.go b/sim/shaman/totems.go
index fdf73dd247..701228ee2a 100644
--- a/sim/shaman/totems.go
+++ b/sim/shaman/totems.go
@@ -187,47 +187,6 @@ func (shaman *Shaman) registerCallOfTheElements() {
 	})
 }
 
-func (shaman *Shaman) NextTotemAt(_ *core.Simulation) time.Duration {
-	return min(shaman.NextTotemDrops[0], shaman.NextTotemDrops[1], shaman.NextTotemDrops[2], shaman.NextTotemDrops[3])
-}
-
-// TryDropTotems will check to see if totems need to be re-cast.
-//
-//	Returns whether we tried to cast a totem, regardless of whether it succeeded.
-func (shaman *Shaman) TryDropTotems(sim *core.Simulation) bool {
-	var spell *core.Spell
-
-	casted := false
-	for totemTypeIdx, totemExpiration := range shaman.NextTotemDrops {
-		spell = nil
-		nextDrop := shaman.NextTotemDropType[totemTypeIdx]
-		if sim.CurrentTime >= totemExpiration {
-			switch totemTypeIdx {
-			case AirTotem:
-				spell = shaman.getAirTotemSpell(proto.AirTotem(nextDrop))
-			case EarthTotem:
-				spell = shaman.getEarthTotemSpell(proto.EarthTotem(nextDrop))
-			case FireTotem:
-				spell = shaman.getFireTotemSpell(proto.FireTotem(nextDrop))
-			case WaterTotem:
-				spell = shaman.getWaterTotemSpell(proto.WaterTotem(nextDrop))
-			}
-		}
-		if spell != nil {
-			if success := spell.Cast(sim, shaman.CurrentTarget); !success {
-				shaman.WaitForMana(sim, spell.CurCast.Cost)
-				return true
-			}
-			casted = true
-		}
-	}
-
-	if casted {
-		shaman.WaitUntil(sim, sim.CurrentTime+time.Second)
-	}
-	return casted
-}
-
 func (shaman *Shaman) getAirTotemSpell(totemType proto.AirTotem) *core.Spell {
 	switch totemType {
 	case proto.AirTotem_WrathOfAirTotem:
diff --git a/ui/elemental_shaman/apls/advanced.apl.json b/ui/elemental_shaman/apls/advanced.apl.json
index 3240d5bc13..73b7d2f0b4 100644
--- a/ui/elemental_shaman/apls/advanced.apl.json
+++ b/ui/elemental_shaman/apls/advanced.apl.json
@@ -1,5 +1,4 @@
 {
-  "enabled": true,
   "type": "TypeAPL",
   "prepullActions": [
     {"action":{"castSpell":{"spellId":{"spellId":59159}}},"doAtValue":{"const":{"val":"-5"}}},

From c5c01550b7b4123bcf47d4fddedc197d5ca73ae4 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Sat, 6 Jan 2024 13:34:34 -0800
Subject: [PATCH 12/28] Commit before fixing warlock tests

---
 proto/shaman.proto                         |  14 -
 sim/shaman/apl_values.go                   |   8 +-
 sim/shaman/elemental/elemental.go          |   1 -
 sim/shaman/fire_elemental_totem.go         |   4 +-
 sim/shaman/fire_totems.go                  |   4 +-
 sim/shaman/restoration/restoration_test.go |   3 +-
 sim/shaman/shaman.go                       |  43 +-
 sim/shaman/talents.go                      |   2 +-
 sim/shaman/totems.go                       |  18 +-
 sim/warlock/inferno.go                     |   6 +-
 sim/warlock/pet.go                         |   1 -
 sim/warlock/rotation.go                    | 690 ---------------------
 sim/warlock/seed.go                        |   6 +-
 sim/warlock/warlock.go                     |  55 +-
 14 files changed, 27 insertions(+), 828 deletions(-)
 delete mode 100644 sim/warlock/rotation.go

diff --git a/proto/shaman.proto b/proto/shaman.proto
index 43dd006743..b5fde9f161 100644
--- a/proto/shaman.proto
+++ b/proto/shaman.proto
@@ -176,24 +176,10 @@ message ShamanTotems {
 	FireTotem fire = 3;
 	WaterTotem water = 4;
 
-	// If set, will use mana tide when appropriate.
-	bool use_mana_tide = 5;
-
 	// If set, will use fire elemental totem at the start and revert to regular
 	// fire totems when it expires.
 	bool use_fire_elemental = 6;
 
-	// If set, will revert to regular fire totems when fire elemental goes OOM,
-	// instead of waiting the full 2 minutes.
-	bool recall_fire_elemental_on_oom = 7;
-
-	// If set, any time a 2-minute totem is about to expire, will recall and
-	// replace all totems.
-	bool recall_totems = 8;
-
-	// If set will use fire totems as an MCD instead of manually controlling when to place them.
-	bool use_fire_mcd = 9;
-
 	// Bonus spell power for fire elemental snapshotting.
 	int32 bonus_spellpower = 10;
 
diff --git a/sim/shaman/apl_values.go b/sim/shaman/apl_values.go
index 447906b0b9..068726e23b 100644
--- a/sim/shaman/apl_values.go
+++ b/sim/shaman/apl_values.go
@@ -38,13 +38,13 @@ func (value *APLValueTotemRemainingTime) Type() proto.APLValueType {
 }
 func (value *APLValueTotemRemainingTime) GetDuration(sim *core.Simulation) time.Duration {
 	if value.totemType == proto.ShamanTotems_Earth {
-		return max(0, value.shaman.NextTotemDrops[EarthTotem]-sim.CurrentTime)
+		return max(0, value.shaman.TotemExpirations[EarthTotem]-sim.CurrentTime)
 	} else if value.totemType == proto.ShamanTotems_Air {
-		return max(0, value.shaman.NextTotemDrops[AirTotem]-sim.CurrentTime)
+		return max(0, value.shaman.TotemExpirations[AirTotem]-sim.CurrentTime)
 	} else if value.totemType == proto.ShamanTotems_Fire {
-		return max(0, value.shaman.NextTotemDrops[FireTotem]-sim.CurrentTime)
+		return max(0, value.shaman.TotemExpirations[FireTotem]-sim.CurrentTime)
 	} else if value.totemType == proto.ShamanTotems_Water {
-		return max(0, value.shaman.NextTotemDrops[WaterTotem]-sim.CurrentTime)
+		return max(0, value.shaman.TotemExpirations[WaterTotem]-sim.CurrentTime)
 	} else {
 		return 0
 	}
diff --git a/sim/shaman/elemental/elemental.go b/sim/shaman/elemental/elemental.go
index aa4a5e1bfd..b33058633b 100644
--- a/sim/shaman/elemental/elemental.go
+++ b/sim/shaman/elemental/elemental.go
@@ -35,7 +35,6 @@ func NewElementalShaman(character *core.Character, options *proto.Player) *Eleme
 	totems := &proto.ShamanTotems{}
 	if eleShamOptions.Options.Totems != nil {
 		totems = eleShamOptions.Options.Totems
-		totems.UseFireMcd = true // Control fire totems as MCD.
 	}
 
 	inRange := eleShamOptions.Options.ThunderstormRange == proto.ElementalShaman_Options_TSInRange
diff --git a/sim/shaman/fire_elemental_totem.go b/sim/shaman/fire_elemental_totem.go
index cebf9fed61..c7665c4646 100644
--- a/sim/shaman/fire_elemental_totem.go
+++ b/sim/shaman/fire_elemental_totem.go
@@ -42,9 +42,9 @@ func (shaman *Shaman) registerFireElementalTotem() {
 			// TODO: ToW needs a unique buff/debuff aura for each raidmember/target.
 			//  Otherwise we will be possibly disabling another ele shaman's ToW debuff/buff.
 			if shaman.Totems.Fire == proto.FireTotem_TotemOfWrath {
-				shaman.NextTotemDrops[FireTotem] = sim.CurrentTime + fireTotemDuration
+				shaman.TotemExpirations[FireTotem] = sim.CurrentTime + fireTotemDuration
 			} else if shaman.Totems.Fire != proto.FireTotem_NoFireTotem && !shaman.Totems.UseFireMcd {
-				shaman.NextTotemDrops[FireTotem] = sim.CurrentTime + fireTotemDuration
+				shaman.TotemExpirations[FireTotem] = sim.CurrentTime + fireTotemDuration
 			}
 
 			shaman.MagmaTotem.AOEDot().Cancel(sim)
diff --git a/sim/shaman/fire_totems.go b/sim/shaman/fire_totems.go
index 689a92328b..a66cc6573d 100644
--- a/sim/shaman/fire_totems.go
+++ b/sim/shaman/fire_totems.go
@@ -52,7 +52,7 @@ func (shaman *Shaman) registerSearingTotemSpell() {
 			shaman.FireElemental.Disable(sim)
 			spell.Dot(sim.GetTargetUnit(0)).Apply(sim)
 			// +1 needed because of rounding issues with totem tick time.
-			shaman.NextTotemDrops[FireTotem] = sim.CurrentTime + time.Second*60 + 1
+			shaman.TotemExpirations[FireTotem] = sim.CurrentTime + time.Second*60 + 1
 		},
 	})
 }
@@ -102,7 +102,7 @@ func (shaman *Shaman) registerMagmaTotemSpell() {
 			shaman.FireElemental.Disable(sim)
 			spell.AOEDot().Apply(sim)
 			// +1 needed because of rounding issues with totem tick time.
-			shaman.NextTotemDrops[FireTotem] = sim.CurrentTime + time.Second*20 + 1
+			shaman.TotemExpirations[FireTotem] = sim.CurrentTime + time.Second*20 + 1
 		},
 	})
 }
diff --git a/sim/shaman/restoration/restoration_test.go b/sim/shaman/restoration/restoration_test.go
index 3944f25510..629b68812a 100644
--- a/sim/shaman/restoration/restoration_test.go
+++ b/sim/shaman/restoration/restoration_test.go
@@ -91,8 +91,7 @@ var restoShamOptions = &proto.RestorationShaman_Options{
 }
 var PlayerOptionsStandard = &proto.Player_RestorationShaman{
 	RestorationShaman: &proto.RestorationShaman{
-		Options:  restoShamOptions,
-		Rotation: &proto.RestorationShaman_Rotation{},
+		Options: restoShamOptions,
 	},
 }
 
diff --git a/sim/shaman/shaman.go b/sim/shaman/shaman.go
index a895f3acc3..ca370a75ba 100644
--- a/sim/shaman/shaman.go
+++ b/sim/shaman/shaman.go
@@ -85,10 +85,8 @@ type Shaman struct {
 
 	Totems *proto.ShamanTotems
 
-	// The type of totem which should be dropped next and time to drop it, for
-	// each totem type (earth, air, fire, water).
-	NextTotemDropType [4]int32
-	NextTotemDrops    [4]time.Duration
+	// The expiration time of each totem (earth, air, fire, water).
+	TotemExpirations [4]time.Duration
 
 	LightningBolt   *core.Spell
 	LightningBoltLO *core.Spell
@@ -282,42 +280,9 @@ func (shaman *Shaman) RegisterHealingSpells() {
 }
 
 func (shaman *Shaman) Reset(sim *core.Simulation) {
-	// Check to see if we are casting a totem to set its expire time.
-	for i := range shaman.NextTotemDrops {
-		shaman.NextTotemDrops[i] = core.NeverExpires
-		switch i {
-		case AirTotem:
-			if shaman.Totems.Air != proto.AirTotem_NoAirTotem {
-				shaman.NextTotemDrops[i] = TotemRefreshTime5M
-				shaman.NextTotemDropType[i] = int32(shaman.Totems.Air)
-			}
-		case EarthTotem:
-			if shaman.Totems.Earth != proto.EarthTotem_NoEarthTotem {
-				shaman.NextTotemDrops[i] = TotemRefreshTime5M
-				shaman.NextTotemDropType[i] = int32(shaman.Totems.Earth)
-			}
-		case FireTotem:
-			shaman.NextTotemDropType[FireTotem] = int32(shaman.Totems.Fire)
-			if shaman.NextTotemDropType[FireTotem] != int32(proto.FireTotem_NoFireTotem) {
-				if shaman.NextTotemDropType[FireTotem] != int32(proto.FireTotem_TotemOfWrath) &&
-					shaman.NextTotemDropType[FireTotem] != int32(proto.FireTotem_FlametongueTotem) {
-					if !shaman.Totems.UseFireMcd {
-						shaman.NextTotemDrops[FireTotem] = 0
-					}
-				} else {
-					shaman.NextTotemDrops[FireTotem] = TotemRefreshTime5M
-					if shaman.NextTotemDropType[FireTotem] == int32(proto.FireTotem_TotemOfWrath) {
-						shaman.applyToWDebuff(sim)
-					}
-				}
-			}
-		case WaterTotem:
-			shaman.NextTotemDropType[i] = int32(shaman.Totems.Water)
-			shaman.NextTotemDrops[i] = TotemRefreshTime5M
-		}
+	if shaman.Totems.Fire == proto.FireTotem_TotemOfWrath {
+		shaman.applyToWDebuff(sim)
 	}
-
-	shaman.FlameShock.CD.Reset()
 }
 
 func (shaman *Shaman) ElementalCritMultiplier(secondary float64) float64 {
diff --git a/sim/shaman/talents.go b/sim/shaman/talents.go
index 0ea408dbbd..36701c9799 100644
--- a/sim/shaman/talents.go
+++ b/sim/shaman/talents.go
@@ -482,7 +482,7 @@ func (shaman *Shaman) registerManaTideTotemCD() {
 
 			// TODO: Current water totem buff needs to be removed from party/raid.
 			if shaman.Totems.Water != proto.WaterTotem_NoWaterTotem {
-				shaman.NextTotemDrops[WaterTotem] = sim.CurrentTime + time.Second*12
+				shaman.TotemExpirations[WaterTotem] = sim.CurrentTime + time.Second*12
 			}
 		},
 	})
diff --git a/sim/shaman/totems.go b/sim/shaman/totems.go
index 701228ee2a..9b7e51ed6e 100644
--- a/sim/shaman/totems.go
+++ b/sim/shaman/totems.go
@@ -24,7 +24,7 @@ func (shaman *Shaman) newTotemSpellConfig(baseCost float64, spellID int32) core.
 func (shaman *Shaman) registerWrathOfAirTotemSpell() {
 	config := shaman.newTotemSpellConfig(0.11, 3738)
 	config.ApplyEffects = func(sim *core.Simulation, _ *core.Unit, _ *core.Spell) {
-		shaman.NextTotemDrops[AirTotem] = sim.CurrentTime + time.Second*300
+		shaman.TotemExpirations[AirTotem] = sim.CurrentTime + time.Second*300
 	}
 	shaman.WrathOfAirTotem = shaman.RegisterSpell(config)
 }
@@ -32,7 +32,7 @@ func (shaman *Shaman) registerWrathOfAirTotemSpell() {
 func (shaman *Shaman) registerWindfuryTotemSpell() {
 	config := shaman.newTotemSpellConfig(0.11, 8512)
 	config.ApplyEffects = func(sim *core.Simulation, _ *core.Unit, _ *core.Spell) {
-		shaman.NextTotemDrops[AirTotem] = sim.CurrentTime + time.Second*300
+		shaman.TotemExpirations[AirTotem] = sim.CurrentTime + time.Second*300
 	}
 	shaman.WindfuryTotem = shaman.RegisterSpell(config)
 }
@@ -40,7 +40,7 @@ func (shaman *Shaman) registerWindfuryTotemSpell() {
 func (shaman *Shaman) registerManaSpringTotemSpell() {
 	config := shaman.newTotemSpellConfig(0.04, 58774)
 	config.ApplyEffects = func(sim *core.Simulation, _ *core.Unit, _ *core.Spell) {
-		shaman.NextTotemDrops[WaterTotem] = sim.CurrentTime + time.Second*300
+		shaman.TotemExpirations[WaterTotem] = sim.CurrentTime + time.Second*300
 	}
 	shaman.ManaSpringTotem = shaman.RegisterSpell(config)
 }
@@ -72,7 +72,7 @@ func (shaman *Shaman) registerHealingStreamTotemSpell() {
 		},
 	}
 	config.ApplyEffects = func(sim *core.Simulation, _ *core.Unit, spell *core.Spell) {
-		shaman.NextTotemDrops[WaterTotem] = sim.CurrentTime + time.Second*300
+		shaman.TotemExpirations[WaterTotem] = sim.CurrentTime + time.Second*300
 		for _, agent := range shaman.Party.Players {
 			spell.Hot(&agent.GetCharacter().Unit).Activate(sim)
 		}
@@ -83,7 +83,7 @@ func (shaman *Shaman) registerHealingStreamTotemSpell() {
 func (shaman *Shaman) registerTotemOfWrathSpell() {
 	config := shaman.newTotemSpellConfig(0.05, 57722)
 	config.ApplyEffects = func(sim *core.Simulation, _ *core.Unit, _ *core.Spell) {
-		shaman.NextTotemDrops[FireTotem] = sim.CurrentTime + time.Second*300
+		shaman.TotemExpirations[FireTotem] = sim.CurrentTime + time.Second*300
 		shaman.applyToWDebuff(sim)
 	}
 	shaman.TotemOfWrath = shaman.RegisterSpell(config)
@@ -99,7 +99,7 @@ func (shaman *Shaman) applyToWDebuff(sim *core.Simulation) {
 func (shaman *Shaman) registerFlametongueTotemSpell() {
 	config := shaman.newTotemSpellConfig(0.11, 58656)
 	config.ApplyEffects = func(sim *core.Simulation, _ *core.Unit, _ *core.Spell) {
-		shaman.NextTotemDrops[FireTotem] = sim.CurrentTime + time.Second*300
+		shaman.TotemExpirations[FireTotem] = sim.CurrentTime + time.Second*300
 	}
 	shaman.FlametongueTotem = shaman.RegisterSpell(config)
 }
@@ -107,7 +107,7 @@ func (shaman *Shaman) registerFlametongueTotemSpell() {
 func (shaman *Shaman) registerStrengthOfEarthTotemSpell() {
 	config := shaman.newTotemSpellConfig(0.1, 58643)
 	config.ApplyEffects = func(sim *core.Simulation, _ *core.Unit, _ *core.Spell) {
-		shaman.NextTotemDrops[EarthTotem] = sim.CurrentTime + time.Second*300
+		shaman.TotemExpirations[EarthTotem] = sim.CurrentTime + time.Second*300
 	}
 	shaman.StrengthOfEarthTotem = shaman.RegisterSpell(config)
 }
@@ -115,7 +115,7 @@ func (shaman *Shaman) registerStrengthOfEarthTotemSpell() {
 func (shaman *Shaman) registerTremorTotemSpell() {
 	config := shaman.newTotemSpellConfig(0.02, 8143)
 	config.ApplyEffects = func(sim *core.Simulation, _ *core.Unit, _ *core.Spell) {
-		shaman.NextTotemDrops[EarthTotem] = sim.CurrentTime + time.Second*300
+		shaman.TotemExpirations[EarthTotem] = sim.CurrentTime + time.Second*300
 	}
 	shaman.TremorTotem = shaman.RegisterSpell(config)
 }
@@ -123,7 +123,7 @@ func (shaman *Shaman) registerTremorTotemSpell() {
 func (shaman *Shaman) registerStoneskinTotemSpell() {
 	config := shaman.newTotemSpellConfig(0.1, 58753)
 	config.ApplyEffects = func(sim *core.Simulation, _ *core.Unit, _ *core.Spell) {
-		shaman.NextTotemDrops[EarthTotem] = sim.CurrentTime + time.Second*300
+		shaman.TotemExpirations[EarthTotem] = sim.CurrentTime + time.Second*300
 	}
 	shaman.StoneskinTotem = shaman.RegisterSpell(config)
 }
diff --git a/sim/warlock/inferno.go b/sim/warlock/inferno.go
index 509bfc26ae..54858c9d59 100644
--- a/sim/warlock/inferno.go
+++ b/sim/warlock/inferno.go
@@ -59,12 +59,8 @@ func (warlock *Warlock) registerInfernoSpell() {
 
 	warlock.AddMajorCooldown(core.MajorCooldown{
 		Spell: warlock.Inferno,
-		Type:  core.CooldownTypeDPS,
+		Type:  core.CooldownTypeUnknown,
 		ShouldActivate: func(sim *core.Simulation, character *core.Character) bool {
-			if !warlock.Rotation.UseInfernal {
-				return false
-			}
-
 			return sim.GetRemainingDuration() <= 61*time.Second
 		},
 	})
diff --git a/sim/warlock/pet.go b/sim/warlock/pet.go
index 041c559854..16bcb49c3e 100644
--- a/sim/warlock/pet.go
+++ b/sim/warlock/pet.go
@@ -120,7 +120,6 @@ func (warlock *Warlock) NewWarlockPet() *WarlockPet {
 	}
 
 	wp.EnableManaBarWithModifier(cfg.PowerModifier)
-	wp.EnableResumeAfterManaWait(wp.OnGCDReady)
 
 	wp.AddStatDependency(stats.Strength, stats.AttackPower, 2)
 	wp.AddStat(stats.AttackPower, -20)
diff --git a/sim/warlock/rotation.go b/sim/warlock/rotation.go
deleted file mode 100644
index 601dab5b37..0000000000
--- a/sim/warlock/rotation.go
+++ /dev/null
@@ -1,690 +0,0 @@
-package warlock
-
-import (
-	"math"
-	"sort"
-	"time"
-
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-	"github.com/wowsims/wotlk/sim/core/stats"
-)
-
-const humanReactionTime = 150 * time.Millisecond
-
-func (warlock *Warlock) setupCooldowns(sim *core.Simulation) {
-	// TODO: also need to consider shared CDs that cause delays, like double on-use trinkets
-
-	// check if waiting `waitDuration` amount of time results in fewer cooldown usages
-	retainUses := func(timeLeft time.Duration, spellCD time.Duration, waitDuration time.Duration) bool {
-		return math.Floor(float64(timeLeft)/float64(spellCD)+1) ==
-			math.Floor(float64(timeLeft-waitDuration)/float64(spellCD)+1)
-	}
-
-	// TODO: find a way of getting the duration directly from the spell instead
-	durMap := make(map[core.ActionID]time.Duration)
-	if warlock.MetamorphosisAura != nil {
-		durMap[core.ActionID{SpellID: 47241}] = warlock.MetamorphosisAura.Duration
-	}
-	durMap[core.ActionID{SpellID: 33697}] = 15 * time.Second
-	durMap[core.ActionID{SpellID: 54758}] = 12 * time.Second
-	durMap[core.ActionID{SpellID: 10060}.WithTag(-1)] = 15 * time.Second
-	durMap[core.ActionID{ItemID: 40211}] = 15 * time.Second
-	durMap[core.ActionID{ItemID: 40212}] = 15 * time.Second
-	durMap[core.ActionID{ItemID: 45466}] = 20 * time.Second
-	durMap[core.ActionID{ItemID: 45148}] = 20 * time.Second
-	durMap[core.ActionID{ItemID: 37873}] = 20 * time.Second
-
-	ignoredCDs := make(map[core.ActionID]struct{})
-	ignoredCDs[core.ActionID{ItemID: 42641}] = struct{}{}       // sapper
-	ignoredCDs[core.ActionID{ItemID: 41119}] = struct{}{}       // saronite bomb
-	ignoredCDs[core.ActionID{ItemID: 40536}] = struct{}{}       // explosive decoy
-	ignoredCDs[core.BloodlustActionID.WithTag(-1)] = struct{}{} // don't mess with BL
-	if warlock.Inferno != nil {
-		ignoredCDs[warlock.Inferno.ActionID] = struct{}{}
-	}
-
-	var executeActive func() bool
-	var executePhase time.Duration
-	if warlock.Talents.Decimation > 0 {
-		// approximation since we don't know exactly when decimation will be active
-		executePhase = time.Duration(float64(sim.Duration)*(1.0-sim.Encounter.ExecuteProportion_35)) + 3*time.Second
-		executeActive = func() bool { return warlock.DecimationAura.IsActive() }
-	} else if warlock.Talents.Haunt {
-		executePhase = time.Duration(float64(sim.Duration) * (1.0 - sim.Encounter.ExecuteProportion_25))
-		executeActive = func() bool { return sim.IsExecutePhase25() }
-	} else {
-		executePhase = time.Duration(0)
-		executeActive = func() bool { return true }
-	}
-
-	lustCD := warlock.GetMajorCooldownIgnoreTag(core.BloodlustActionID)
-
-	for _, cd := range warlock.GetMajorCooldowns() {
-		if _, ignored := ignoredCDs[cd.Spell.ActionID]; ignored {
-			continue
-		}
-
-		spellCD := max(cd.Spell.CD.Duration, cd.Spell.SharedCD.Duration)
-		runTime := time.Duration(float64(durMap[cd.Spell.ActionID]) * 0.75)
-		spell := cd.Spell
-
-		cd.ShouldActivate = func(sim *core.Simulation, character *core.Character) bool {
-			timeLeft := sim.GetRemainingDuration() - runTime
-			timeUntilExecute := max(0, executePhase-sim.CurrentTime)
-			lustIsActive := lustCD != nil && character.HasActiveAura("Bloodlust-"+lustCD.Spell.ActionID.String())
-
-			// if time until execute is less than the CD AND remaining time minus time till execute gives
-			// the same amount of uses as remaining time alone then delay
-			if !executeActive() && timeUntilExecute < spellCD+runTime &&
-				retainUses(timeLeft, spellCD, timeUntilExecute) {
-				return false
-			}
-
-			if warlock.Talents.Metamorphosis && spell.ActionID != warlock.Metamorphosis.ActionID {
-				metaCD := warlock.GetMajorCooldown(warlock.Metamorphosis.ActionID)
-				if !warlock.MetamorphosisAura.IsActive() && metaCD != nil && metaCD.TimeToNextCast(sim) < spellCD+runTime &&
-					retainUses(timeLeft, spellCD, metaCD.TimeToNextCast(sim)) {
-					return false
-				}
-			}
-
-			if lustCD != nil && !lustIsActive &&
-				lustCD.TimeToNextCast(sim) < spellCD+runTime && retainUses(timeLeft, spellCD,
-				lustCD.TimeToNextCast(sim)) {
-				return false
-			}
-
-			if spell.ActionID.SameActionIgnoreTag(core.PowerInfusionActionID) &&
-				(lustIsActive || (lustCD != nil && lustCD.TimeToNextCast(sim) < runTime)) {
-				return false // don't use PI while lust is active or it would overlap
-			}
-
-			return true
-		}
-	}
-}
-
-func (warlock *Warlock) calcRelativeCorruptionInc(target *core.Unit) float64 {
-	dot := warlock.Corruption.Dot(target)
-	snapshotCrit := dot.SnapshotCritChance
-	snapshotDmg := dot.SnapshotAttackerMultiplier * (snapshotCrit*(warlock.Corruption.CritMultiplier-1) + 1)
-
-	attackTable := warlock.AttackTables[target.UnitIndex]
-	curCrit := warlock.Corruption.SpellCritChance(target)
-	curDmg := dot.Spell.AttackerDamageMultiplier(attackTable) * (curCrit*(warlock.Corruption.CritMultiplier-1) + 1)
-
-	return curDmg / snapshotDmg
-}
-
-func aclAppendSimple(acl []ActionCondition, spell *core.Spell, cond func(sim *core.Simulation) (
-	bool, *core.Unit)) []ActionCondition {
-	return append(acl, ActionCondition{
-		Spell: spell,
-		Condition: func(sim *core.Simulation) (ACLaction, *core.Unit) {
-			if cond, target := cond(sim); cond {
-				return ACLCast, target
-			} else {
-				return ACLNext, nil
-			}
-		},
-	})
-}
-
-func (warlock *Warlock) defineRotation() {
-	acl := warlock.acl
-	mainTarget := warlock.CurrentTarget // assumed to be the first element in the target list
-	var hauntTravel time.Duration
-	if warlock.Talents.Haunt {
-		hauntTravel = warlock.Haunt.TravelTime()
-	}
-	critDebuffCat := warlock.GetEnemyExclusiveCategories(core.SpellCritEffectCategory).Get(mainTarget)
-
-	logInfo := func(sim *core.Simulation, msg string, vals ...interface{}) {
-		if sim.Log != nil {
-			warlock.Log(sim, "[Info] "+msg, vals...)
-		}
-	}
-
-	allUnits := warlock.Env.Encounter.TargetUnits
-	if mainTarget != allUnits[0] {
-		panic("CurrentTarget assumption violated")
-	}
-
-	var multidotTargets, uaDotTargets []*core.Unit
-	multidotCount := min(len(allUnits), 3)
-	if warlock.Rotation.Type == proto.Warlock_Rotation_Affliction {
-		// up to 3 targets: multidot, no seed
-		// 4 targets: corruption+UA 3x, seed on 4th; possibly only 1x UA since it's close in value
-		// 5 targets: corruption x3, UA 1x, seed
-		// 6 targets: corruption x2, UA 1x, seed; only 1x corruption + UA is close in value
-		// 7-9 targets: corruption x1, no UA, seed
-		// 10+ targets: no corruption anymore probably
-		uaCount := min(len(allUnits), 3)
-
-		if len(allUnits) > 4 {
-			uaCount = 1
-		}
-		if len(allUnits) == 6 {
-			multidotCount = 2
-		} else if len(allUnits) > 6 {
-			uaCount = 0
-			multidotCount = core.TernaryInt(len(allUnits) > 9, 0, 1)
-		}
-
-		uaDotTargets = allUnits[:uaCount]
-	} else if warlock.Rotation.Type == proto.Warlock_Rotation_Destruction {
-		multidotCount = min(len(allUnits), 4)
-	}
-	multidotTargets = allUnits[:multidotCount]
-
-	if warlock.Talents.DemonicEmpowerment && warlock.Options.Summon != proto.Warlock_Options_NoSummon {
-		acl = aclAppendSimple(acl, warlock.DemonicEmpowerment, func(sim *core.Simulation) (bool, *core.Unit) {
-			return !warlock.Rotation.UseInfernal || warlock.Inferno.IsReady(sim), mainTarget
-		})
-	}
-
-	if warlock.Talents.Metamorphosis {
-		acl = aclAppendSimple(acl, warlock.ImmolationAura, func(sim *core.Simulation) (bool, *core.Unit) {
-			// TODO: potentially wait for procs
-			return true, nil
-		})
-	}
-
-	// only handles deliberate overrides of the primary spell
-	if warlock.Rotation.PrimarySpell == proto.Warlock_Rotation_Seed {
-		acl = aclAppendSimple(acl, warlock.Seed, func(sim *core.Simulation) (bool, *core.Unit) {
-			return warlock.Rotation.DetonateSeed || !warlock.Seed.Dot(mainTarget).IsActive(), mainTarget
-		})
-	}
-
-	if warlock.Talents.Conflagrate {
-		acl = aclAppendSimple(acl, warlock.Conflagrate, func(sim *core.Simulation) (bool, *core.Unit) {
-			return warlock.Immolate.Dot(mainTarget).IsActive(), mainTarget
-		})
-	}
-
-	if warlock.Talents.Haunt && warlock.Rotation.SpecSpell == proto.Warlock_Rotation_Haunt {
-		curIndex := len(acl)
-
-		acl = aclAppendSimple(acl, warlock.Haunt, func(sim *core.Simulation) (bool, *core.Unit) {
-			// no need for haunt until dots are up, mostly relevant in the opener
-			if !warlock.Corruption.Dot(mainTarget).IsActive() && !warlock.UnstableAffliction.Dot(mainTarget).IsActive() {
-				return false, nil
-			}
-
-			if !warlock.Haunt.CD.IsReady(sim) {
-				return false, nil
-			}
-
-			if sim.GetRemainingDuration() < 5*time.Second {
-				return false, nil
-			}
-
-			castTime := warlock.Haunt.CastTime()
-			_, nextActionTime := warlock.getAlternativeAction(sim, curIndex)
-			hauntRem := warlock.HauntDebuffAuras.Get(mainTarget).RemainingDuration(sim)
-
-			// 250ms of leeway in case haste buffs run out
-			return hauntRem-castTime-hauntTravel < nextActionTime+250*time.Millisecond, mainTarget
-		})
-
-		acl = aclAppendSimple(acl, warlock.LifeTap, func(sim *core.Simulation) (bool, *core.Unit) {
-			val := warlock.ShadowBolt.DefaultCast.Cost
-
-			if sim.IsExecutePhase25() {
-				dsDot := warlock.DrainSoul.CurDot()
-				if dsDot.IsActive() && dsDot.NumTicksRemaining(sim) >= 1 {
-					return false, nil // continuing to channel drain soul doesn't cost us any mana
-				}
-
-				val = warlock.UnstableAffliction.DefaultCast.Cost // highest mana cost spell outside SB
-			}
-			val += warlock.Haunt.DefaultCast.Cost
-
-			if warlock.CurrentMana() > val || sim.GetRemainingDuration() > 5*time.Second {
-				return false, nil
-			}
-
-			logInfo(sim, "Casting life tap to not drop haunt")
-			return true, nil
-		})
-	}
-
-	// refresh corruption with shadow bolt if it's running out
-	if warlock.Talents.EverlastingAffliction == 5 && len(allUnits) > 1 {
-		travel := warlock.ShadowBolt.TravelTime()
-		curIndex := len(acl)
-
-		acl = aclAppendSimple(acl, warlock.ShadowBolt, func(sim *core.Simulation) (bool, *core.Unit) {
-			type targetRem struct {
-				target *core.Unit
-				rem    time.Duration
-			}
-			targets := make([]targetRem, 0, len(sim.Encounter.TargetUnits))
-			for _, target := range sim.Encounter.TargetUnits {
-				// if there's already an shadowbolt on the way then skip
-				if warlock.corrRefreshList[target.UnitIndex] >= sim.CurrentTime-travel {
-					continue
-				}
-
-				// same when we can't refresh in time
-				if warlock.Corruption.Dot(target).RemainingDuration(sim) < travel+warlock.ShadowBolt.CastTime() {
-					continue
-				}
-
-				// assuming haunt doesn't drop, which it shouldn't, corruption will already be refreshed
-				if target == mainTarget && warlock.HauntDebuffAuras.Get(target).RemainingDuration(sim) <
-					warlock.Corruption.Dot(target).RemainingDuration(sim) {
-					continue
-				}
-
-				rem := min(warlock.Corruption.Dot(target).RemainingDuration(sim),
-					warlock.ShadowEmbraceAuras.Get(target).RemainingDuration(sim))
-				targets = append(targets, targetRem{rem: rem, target: target})
-			}
-			sort.Slice(targets, func(i, j int) bool { return targets[i].rem < targets[j].rem })
-
-			// we know that the only higher priority action is haunt, thus the only 2 things we need to
-			// consider outside of shadow bolts is haunt and mana
-			nextSpell, timeAdvance := warlock.getAlternativeAction(sim, curIndex)
-			sbCastTime := warlock.ShadowBolt.EffectiveCastTime()
-			timeAdvance += sbCastTime
-			recast := false
-			// shadow trance proc will only speed up one cast
-			if warlock.ShadowBolt.CastTimeMultiplier == 0 {
-				// somewhat hacky, breaks if CastTimeMultiplier is ever changed by anything else
-				sbCastTime = time.Duration(float64(warlock.ShadowBolt.DefaultCast.CastTime) * warlock.CastSpeed)
-				sbCastTime = max(sbCastTime, warlock.SpellGCD())
-
-				if nextSpell == warlock.ShadowBolt {
-					timeAdvance += sbCastTime - warlock.ShadowBolt.EffectiveCastTime()
-				}
-			}
-			mana := warlock.CurrentMana() - warlock.ShadowBolt.DefaultCast.Cost
-			consideredHaunt := false
-			for _, ele := range targets {
-				if mana < warlock.ShadowBolt.DefaultCast.Cost {
-					timeAdvance += warlock.LifeTap.EffectiveCastTime()
-					mana += 10000.0 // we only need 1 life tap, so the exact value doesn't matter
-				}
-				mana -= warlock.ShadowBolt.DefaultCast.Cost
-
-				if !consideredHaunt && timeAdvance+warlock.Haunt.CastTime()+hauntTravel >=
-					warlock.HauntDebuffAuras.Get(mainTarget).RemainingDuration(sim) {
-					timeAdvance += warlock.Haunt.EffectiveCastTime()
-					mana -= warlock.Haunt.DefaultCast.Cost
-					consideredHaunt = true
-				}
-
-				// some extra time to accommodate haste buffs running out
-				if timeAdvance+travel+250*time.Millisecond >= ele.rem {
-					recast = true
-					break
-				}
-
-				timeAdvance += sbCastTime + 50*time.Millisecond
-			}
-
-			if recast {
-				return true, targets[0].target
-			} else {
-				return false, nil
-			}
-		})
-	}
-
-	if warlock.Rotation.Corruption && warlock.Talents.EverlastingAffliction > 0 {
-		acl = aclAppendSimple(acl, warlock.Corruption, func(sim *core.Simulation) (bool, *core.Unit) {
-			// TODO: wait for all targets SB debuff?
-			if !critDebuffCat.AnyActive() &&
-				warlock.Talents.ImprovedShadowBolt > 0 && sim.CurrentTime < 25 {
-				return false, nil
-			}
-
-			for _, target := range multidotTargets {
-				if !warlock.Corruption.Dot(target).IsActive() {
-					return true, target
-				}
-
-				// check if reapplying corruption is worthwhile
-				relDmgInc := warlock.calcRelativeCorruptionInc(target)
-				snapshotDmg := warlock.Corruption.ExpectedTickDamageFromCurrentSnapshot(sim, target)
-				snapshotDmg *= float64(sim.GetRemainingDuration()) / float64(warlock.Corruption.Dot(target).TickPeriod())
-				snapshotDmg *= (relDmgInc - 1)
-				snapshotDmg -= warlock.Corruption.ExpectedTickDamageFromCurrentSnapshot(sim, target)
-
-				logInfo(sim, "Relative Corruption Inc: [%.2f], expected dmg gain: [%.2f]", relDmgInc, snapshotDmg)
-
-				if relDmgInc > 1.15 || snapshotDmg > 10000 {
-					return true, target
-				}
-			}
-
-			return false, nil
-		})
-	}
-
-	prefCurse := warlock.CurseOfAgony.CurDot().Aura
-	switch warlock.Rotation.Curse {
-	case proto.Warlock_Rotation_Elements:
-		prefCurse = warlock.CurseOfElementsAuras.Get(mainTarget)
-		acl = aclAppendSimple(acl, warlock.CurseOfElements, func(sim *core.Simulation) (bool, *core.Unit) {
-			return warlock.CurseOfElementsAuras.Get(mainTarget).RemainingDuration(sim) < 3*time.Second, mainTarget
-		})
-	case proto.Warlock_Rotation_Weakness:
-		prefCurse = warlock.CurseOfWeaknessAuras.Get(mainTarget)
-		acl = aclAppendSimple(acl, warlock.CurseOfWeakness, func(sim *core.Simulation) (bool, *core.Unit) {
-			return warlock.CurseOfWeaknessAuras.Get(mainTarget).RemainingDuration(sim) < 3*time.Second, mainTarget
-		})
-	case proto.Warlock_Rotation_Tongues:
-		prefCurse = warlock.CurseOfTonguesAuras.Get(mainTarget)
-		acl = aclAppendSimple(acl, warlock.CurseOfTongues, func(sim *core.Simulation) (bool, *core.Unit) {
-			return warlock.CurseOfTonguesAuras.Get(mainTarget).RemainingDuration(sim) < 3*time.Second, mainTarget
-		})
-	}
-
-	if warlock.HasMajorGlyph(proto.WarlockMajorGlyph_GlyphOfLifeTap) {
-		curIndex := len(acl)
-		acl = aclAppendSimple(acl, warlock.LifeTap, func(sim *core.Simulation) (bool, *core.Unit) {
-			// try to keep up the buff for the entire execute phase if possible
-			expiresAt := max(0, warlock.GlyphOfLifeTapAura.RemainingDuration(sim))
-			if sim.GetRemainingDuration() <= 40*time.Second &&
-				expiresAt+10*time.Second < sim.GetRemainingDuration() &&
-				warlock.CurrentManaPercent() < 0.35 {
-				logInfo(sim, "Casting life tap to keep up GoLT (40s till EOF)")
-				return true, nil
-			}
-
-			if _, dur := warlock.getAlternativeAction(sim, curIndex); dur > expiresAt &&
-				sim.GetRemainingDuration() > 12*time.Second {
-				logInfo(sim, "Casting life tap to keep up GoLT")
-				return true, nil
-			}
-
-			return false, nil
-		})
-	}
-
-	if warlock.Talents.UnstableAffliction && warlock.Rotation.SecondaryDot == proto.Warlock_Rotation_UnstableAffliction {
-		acl = aclAppendSimple(acl, warlock.UnstableAffliction, func(sim *core.Simulation) (bool, *core.Unit) {
-			castTime := warlock.UnstableAffliction.CastTime()
-			for _, target := range uaDotTargets {
-				if warlock.UnstableAffliction.Dot(target).RemainingDuration(sim)-castTime <= 0 &&
-					sim.GetRemainingDuration() >= 9*time.Second+castTime {
-					return true, target
-				}
-			}
-
-			return false, nil
-		})
-	}
-
-	if len(allUnits) > len(multidotTargets) {
-		acl = aclAppendSimple(acl, warlock.Seed, func(sim *core.Simulation) (bool, *core.Unit) {
-			for _, target := range sim.Encounter.TargetUnits {
-				// avoid mainTarget as we may want to corruption that later
-				if !warlock.Corruption.Dot(target).IsActive() && target != mainTarget {
-					return true, target
-				}
-			}
-			panic("No viable seed target found")
-		})
-	}
-
-	// TODO: automatically determine based on haunt/SE?
-	if warlock.Rotation.Curse == proto.Warlock_Rotation_Doom {
-		acl = aclAppendSimple(acl, warlock.CurseOfDoom, func(sim *core.Simulation) (bool, *core.Unit) {
-			return warlock.CurseOfDoom.Dot(mainTarget).RemainingDuration(sim) <= 0 &&
-				sim.GetRemainingDuration() >= 60*time.Second, mainTarget
-		})
-	}
-
-	if warlock.Rotation.Corruption && warlock.Talents.EverlastingAffliction <= 0 {
-		acl = aclAppendSimple(acl, warlock.Corruption, func(sim *core.Simulation) (bool, *core.Unit) {
-			for _, target := range multidotTargets {
-				dot := warlock.Corruption.Dot(target)
-				if dot.IsActive() {
-					continue
-				}
-
-				tickLen := dot.TickLength
-				if dot.AffectedByCastSpeed {
-					tickLen = warlock.ApplyCastSpeed(tickLen)
-				}
-
-				if sim.GetRemainingDuration() >= 4*tickLen {
-					return true, target
-				}
-			}
-			return false, nil
-		})
-	}
-
-	if warlock.Rotation.Curse == proto.Warlock_Rotation_Agony || warlock.Rotation.Curse == proto.Warlock_Rotation_Doom {
-		tickHeuristic := core.TernaryDuration(warlock.Talents.Haunt, 16*time.Second, 22*time.Second)
-
-		acl = aclAppendSimple(acl, warlock.CurseOfAgony, func(sim *core.Simulation) (bool, *core.Unit) {
-			for _, target := range multidotTargets {
-				if !warlock.CurseOfDoom.Dot(target).IsActive() && !warlock.CurseOfAgony.
-					Dot(target).IsActive() && sim.GetRemainingDuration() >= tickHeuristic {
-					return true, target
-				}
-			}
-
-			return false, nil
-		})
-	}
-
-	if !warlock.Talents.UnstableAffliction && warlock.Rotation.SecondaryDot == proto.Warlock_Rotation_Immolate {
-		tickHeuristic := core.TernaryDuration(warlock.Talents.Conflagrate, 6*time.Second, 12*time.Second)
-
-		acl = aclAppendSimple(acl, warlock.Immolate, func(sim *core.Simulation) (bool, *core.Unit) {
-			castTime := warlock.Immolate.CastTime()
-			for _, target := range multidotTargets {
-				if warlock.Immolate.Dot(target).RemainingDuration(sim)-castTime <= 0 &&
-					sim.GetRemainingDuration() >= tickHeuristic+castTime {
-					return true, target
-				}
-			}
-			return false, nil
-		})
-	}
-
-	if warlock.Talents.ChaosBolt {
-		acl = aclAppendSimple(acl, warlock.ChaosBolt, func(sim *core.Simulation) (bool, *core.Unit) {
-			return true, mainTarget
-		})
-	}
-
-	if warlock.Talents.Haunt {
-		function := func(sim *core.Simulation) (ACLaction, *core.Unit) {
-			dsDot := warlock.DrainSoul.CurDot()
-			if !sim.IsExecutePhase25() {
-				return ACLNext, nil
-			}
-
-			if !dsDot.IsActive() || dsDot.TimeUntilNextTick(sim) < dsDot.TickPeriod()-humanReactionTime {
-				return ACLCast, mainTarget
-			}
-
-			if warlock.Corruption.CurDot().RemainingDuration(sim) < dsDot.TickPeriod() {
-				logInfo(sim, "Recasting drain soul to not let corruption drop")
-				return ACLRecast, mainTarget
-			}
-
-			// check if recasting drain soul is worthwhile
-
-			// check when UA, CoA and haunt have to be refreshed, respectively
-			uaRefresh := warlock.UnstableAffliction.Dot(mainTarget).RemainingDuration(sim) -
-				warlock.UnstableAffliction.CastTime()
-
-			curseRefresh := max(prefCurse.RemainingDuration(sim),
-				warlock.CurseOfDoom.CurDot().RemainingDuration(sim)) - warlock.CurseOfAgony.CastTime()
-
-			hauntRefresh := warlock.HauntDebuffAuras.Get(mainTarget).RemainingDuration(sim) -
-				warlock.Haunt.CastTime() - hauntTravel
-
-			timeUntilRefresh := min(uaRefresh, curseRefresh)
-
-			// the amount of ticks we have left, assuming we continue channeling
-			ticksLeft := int(timeUntilRefresh/dsDot.TickPeriod()) + 1
-			ticksLeft = min(ticksLeft, int(hauntRefresh/dsDot.TickPeriod()))
-			ticksLeft = min(ticksLeft, dsDot.NumTicksRemaining(sim))
-
-			// amount of ticks we'd get assuming we recast drain soul
-			recastTicks := int(timeUntilRefresh/warlock.ApplyCastSpeed(dsDot.TickLength)) + 1
-			recastTicks = min(recastTicks, int(hauntRefresh/warlock.ApplyCastSpeed(dsDot.TickLength)))
-			recastTicks = min(recastTicks, int(dsDot.NumberOfTicks))
-
-			if ticksLeft <= 0 || recastTicks <= 0 {
-				return ACLCast, mainTarget
-			}
-
-			snapshotDmg := warlock.DrainSoul.ExpectedTickDamageFromCurrentSnapshot(sim, mainTarget) * float64(ticksLeft)
-			recastDmg := warlock.DrainSoul.ExpectedTickDamage(sim, mainTarget) * float64(recastTicks)
-			snapshotDPS := snapshotDmg / (float64(ticksLeft) * dsDot.TickPeriod().Seconds())
-			recastDps := recastDmg / (float64(recastTicks)*warlock.ApplyCastSpeed(dsDot.TickLength).Seconds() +
-				humanReactionTime.Seconds())
-
-			if recastDps > snapshotDPS {
-				logInfo(sim, "Recasting drain soul, %.2f (%d) > %.2f (%d)",
-					recastDps, recastTicks, snapshotDPS, ticksLeft)
-				return ACLRecast, mainTarget
-			}
-
-			// TODO: if number of ticks left < number of ticks until we need to recast dots/haunt
-			// and some proc effect falls off before the next tick, check if recasting is a DPS gain
-
-			return ACLCast, mainTarget
-		}
-
-		acl = append(acl, ActionCondition{
-			Spell:     warlock.DrainSoul,
-			Condition: function,
-		})
-	}
-
-	if warlock.Talents.Decimation > 0 {
-		acl = aclAppendSimple(acl, warlock.SoulFire, func(sim *core.Simulation) (bool, *core.Unit) {
-			return warlock.DecimationAura.IsActive(), mainTarget
-		})
-	}
-
-	if warlock.Talents.MoltenCore > 0 {
-		acl = aclAppendSimple(acl, warlock.Incinerate, func(sim *core.Simulation) (bool, *core.Unit) {
-			return warlock.MoltenCoreAura.IsActive(), mainTarget
-		})
-	}
-
-	if warlock.Rotation.PrimarySpell == proto.Warlock_Rotation_Incinerate {
-		acl = aclAppendSimple(acl, warlock.Incinerate, func(sim *core.Simulation) (bool, *core.Unit) {
-			return true, mainTarget
-		})
-	}
-
-	acl = aclAppendSimple(acl, warlock.ShadowBolt, func(sim *core.Simulation) (bool, *core.Unit) {
-		return true, mainTarget
-	})
-
-	if warlock.Talents.DarkPact {
-		acl = aclAppendSimple(acl, warlock.DarkPact, func(sim *core.Simulation) (bool, *core.Unit) {
-			// if pet has enough mana, prefer dark pact over life tap
-			return warlock.Pet.CurrentMana() > warlock.GetStat(stats.SpellPower)+1200+131, nil
-		})
-	}
-
-	acl = aclAppendSimple(acl, warlock.LifeTap, func(sim *core.Simulation) (bool, *core.Unit) {
-		return true, nil
-	})
-
-	warlock.acl = acl
-}
-
-func aclNextAction(sim *core.Simulation, acl []ActionCondition, skipIndex int) (*core.Spell, bool) {
-	logfunc := sim.Log
-	sim.Log = nil // disable logging here since it's not useful
-	for _, ac := range acl[skipIndex+1:] {
-		if action, _ := ac.Condition(sim); action != ACLNext && ac.Spell.IsReady(sim) {
-			sim.Log = logfunc
-			return ac.Spell, action == ACLRecast
-		}
-	}
-
-	panic("ACL list exhausted but no match found")
-}
-
-// Returns the spell and casttime of the alternative action we'd take, if we skip skipIndex
-func (warlock *Warlock) getAlternativeAction(sim *core.Simulation, skipIndex int) (*core.Spell, time.Duration) {
-	var nextSpellTime time.Duration
-	nextSpell, recast := aclNextAction(sim, warlock.acl, skipIndex)
-
-	if nextSpell == warlock.DrainSoul {
-		if recast || !nextSpell.CurDot().IsActive() {
-			nextSpellTime = warlock.ApplyCastSpeed(nextSpell.CurDot().TickLength) + humanReactionTime
-		} else {
-			nextSpellTime = nextSpell.CurDot().TickPeriod() + humanReactionTime
-		}
-	} else {
-		nextSpellTime = nextSpell.EffectiveCastTime()
-	}
-
-	return nextSpell, max(core.GCDMin, nextSpellTime)
-}
-
-func (warlock *Warlock) OnGCDReady(sim *core.Simulation) {
-	if warlock.IsUsingAPL {
-		return
-	}
-
-	for _, ac := range warlock.acl {
-		action, target := ac.Condition(sim)
-		if action == ACLNext || !ac.Spell.IsReady(sim) {
-			continue
-		}
-
-		// TODO: find a more general way of dealing with channeling spells, but for now this is fine since drain
-		// soul is the only one being used anyway
-		if action == ACLRecast {
-			if ac.Spell != warlock.DrainSoul {
-				panic("Trying to recast unknown spell")
-			}
-			warlock.DrainSoul.Dot(target).Cancel(sim)
-		}
-
-		if warlock.DrainSoul.CurDot().IsActive() {
-			if ac.Spell != warlock.DrainSoul && warlock.DrainSoul.CurDot().TickCount != 0 {
-				warlock.DrainSoul.CurDot().Cancel(sim)
-			} else {
-				warlock.WaitUntil(sim, sim.CurrentTime+warlock.DrainSoul.CurDot().TimeUntilNextTick(sim)+humanReactionTime)
-				return
-			}
-		}
-
-		castTime := ac.Spell.CastTime()
-		if success := ac.Spell.Cast(sim, target); success {
-			// track shadowbolts "in the air" that haven't refreshed corruption yet
-			if ac.Spell == warlock.ShadowBolt || ac.Spell == warlock.Haunt {
-				warlock.corrRefreshList[target.UnitIndex] = sim.CurrentTime + castTime
-			}
-
-			if !warlock.GCD.IsReady(sim) {
-				// after-GCD actions
-				if ac.Spell == warlock.Corruption && warlock.ItemSwap.IsEnabled() && warlock.ItemSwap.IsSwapped() {
-					warlock.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand,
-						proto.ItemSlot_ItemSlotOffHand, proto.ItemSlot_ItemSlotRanged})
-				}
-
-				return
-			}
-		} else if warlock.CurrentMana() < ac.Spell.DefaultCast.Cost {
-			// TODO: this will only cast life tap right now
-			if success := warlock.acl[len(warlock.acl)-1].Spell.Cast(sim, nil); !success {
-				panic("Failed to cast life tap / dark pact")
-			}
-			return
-		}
-	}
-
-	panic("ACL list exhausted but no match found")
-}
diff --git a/sim/warlock/seed.go b/sim/warlock/seed.go
index 5cd7289d8a..d404d16861 100644
--- a/sim/warlock/seed.go
+++ b/sim/warlock/seed.go
@@ -108,11 +108,7 @@ func (warlock *Warlock) registerSeedSpell() {
 					// seed is mutually exclusive with corruption
 					warlock.Corruption.Dot(target).Deactivate(sim)
 
-					if warlock.Rotation.DetonateSeed {
-						seedExplosion.Cast(sim, target)
-					} else {
-						spell.Dot(target).Apply(sim)
-					}
+					spell.Dot(target).Apply(sim)
 				}
 			})
 		},
diff --git a/sim/warlock/warlock.go b/sim/warlock/warlock.go
index f25546e27d..0f281e6cbb 100644
--- a/sim/warlock/warlock.go
+++ b/sim/warlock/warlock.go
@@ -14,9 +14,8 @@ var TalentTreeSizes = [3]int{28, 27, 26}
 
 type Warlock struct {
 	core.Character
-	Talents  *proto.WarlockTalents
-	Options  *proto.Warlock_Options
-	Rotation *proto.Warlock_Rotation
+	Talents *proto.WarlockTalents
+	Options *proto.Warlock_Options
 
 	Pet *WarlockPet
 
@@ -73,23 +72,6 @@ type Warlock struct {
 	PreviousTime  time.Duration
 
 	petStmBonusSP float64
-	acl           []ActionCondition
-
-	// contains for each target the time the last shadowbolt was casted onto them
-	corrRefreshList []time.Duration
-}
-
-type ACLaction int
-
-const (
-	ACLCast ACLaction = iota
-	ACLNext
-	ACLRecast
-)
-
-type ActionCondition struct {
-	Spell     *core.Spell
-	Condition func(*core.Simulation) (ACLaction, *core.Unit)
 }
 
 func (warlock *Warlock) GetCharacter() *core.Character {
@@ -133,12 +115,6 @@ func (warlock *Warlock) Initialize() {
 	warlock.registerInfernoSpell()
 	warlock.registerBlackBook()
 
-	warlock.defineRotation()
-
-	precastSpell := warlock.ShadowBolt
-	if warlock.Rotation.Type == proto.Warlock_Rotation_Destruction {
-		precastSpell = warlock.SoulFire
-	}
 	// Do this post-finalize so cast speed is updated with new stats
 	warlock.Env.RegisterPostFinalizeEffect(func() {
 		// if itemswap is enabled, correct for any possible haste changes
@@ -164,25 +140,6 @@ func (warlock *Warlock) Initialize() {
 				}
 			})
 		}
-
-		if warlock.IsUsingAPL {
-			return
-		}
-
-		precastSpellAt := -warlock.ApplyCastSpeedForSpell(precastSpell.DefaultCast.CastTime, precastSpell)
-
-		warlock.RegisterPrepullAction(precastSpellAt, func(sim *core.Simulation) {
-			precastSpell.Cast(sim, warlock.CurrentTarget)
-		})
-		if warlock.GlyphOfLifeTapAura != nil || warlock.SpiritsoftheDamnedAura != nil {
-			warlock.RegisterPrepullAction(precastSpellAt-warlock.SpellGCD(), func(sim *core.Simulation) {
-				warlock.LifeTap.Cast(sim, nil)
-			})
-		}
-		if warlock.ItemSwap.IsEnabled() {
-			warlock.AddStats(correction.Invert())
-			warlock.MultiplyCastSpeed(1.0)
-		}
 	})
 }
 
@@ -202,13 +159,6 @@ func (warlock *Warlock) Reset(sim *core.Simulation) {
 	if sim.CurrentTime == 0 {
 		warlock.petStmBonusSP = 0
 	}
-
-	if !warlock.IsUsingAPL {
-		warlock.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand,
-			proto.ItemSlot_ItemSlotOffHand, proto.ItemSlot_ItemSlotRanged})
-	}
-	warlock.corrRefreshList = make([]time.Duration, len(warlock.Env.Encounter.TargetUnits))
-	warlock.setupCooldowns(sim)
 }
 
 func NewWarlock(character *core.Character, options *proto.Player) *Warlock {
@@ -218,7 +168,6 @@ func NewWarlock(character *core.Character, options *proto.Player) *Warlock {
 		Character: *character,
 		Talents:   &proto.WarlockTalents{},
 		Options:   warlockOptions.Options,
-		Rotation:  warlockOptions.Rotation,
 	}
 	core.FillTalentsProto(warlock.Talents.ProtoReflect(), options.TalentsString, TalentTreeSizes)
 	warlock.EnableManaBar()

From fe1d147a48b645c928381941d00cccb3fe95e793 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Sat, 6 Jan 2024 14:27:30 -0800
Subject: [PATCH 13/28] Merge from master

---
 sim/core/spell_snapshot.go          |  90 ------
 sim/rogue/TestCombat.results        | 192 ++++++-------
 sim/warlock/TestDemonology.results  | 424 ++++++++++++++--------------
 sim/warlock/TestDestruction.results | 212 +++++++-------
 sim/warlock/inferno.go              |   3 -
 sim/warlock/warlock_test.go         |  46 +--
 sim/warrior/heroic_strike_cleave.go |   3 -
 sim/warrior/talents.go              |   3 +-
 8 files changed, 418 insertions(+), 555 deletions(-)
 delete mode 100644 sim/core/spell_snapshot.go

diff --git a/sim/core/spell_snapshot.go b/sim/core/spell_snapshot.go
deleted file mode 100644
index c4f445203c..0000000000
--- a/sim/core/spell_snapshot.go
+++ /dev/null
@@ -1,90 +0,0 @@
-package core
-
-import (
-	"time"
-)
-
-type procTracker struct {
-	aura        *Aura
-	didActivate bool
-	isActive    bool
-	expiresAt   time.Duration
-}
-
-type SnapshotManager struct {
-	procTrackers   []*procTracker
-	majorCooldowns []*MajorCooldown
-	character      *Character
-}
-
-func NewSnapshotManager(character *Character) *SnapshotManager {
-	return &SnapshotManager{
-		procTrackers:   make([]*procTracker, 0),
-		majorCooldowns: make([]*MajorCooldown, 0),
-		character:      character,
-	}
-}
-
-func (manager *SnapshotManager) AddProc(id int32, label string, isActive bool) bool {
-	character := manager.character
-
-	if !character.HasAura(label) {
-		return false
-	}
-
-	manager.procTrackers = append(manager.procTrackers, &procTracker{
-		didActivate: false,
-		isActive:    isActive,
-		expiresAt:   -1,
-		aura:        character.GetAura(label),
-	})
-	return true
-}
-
-func (manager *SnapshotManager) CanSnapShot(sim *Simulation, castTime time.Duration) bool {
-	success := true
-
-	for _, procTracker := range manager.procTrackers {
-		if !procTracker.didActivate && procTracker.aura.IsActive() {
-			procTracker.didActivate = true
-			procTracker.expiresAt = procTracker.aura.ExpiresAt()
-		}
-
-		// A proc is about to drop
-		if procTracker.didActivate && procTracker.expiresAt <= sim.CurrentTime+castTime {
-			if sim.Log != nil {
-				sim.Log("Proc dropping " + procTracker.aura.Label)
-			}
-			return true
-		}
-
-		if !procTracker.didActivate && !procTracker.isActive {
-			success = false
-		}
-	}
-
-	return success
-}
-
-func (manager *SnapshotManager) ActivateMajorCooldowns(sim *Simulation) {
-	for _, majorCd := range manager.majorCooldowns {
-		if majorCd.IsReady(sim) {
-			majorCd.TryActivate(sim, manager.character)
-		}
-	}
-}
-
-func (manager *SnapshotManager) ResetProcTrackers() {
-	for _, procTracker := range manager.procTrackers {
-		procTracker.didActivate = false
-		procTracker.expiresAt = -1
-	}
-}
-
-func (manager *SnapshotManager) ClearMajorCooldowns() {
-	manager.majorCooldowns = make([]*MajorCooldown, 0)
-}
-
-func (manager *SnapshotManager) AddMajorCooldown(majorCd *MajorCooldown) {
-	manager.majorCooldowns = append(manager.majorCooldowns, majorCd)
-}
diff --git a/sim/rogue/TestCombat.results b/sim/rogue/TestCombat.results
index 78ab4bff78..094e9c81fc 100644
--- a/sim/rogue/TestCombat.results
+++ b/sim/rogue/TestCombat.results
@@ -981,43 +981,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 21132.20952
-  tps: 15003.86876
+  dps: 21435.04268
+  tps: 15218.8803
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 3516.89637
-  tps: 2496.99642
+  dps: 3539.13966
+  tps: 2512.78916
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 4109.75712
-  tps: 2917.92755
+  dps: 4297.57812
+  tps: 3051.28047
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 13003.58802
-  tps: 9232.54749
+  dps: 13206.06312
+  tps: 9376.30481
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 1778.52693
-  tps: 1262.75412
+  dps: 1785.96568
+  tps: 1268.03563
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 1863.43428
-  tps: 1323.03834
+  dps: 1898.48085
+  tps: 1347.9214
  }
 }
 dps_results: {
@@ -1149,43 +1149,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 22695.82072
-  tps: 16114.03271
+  dps: 23026.48078
+  tps: 16348.80135
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 4601.62498
-  tps: 3267.15373
+  dps: 4633.5605
+  tps: 3289.82796
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 5306.8537
-  tps: 3767.86612
+  dps: 5540.589
+  tps: 3933.81819
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 13260.84412
-  tps: 9415.19933
+  dps: 13454.08262
+  tps: 9552.39866
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2301.87343
-  tps: 1634.33013
+  dps: 2312.07404
+  tps: 1641.57257
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2337.44435
-  tps: 1659.58549
+  dps: 2393.64181
+  tps: 1699.48569
  }
 }
 dps_results: {
@@ -1317,43 +1317,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 25256.98021
-  tps: 17932.45595
+  dps: 25576.22706
+  tps: 18159.12122
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5018.34623
-  tps: 3563.02583
+  dps: 5051.3607
+  tps: 3586.46609
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 5809.23206
-  tps: 4124.55476
+  dps: 6092.44116
+  tps: 4325.63322
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 14807.07114
-  tps: 10513.02051
+  dps: 15002.54589
+  tps: 10651.80759
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2519.14739
-  tps: 1788.59464
+  dps: 2529.97284
+  tps: 1796.28072
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2588.00612
-  tps: 1837.48434
+  dps: 2644.86445
+  tps: 1877.85376
  }
 }
 dps_results: {
@@ -1485,43 +1485,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 18824.30249
-  tps: 13365.25477
+  dps: 19109.11172
+  tps: 13567.46932
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 4189.47287
-  tps: 2974.52573
+  dps: 4217.58491
+  tps: 2994.48528
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 5004.60982
-  tps: 3553.27297
+  dps: 5274.9979
+  tps: 3745.24851
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 10831.81586
-  tps: 7690.58926
+  dps: 11025.5797
+  tps: 7828.16159
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 1983.7099
-  tps: 1408.43403
+  dps: 1993.74101
+  tps: 1415.55612
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2101.37058
-  tps: 1491.97311
+  dps: 2156.33219
+  tps: 1530.99585
  }
 }
 dps_results: {
@@ -1653,43 +1653,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 21290.63515
-  tps: 15116.35096
+  dps: 21615.92487
+  tps: 15347.30666
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 3540.60866
-  tps: 2513.83215
+  dps: 3565.29603
+  tps: 2531.36018
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 4172.47671
-  tps: 2962.45847
+  dps: 4362.48163
+  tps: 3097.36196
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 13113.07646
-  tps: 9310.28429
+  dps: 13327.72899
+  tps: 9462.68758
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 1793.27032
-  tps: 1273.22193
+  dps: 1801.49013
+  tps: 1279.05799
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 1900.39941
-  tps: 1349.28358
+  dps: 1933.6019
+  tps: 1372.85735
  }
 }
 dps_results: {
@@ -1821,43 +1821,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 22855.23185
-  tps: 16227.21462
+  dps: 23204.3601
+  tps: 16475.09567
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 4631.68558
-  tps: 3288.49676
+  dps: 4667.4418
+  tps: 3313.88368
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 5384.15189
-  tps: 3822.74784
+  dps: 5622.83334
+  tps: 3992.21167
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 13404.07154
-  tps: 9516.8908
+  dps: 13586.68098
+  tps: 9646.54349
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2322.07483
-  tps: 1648.67313
+  dps: 2332.01436
+  tps: 1655.7302
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2383.73941
-  tps: 1692.45498
+  dps: 2435.28497
+  tps: 1729.05233
  }
 }
 dps_results: {
@@ -1989,43 +1989,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 25439.18557
-  tps: 18061.82176
+  dps: 25775.50672
+  tps: 18300.60977
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5052.03546
-  tps: 3586.94518
+  dps: 5087.70125
+  tps: 3612.26789
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 5894.33138
-  tps: 4184.97528
+  dps: 6179.0402
+  tps: 4387.11854
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 14939.32565
-  tps: 10606.92121
+  dps: 15142.98192
+  tps: 10751.51716
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2539.17634
-  tps: 1802.8152
+  dps: 2550.98882
+  tps: 1811.20206
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2635.71107
-  tps: 1871.35486
+  dps: 2689.5647
+  tps: 1909.59093
  }
 }
 dps_results: {
@@ -2157,43 +2157,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 18971.67938
-  tps: 13469.89236
+  dps: 19281.73013
+  tps: 13690.02839
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 4220.37505
-  tps: 2996.46629
+  dps: 4255.39217
+  tps: 3021.32844
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 5084.05186
-  tps: 3609.67682
+  dps: 5352.90194
+  tps: 3800.56038
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 10943.42878
-  tps: 7769.83444
+  dps: 11144.86205
+  tps: 7912.85206
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 1997.50981
-  tps: 1418.23196
+  dps: 2007.95517
+  tps: 1425.64817
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2147.90528
-  tps: 1525.01275
+  dps: 2196.75486
+  tps: 1559.69595
  }
 }
 dps_results: {
diff --git a/sim/warlock/TestDemonology.results b/sim/warlock/TestDemonology.results
index f4df027900..2ae42ae9aa 100644
--- a/sim/warlock/TestDemonology.results
+++ b/sim/warlock/TestDemonology.results
@@ -46,738 +46,738 @@ character_stats_results: {
 dps_results: {
  key: "TestDemonology-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 13417.31696
-  tps: 11658.64291
+  dps: 13243.90588
+  tps: 11492.86523
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 13436.63519
-  tps: 11673.70642
+  dps: 13278.4155
+  tps: 11523.00644
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-AshtongueTalismanofShadows-32493"
  value: {
-  dps: 13245.14315
-  tps: 11501.91282
+  dps: 13074.67441
+  tps: 11336.3098
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 13620.57761
-  tps: 11843.39499
+  dps: 13418.79874
+  tps: 11655.82175
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 13077.13033
-  tps: 11353.0749
-  hps: 102.49336
+  dps: 12896.06082
+  tps: 11177.10371
+  hps: 100.95364
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 13077.13033
-  tps: 11353.0749
-  hps: 102.49336
+  dps: 12896.06082
+  tps: 11177.10371
+  hps: 100.95364
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 13642.25995
-  tps: 11864.89637
+  dps: 13401.77362
+  tps: 11638.54725
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 9921.59431
-  tps: 8459.24974
+  dps: 9817.47634
+  tps: 8342.96725
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 13655.78385
-  tps: 11638.99091
+  dps: 13449.10871
+  tps: 11449.20433
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 13987.55061
-  tps: 12211.3241
+  dps: 13766.09428
+  tps: 12003.46809
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 13143.97526
-  tps: 11413.75089
+  dps: 12950.07749
+  tps: 11226.26804
   hps: 64
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DarkCoven'sRegalia"
  value: {
-  dps: 12755.71414
-  tps: 11061.01924
+  dps: 12601.16483
+  tps: 10909.94106
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 13240.73419
-  tps: 11516.47627
+  dps: 13069.14213
+  tps: 11349.19229
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 13264.93211
-  tps: 11540.79981
+  dps: 13108.2148
+  tps: 11388.41437
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 13181.03399
-  tps: 11450.98376
+  dps: 12992.95845
+  tps: 11268.3201
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Death'sChoice-47464"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 13133.75022
-  tps: 11409.4923
+  dps: 12949.96635
+  tps: 11230.01651
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DeathbringerGarb"
  value: {
-  dps: 10851.44914
-  tps: 9282.50751
+  dps: 10738.51563
+  tps: 9156.02028
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Defender'sCode-40257"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 13652.50644
-  tps: 11876.27993
+  dps: 13439.12732
+  tps: 11676.50114
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 13874.94151
-  tps: 12104.63929
+  dps: 13680.51811
+  tps: 11918.02187
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 13620.57761
-  tps: 11843.39499
+  dps: 13418.79874
+  tps: 11655.82175
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 13677.75974
-  tps: 11895.67881
+  dps: 13453.51023
+  tps: 11686.79689
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 13646.0225
-  tps: 11869.79598
+  dps: 13432.54537
+  tps: 11669.91919
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 13637.52959
-  tps: 11861.30307
+  dps: 13424.03982
+  tps: 11661.41364
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 13260.66392
-  tps: 11537.05537
+  dps: 13053.22335
+  tps: 11338.404
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 13105.61399
-  tps: 11378.55729
+  dps: 12906.6269
+  tps: 11185.18743
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 13609.78634
-  tps: 11833.55983
+  dps: 13398.47567
+  tps: 11635.84948
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 13206.33109
-  tps: 11482.04155
+  dps: 13122.05364
+  tps: 11402.52929
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 13456.75793
-  tps: 11707.82466
+  dps: 13283.42777
+  tps: 11540.1986
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 13099.69744
-  tps: 11374.06795
+  dps: 12918.53373
+  tps: 11201.64113
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 13272.02443
-  tps: 11527.43425
+  dps: 13107.02913
+  tps: 11366.94101
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ForgeEmber-37660"
  value: {
-  dps: 13414.74685
-  tps: 11668.49755
+  dps: 13248.07367
+  tps: 11508.02234
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 13655.78385
-  tps: 11874.35204
+  dps: 13449.10871
+  tps: 11680.5325
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 13654.90049
-  tps: 11874.46637
+  dps: 13453.88096
+  tps: 11686.13342
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-FuturesightRune-38763"
  value: {
-  dps: 13244.55905
-  tps: 11503.83787
+  dps: 13095.96384
+  tps: 11358.30044
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Gladiator'sFelshroud"
  value: {
-  dps: 10687.3293
-  tps: 9107.41386
+  dps: 10560.32217
+  tps: 8973.36025
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 13435.16729
-  tps: 11674.16246
+  dps: 13266.94683
+  tps: 11513.37249
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 13473.9598
-  tps: 11707.62348
+  dps: 13309.51349
+  tps: 11550.47562
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 13231.96529
-  tps: 11507.84404
+  dps: 13062.08411
+  tps: 11343.78646
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Gul'dan'sRegalia"
  value: {
-  dps: 11188.33387
-  tps: 9443.73545
+  dps: 11045.2536
+  tps: 9314.45004
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
-  dps: 13435.95467
-  tps: 11673.61487
+  dps: 13271.82086
+  tps: 11517.03807
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 13646.0225
-  tps: 11869.79598
+  dps: 13432.54537
+  tps: 11669.91919
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 13637.52959
-  tps: 11861.30307
+  dps: 13424.03982
+  tps: 11661.41364
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-IncisorFragment-37723"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 13657.68356
-  tps: 11882.59037
+  dps: 13427.78541
+  tps: 11661.40926
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 13609.78634
-  tps: 11833.55983
+  dps: 13398.47567
+  tps: 11635.84948
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 13275.52579
-  tps: 11529.87007
+  dps: 13093.97928
+  tps: 11353.32214
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-MaleficRaiment"
  value: {
-  dps: 8523.72687
-  tps: 7157.22159
+  dps: 8418.28288
+  tps: 7041.57676
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 13196.82754
-  tps: 11472.56962
+  dps: 13020.35249
+  tps: 11300.40265
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 13342.65729
-  tps: 11598.0671
+  dps: 13171.60746
+  tps: 11431.51935
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 13609.78634
-  tps: 11833.55983
+  dps: 13398.47567
+  tps: 11635.84948
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 13609.78634
-  tps: 11833.55983
+  dps: 13398.47567
+  tps: 11635.84948
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PlagueheartGarb"
  value: {
-  dps: 10360.85416
-  tps: 8820.18392
+  dps: 10266.66359
+  tps: 8716.63581
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 13618.779
-  tps: 11841.74809
+  dps: 13417.36058
+  tps: 11654.52072
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 13620.57761
-  tps: 11843.39499
+  dps: 13418.79874
+  tps: 11655.82175
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 13615.25479
-  tps: 11861.71483
+  dps: 13407.68508
+  tps: 11661.54729
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 13668.67641
-  tps: 11912.65287
+  dps: 13469.23514
+  tps: 11720.19531
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 13948.0532
-  tps: 12171.82669
+  dps: 13728.9583
+  tps: 11966.33211
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 13605.83603
-  tps: 11830.72831
+  dps: 13405.02361
+  tps: 11639.40886
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 13143.97526
-  tps: 11413.75089
+  dps: 12950.06
+  tps: 11226.25054
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 13393.37816
-  tps: 11638.48125
+  dps: 13220.81763
+  tps: 11472.0353
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 13433.76001
-  tps: 11672.68692
+  dps: 13239.16958
+  tps: 11485.33592
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SoulPreserver-37111"
  value: {
-  dps: 13192.98633
-  tps: 11456.81998
+  dps: 13041.95765
+  tps: 11308.77052
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SouloftheDead-40382"
  value: {
-  dps: 13205.81551
-  tps: 11482.4252
+  dps: 13069.23424
+  tps: 11351.11117
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SparkofLife-37657"
  value: {
-  dps: 13219.18241
-  tps: 11490.95906
+  dps: 13064.67402
+  tps: 11346.30818
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 13094.19975
-  tps: 11355.22268
+  dps: 12920.45511
+  tps: 11186.49878
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 13609.78634
-  tps: 11833.55983
+  dps: 13398.47567
+  tps: 11635.84948
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 13609.78634
-  tps: 11833.55983
+  dps: 13398.47567
+  tps: 11635.84948
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 13609.78634
-  tps: 11833.55983
+  dps: 13398.47567
+  tps: 11635.84948
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 13162.97939
-  tps: 11430.78677
+  dps: 12984.04915
+  tps: 11256.55694
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 13124.68662
-  tps: 11395.09828
+  dps: 12936.43462
+  tps: 11217.1032
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 13080.90366
-  tps: 11356.64575
+  dps: 12900.43003
+  tps: 11180.48019
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 13609.78634
-  tps: 11833.55983
+  dps: 13398.47567
+  tps: 11635.84948
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 13088.36912
-  tps: 11343.35538
+  dps: 12931.5795
+  tps: 11189.2114
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 13088.36912
-  tps: 11343.35538
+  dps: 12931.5795
+  tps: 11189.2114
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 13655.78385
-  tps: 11874.35204
+  dps: 13449.10871
+  tps: 11680.5325
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 13654.90049
-  tps: 11874.46637
+  dps: 13453.88096
+  tps: 11686.13342
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 13318.20984
-  tps: 11585.33436
+  dps: 13135.3056
+  tps: 11407.64705
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 13654.90049
-  tps: 11874.46637
+  dps: 13453.88096
+  tps: 11686.13342
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 13655.78385
-  tps: 11874.35204
+  dps: 13449.10871
+  tps: 11680.5325
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-WingedTalisman-37844"
  value: {
-  dps: 13285.8383
-  tps: 11543.08569
+  dps: 13090.00588
+  tps: 11354.54378
  }
 }
 dps_results: {
  key: "TestDemonology-Average-Default"
  value: {
-  dps: 14120.88245
-  tps: 12330.57619
+  dps: 13946.57103
+  tps: 12163.30576
  }
 }
 dps_results: {
  key: "TestDemonology-Settings-Orc-p4_demo-Demonology Warlock-demo-FullBuffs-LongMultiTarget"
  value: {
-  dps: 17438.86787
-  tps: 17278.70009
+  dps: 17361.12567
+  tps: 17435.98697
  }
 }
 dps_results: {
  key: "TestDemonology-Settings-Orc-p4_demo-Demonology Warlock-demo-FullBuffs-LongSingleTarget"
  value: {
-  dps: 13987.55061
-  tps: 12211.3241
+  dps: 13766.09428
+  tps: 12003.46809
  }
 }
 dps_results: {
  key: "TestDemonology-Settings-Orc-p4_demo-Demonology Warlock-demo-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 15867.80813
-  tps: 13801.53668
+  dps: 14524.59149
+  tps: 12818.53577
  }
 }
 dps_results: {
  key: "TestDemonology-Settings-Orc-p4_demo-Demonology Warlock-demo-NoBuffs-LongMultiTarget"
  value: {
-  dps: 10675.54295
-  tps: 12403.23872
+  dps: 10287.07914
+  tps: 12106.19892
  }
 }
 dps_results: {
  key: "TestDemonology-Settings-Orc-p4_demo-Demonology Warlock-demo-NoBuffs-LongSingleTarget"
  value: {
-  dps: 8150.0904
-  tps: 7534.10248
+  dps: 7943.25049
+  tps: 7314.42382
  }
 }
 dps_results: {
  key: "TestDemonology-Settings-Orc-p4_demo-Demonology Warlock-demo-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 8579.77973
-  tps: 7822.12309
+  dps: 6929.63748
+  tps: 6137.98157
  }
 }
 dps_results: {
  key: "TestDemonology-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 13872.1688
-  tps: 12238.92734
+  dps: 13685.59167
+  tps: 12068.09598
  }
 }
diff --git a/sim/warlock/TestDestruction.results b/sim/warlock/TestDestruction.results
index 72ed924ef4..dfd0bc5387 100644
--- a/sim/warlock/TestDestruction.results
+++ b/sim/warlock/TestDestruction.results
@@ -47,42 +47,42 @@ dps_results: {
  key: "TestDestruction-AllItems-Althor'sAbacus-50359"
  value: {
   dps: 13389.20634
-  tps: 11054.85323
+  tps: 11055.91814
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-Althor'sAbacus-50366"
  value: {
   dps: 13435.121
-  tps: 11094.08882
+  tps: 11095.13998
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-AshtongueTalismanofShadows-32493"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-AustereEarthsiegeDiamond"
  value: {
   dps: 13278.35585
-  tps: 10956.83061
+  tps: 10958.03654
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-Bandit'sInsignia-40371"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-BaubleofTrueBlood-50354"
  value: {
   dps: 13020.8403
-  tps: 10740.73409
+  tps: 10741.89779
   hps: 102.01591
  }
 }
@@ -90,7 +90,7 @@ dps_results: {
  key: "TestDestruction-AllItems-BaubleofTrueBlood-50726"
  value: {
   dps: 13020.8403
-  tps: 10740.73409
+  tps: 10741.89779
   hps: 102.01591
  }
 }
@@ -98,49 +98,49 @@ dps_results: {
  key: "TestDestruction-AllItems-BeamingEarthsiegeDiamond"
  value: {
   dps: 13313.85188
-  tps: 10989.40455
+  tps: 10990.42966
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 10034.11635
-  tps: 8161.63757
+  dps: 10049.22959
+  tps: 8178.14603
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-BracingEarthsiegeDiamond"
  value: {
   dps: 13329.62911
-  tps: 10783.32088
+  tps: 10784.50569
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-ChaoticSkyflareDiamond"
  value: {
   dps: 13740.25849
-  tps: 11372.58946
+  tps: 11373.79539
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-CorpseTongueCoin-50349"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-CorpseTongueCoin-50352"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-CorrodedSkeletonKey-50356"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
   hps: 64
  }
 }
@@ -148,609 +148,609 @@ dps_results: {
  key: "TestDestruction-AllItems-DarkCoven'sRegalia"
  value: {
   dps: 12529.42945
-  tps: 10306.09456
+  tps: 10307.31339
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
   dps: 13172.29251
-  tps: 10876.66872
+  tps: 10877.83242
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-DarkmoonCard:Death-42990"
  value: {
   dps: 13228.31371
-  tps: 10937.94004
+  tps: 10939.10375
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-DarkmoonCard:Greatness-44255"
  value: {
   dps: 13111.70956
-  tps: 10827.76372
+  tps: 10823.45164
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-Death'sChoice-47464"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-DeathKnight'sAnguish-38212"
  value: {
   dps: 13060.88686
-  tps: 10776.3729
+  tps: 10777.5366
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-Deathbringer'sWill-50362"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-Deathbringer'sWill-50363"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-DeathbringerGarb"
  value: {
   dps: 10977.39094
-  tps: 8957.62965
+  tps: 8960.47538
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-Defender'sCode-40257"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-DestructiveSkyflareDiamond"
  value: {
   dps: 13321.92919
-  tps: 10996.04661
+  tps: 10997.25254
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-DislodgedForeignObject-50353"
  value: {
   dps: 13702.8365
-  tps: 11338.76511
+  tps: 11340.05406
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-EffulgentSkyflareDiamond"
  value: {
   dps: 13278.35585
-  tps: 10956.83061
+  tps: 10958.03654
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-EmberSkyflareDiamond"
  value: {
   dps: 13347.01795
-  tps: 11016.56304
+  tps: 11017.59017
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-EnigmaticSkyflareDiamond"
  value: {
   dps: 13313.85188
-  tps: 10988.77703
+  tps: 10989.98297
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-EnigmaticStarflareDiamond"
  value: {
   dps: 13306.40413
-  tps: 10982.07406
+  tps: 10983.27999
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-EphemeralSnowflake-50260"
  value: {
   dps: 13131.99417
-  tps: 10846.45349
+  tps: 10847.91554
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-EssenceofGossamer-37220"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-EternalEarthsiegeDiamond"
  value: {
   dps: 13278.35585
-  tps: 10956.83061
+  tps: 10958.03654
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-ExtractofNecromanticPower-40373"
  value: {
   dps: 13225.57447
-  tps: 10934.13049
+  tps: 10935.29419
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-EyeoftheBroodmother-45308"
  value: {
   dps: 13414.44181
-  tps: 11082.22826
+  tps: 11083.32309
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-Figurine-SapphireOwl-42413"
  value: {
   dps: 13037.33164
-  tps: 10755.25739
+  tps: 10756.38657
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-ForethoughtTalisman-40258"
  value: {
   dps: 13247.2883
-  tps: 10933.57834
+  tps: 10934.68446
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-ForgeEmber-37660"
  value: {
   dps: 13317.79421
-  tps: 10998.73333
+  tps: 10999.84184
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-ForlornSkyflareDiamond"
  value: {
   dps: 13329.62911
-  tps: 11000.62695
+  tps: 11001.81176
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-ForlornStarflareDiamond"
  value: {
   dps: 13319.37446
-  tps: 10991.8685
+  tps: 10993.05691
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-FuryoftheFiveFlights-40431"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-FuturesightRune-38763"
  value: {
   dps: 13197.44343
-  tps: 10891.01264
+  tps: 10892.14568
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-Gladiator'sFelshroud"
  value: {
   dps: 10539.28532
-  tps: 8572.46637
+  tps: 8575.85313
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-GlowingTwilightScale-54573"
  value: {
   dps: 13412.16367
-  tps: 11074.47102
+  tps: 11075.52906
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-GlowingTwilightScale-54589"
  value: {
   dps: 13464.33942
-  tps: 11119.05713
+  tps: 11120.09955
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-GnomishLightningGenerator-41121"
  value: {
   dps: 13178.65941
-  tps: 10887.49102
+  tps: 10888.65472
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-Gul'dan'sRegalia"
  value: {
   dps: 11239.39468
-  tps: 9123.46118
+  tps: 9125.35069
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
   dps: 13424.99484
-  tps: 11085.1676
+  tps: 11086.21939
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-ImpassiveSkyflareDiamond"
  value: {
   dps: 13313.85188
-  tps: 10988.77703
+  tps: 10989.98297
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-ImpassiveStarflareDiamond"
  value: {
   dps: 13306.40413
-  tps: 10982.07406
+  tps: 10983.27999
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-IncisorFragment-37723"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-InsightfulEarthsiegeDiamond"
  value: {
   dps: 13293.59654
-  tps: 10975.31009
+  tps: 10975.74093
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
   dps: 13278.35585
-  tps: 10956.83061
+  tps: 10958.03654
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-Lavanthor'sTalisman-37872"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-MajesticDragonFigurine-40430"
  value: {
   dps: 13254.73916
-  tps: 10939.77219
+  tps: 10940.87656
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-MaleficRaiment"
  value: {
-  dps: 8360.99106
-  tps: 6692.24704
+  dps: 8391.03484
+  tps: 6711.58647
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-MeteoriteWhetstone-37390"
  value: {
   dps: 13123.39121
-  tps: 10832.62682
+  tps: 10833.79052
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-NevermeltingIceCrystal-50259"
  value: {
   dps: 13280.06074
-  tps: 10963.07354
+  tps: 10964.17965
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-OfferingofSacrifice-37638"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-PersistentEarthshatterDiamond"
  value: {
   dps: 13278.35585
-  tps: 10956.83061
+  tps: 10958.03654
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-PersistentEarthsiegeDiamond"
  value: {
   dps: 13278.35585
-  tps: 10956.83061
+  tps: 10958.03654
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-PetrifiedScarab-21685"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-PetrifiedTwilightScale-54571"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-PetrifiedTwilightScale-54591"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-PlagueheartGarb"
  value: {
-  dps: 10206.67303
-  tps: 8267.41117
+  dps: 10210.45501
+  tps: 8273.69263
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-PowerfulEarthshatterDiamond"
  value: {
   dps: 13278.35585
-  tps: 10956.83061
+  tps: 10958.03654
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-PowerfulEarthsiegeDiamond"
  value: {
   dps: 13278.35585
-  tps: 10956.83061
+  tps: 10958.03654
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-PurifiedShardoftheGods"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-ReignoftheDead-47316"
  value: {
   dps: 13577.13263
-  tps: 11251.09524
+  tps: 11252.17796
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-ReignoftheDead-47477"
  value: {
   dps: 13647.53166
-  tps: 11316.03299
+  tps: 11317.10478
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-RelentlessEarthsiegeDiamond"
  value: {
   dps: 13701.66868
-  tps: 11337.85863
+  tps: 11339.06456
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-RevitalizingSkyflareDiamond"
  value: {
   dps: 13278.35585
-  tps: 10956.48724
+  tps: 10957.58633
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-RuneofRepulsion-40372"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-SealofthePantheon-36993"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-ShinyShardoftheGods"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-SliverofPureIce-50339"
  value: {
   dps: 13345.37871
-  tps: 11021.69552
+  tps: 11022.52196
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-SliverofPureIce-50346"
  value: {
   dps: 13387.11931
-  tps: 11057.84275
+  tps: 11058.55731
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-SoulPreserver-37111"
  value: {
   dps: 13172.15522
-  tps: 10869.37274
+  tps: 10870.49874
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-SouloftheDead-40382"
  value: {
   dps: 13149.20484
-  tps: 10860.8469
+  tps: 10861.13615
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-SparkofLife-37657"
  value: {
   dps: 13065.8288
-  tps: 10778.64012
+  tps: 10779.53333
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
   dps: 13022.23918
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-SwiftSkyflareDiamond"
  value: {
   dps: 13278.35585
-  tps: 10956.83061
+  tps: 10958.03654
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-SwiftStarflareDiamond"
  value: {
   dps: 13278.35585
-  tps: 10956.83061
+  tps: 10958.03654
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-SwiftWindfireDiamond"
  value: {
   dps: 13278.35585
-  tps: 10956.83061
+  tps: 10958.03654
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-TalismanofTrollDivinity-37734"
  value: {
   dps: 13114.50519
-  tps: 10820.09928
+  tps: 10821.24086
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-TearsoftheVanquished-47215"
  value: {
   dps: 13060.91843
-  tps: 10776.01541
+  tps: 10776.83215
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-TheGeneral'sHeart-45507"
  value: {
   dps: 13015.62797
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-ThunderingSkyflareDiamond"
  value: {
   dps: 13278.35585
-  tps: 10956.83061
+  tps: 10958.03654
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-TinyAbominationinaJar-50351"
  value: {
   dps: 13023.96692
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-TinyAbominationinaJar-50706"
  value: {
   dps: 13023.96692
-  tps: 10735.61276
+  tps: 10736.77646
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-TirelessSkyflareDiamond"
  value: {
   dps: 13329.62911
-  tps: 11000.62695
+  tps: 11001.81176
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-TirelessStarflareDiamond"
  value: {
   dps: 13319.37446
-  tps: 10991.8685
+  tps: 10993.05691
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-TomeofArcanePhenomena-36972"
  value: {
   dps: 13293.92049
-  tps: 10985.03588
+  tps: 10986.16105
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-TrenchantEarthshatterDiamond"
  value: {
   dps: 13319.37446
-  tps: 10991.8685
+  tps: 10993.05691
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-TrenchantEarthsiegeDiamond"
  value: {
   dps: 13329.62911
-  tps: 11000.62695
+  tps: 11001.81176
  }
 }
 dps_results: {
  key: "TestDestruction-AllItems-WingedTalisman-37844"
  value: {
   dps: 13167.51032
-  tps: 10865.49212
+  tps: 10866.65582
  }
 }
 dps_results: {
  key: "TestDestruction-Average-Default"
  value: {
   dps: 13933.56914
-  tps: 11535.47493
+  tps: 11536.39471
  }
 }
 dps_results: {
  key: "TestDestruction-Settings-Orc-p4_destro-Destruction Warlock-destro-FullBuffs-LongMultiTarget"
  value: {
   dps: 13954.55789
-  tps: 14157.31002
+  tps: 14181.4287
  }
 }
 dps_results: {
  key: "TestDestruction-Settings-Orc-p4_destro-Destruction Warlock-destro-FullBuffs-LongSingleTarget"
  value: {
   dps: 13740.25849
-  tps: 11372.58946
+  tps: 11373.79539
  }
 }
 dps_results: {
  key: "TestDestruction-Settings-Orc-p4_destro-Destruction Warlock-destro-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 15068.92765
-  tps: 12278.35906
+  dps: 15092.54473
+  tps: 12305.56218
  }
 }
 dps_results: {
@@ -778,6 +778,6 @@ dps_results: {
  key: "TestDestruction-SwitchInFrontOfTarget-Default"
  value: {
   dps: 13707.6834
-  tps: 11372.58946
+  tps: 11373.79539
  }
 }
diff --git a/sim/warlock/inferno.go b/sim/warlock/inferno.go
index 54858c9d59..da4b7740f3 100644
--- a/sim/warlock/inferno.go
+++ b/sim/warlock/inferno.go
@@ -60,9 +60,6 @@ func (warlock *Warlock) registerInfernoSpell() {
 	warlock.AddMajorCooldown(core.MajorCooldown{
 		Spell: warlock.Inferno,
 		Type:  core.CooldownTypeUnknown,
-		ShouldActivate: func(sim *core.Simulation, character *core.Character) bool {
-			return sim.GetRemainingDuration() <= 61*time.Second
-		},
 	})
 }
 
diff --git a/sim/warlock/warlock_test.go b/sim/warlock/warlock_test.go
index fcb4dbb066..1e8e56443e 100644
--- a/sim/warlock/warlock_test.go
+++ b/sim/warlock/warlock_test.go
@@ -110,16 +110,14 @@ var defaultDestroOptions = &proto.Warlock_Options{
 
 var DefaultDestroWarlock = &proto.Player_Warlock{
 	Warlock: &proto.Warlock{
-		Options:  defaultDestroOptions,
-		Rotation: defaultDestroRotation,
+		Options: defaultDestroOptions,
 	},
 }
 
 // ---------------------------------------
 var DefaultAfflictionWarlock = &proto.Player_Warlock{
 	Warlock: &proto.Warlock{
-		Options:  defaultAfflictionOptions,
-		Rotation: defaultAfflictionRotation,
+		Options: defaultAfflictionOptions,
 	},
 }
 
@@ -129,39 +127,10 @@ var defaultAfflictionOptions = &proto.Warlock_Options{
 	WeaponImbue: proto.Warlock_Options_GrandSpellstone,
 }
 
-var defaultAfflictionRotation = &proto.Warlock_Rotation{
-	Type:         proto.Warlock_Rotation_Affliction,
-	PrimarySpell: proto.Warlock_Rotation_ShadowBolt,
-	SecondaryDot: proto.Warlock_Rotation_UnstableAffliction,
-	SpecSpell:    proto.Warlock_Rotation_Haunt,
-	Curse:        proto.Warlock_Rotation_Agony,
-	Corruption:   true,
-	DetonateSeed: true,
-}
-
-var afflictionItemSwapRotation = &proto.Warlock_Rotation{
-	Type:             proto.Warlock_Rotation_Affliction,
-	PrimarySpell:     proto.Warlock_Rotation_ShadowBolt,
-	SecondaryDot:     proto.Warlock_Rotation_UnstableAffliction,
-	SpecSpell:        proto.Warlock_Rotation_Haunt,
-	Curse:            proto.Warlock_Rotation_Agony,
-	Corruption:       true,
-	DetonateSeed:     true,
-	EnableWeaponSwap: true,
-	WeaponSwap: &proto.ItemSwap{
-		MhItem: &proto.ItemSpec{
-			Id:      45457,
-			Enchant: 3790,
-			Gems:    []int32{40013, 40013},
-		},
-	},
-}
-
 // ---------------------------------------
 var DefaultDemonologyWarlock = &proto.Player_Warlock{
 	Warlock: &proto.Warlock{
-		Options:  defaultDemonologyOptions,
-		Rotation: defaultDemonologyRotation,
+		Options: defaultDemonologyOptions,
 	},
 }
 
@@ -171,15 +140,6 @@ var defaultDemonologyOptions = &proto.Warlock_Options{
 	WeaponImbue: proto.Warlock_Options_GrandSpellstone,
 }
 
-var defaultDemonologyRotation = &proto.Warlock_Rotation{
-	Type:         proto.Warlock_Rotation_Demonology,
-	PrimarySpell: proto.Warlock_Rotation_ShadowBolt,
-	SecondaryDot: proto.Warlock_Rotation_Immolate,
-	Curse:        proto.Warlock_Rotation_Doom,
-	Corruption:   true,
-	DetonateSeed: true,
-}
-
 // ---------------------------------------------------------
 
 var FullConsumes = &proto.Consumes{
diff --git a/sim/warrior/heroic_strike_cleave.go b/sim/warrior/heroic_strike_cleave.go
index c828d76424..b75f455c6d 100644
--- a/sim/warrior/heroic_strike_cleave.go
+++ b/sim/warrior/heroic_strike_cleave.go
@@ -181,7 +181,4 @@ func (warrior *Warrior) RegisterHSOrCleave(useCleave bool, rageThreshold float64
 	}
 
 	warrior.HSRageThreshold = max(autoSpell.DefaultCast.Cost, rageThreshold)
-	if warrior.IsUsingAPL {
-		warrior.HSRageThreshold = 0
-	}
 }
diff --git a/sim/warrior/talents.go b/sim/warrior/talents.go
index e32b8f066a..af8f2bdded 100644
--- a/sim/warrior/talents.go
+++ b/sim/warrior/talents.go
@@ -812,8 +812,7 @@ func (warrior *Warrior) RegisterBladestormCD() {
 		},
 		Cast: core.CastConfig{
 			DefaultCast: core.Cast{
-				ChannelTime: time.Second * 6,
-				GCD:         core.GCDDefault,
+				GCD: core.GCDDefault,
 			},
 			CD: core.Cooldown{
 				Timer:    warrior.NewTimer(),

From 8ebb2f7f159d007418335016b7078a57c0b7e054 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Sat, 6 Jan 2024 15:18:58 -0800
Subject: [PATCH 14/28] Backend code all compiles now

---
 sim/deathknight/dps/TestBlood.results         |  12 +-
 sim/deathknight/dps/TestUnholy.results        | 568 ++++++------
 sim/priest/healing/TestHoly.results           |   4 +-
 sim/priest/smite/TestSmite.results            |   2 +-
 sim/rogue/TestCombat.results                  | 192 ++--
 .../enhancement/TestEnhancement.results       | 832 +++++++++---------
 sim/shaman/fire_elemental_totem.go            |   4 +-
 .../restoration/TestRestoration.results       | 559 +++---------
 sim/warlock/TestDemonology.results            | 416 ++++-----
 sim/warrior/berserker_rage.go                 |   4 -
 sim/warrior/bloodthirst.go                    |   6 +-
 sim/warrior/demoralizing_shout.go             |  15 -
 sim/warrior/dps/TestArms.results              | 534 +++++------
 sim/warrior/dps/TestFury.results              | 532 +++++------
 sim/warrior/dps/dps_warrior.go                |  58 +-
 sim/warrior/dps/dps_warrior_test.go           |   6 +-
 sim/warrior/dps/rotation.go                   | 451 ----------
 sim/warrior/execute.go                        |   8 -
 sim/warrior/heroic_strike_cleave.go           |  41 +-
 sim/warrior/overpower.go                      |  24 -
 .../protection/TestProtectionWarrior.results  | 540 ++++++------
 sim/warrior/protection/protection_warrior.go  |  15 +-
 sim/warrior/protection/rotation.go            | 113 ---
 sim/warrior/rend.go                           |  23 +-
 sim/warrior/shouts.go                         |  18 +-
 sim/warrior/warrior.go                        |  37 +-
 26 files changed, 1990 insertions(+), 3024 deletions(-)
 delete mode 100644 sim/warrior/dps/rotation.go
 delete mode 100644 sim/warrior/protection/rotation.go

diff --git a/sim/deathknight/dps/TestBlood.results b/sim/deathknight/dps/TestBlood.results
index fb42a80807..350f451b81 100644
--- a/sim/deathknight/dps/TestBlood.results
+++ b/sim/deathknight/dps/TestBlood.results
@@ -287,8 +287,8 @@ dps_results: {
 dps_results: {
  key: "TestBlood-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 6896.49505
-  tps: 3364.06484
+  dps: 7006.33997
+  tps: 3423.96806
  }
 }
 dps_results: {
@@ -666,8 +666,8 @@ dps_results: {
 dps_results: {
  key: "TestBlood-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 6949.06461
-  tps: 3403.90157
+  dps: 6975.8539
+  tps: 3419.25098
  }
 }
 dps_results: {
@@ -778,8 +778,8 @@ dps_results: {
 dps_results: {
  key: "TestBlood-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 6904.99937
-  tps: 3377.67182
+  dps: 6936.74073
+  tps: 3393.67215
  }
 }
 dps_results: {
diff --git a/sim/deathknight/dps/TestUnholy.results b/sim/deathknight/dps/TestUnholy.results
index feb5304359..7b1a4caecc 100644
--- a/sim/deathknight/dps/TestUnholy.results
+++ b/sim/deathknight/dps/TestUnholy.results
@@ -46,984 +46,984 @@ character_stats_results: {
 dps_results: {
  key: "TestUnholy-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 9922.78043
-  tps: 6086.42693
+  dps: 9951.338
+  tps: 6092.27674
   hps: 63.09596
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 9922.78043
-  tps: 6086.42693
+  dps: 9951.338
+  tps: 6092.27674
   hps: 65.85359
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 10430.00844
-  tps: 6390.24383
+  dps: 10460.21938
+  tps: 6397.00184
   hps: 42.71551
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 10140.71531
-  tps: 6245.81979
+  dps: 10169.13695
+  tps: 6251.48805
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 9922.77419
-  tps: 6086.49657
+  dps: 9951.33176
+  tps: 6092.34637
   hps: 134.70651
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 9922.77419
-  tps: 6086.49657
+  dps: 9951.33176
+  tps: 6092.34637
   hps: 134.70651
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 10455.19915
-  tps: 6410.93066
+  dps: 10485.43439
+  tps: 6417.68614
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Beast-tamer'sShoulders-30892"
  value: {
-  dps: 9846.78215
-  tps: 5955.98182
+  dps: 9876.75662
+  tps: 5962.69236
   hps: 40.4349
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BlessedBattlegearofUndeadSlaying"
  value: {
-  dps: 7658.49304
-  tps: 4665.49202
+  dps: 7686.05311
+  tps: 4672.10685
   hps: 34.38394
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BlessedGarboftheUndeadSlayer"
  value: {
-  dps: 7538.22509
-  tps: 4625.91137
+  dps: 7565.46676
+  tps: 4632.19878
   hps: 32.96775
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 7286.40425
-  tps: 4429.18278
+  dps: 7313.0717
+  tps: 4435.17023
   hps: 32.72866
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 10430.00844
-  tps: 6262.43896
+  dps: 10460.21938
+  tps: 6269.0618
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Bryntroll,theBoneArbiter-50415"
  value: {
-  dps: 10573.70985
-  tps: 6509.45394
+  dps: 10604.08736
+  tps: 6516.32402
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Bryntroll,theBoneArbiter-50709"
  value: {
-  dps: 10573.70985
-  tps: 6509.45394
+  dps: 10604.08736
+  tps: 6516.32402
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 10584.03517
-  tps: 6518.15774
+  dps: 10614.41705
+  tps: 6525.02957
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 110.32034
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 10068.4566
-  tps: 6205.16448
+  dps: 10097.13017
+  tps: 6211.0806
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 10121.75867
-  tps: 6244.79637
+  dps: 10150.34231
+  tps: 6250.56469
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 10241.54185
-  tps: 6241.52025
+  dps: 10270.09942
+  tps: 6247.37005
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DarkrunedBattlegear"
  value: {
-  dps: 8496.17872
-  tps: 5163.63257
+  dps: 8524.35203
+  tps: 5169.97326
   hps: 38.90837
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DarkrunedPlate"
  value: {
-  dps: 7511.80491
-  tps: 4529.17514
+  dps: 7538.72973
+  tps: 4535.38488
   hps: 40.98666
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DeadlyGladiator'sSigilofStrife-42620"
  value: {
-  dps: 10671.19243
-  tps: 6578.7694
+  dps: 10701.56994
+  tps: 6585.63948
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 10038.19826
-  tps: 6183.10117
+  dps: 10066.91028
+  tps: 6189.0192
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 10492.31799
-  tps: 6459.5456
+  dps: 10523.45653
+  tps: 6466.4946
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 10550.63046
-  tps: 6526.63462
+  dps: 10581.1952
+  tps: 6533.19654
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Defender'sCode-40257"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 10458.48929
-  tps: 6413.51148
+  dps: 10488.76642
+  tps: 6420.30481
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DislodgedForeignObject-50348"
  value: {
-  dps: 10297.5309
-  tps: 6344.6214
+  dps: 10328.43271
+  tps: 6351.44496
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 10234.97586
-  tps: 6256.43357
+  dps: 10265.45773
+  tps: 6262.88224
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 10430.00844
-  tps: 6390.24383
+  dps: 10460.21938
+  tps: 6397.00184
   hps: 42.71551
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 10430.00844
-  tps: 6390.24383
+  dps: 10460.21938
+  tps: 6397.00184
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 10455.19915
-  tps: 6410.93066
+  dps: 10485.43439
+  tps: 6417.68614
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 10451.33499
-  tps: 6407.77762
+  dps: 10481.55787
+  tps: 6414.52322
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 10158.62422
-  tps: 6177.3893
+  dps: 10199.29803
+  tps: 6233.23221
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 44.16848
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 10430.00844
-  tps: 6390.24383
+  dps: 10460.21938
+  tps: 6397.00184
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 10101.84556
-  tps: 6231.78998
+  dps: 10130.32277
+  tps: 6237.53952
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 10051.19915
-  tps: 6191.80096
+  dps: 10079.77279
+  tps: 6197.65957
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ForgeEmber-37660"
  value: {
-  dps: 10030.72681
-  tps: 6175.63071
+  dps: 10059.33603
+  tps: 6181.50535
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 10430.00844
-  tps: 6390.24383
+  dps: 10460.21938
+  tps: 6397.00184
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 10430.00844
-  tps: 6390.24383
+  dps: 10460.21938
+  tps: 6397.00184
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-FuriousGladiator'sSigilofStrife-42621"
  value: {
-  dps: 10690.68894
-  tps: 6592.6325
+  dps: 10721.06645
+  tps: 6599.50258
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 10167.17894
-  tps: 6262.54681
+  dps: 10195.73651
+  tps: 6268.39662
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-FuturesightRune-38763"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 10084.45394
-  tps: 6213.80463
+  dps: 10113.06173
+  tps: 6219.59022
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-HatefulGladiator'sSigilofStrife-42619"
  value: {
-  dps: 10638.92445
-  tps: 6553.50409
+  dps: 10669.30196
+  tps: 6560.37417
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 10455.19915
-  tps: 6410.93066
+  dps: 10485.43439
+  tps: 6417.68614
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 10451.33499
-  tps: 6407.77762
+  dps: 10481.55787
+  tps: 6414.52322
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-IncisorFragment-37723"
  value: {
-  dps: 10115.90059
-  tps: 6237.30216
+  dps: 10145.46508
+  tps: 6243.97919
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 10430.00844
-  tps: 6390.24383
+  dps: 10460.21938
+  tps: 6397.00184
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 10463.88929
-  tps: 6414.35598
+  dps: 10494.10024
+  tps: 6421.11398
   hps: 58.71565
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-LastWord-50179"
  value: {
-  dps: 10573.70985
-  tps: 6509.45394
+  dps: 10604.08736
+  tps: 6516.32402
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-LastWord-50708"
  value: {
-  dps: 10573.70985
-  tps: 6509.45394
+  dps: 10604.08736
+  tps: 6516.32402
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 10281.96583
-  tps: 6397.7295
+  dps: 10312.44062
+  tps: 6404.84993
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 10041.86928
-  tps: 6191.38589
+  dps: 10070.71225
+  tps: 6197.56001
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 10457.4358
-  tps: 6409.76319
+  dps: 10487.64674
+  tps: 6416.52119
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 10463.88929
-  tps: 6414.35598
+  dps: 10494.10024
+  tps: 6421.11398
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 10430.00844
-  tps: 6390.24383
+  dps: 10460.21938
+  tps: 6397.00184
   hps: 42.60516
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 10430.00844
-  tps: 6390.24383
+  dps: 10460.21938
+  tps: 6397.00184
   hps: 42.71551
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 10040.41016
-  tps: 6178.60998
+  dps: 10069.22365
+  tps: 6184.61313
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 10054.02498
-  tps: 6189.82165
+  dps: 10082.83847
+  tps: 6195.8248
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 10573.70985
-  tps: 6509.45394
+  dps: 10604.08736
+  tps: 6516.32402
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-RelentlessGladiator'sSigilofStrife-42622"
  value: {
-  dps: 10713.43488
-  tps: 6608.8061
+  dps: 10743.81239
+  tps: 6615.67618
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 10430.00844
-  tps: 6390.24383
+  dps: 10460.21938
+  tps: 6397.00184
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SavageGladiator'sSigilofStrife-42618"
  value: {
-  dps: 10631.54167
-  tps: 6548.51728
+  dps: 10661.91918
+  tps: 6555.38736
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ScourgeborneBattlegear"
  value: {
-  dps: 8154.20256
-  tps: 4967.45223
+  dps: 8182.92506
+  tps: 4974.35252
   hps: 36.48062
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ScourgebornePlate"
  value: {
-  dps: 7458.41543
-  tps: 4483.91626
+  dps: 7485.29071
+  tps: 4489.99814
   hps: 38.04394
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Scourgelord'sBattlegear"
  value: {
-  dps: 9705.69014
-  tps: 6081.2567
+  dps: 9733.87841
+  tps: 6088.28622
   hps: 44.60989
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Scourgelord'sPlate"
  value: {
-  dps: 8277.82101
-  tps: 4960.67431
+  dps: 8304.60081
+  tps: 4966.63547
   hps: 48.06758
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Shadowmourne-49623"
  value: {
-  dps: 10573.70985
-  tps: 6509.45394
+  dps: 10604.08736
+  tps: 6516.32402
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigilofDeflection-45144"
  value: {
-  dps: 10573.70985
-  tps: 6509.45394
+  dps: 10604.08736
+  tps: 6516.32402
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigilofHauntedDreams-40715"
  value: {
-  dps: 10611.42935
-  tps: 6539.70724
+  dps: 10641.93598
+  tps: 6546.65478
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigilofInsolence-47672"
  value: {
-  dps: 10573.70985
-  tps: 6509.45394
+  dps: 10604.08736
+  tps: 6516.32402
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigiloftheBoneGryphon-50462"
  value: {
-  dps: 10573.70985
-  tps: 6509.45394
+  dps: 10604.08736
+  tps: 6516.32402
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigiloftheHangedMan-50459"
  value: {
-  dps: 10573.70985
-  tps: 6509.45394
+  dps: 10604.08736
+  tps: 6516.32402
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SigiloftheUnfalteringKnight-40714"
  value: {
-  dps: 10573.70985
-  tps: 6509.45394
+  dps: 10604.08736
+  tps: 6516.32402
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 46.32034
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SoulPreserver-37111"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SouloftheDead-40382"
  value: {
-  dps: 10057.09451
-  tps: 6196.45815
+  dps: 10085.7005
+  tps: 6202.34138
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SparkofLife-37657"
  value: {
-  dps: 10085.00246
-  tps: 6173.51437
+  dps: 10114.43165
+  tps: 6179.65119
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 10062.46216
-  tps: 6206.54716
+  dps: 10161.26061
+  tps: 6233.20408
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-StormshroudArmor"
  value: {
-  dps: 7154.90213
-  tps: 4377.4337
+  dps: 7180.45811
+  tps: 4383.13828
   hps: 30.7975
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 10463.88929
-  tps: 6414.35598
+  dps: 10494.10024
+  tps: 6421.11398
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 10457.4358
-  tps: 6409.76319
+  dps: 10487.64674
+  tps: 6416.52119
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 10446.14218
-  tps: 6401.72581
+  dps: 10476.35312
+  tps: 6408.48381
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Thassarian'sBattlegear"
  value: {
-  dps: 8723.79314
-  tps: 5354.1717
+  dps: 8751.89481
+  tps: 5360.73152
   hps: 38.68766
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Thassarian'sPlate"
  value: {
-  dps: 7520.05866
-  tps: 4522.58541
+  dps: 7546.77609
+  tps: 4528.56765
   hps: 40.30616
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TheTwinBladesofAzzinoth"
  value: {
-  dps: 9092.76004
-  tps: 5477.55947
+  dps: 9123.34118
+  tps: 5484.52488
   hps: 40.74757
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 10553.1811
-  tps: 6445.70617
+  dps: 10584.28638
+  tps: 6452.81963
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 10181.80388
-  tps: 6309.64479
+  dps: 10211.64921
+  tps: 6316.5035
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 10197.97827
-  tps: 6314.84165
+  dps: 10227.38494
+  tps: 6321.31812
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 10430.00844
-  tps: 6390.24383
+  dps: 10460.21938
+  tps: 6397.00184
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 10430.00844
-  tps: 6390.24383
+  dps: 10460.21938
+  tps: 6397.00184
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 10052.06286
-  tps: 6182.44029
+  dps: 10091.66213
+  tps: 6216.2165
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 10430.00844
-  tps: 6390.24383
+  dps: 10460.21938
+  tps: 6397.00184
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 10430.00844
-  tps: 6390.24383
+  dps: 10460.21938
+  tps: 6397.00184
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-UndeadSlayer'sBlessedArmor"
  value: {
-  dps: 7548.03803
-  tps: 4626.05614
+  dps: 7574.98389
+  tps: 4632.27698
   hps: 33.68504
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-Val'anyr,HammerofAncientKings-46017"
  value: {
-  dps: 9816.38871
-  tps: 5946.71882
+  dps: 9846.88961
+  tps: 5953.65973
   hps: 42.81036
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-WingedTalisman-37844"
  value: {
-  dps: 9922.7413
-  tps: 6086.4498
+  dps: 9951.29887
+  tps: 6092.29961
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-AllItems-WrathfulGladiator'sSigilofStrife-51417"
  value: {
-  dps: 10739.43023
-  tps: 6627.29023
+  dps: 10769.80774
+  tps: 6634.16031
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-Average-Default"
  value: {
-  dps: 10554.64816
-  tps: 6494.67871
+  dps: 10585.28189
+  tps: 6501.70107
   hps: 42.14164
  }
 }
@@ -1174,152 +1174,152 @@ dps_results: {
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-FullBuffs-LongMultiTarget"
  value: {
-  dps: 13295.6274
-  tps: 8750.5226
+  dps: 13330.8778
+  tps: 8761.22702
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-FullBuffs-LongSingleTarget"
  value: {
-  dps: 10573.70985
-  tps: 6509.45394
+  dps: 10604.08736
+  tps: 6516.32402
   hps: 42.12697
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 14607.97681
-  tps: 8255.15077
+  dps: 14714.54643
+  tps: 8253.24683
   hps: 210.63484
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-NoBuffs-LongMultiTarget"
  value: {
-  dps: 6470.56088
-  tps: 4464.0143
+  dps: 6489.15968
+  tps: 4469.42626
   hps: 27.7272
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-NoBuffs-LongSingleTarget"
  value: {
-  dps: 5264.32478
-  tps: 3463.83227
+  dps: 5281.0176
+  tps: 3467.76472
   hps: 27.7272
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_2h_ss-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 6520.66801
-  tps: 4200.50101
+  dps: 6575.03317
+  tps: 4196.88405
   hps: 138.636
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 34536.31268
-  tps: 26123.72876
+  dps: 34597.44338
+  tps: 26150.38042
   hps: 187.04374
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 10399.27957
-  tps: 6285.11904
+  dps: 10428.49399
+  tps: 6290.99572
   hps: 185.35866
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 14265.93791
-  tps: 7821.68174
+  dps: 14370.90078
+  tps: 7818.17776
   hps: 421.26968
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 21452.09038
-  tps: 16812.98236
+  dps: 21506.52324
+  tps: 16844.45945
   hps: 58.22712
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 5110.62663
-  tps: 3335.81335
+  dps: 5127.53854
+  tps: 3339.88717
   hps: 56.84076
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-uh_dnd_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 6331.61226
-  tps: 4002.18567
+  dps: 6385.38574
+  tps: 3997.92592
   hps: 275.88564
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-FullBuffs-LongMultiTarget"
  value: {
-  dps: 13120.36841
-  tps: 8476.68206
+  dps: 13155.35462
+  tps: 8487.31602
   hps: 235.91102
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-FullBuffs-LongSingleTarget"
  value: {
-  dps: 10427.85852
-  tps: 6259.85959
+  dps: 10457.69537
+  tps: 6266.31718
   hps: 235.91102
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 14453.14559
-  tps: 7918.89701
+  dps: 14557.52485
+  tps: 7915.30954
   hps: 337.01574
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-NoBuffs-LongMultiTarget"
  value: {
-  dps: 6489.06625
-  tps: 4413.82845
+  dps: 6509.12224
+  tps: 4420.4188
   hps: 155.27232
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-NoBuffs-LongSingleTarget"
  value: {
-  dps: 5302.30751
-  tps: 3435.96613
+  dps: 5319.40919
+  tps: 3440.16963
   hps: 155.27232
  }
 }
 dps_results: {
  key: "TestUnholy-Settings-Orc-p3_uh_dw-Basic-unholy_dw_ss-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 6361.79663
-  tps: 3924.97701
+  dps: 6414.90391
+  tps: 3920.0793
   hps: 221.8176
  }
 }
 dps_results: {
  key: "TestUnholy-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 9945.03108
-  tps: 6110.9625
+  dps: 9974.31232
+  tps: 6117.20239
   hps: 42.12697
  }
 }
diff --git a/sim/priest/healing/TestHoly.results b/sim/priest/healing/TestHoly.results
index 91517efd66..7de62a4da8 100644
--- a/sim/priest/healing/TestHoly.results
+++ b/sim/priest/healing/TestHoly.results
@@ -172,8 +172,8 @@ dps_results: {
 dps_results: {
  key: "TestHoly-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  tps: 36.25385
-  hps: 4991.42137
+  tps: 35.9717
+  hps: 4983.05245
  }
 }
 dps_results: {
diff --git a/sim/priest/smite/TestSmite.results b/sim/priest/smite/TestSmite.results
index d4ee33a0d2..110aae8059 100644
--- a/sim/priest/smite/TestSmite.results
+++ b/sim/priest/smite/TestSmite.results
@@ -855,7 +855,7 @@ dps_results: {
  key: "TestSmite-Settings-Undead-p1-Basic-default-NoBuffs-ShortSingleTarget"
  value: {
   dps: 2520.9093
-  tps: 2134.74022
+  tps: 2126.72097
  }
 }
 dps_results: {
diff --git a/sim/rogue/TestCombat.results b/sim/rogue/TestCombat.results
index 094e9c81fc..78ab4bff78 100644
--- a/sim/rogue/TestCombat.results
+++ b/sim/rogue/TestCombat.results
@@ -981,43 +981,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 21435.04268
-  tps: 15218.8803
+  dps: 21132.20952
+  tps: 15003.86876
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 3539.13966
-  tps: 2512.78916
+  dps: 3516.89637
+  tps: 2496.99642
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 4297.57812
-  tps: 3051.28047
+  dps: 4109.75712
+  tps: 2917.92755
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 13206.06312
-  tps: 9376.30481
+  dps: 13003.58802
+  tps: 9232.54749
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 1785.96568
-  tps: 1268.03563
+  dps: 1778.52693
+  tps: 1262.75412
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 1898.48085
-  tps: 1347.9214
+  dps: 1863.43428
+  tps: 1323.03834
  }
 }
 dps_results: {
@@ -1149,43 +1149,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 23026.48078
-  tps: 16348.80135
+  dps: 22695.82072
+  tps: 16114.03271
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 4633.5605
-  tps: 3289.82796
+  dps: 4601.62498
+  tps: 3267.15373
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 5540.589
-  tps: 3933.81819
+  dps: 5306.8537
+  tps: 3767.86612
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 13454.08262
-  tps: 9552.39866
+  dps: 13260.84412
+  tps: 9415.19933
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2312.07404
-  tps: 1641.57257
+  dps: 2301.87343
+  tps: 1634.33013
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2393.64181
-  tps: 1699.48569
+  dps: 2337.44435
+  tps: 1659.58549
  }
 }
 dps_results: {
@@ -1317,43 +1317,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 25576.22706
-  tps: 18159.12122
+  dps: 25256.98021
+  tps: 17932.45595
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5051.3607
-  tps: 3586.46609
+  dps: 5018.34623
+  tps: 3563.02583
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6092.44116
-  tps: 4325.63322
+  dps: 5809.23206
+  tps: 4124.55476
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 15002.54589
-  tps: 10651.80759
+  dps: 14807.07114
+  tps: 10513.02051
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2529.97284
-  tps: 1796.28072
+  dps: 2519.14739
+  tps: 1788.59464
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2644.86445
-  tps: 1877.85376
+  dps: 2588.00612
+  tps: 1837.48434
  }
 }
 dps_results: {
@@ -1485,43 +1485,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19109.11172
-  tps: 13567.46932
+  dps: 18824.30249
+  tps: 13365.25477
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 4217.58491
-  tps: 2994.48528
+  dps: 4189.47287
+  tps: 2974.52573
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 5274.9979
-  tps: 3745.24851
+  dps: 5004.60982
+  tps: 3553.27297
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 11025.5797
-  tps: 7828.16159
+  dps: 10831.81586
+  tps: 7690.58926
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 1993.74101
-  tps: 1415.55612
+  dps: 1983.7099
+  tps: 1408.43403
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Human-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2156.33219
-  tps: 1530.99585
+  dps: 2101.37058
+  tps: 1491.97311
  }
 }
 dps_results: {
@@ -1653,43 +1653,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 21615.92487
-  tps: 15347.30666
+  dps: 21290.63515
+  tps: 15116.35096
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 3565.29603
-  tps: 2531.36018
+  dps: 3540.60866
+  tps: 2513.83215
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 4362.48163
-  tps: 3097.36196
+  dps: 4172.47671
+  tps: 2962.45847
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 13327.72899
-  tps: 9462.68758
+  dps: 13113.07646
+  tps: 9310.28429
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 1801.49013
-  tps: 1279.05799
+  dps: 1793.27032
+  tps: 1273.22193
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Deadly-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 1933.6019
-  tps: 1372.85735
+  dps: 1900.39941
+  tps: 1349.28358
  }
 }
 dps_results: {
@@ -1821,43 +1821,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 23204.3601
-  tps: 16475.09567
+  dps: 22855.23185
+  tps: 16227.21462
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 4667.4418
-  tps: 3313.88368
+  dps: 4631.68558
+  tps: 3288.49676
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 5622.83334
-  tps: 3992.21167
+  dps: 5384.15189
+  tps: 3822.74784
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 13586.68098
-  tps: 9646.54349
+  dps: 13404.07154
+  tps: 9516.8908
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2332.01436
-  tps: 1655.7302
+  dps: 2322.07483
+  tps: 1648.67313
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Deadly OH Instant-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2435.28497
-  tps: 1729.05233
+  dps: 2383.73941
+  tps: 1692.45498
  }
 }
 dps_results: {
@@ -1989,43 +1989,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 25775.50672
-  tps: 18300.60977
+  dps: 25439.18557
+  tps: 18061.82176
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5087.70125
-  tps: 3612.26789
+  dps: 5052.03546
+  tps: 3586.94518
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6179.0402
-  tps: 4387.11854
+  dps: 5894.33138
+  tps: 4184.97528
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 15142.98192
-  tps: 10751.51716
+  dps: 14939.32565
+  tps: 10606.92121
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2550.98882
-  tps: 1811.20206
+  dps: 2539.17634
+  tps: 1802.8152
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Deadly-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2689.5647
-  tps: 1909.59093
+  dps: 2635.71107
+  tps: 1871.35486
  }
 }
 dps_results: {
@@ -2157,43 +2157,43 @@ dps_results: {
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-LongMultiTarget"
  value: {
-  dps: 19281.73013
-  tps: 13690.02839
+  dps: 18971.67938
+  tps: 13469.89236
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-LongSingleTarget"
  value: {
-  dps: 4255.39217
-  tps: 3021.32844
+  dps: 4220.37505
+  tps: 2996.46629
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 5352.90194
-  tps: 3800.56038
+  dps: 5084.05186
+  tps: 3609.67682
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-LongMultiTarget"
  value: {
-  dps: 11144.86205
-  tps: 7912.85206
+  dps: 10943.42878
+  tps: 7769.83444
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2007.95517
-  tps: 1425.64817
+  dps: 1997.50981
+  tps: 1418.23196
  }
 }
 dps_results: {
  key: "TestCombat-Settings-Orc-p1_combat-MH Instant OH Instant-fan_aoe-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2196.75486
-  tps: 1559.69595
+  dps: 2147.90528
+  tps: 1525.01275
  }
 }
 dps_results: {
diff --git a/sim/shaman/enhancement/TestEnhancement.results b/sim/shaman/enhancement/TestEnhancement.results
index 28ef007254..c72deb447e 100644
--- a/sim/shaman/enhancement/TestEnhancement.results
+++ b/sim/shaman/enhancement/TestEnhancement.results
@@ -46,1453 +46,1453 @@ character_stats_results: {
 dps_results: {
  key: "TestEnhancement-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 7488.38178
-  tps: 4471.14087
+  dps: 7473.62711
+  tps: 4486.98496
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 7508.33354
-  tps: 4482.66852
+  dps: 7493.67413
+  tps: 4498.5665
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 7454.30839
-  tps: 4467.9134
+  dps: 7346.00201
+  tps: 4424.4531
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 7551.1502
-  tps: 4523.95606
+  dps: 7483.83142
+  tps: 4502.49884
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 7325.51947
-  tps: 4376.95395
-  hps: 95.31154
+  dps: 7311.07446
+  tps: 4393.1357
+  hps: 95.27757
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 7325.51947
-  tps: 4376.95395
-  hps: 95.31154
+  dps: 7311.07446
+  tps: 4393.1357
+  hps: 95.27757
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 7475.31026
-  tps: 4477.63467
+  dps: 7369.56511
+  tps: 4446.94008
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Beast-tamer'sShoulders-30892"
  value: {
-  dps: 7225.49395
-  tps: 4309.69546
+  dps: 7159.69031
+  tps: 4287.40809
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Bizuri'sTotemofShatteredIce-50458"
  value: {
-  dps: 7850.22382
-  tps: 4735.79827
+  dps: 7809.01135
+  tps: 4742.91513
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-BlackBruise-50035"
  value: {
-  dps: 7408.99601
-  tps: 4486.81619
+  dps: 7374.11885
+  tps: 4494.75561
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-BlackBruise-50692"
  value: {
-  dps: 7467.32762
-  tps: 4529.60019
+  dps: 7444.47342
+  tps: 4543.98809
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-BlessedGarboftheUndeadSlayer"
  value: {
-  dps: 6078.33371
-  tps: 3593.5615
+  dps: 6068.11402
+  tps: 3613.64186
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 6037.69839
-  tps: 3568.06345
+  dps: 5987.99364
+  tps: 3553.7404
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 7477.31137
-  tps: 4394.33877
+  dps: 7368.87371
+  tps: 4351.65413
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Bryntroll,theBoneArbiter-50415"
  value: {
-  dps: 7635.18572
-  tps: 4587.17383
+  dps: 7514.92387
+  tps: 4535.31429
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Bryntroll,theBoneArbiter-50709"
  value: {
-  dps: 7635.18572
-  tps: 4587.17383
+  dps: 7514.92387
+  tps: 4535.31429
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 7649.96101
-  tps: 4595.0705
+  dps: 7517.5956
+  tps: 4541.24132
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 7326.04701
-  tps: 4377.34772
+  dps: 7310.51729
+  tps: 4392.75331
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 7326.04701
-  tps: 4377.34772
+  dps: 7310.51729
+  tps: 4392.75331
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 7326.04701
-  tps: 4377.34772
+  dps: 7310.52686
+  tps: 4392.75331
   hps: 64
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 7480.62028
-  tps: 4484.86315
+  dps: 7440.73677
+  tps: 4482.07352
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 7536.82052
-  tps: 4525.80141
+  dps: 7463.71029
+  tps: 4500.08788
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 7567.771
-  tps: 4527.38423
+  dps: 7501.11996
+  tps: 4512.2353
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-DeadlyGladiator'sTotemofSurvival-42602"
  value: {
-  dps: 7605.77405
-  tps: 4560.46893
+  dps: 7533.22744
+  tps: 4546.50828
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Death'sChoice-47464"
  value: {
-  dps: 7748.22319
-  tps: 4642.15728
+  dps: 7666.13304
+  tps: 4612.98322
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 7439.44615
-  tps: 4452.1961
+  dps: 7386.26048
+  tps: 4446.99909
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 7684.63476
-  tps: 4600.66827
+  dps: 7647.62612
+  tps: 4612.82201
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 7721.15526
-  tps: 4614.3344
+  dps: 7665.11029
+  tps: 4624.98575
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Defender'sCode-40257"
  value: {
-  dps: 7326.04039
-  tps: 4377.34772
+  dps: 7310.51899
+  tps: 4392.75331
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 7489.31743
-  tps: 4490.14626
+  dps: 7367.83375
+  tps: 4442.52403
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-DislodgedForeignObject-50348"
  value: {
-  dps: 7837.08279
-  tps: 4702.31422
+  dps: 7768.88608
+  tps: 4713.91149
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 7788.78419
-  tps: 4672.36168
+  dps: 7710.46663
+  tps: 4670.88412
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-EarthshatterBattlegear"
  value: {
-  dps: 6768.69336
-  tps: 4016.83039
+  dps: 6790.91636
+  tps: 4060.30955
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-EarthshatterGarb"
  value: {
-  dps: 6439.2724
-  tps: 3775.26162
+  dps: 6411.15303
+  tps: 3792.23839
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 7454.30839
-  tps: 4467.9134
+  dps: 7346.00201
+  tps: 4424.4531
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 7494.90311
-  tps: 4490.38641
+  dps: 7398.33067
+  tps: 4463.95773
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 7492.09984
-  tps: 4490.98333
+  dps: 7364.28305
+  tps: 4440.3837
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 7486.19024
-  tps: 4487.04024
+  dps: 7361.70024
+  tps: 4438.28352
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 7487.40223
-  tps: 4485.81416
+  dps: 7432.50027
+  tps: 4479.01577
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 7326.04701
-  tps: 4377.34772
+  dps: 7310.51729
+  tps: 4392.75331
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 7454.30839
-  tps: 4467.9134
+  dps: 7346.00201
+  tps: 4424.4531
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 7527.85274
-  tps: 4512.88071
+  dps: 7449.0404
+  tps: 4487.33614
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 7566.43894
-  tps: 4529.09878
+  dps: 7535.20879
+  tps: 4536.34072
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 7413.90482
-  tps: 4429.84018
+  dps: 7349.67101
+  tps: 4419.81447
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 7426.7127
-  tps: 4435.50995
+  dps: 7411.6636
+  tps: 4451.18746
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-ForgeEmber-37660"
  value: {
-  dps: 7508.3818
-  tps: 4480.52349
+  dps: 7446.75936
+  tps: 4482.46708
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 7477.31137
-  tps: 4481.24083
+  dps: 7368.87371
+  tps: 4437.68613
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 7472.71077
-  tps: 4478.57534
+  dps: 7364.29937
+  tps: 4435.03952
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-FrostWitch'sBattlegear"
  value: {
-  dps: 8289.67868
-  tps: 4957.34432
+  dps: 8292.39442
+  tps: 5005.20533
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-FrostWitch'sRegalia"
  value: {
-  dps: 7857.35684
-  tps: 4666.28332
+  dps: 7786.14051
+  tps: 4653.95555
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-FuriousGladiator'sTotemofSurvival-42603"
  value: {
-  dps: 7618.64095
-  tps: 4567.92265
+  dps: 7544.46386
+  tps: 4553.89617
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 7541.64458
-  tps: 4511.40204
+  dps: 7504.73215
+  tps: 4517.74575
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-FuturesightRune-38763"
  value: {
-  dps: 7382.27072
-  tps: 4409.83473
+  dps: 7367.01513
+  tps: 4425.3922
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Gladiator'sEarthshaker"
  value: {
-  dps: 7079.86321
-  tps: 4194.74302
+  dps: 7052.82376
+  tps: 4197.98938
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Gladiator'sWartide"
  value: {
-  dps: 6225.70802
-  tps: 3614.913
+  dps: 6106.41819
+  tps: 3560.73425
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 7498.35368
-  tps: 4476.90469
+  dps: 7483.65725
+  tps: 4492.77573
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 7521.02613
-  tps: 4490.00429
+  dps: 7506.43795
+  tps: 4505.93657
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 7517.24951
-  tps: 4510.94307
+  dps: 7479.35095
+  tps: 4505.02624
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-HatefulGladiator'sTotemofSurvival-42601"
  value: {
-  dps: 7581.74793
-  tps: 4545.65553
+  dps: 7511.21886
+  tps: 4531.78909
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Heartpierce-49982"
  value: {
-  dps: 7635.18572
-  tps: 4587.17383
+  dps: 7514.92387
+  tps: 4535.31429
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Heartpierce-50641"
  value: {
-  dps: 7635.18572
-  tps: 4587.17383
+  dps: 7514.92387
+  tps: 4535.31429
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
-  dps: 7495.13054
-  tps: 4477.927
+  dps: 7463.92839
+  tps: 4493.31303
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 7492.09984
-  tps: 4490.98333
+  dps: 7364.28305
+  tps: 4440.3837
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 7486.19024
-  tps: 4487.04024
+  dps: 7361.70024
+  tps: 4438.28352
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-IncisorFragment-37723"
  value: {
-  dps: 7467.3505
-  tps: 4467.95531
+  dps: 7442.21357
+  tps: 4477.08692
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 7472.58982
-  tps: 4472.94689
+  dps: 7373.84111
+  tps: 4441.65494
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 7479.29241
-  tps: 4483.20242
+  dps: 7373.58906
+  tps: 4440.37085
   hps: 11.01615
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-LastWord-50179"
  value: {
-  dps: 7635.18572
-  tps: 4587.17383
+  dps: 7514.92387
+  tps: 4535.31429
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-LastWord-50708"
  value: {
-  dps: 7635.18572
-  tps: 4587.17383
+  dps: 7514.92387
+  tps: 4535.31429
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 7326.04039
-  tps: 4377.34772
+  dps: 7310.51729
+  tps: 4392.75331
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 7326.04701
-  tps: 4377.34772
+  dps: 7310.51729
+  tps: 4392.75331
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 7453.37936
-  tps: 4454.31711
+  dps: 7430.17249
+  tps: 4467.08842
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Nibelung-49992"
  value: {
-  dps: 7635.18572
-  tps: 4587.17383
+  dps: 7514.92387
+  tps: 4535.31429
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Nibelung-50648"
  value: {
-  dps: 7635.18572
-  tps: 4587.17383
+  dps: 7514.92387
+  tps: 4535.31429
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 7326.04039
-  tps: 4377.34772
+  dps: 7310.51729
+  tps: 4392.75331
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 7476.53862
-  tps: 4481.57701
+  dps: 7369.45757
+  tps: 4438.06891
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 7479.29241
-  tps: 4483.20242
+  dps: 7373.58906
+  tps: 4440.37085
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 7326.04996
-  tps: 4377.34772
+  dps: 7310.51729
+  tps: 4392.75331
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 7326.04701
-  tps: 4377.34772
+  dps: 7310.51729
+  tps: 4392.75331
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 7326.04701
-  tps: 4377.34772
+  dps: 7310.51729
+  tps: 4392.75331
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 7454.30839
-  tps: 4467.9134
+  dps: 7346.00201
+  tps: 4424.4531
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 7454.30839
-  tps: 4467.9134
+  dps: 7346.00201
+  tps: 4424.4531
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 7326.04701
-  tps: 4377.34772
+  dps: 7310.51729
+  tps: 4392.75331
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 7751.32915
-  tps: 4658.15276
+  dps: 7705.61896
+  tps: 4647.09065
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 7801.95946
-  tps: 4691.59031
+  dps: 7755.80612
+  tps: 4680.20393
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 7635.18572
-  tps: 4587.17383
+  dps: 7514.92387
+  tps: 4535.31429
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-RelentlessGladiator'sTotemofSurvival-42604"
  value: {
-  dps: 7634.26504
-  tps: 4576.97358
+  dps: 7558.10809
+  tps: 4562.86718
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 7437.4123
-  tps: 4452.94097
+  dps: 7345.32488
+  tps: 4428.24202
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 7326.04039
-  tps: 4377.34772
+  dps: 7310.51899
+  tps: 4392.75331
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-SavageGladiator'sTotemofSurvival-42594"
  value: {
-  dps: 7575.24551
-  tps: 4542.03373
+  dps: 7505.70742
+  tps: 4528.20518
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 7326.04039
-  tps: 4377.34772
+  dps: 7310.51899
+  tps: 4392.75331
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Shadowmourne-49623"
  value: {
-  dps: 7635.18572
-  tps: 4587.17383
+  dps: 7514.92387
+  tps: 4535.31429
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 7326.04701
-  tps: 4377.34772
+  dps: 7310.51729
+  tps: 4392.75331
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 7326.04966
-  tps: 4377.34772
+  dps: 7310.51729
+  tps: 4392.75331
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-SkycallTotem-33506"
  value: {
-  dps: 7545.59324
-  tps: 4529.74767
+  dps: 7499.23724
+  tps: 4523.8858
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-SkyshatterHarness"
  value: {
-  dps: 5479.81786
-  tps: 3192.07373
+  dps: 5377.30907
+  tps: 3148.78494
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-SkyshatterRegalia"
  value: {
-  dps: 5353.82473
-  tps: 3091.78696
+  dps: 5328.79735
+  tps: 3108.14322
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 7516.4109
-  tps: 4494.16019
+  dps: 7432.94579
+  tps: 4464.48378
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 7534.10476
-  tps: 4507.56808
+  dps: 7459.91829
+  tps: 4485.43717
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-SoulPreserver-37111"
  value: {
-  dps: 7394.06437
-  tps: 4416.64653
+  dps: 7378.85939
+  tps: 4432.23584
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-SouloftheDead-40382"
  value: {
-  dps: 7488.25177
-  tps: 4482.96447
+  dps: 7423.00569
+  tps: 4469.89189
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-SparkofLife-37657"
  value: {
-  dps: 7457.55827
-  tps: 4462.34188
+  dps: 7425.72719
+  tps: 4466.47322
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 7516.72509
-  tps: 4483.3304
+  dps: 7487.72802
+  tps: 4492.83758
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Stonebreaker'sTotem-33507"
  value: {
-  dps: 7575.53117
-  tps: 4543.60715
+  dps: 7510.17313
+  tps: 4531.32076
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-StormshroudArmor"
  value: {
-  dps: 5699.86409
-  tps: 3340.55525
+  dps: 5640.69027
+  tps: 3328.95814
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 7479.29241
-  tps: 4483.20242
+  dps: 7373.58906
+  tps: 4440.37085
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 7476.53862
-  tps: 4481.57701
+  dps: 7369.45757
+  tps: 4438.06891
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 7467.385
-  tps: 4475.9521
+  dps: 7360.61396
+  tps: 4432.53129
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 7326.04304
-  tps: 4377.34772
+  dps: 7310.51899
+  tps: 4392.75331
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 7450.73558
-  tps: 4458.91097
+  dps: 7384.72736
+  tps: 4441.7823
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-TheFistsofFury"
  value: {
-  dps: 6540.77571
-  tps: 3888.80346
+  dps: 6490.46123
+  tps: 3890.07641
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 7326.04701
-  tps: 4377.34772
+  dps: 7310.51729
+  tps: 4392.75331
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Thrall'sBattlegear"
  value: {
-  dps: 7325.74284
-  tps: 4372.24836
+  dps: 7316.99807
+  tps: 4397.73496
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Thrall'sRegalia"
  value: {
-  dps: 6965.97053
-  tps: 4129.37226
+  dps: 6927.61904
+  tps: 4135.00625
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 7499.48909
-  tps: 4502.25812
+  dps: 7417.24688
+  tps: 4476.96946
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-TidefuryRaiment"
  value: {
-  dps: 5384.78965
-  tps: 3132.68235
+  dps: 5341.61716
+  tps: 3131.02818
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 7553.51272
-  tps: 4530.87655
+  dps: 7557.70262
+  tps: 4566.4818
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 7582.46255
-  tps: 4561.50757
+  dps: 7507.05358
+  tps: 4535.88636
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 7477.31137
-  tps: 4481.24083
+  dps: 7368.87371
+  tps: 4437.68613
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 7472.71077
-  tps: 4478.57534
+  dps: 7364.29937
+  tps: 4435.03952
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 7513.5459
-  tps: 4499.33652
+  dps: 7466.78591
+  tps: 4494.58726
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-TotemofElectrifyingWind-47666"
  value: {
-  dps: 7764.51652
-  tps: 4675.42464
+  dps: 7738.81881
+  tps: 4697.85822
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-TotemofQuakingEarth-47667"
  value: {
-  dps: 7676.40426
-  tps: 4603.89695
+  dps: 7612.07948
+  tps: 4597.09556
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-TotemoftheAvalanche-50463"
  value: {
-  dps: 7824.11343
-  tps: 4696.03853
+  dps: 7771.52696
+  tps: 4699.8162
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-TotemoftheElementalPlane-40708"
  value: {
-  dps: 7604.09352
-  tps: 4569.75049
+  dps: 7532.79546
+  tps: 4547.11215
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 7472.71077
-  tps: 4478.57534
+  dps: 7364.29937
+  tps: 4435.03952
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 7477.31137
-  tps: 4481.24083
+  dps: 7368.87371
+  tps: 4437.68613
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-UndeadSlayer'sBlessedArmor"
  value: {
-  dps: 6035.89435
-  tps: 3564.18686
+  dps: 5986.31819
+  tps: 3560.20524
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-Val'anyr,HammerofAncientKings-46017"
  value: {
-  dps: 7801.25237
-  tps: 4676.6669
+  dps: 7740.5687
+  tps: 4661.99781
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-WingedTalisman-37844"
  value: {
-  dps: 7423.62048
-  tps: 4416.6578
+  dps: 7409.1555
+  tps: 4432.57624
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-WorldbreakerBattlegear"
  value: {
-  dps: 7290.8706
-  tps: 4361.38972
+  dps: 7267.06856
+  tps: 4379.67499
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-WorldbreakerGarb"
  value: {
-  dps: 6980.85326
-  tps: 4135.00877
+  dps: 6954.483
+  tps: 4146.43269
  }
 }
 dps_results: {
  key: "TestEnhancement-AllItems-WrathfulGladiator'sTotemofSurvival-51513"
  value: {
-  dps: 7650.8082
-  tps: 4586.55693
+  dps: 7572.55491
+  tps: 4572.36589
  }
 }
 dps_results: {
  key: "TestEnhancement-Average-Default"
  value: {
-  dps: 7600.83868
-  tps: 4561.9259
+  dps: 7551.26338
+  tps: 4560.96266
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-default_ft-FullBuffs-LongMultiTarget"
  value: {
-  dps: 26186.16858
-  tps: 16106.84594
+  dps: 26077.94558
+  tps: 16183.27196
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-default_ft-FullBuffs-LongSingleTarget"
  value: {
-  dps: 7669.91337
-  tps: 4561.42622
+  dps: 7593.77146
+  tps: 4540.16784
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-default_ft-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 9574.68404
-  tps: 5036.32873
+  dps: 9568.76656
+  tps: 5009.87027
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-default_ft-NoBuffs-LongMultiTarget"
  value: {
-  dps: 13684.23297
-  tps: 9030.60783
+  dps: 13399.27666
+  tps: 8959.44778
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-default_ft-NoBuffs-LongSingleTarget"
  value: {
-  dps: 4003.19283
-  tps: 2340.75866
+  dps: 3979.21808
+  tps: 2354.00911
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-default_ft-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 5184.0533
-  tps: 2660.2416
+  dps: 5205.48107
+  tps: 2660.50992
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-default_wf-FullBuffs-LongMultiTarget"
  value: {
-  dps: 25622.96296
-  tps: 15671.56499
+  dps: 25457.87272
+  tps: 15797.21094
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-default_wf-FullBuffs-LongSingleTarget"
  value: {
-  dps: 7784.50899
-  tps: 4630.54117
+  dps: 7688.44885
+  tps: 4598.79125
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-default_wf-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 9652.83704
-  tps: 5090.78406
+  dps: 9647.51631
+  tps: 5053.88397
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-default_wf-NoBuffs-LongMultiTarget"
  value: {
-  dps: 13049.53241
-  tps: 8581.42761
+  dps: 12914.12299
+  tps: 8621.4819
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-default_wf-NoBuffs-LongSingleTarget"
  value: {
-  dps: 4042.05522
-  tps: 2367.85095
+  dps: 4029.71242
+  tps: 2385.73443
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-default_wf-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 5271.2156
-  tps: 2724.6169
+  dps: 5243.81734
+  tps: 2678.68179
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-phase_3-FullBuffs-LongMultiTarget"
  value: {
-  dps: 26133.29171
-  tps: 16058.07007
+  dps: 25743.35726
+  tps: 15961.23808
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-phase_3-FullBuffs-LongSingleTarget"
  value: {
-  dps: 7640.22094
-  tps: 4536.36977
+  dps: 7616.89185
+  tps: 4549.13163
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-phase_3-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 9609.50631
-  tps: 5070.27954
+  dps: 9507.62827
+  tps: 4968.53777
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-phase_3-NoBuffs-LongMultiTarget"
  value: {
-  dps: 13381.84078
-  tps: 8804.64771
+  dps: 13299.98095
+  tps: 8851.89346
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-phase_3-NoBuffs-LongSingleTarget"
  value: {
-  dps: 4001.65647
-  tps: 2339.19254
+  dps: 3958.89816
+  tps: 2336.15864
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-FT-phase_3-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 5240.91568
-  tps: 2718.50875
+  dps: 5279.82633
+  tps: 2705.3678
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_ft-FullBuffs-LongMultiTarget"
  value: {
-  dps: 21680.16437
-  tps: 13638.71484
+  dps: 4944.24822
+  tps: 4008.12786
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_ft-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6507.784
-  tps: 3861.80638
+  dps: 4944.24822
+  tps: 3210.89845
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_ft-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 8428.97103
-  tps: 4349.02986
+  dps: 6208.66309
+  tps: 3584.79758
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_ft-NoBuffs-LongMultiTarget"
  value: {
-  dps: 10610.24642
-  tps: 7579.48976
+  dps: 2440.10311
+  tps: 2574.46437
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_ft-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2968.93615
-  tps: 1778.93524
+  dps: 2440.10311
+  tps: 1639.91575
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_ft-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 3957.85135
-  tps: 2025.16372
+  dps: 2708.33712
+  tps: 1602.97462
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_wf-FullBuffs-LongMultiTarget"
  value: {
-  dps: 21032.76479
-  tps: 13230.12753
+  dps: 5091.26951
+  tps: 4166.23495
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_wf-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6576.80932
-  tps: 3913.78725
+  dps: 5091.26951
+  tps: 3317.31698
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_wf-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 8516.56232
-  tps: 4409.27557
+  dps: 6396.28053
+  tps: 3720.37032
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_wf-NoBuffs-LongMultiTarget"
  value: {
-  dps: 10063.51092
-  tps: 7218.51259
+  dps: 2568.28624
+  tps: 2810.23722
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_wf-NoBuffs-LongSingleTarget"
  value: {
-  dps: 3037.8618
-  tps: 1827.22131
+  dps: 2568.28624
+  tps: 1736.89243
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_wf-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 4047.1475
-  tps: 2089.28001
+  dps: 2834.49133
+  tps: 1691.14902
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-phase_3-FullBuffs-LongMultiTarget"
  value: {
-  dps: 21374.01664
-  tps: 13399.67835
+  dps: 4866.76982
+  tps: 3900.96896
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-phase_3-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6519.95465
-  tps: 3877.31261
+  dps: 4866.76982
+  tps: 3154.8846
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-phase_3-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 8411.63234
-  tps: 4372.2593
+  dps: 6155.11998
+  tps: 3552.33946
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-phase_3-NoBuffs-LongMultiTarget"
  value: {
-  dps: 10422.08541
-  tps: 7382.86132
+  dps: 2357.03434
+  tps: 2371.87381
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-phase_3-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2981.21683
-  tps: 1783.52357
+  dps: 2357.03434
+  tps: 1574.49498
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-phase_3-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 3991.83663
-  tps: 2064.13251
+  dps: 2620.98068
+  tps: 1541.92994
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-default_ft-FullBuffs-LongMultiTarget"
  value: {
-  dps: 25913.97202
-  tps: 16067.76099
+  dps: 25768.84208
+  tps: 16137.21693
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-default_ft-FullBuffs-LongSingleTarget"
  value: {
-  dps: 7635.18572
-  tps: 4587.17383
+  dps: 7514.92387
+  tps: 4535.31429
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-default_ft-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 9545.73434
-  tps: 5164.04585
+  dps: 9447.28966
+  tps: 5071.64701
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-default_ft-NoBuffs-LongMultiTarget"
  value: {
-  dps: 13359.69933
-  tps: 8949.04097
+  dps: 13155.51262
+  tps: 8891.92044
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-default_ft-NoBuffs-LongSingleTarget"
  value: {
-  dps: 3975.08621
-  tps: 2363.4439
+  dps: 3920.6067
+  tps: 2353.44766
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-default_ft-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 5122.08272
-  tps: 2715.87209
+  dps: 5121.77357
+  tps: 2698.80099
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-default_wf-FullBuffs-LongMultiTarget"
  value: {
-  dps: 25269.54423
-  tps: 15670.5321
+  dps: 25078.91942
+  tps: 15673.4403
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-default_wf-FullBuffs-LongSingleTarget"
  value: {
-  dps: 7725.30017
-  tps: 4646.1106
+  dps: 7654.6546
+  tps: 4639.04206
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-default_wf-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 9624.68112
-  tps: 5215.72835
+  dps: 9610.38768
+  tps: 5179.52556
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-default_wf-NoBuffs-LongMultiTarget"
  value: {
-  dps: 12859.48482
-  tps: 8553.43215
+  dps: 12538.4128
+  tps: 8449.78826
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-default_wf-NoBuffs-LongSingleTarget"
  value: {
-  dps: 4014.39743
-  tps: 2388.65633
+  dps: 3987.51408
+  tps: 2396.1965
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-default_wf-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 5198.75134
-  tps: 2761.36177
+  dps: 5236.52106
+  tps: 2780.66198
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-phase_3-FullBuffs-LongMultiTarget"
  value: {
-  dps: 25703.26
-  tps: 15982.49007
+  dps: 25540.59993
+  tps: 15991.23922
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-phase_3-FullBuffs-LongSingleTarget"
  value: {
-  dps: 7622.92899
-  tps: 4581.17305
+  dps: 7575.70226
+  tps: 4576.6514
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-phase_3-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 9438.30258
-  tps: 5075.60119
+  dps: 9558.20946
+  tps: 5142.99358
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-phase_3-NoBuffs-LongMultiTarget"
  value: {
-  dps: 13316.59458
-  tps: 8887.90176
+  dps: 12906.59189
+  tps: 8691.88066
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-phase_3-NoBuffs-LongSingleTarget"
  value: {
-  dps: 3954.93812
-  tps: 2342.72141
+  dps: 3901.46747
+  tps: 2334.1986
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-FT-phase_3-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 5131.35099
-  tps: 2723.9177
+  dps: 5177.22241
+  tps: 2729.37239
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_ft-FullBuffs-LongMultiTarget"
  value: {
-  dps: 21384.38777
-  tps: 13548.39071
+  dps: 4987.1598
+  tps: 4061.9771
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_ft-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6514.51764
-  tps: 3900.36173
+  dps: 4987.1598
+  tps: 3258.62378
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_ft-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 8379.37137
-  tps: 4403.48572
+  dps: 6308.24779
+  tps: 3709.32128
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_ft-NoBuffs-LongMultiTarget"
  value: {
-  dps: 10416.80384
-  tps: 7502.68723
+  dps: 2444.83649
+  tps: 2561.92902
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_ft-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2955.47922
-  tps: 1785.17477
+  dps: 2444.83649
+  tps: 1648.29155
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_ft-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 3959.33256
-  tps: 2076.57379
+  dps: 2711.86435
+  tps: 1621.83785
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_wf-FullBuffs-LongMultiTarget"
  value: {
-  dps: 20990.67972
-  tps: 13266.53791
+  dps: 5115.23319
+  tps: 4209.88967
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_wf-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6554.30878
-  tps: 3931.71223
+  dps: 5115.23319
+  tps: 3351.39836
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_wf-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 8471.25083
-  tps: 4465.71853
+  dps: 6447.86216
+  tps: 3809.26534
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_wf-NoBuffs-LongMultiTarget"
  value: {
-  dps: 9914.00758
-  tps: 7151.38029
+  dps: 2558.15125
+  tps: 2852.05761
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_wf-NoBuffs-LongSingleTarget"
  value: {
-  dps: 3022.61476
-  tps: 1832.60228
+  dps: 2558.15125
+  tps: 1737.43083
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_wf-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 3986.52439
-  tps: 2093.66433
+  dps: 2841.24541
+  tps: 1712.38615
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-phase_3-FullBuffs-LongMultiTarget"
  value: {
-  dps: 21260.44814
-  tps: 13290.20528
+  dps: 4868.01714
+  tps: 3921.40877
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-phase_3-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6449.72884
-  tps: 3857.26454
+  dps: 4868.01714
+  tps: 3172.16547
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-phase_3-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 8333.66807
-  tps: 4388.32522
+  dps: 6181.81733
+  tps: 3618.34966
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-phase_3-NoBuffs-LongMultiTarget"
  value: {
-  dps: 10399.48238
-  tps: 7461.54358
+  dps: 2362.5928
+  tps: 2365.21563
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-phase_3-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2937.7638
-  tps: 1776.65144
+  dps: 2362.5928
+  tps: 1583.3199
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-phase_3-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 3993.04766
-  tps: 2107.34847
+  dps: 2633.31988
+  tps: 1567.36545
  }
 }
 dps_results: {
  key: "TestEnhancement-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 7267.23608
-  tps: 4331.58151
+  dps: 7188.35082
+  tps: 4308.63993
  }
 }
diff --git a/sim/shaman/fire_elemental_totem.go b/sim/shaman/fire_elemental_totem.go
index c7665c4646..893fc5ab8c 100644
--- a/sim/shaman/fire_elemental_totem.go
+++ b/sim/shaman/fire_elemental_totem.go
@@ -41,9 +41,7 @@ func (shaman *Shaman) registerFireElementalTotem() {
 		ApplyEffects: func(sim *core.Simulation, target *core.Unit, _ *core.Spell) {
 			// TODO: ToW needs a unique buff/debuff aura for each raidmember/target.
 			//  Otherwise we will be possibly disabling another ele shaman's ToW debuff/buff.
-			if shaman.Totems.Fire == proto.FireTotem_TotemOfWrath {
-				shaman.TotemExpirations[FireTotem] = sim.CurrentTime + fireTotemDuration
-			} else if shaman.Totems.Fire != proto.FireTotem_NoFireTotem && !shaman.Totems.UseFireMcd {
+			if shaman.Totems.Fire != proto.FireTotem_NoFireTotem {
 				shaman.TotemExpirations[FireTotem] = sim.CurrentTime + fireTotemDuration
 			}
 
diff --git a/sim/shaman/restoration/TestRestoration.results b/sim/shaman/restoration/TestRestoration.results
index e6a1571e31..b35c797e1a 100644
--- a/sim/shaman/restoration/TestRestoration.results
+++ b/sim/shaman/restoration/TestRestoration.results
@@ -45,852 +45,573 @@ character_stats_results: {
 }
 dps_results: {
  key: "TestRestoration-AllItems-Althor'sAbacus-50359"
- value: {
-  dps: 438.74262
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Althor'sAbacus-50366"
- value: {
-  dps: 440.81894
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-AustereEarthsiegeDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Bandit'sInsignia-40371"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 421.75678
-  hps: 93.56546
+  hps: 93.75404
  }
 }
 dps_results: {
  key: "TestRestoration-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 421.75678
-  hps: 93.56546
+  hps: 93.75404
  }
 }
 dps_results: {
  key: "TestRestoration-AllItems-BeamingEarthsiegeDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Beast-tamer'sShoulders-30892"
- value: {
-  dps: 417.58166
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Bizuri'sTotemofShatteredIce-50458"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-BlackBruise-50035"
- value: {
-  dps: 374.4883
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-BlackBruise-50692"
- value: {
-  dps: 374.4883
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-BlessedGarboftheUndeadSlayer"
- value: {
-  dps: 372.35338
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-BlessedRegaliaofUndeadCleansing"
- value: {
-  dps: 388.58783
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-BracingEarthsiegeDiamond"
- value: {
-  dps: 432.77723
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Bryntroll,theBoneArbiter-50415"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Bryntroll,theBoneArbiter-50709"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-ChaoticSkyflareDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-CorpseTongueCoin-50349"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-CorpseTongueCoin-50352"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 421.84891
   hps: 64
  }
 }
 dps_results: {
  key: "TestRestoration-AllItems-DarkmoonCard:Berserker!-42989"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-DarkmoonCard:Death-42990"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-DarkmoonCard:Greatness-44255"
- value: {
-  dps: 425.45728
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-DeadlyGladiator'sTotemofSurvival-42602"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Death'sChoice-47464"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-DeathKnight'sAnguish-38212"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Deathbringer'sWill-50362"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Deathbringer'sWill-50363"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Defender'sCode-40257"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-DestructiveSkyflareDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-DislodgedForeignObject-50348"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-DislodgedForeignObject-50353"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-EarthshatterBattlegear"
- value: {
-  dps: 384.11985
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-EarthshatterGarb"
- value: {
-  dps: 433.095
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-EffulgentSkyflareDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-EmberSkyflareDiamond"
- value: {
-  dps: 433.46002
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-EnigmaticSkyflareDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-EnigmaticStarflareDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-EphemeralSnowflake-50260"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-EssenceofGossamer-37220"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-EternalEarthsiegeDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-ExtractofNecromanticPower-40373"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-EyeoftheBroodmother-45308"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Figurine-SapphireOwl-42413"
- value: {
-  dps: 423.67646
-  tps: 0.975
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-ForethoughtTalisman-40258"
- value: {
-  dps: 432.32489
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-ForgeEmber-37660"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-ForlornSkyflareDiamond"
- value: {
-  dps: 432.77723
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-ForlornStarflareDiamond"
- value: {
-  dps: 432.30534
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-FrostWitch'sBattlegear"
- value: {
-  dps: 395.23715
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-FrostWitch'sRegalia"
- value: {
-  dps: 497.92526
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-FuriousGladiator'sTotemofSurvival-42603"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-FuryoftheFiveFlights-40431"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-FuturesightRune-38763"
- value: {
-  dps: 427.70036
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Gladiator'sEarthshaker"
- value: {
-  dps: 365.19514
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Gladiator'sWartide"
- value: {
-  dps: 454.19385
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-GlowingTwilightScale-54573"
- value: {
-  dps: 439.78078
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-GlowingTwilightScale-54589"
- value: {
-  dps: 442.14023
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 422.07575
-  dtps: 32.22786
+  dtps: 32.07764
  }
 }
 dps_results: {
  key: "TestRestoration-AllItems-HatefulGladiator'sTotemofSurvival-42601"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Heartpierce-49982"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Heartpierce-50641"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-IllustrationoftheDragonSoul-40432"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-ImpassiveSkyflareDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-ImpassiveStarflareDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-IncisorFragment-37723"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-InsightfulEarthsiegeDiamond"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-InvigoratingEarthsiegeDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-LastWord-50179"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-LastWord-50708"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Lavanthor'sTalisman-37872"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-MajesticDragonFigurine-40430"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-MeteoriteWhetstone-37390"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-NevermeltingIceCrystal-50259"
- value: {
-  dps: 432.32489
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Nibelung-49992"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Nibelung-50648"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-OfferingofSacrifice-37638"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-PersistentEarthshatterDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-PersistentEarthsiegeDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-PetrifiedScarab-21685"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-PetrifiedTwilightScale-54571"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-PetrifiedTwilightScale-54591"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-PowerfulEarthshatterDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-PowerfulEarthsiegeDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-PurifiedShardoftheGods"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-ReignoftheDead-47316"
- value: {
-  dps: 436.00565
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-ReignoftheDead-47477"
- value: {
-  dps: 437.70446
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-RelentlessEarthsiegeDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-RelentlessGladiator'sTotemofSurvival-42604"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-RevitalizingSkyflareDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-RuneofRepulsion-40372"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-SavageGladiator'sTotemofSurvival-42594"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-SealofthePantheon-36993"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Shadowmourne-49623"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-ShinyShardoftheGods"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Sindragosa'sFlawlessFang-50361"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-SkycallTotem-33506"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-SkyshatterHarness"
- value: {
-  dps: 338.77387
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-SkyshatterRegalia"
- value: {
-  dps: 382.87779
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-SliverofPureIce-50339"
- value: {
-  dps: 436.76067
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-SliverofPureIce-50346"
- value: {
-  dps: 438.64824
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-SouloftheDead-40382"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-SparkofLife-37657"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-SphereofRedDragon'sBlood-37166"
- value: {
-  dps: 432.60699
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Stonebreaker'sTotem-33507"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-StormshroudArmor"
- value: {
-  dps: 368.87888
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-SwiftSkyflareDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-SwiftStarflareDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-SwiftWindfireDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-TalismanofTrollDivinity-37734"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-TearsoftheVanquished-47215"
- value: {
-  dps: 425.26267
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-TheFistsofFury"
- value: {
-  dps: 379.15057
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-TheGeneral'sHeart-45507"
- value: {
-  dps: 421.84891
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Thrall'sBattlegear"
- value: {
-  dps: 393.85565
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Thrall'sRegalia"
- value: {
-  dps: 461.60137
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-ThunderingSkyflareDiamond"
- value: {
-  dps: 430.41777
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-TidefuryRaiment"
- value: {
-  dps: 370.22169
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-TinyAbominationinaJar-50351"
- value: {
-  dps: 439.95675
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-TinyAbominationinaJar-50706"
- value: {
-  dps: 439.95675
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-TirelessSkyflareDiamond"
- value: {
-  dps: 432.77723
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-TirelessStarflareDiamond"
- value: {
-  dps: 432.30534
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-TomeofArcanePhenomena-36972"
- value: {
-  dps: 428.26663
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-TotemofElectrifyingWind-47666"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-TotemofQuakingEarth-47667"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-TotemoftheAvalanche-50463"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-TotemoftheElementalPlane-40708"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-TrenchantEarthshatterDiamond"
- value: {
-  dps: 432.30534
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-TrenchantEarthsiegeDiamond"
- value: {
-  dps: 432.77723
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-UndeadSlayer'sBlessedArmor"
- value: {
-  dps: 368.87888
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-Val'anyr,HammerofAncientKings-46017"
- value: {
-  dps: 442.98141
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-WingedTalisman-37844"
- value: {
-  dps: 454.50379
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-WorldbreakerBattlegear"
- value: {
-  dps: 389.7915
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-WorldbreakerGarb"
- value: {
-  dps: 456.87971
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-AllItems-WrathfulGladiator'sTotemofSurvival-51513"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-Average-Default"
- value: {
-  dps: 433.28018
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-Settings-Troll-p1-Standard-Default-FullBuffs-LongMultiTarget"
- value: {
-  dps: 1991.73894
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-Settings-Troll-p1-Standard-Default-FullBuffs-LongSingleTarget"
- value: {
-  dps: 431.09798
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-Settings-Troll-p1-Standard-Default-FullBuffs-ShortSingleTarget"
- value: {
-  dps: 1119.20682
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-Settings-Troll-p1-Standard-Default-NoBuffs-LongMultiTarget"
- value: {
-  dps: 1513.25039
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-Settings-Troll-p1-Standard-Default-NoBuffs-LongSingleTarget"
- value: {
-  dps: 317.58927
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-Settings-Troll-p1-Standard-Default-NoBuffs-ShortSingleTarget"
- value: {
-  dps: 832.65007
- }
+ value: {}
 }
 dps_results: {
  key: "TestRestoration-SwitchInFrontOfTarget-Default"
- value: {
-  dps: 394.56405
- }
+ value: {}
 }
diff --git a/sim/warlock/TestDemonology.results b/sim/warlock/TestDemonology.results
index 2ae42ae9aa..536e7e6fb0 100644
--- a/sim/warlock/TestDemonology.results
+++ b/sim/warlock/TestDemonology.results
@@ -46,704 +46,704 @@ character_stats_results: {
 dps_results: {
  key: "TestDemonology-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 13243.90588
-  tps: 11492.86523
+  dps: 11029.86291
+  tps: 10767.28533
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 13278.4155
-  tps: 11523.00644
+  dps: 11055.04985
+  tps: 10791.31034
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-AshtongueTalismanofShadows-32493"
  value: {
-  dps: 13074.67441
-  tps: 11336.3098
+  dps: 10875.72599
+  tps: 10616.72071
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 13418.79874
-  tps: 11655.82175
+  dps: 11073.18055
+  tps: 10814.91661
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 12896.06082
-  tps: 11177.10371
-  hps: 100.95364
+  dps: 10722.16856
+  tps: 10465.85596
+  hps: 101.80272
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 12896.06082
-  tps: 11177.10371
-  hps: 100.95364
+  dps: 10722.16856
+  tps: 10465.85596
+  hps: 101.80272
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 13401.77362
-  tps: 11638.54725
+  dps: 11099.41588
+  tps: 10841.25543
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 9817.47634
-  tps: 8342.96725
+  dps: 8005.70237
+  tps: 7802.7555
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 13449.10871
-  tps: 11449.20433
+  dps: 11119.66976
+  tps: 10645.99435
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 13766.09428
-  tps: 12003.46809
+  dps: 11410.3423
+  tps: 11152.09819
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 12950.07749
-  tps: 11226.26804
+  dps: 10770.42571
+  tps: 10513.39859
   hps: 64
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DarkCoven'sRegalia"
  value: {
-  dps: 12601.16483
-  tps: 10909.94106
+  dps: 10425.18922
+  tps: 10175.94046
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 13069.14213
-  tps: 11349.19229
+  dps: 10864.3927
+  tps: 10608.00856
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 13108.2148
-  tps: 11388.41437
+  dps: 10914.04201
+  tps: 10657.69731
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 12992.95845
-  tps: 11268.3201
+  dps: 10827.49237
+  tps: 10566.12402
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Death'sChoice-47464"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.49094
+  tps: 10464.1068
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 12949.96635
-  tps: 11230.01651
+  dps: 10768.14946
+  tps: 10511.76532
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DeathbringerGarb"
  value: {
-  dps: 10738.51563
-  tps: 9156.02028
+  dps: 8750.45172
+  tps: 8513.7745
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Defender'sCode-40257"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 13439.12732
-  tps: 11676.50114
+  dps: 11104.72076
+  tps: 10846.47665
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 13680.51811
-  tps: 11918.02187
+  dps: 11327.35486
+  tps: 11069.91946
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 13418.79874
-  tps: 11655.82175
+  dps: 11073.18055
+  tps: 10814.91661
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 13453.51023
-  tps: 11686.79689
+  dps: 11134.3961
+  tps: 10875.5855
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 13432.54537
-  tps: 11669.91919
+  dps: 11100.49894
+  tps: 10842.25483
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 13424.03982
-  tps: 11661.41364
+  dps: 11092.43436
+  tps: 10834.19025
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 13053.22335
-  tps: 11338.404
+  dps: 10879.10858
+  tps: 10625.58514
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 12906.6269
-  tps: 11185.18743
+  dps: 10742.56294
+  tps: 10485.89317
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 13398.47567
-  tps: 11635.84948
+  dps: 11067.81225
+  tps: 10809.56813
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 13122.05364
-  tps: 11402.52929
+  dps: 10953.30393
+  tps: 10696.91978
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 13283.42777
-  tps: 11540.1986
+  dps: 11069.28
+  tps: 10808.5171
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 12918.53373
-  tps: 11201.64113
+  dps: 10754.22395
+  tps: 10497.0465
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 13107.02913
-  tps: 11366.94101
+  dps: 10918.00416
+  tps: 10657.22787
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ForgeEmber-37660"
  value: {
-  dps: 13248.07367
-  tps: 11508.02234
+  dps: 10966.72513
+  tps: 10707.20814
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 13449.10871
-  tps: 11680.5325
+  dps: 11119.66976
+  tps: 10860.98248
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 13453.88096
-  tps: 11686.13342
+  dps: 11105.65831
+  tps: 10847.10119
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-FuturesightRune-38763"
  value: {
-  dps: 13095.96384
-  tps: 11358.30044
+  dps: 10877.79011
+  tps: 10618.64538
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Gladiator'sFelshroud"
  value: {
-  dps: 10560.32217
-  tps: 8973.36025
+  dps: 8575.50793
+  tps: 8343.43479
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 13266.94683
-  tps: 11513.37249
+  dps: 11034.67418
+  tps: 10771.5057
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 13309.51349
-  tps: 11550.47562
+  dps: 11080.46799
+  tps: 10816.04753
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 13062.08411
-  tps: 11343.78646
+  dps: 10896.47429
+  tps: 10640.15174
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Gul'dan'sRegalia"
  value: {
-  dps: 11045.2536
-  tps: 9314.45004
+  dps: 8893.35742
+  tps: 8648.53034
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
-  dps: 13271.82086
-  tps: 11517.03807
+  dps: 11019.97936
+  tps: 10757.4527
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 13432.54537
-  tps: 11669.91919
+  dps: 11100.49894
+  tps: 10842.25483
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 13424.03982
-  tps: 11661.41364
+  dps: 11092.43436
+  tps: 10834.19025
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-IncisorFragment-37723"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 13427.78541
-  tps: 11661.40926
+  dps: 11111.06498
+  tps: 10852.30362
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 13398.47567
-  tps: 11635.84948
+  dps: 11067.81225
+  tps: 10809.56813
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 13093.97928
-  tps: 11353.32214
+  dps: 10912.86436
+  tps: 10652.26582
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-MaleficRaiment"
  value: {
-  dps: 8418.28288
-  tps: 7041.57676
+  dps: 6854.13966
+  tps: 6659.45182
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 13020.35249
-  tps: 11300.40265
+  dps: 10825.62748
+  tps: 10569.24333
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 13171.60746
-  tps: 11431.51935
+  dps: 10983.322
+  tps: 10722.54571
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 13398.47567
-  tps: 11635.84948
+  dps: 11067.81225
+  tps: 10809.56813
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 13398.47567
-  tps: 11635.84948
+  dps: 11067.81225
+  tps: 10809.56813
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PlagueheartGarb"
  value: {
-  dps: 10266.66359
-  tps: 8716.63581
+  dps: 8326.41706
+  tps: 8103.48409
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 13417.36058
-  tps: 11654.52072
+  dps: 11071.97708
+  tps: 10813.72807
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 13418.79874
-  tps: 11655.82175
+  dps: 11073.18055
+  tps: 10814.91661
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 13407.68508
-  tps: 11661.54729
+  dps: 11182.591
+  tps: 10920.47378
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 13469.23514
-  tps: 11720.19531
+  dps: 11238.80768
+  tps: 10976.33941
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 13728.9583
-  tps: 11966.33211
+  dps: 11374.7138
+  tps: 11116.46969
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 13405.02361
-  tps: 11639.40886
+  dps: 11055.59429
+  tps: 10797.48576
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 12950.06
-  tps: 11226.25054
+  dps: 10770.41707
+  tps: 10513.38995
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 13220.81763
-  tps: 11472.0353
+  dps: 11018.87465
+  tps: 10759.96064
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 13239.16958
-  tps: 11485.33592
+  dps: 11045.70793
+  tps: 10785.47065
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SoulPreserver-37111"
  value: {
-  dps: 13041.95765
-  tps: 11308.77052
+  dps: 10853.08201
+  tps: 10594.90656
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SouloftheDead-40382"
  value: {
-  dps: 13069.23424
-  tps: 11351.11117
+  dps: 10904.13536
+  tps: 10648.91037
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SparkofLife-37657"
  value: {
-  dps: 13064.67402
-  tps: 11346.30818
+  dps: 10875.52441
+  tps: 10614.81598
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 12920.45511
-  tps: 11186.49878
+  dps: 10727.3943
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 13398.47567
-  tps: 11635.84948
+  dps: 11067.81225
+  tps: 10809.56813
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 13398.47567
-  tps: 11635.84948
+  dps: 11067.81225
+  tps: 10809.56813
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 13398.47567
-  tps: 11635.84948
+  dps: 11067.81225
+  tps: 10809.56813
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 12984.04915
-  tps: 11256.55694
+  dps: 10798.36596
+  tps: 10541.0729
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 12936.43462
-  tps: 11217.1032
+  dps: 10776.36851
+  tps: 10515.78096
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 12900.43003
-  tps: 11180.48019
+  dps: 10720.55552
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 13398.47567
-  tps: 11635.84948
+  dps: 11067.81225
+  tps: 10809.56813
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 12931.5795
-  tps: 11189.2114
+  dps: 10729.22572
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 12931.5795
-  tps: 11189.2114
+  dps: 10729.22572
+  tps: 10464.17138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 13449.10871
-  tps: 11680.5325
+  dps: 11119.66976
+  tps: 10860.98248
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 13453.88096
-  tps: 11686.13342
+  dps: 11105.65831
+  tps: 10847.10119
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 13135.3056
-  tps: 11407.64705
+  dps: 10917.10386
+  tps: 10661.13027
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 13453.88096
-  tps: 11686.13342
+  dps: 11105.65831
+  tps: 10847.10119
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 13449.10871
-  tps: 11680.5325
+  dps: 11119.66976
+  tps: 10860.98248
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-WingedTalisman-37844"
  value: {
-  dps: 13090.00588
-  tps: 11354.54378
+  dps: 10843.78785
+  tps: 10585.1107
  }
 }
 dps_results: {
  key: "TestDemonology-Average-Default"
  value: {
-  dps: 13946.57103
-  tps: 12163.30576
+  dps: 11508.98797
+  tps: 11252.67784
  }
 }
 dps_results: {
  key: "TestDemonology-Settings-Orc-p4_demo-Demonology Warlock-demo-FullBuffs-LongMultiTarget"
  value: {
-  dps: 17361.12567
-  tps: 17435.98697
+  dps: 14569.48158
+  tps: 16422.33574
  }
 }
 dps_results: {
  key: "TestDemonology-Settings-Orc-p4_demo-Demonology Warlock-demo-FullBuffs-LongSingleTarget"
  value: {
-  dps: 13766.09428
-  tps: 12003.46809
+  dps: 11410.3423
+  tps: 11152.09819
  }
 }
 dps_results: {
@@ -756,15 +756,15 @@ dps_results: {
 dps_results: {
  key: "TestDemonology-Settings-Orc-p4_demo-Demonology Warlock-demo-NoBuffs-LongMultiTarget"
  value: {
-  dps: 10287.07914
-  tps: 12106.19892
+  dps: 6562.7132
+  tps: 8970.11008
  }
 }
 dps_results: {
  key: "TestDemonology-Settings-Orc-p4_demo-Demonology Warlock-demo-NoBuffs-LongSingleTarget"
  value: {
-  dps: 7943.25049
-  tps: 7314.42382
+  dps: 6266.27386
+  tps: 6225.81786
  }
 }
 dps_results: {
@@ -777,7 +777,7 @@ dps_results: {
 dps_results: {
  key: "TestDemonology-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 13685.59167
-  tps: 12068.09598
+  dps: 11359.99577
+  tps: 11138.46347
  }
 }
diff --git a/sim/warrior/berserker_rage.go b/sim/warrior/berserker_rage.go
index 9ee4fea03f..471862409d 100644
--- a/sim/warrior/berserker_rage.go
+++ b/sim/warrior/berserker_rage.go
@@ -31,7 +31,3 @@ func (warrior *Warrior) registerBerserkerRageSpell() {
 		},
 	})
 }
-
-func (warrior *Warrior) ShouldBerserkerRage(sim *core.Simulation) bool {
-	return warrior.Talents.ImprovedBerserkerRage > 0 && warrior.CurrentRage() < 80 && warrior.BerserkerRage.IsReady(sim)
-}
diff --git a/sim/warrior/bloodthirst.go b/sim/warrior/bloodthirst.go
index 6a88e10a46..19f50b5f25 100644
--- a/sim/warrior/bloodthirst.go
+++ b/sim/warrior/bloodthirst.go
@@ -46,11 +46,7 @@ func (warrior *Warrior) registerBloodthirstSpell(cdTimer *core.Timer) {
 			core.StartDelayedAction(sim, core.DelayedActionOptions{
 				DoAt: sim.CurrentTime + warrior.Bloodthirst.CD.Duration,
 				OnAction: func(_ *core.Simulation) {
-					if warrior.IsUsingAPL {
-						warrior.Rotation.DoNextAction(sim)
-					} else if warrior.Bloodthirst.CanCast(sim, target) {
-						warrior.Bloodthirst.Cast(sim, target)
-					}
+					warrior.Rotation.DoNextAction(sim)
 				},
 			})
 		},
diff --git a/sim/warrior/demoralizing_shout.go b/sim/warrior/demoralizing_shout.go
index 5645ab9eea..e6afa7f1ae 100644
--- a/sim/warrior/demoralizing_shout.go
+++ b/sim/warrior/demoralizing_shout.go
@@ -1,8 +1,6 @@
 package warrior
 
 import (
-	"time"
-
 	"github.com/wowsims/wotlk/sim/core"
 )
 
@@ -42,16 +40,3 @@ func (warrior *Warrior) registerDemoralizingShoutSpell() {
 		RelatedAuras: []core.AuraArray{warrior.DemoralizingShoutAuras},
 	})
 }
-
-func (warrior *Warrior) ShouldDemoralizingShout(sim *core.Simulation, target *core.Unit, filler bool, maintainOnly bool) bool {
-	if !warrior.DemoralizingShout.CanCast(sim, target) {
-		return false
-	}
-
-	if filler {
-		return true
-	}
-
-	return maintainOnly &&
-		warrior.DemoralizingShoutAuras.Get(target).ShouldRefreshExclusiveEffects(sim, time.Second*2)
-}
diff --git a/sim/warrior/dps/TestArms.results b/sim/warrior/dps/TestArms.results
index 8790407454..8c4f33cd79 100644
--- a/sim/warrior/dps/TestArms.results
+++ b/sim/warrior/dps/TestArms.results
@@ -46,935 +46,935 @@ character_stats_results: {
 dps_results: {
  key: "TestArms-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 6578.76707
-  tps: 5317.04783
+  dps: 6482.41744
+  tps: 5220.02072
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 6602.04105
-  tps: 5330.43346
+  dps: 6431.93917
+  tps: 5172.33986
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 6413.27476
-  tps: 5182.03283
+  dps: 6264.67869
+  tps: 5039.7388
   hps: 89.11518
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 6413.27476
-  tps: 5182.03283
+  dps: 6264.67869
+  tps: 5039.7388
   hps: 89.11518
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 6610.9396
-  tps: 5341.97634
+  dps: 6495.57286
+  tps: 5230.5847
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Beast-tamer'sShoulders-30892"
  value: {
-  dps: 6412.14717
-  tps: 5185.57512
+  dps: 6301.65452
+  tps: 5073.33708
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BlackBruise-50035"
  value: {
-  dps: 4866.62556
-  tps: 3976.19228
+  dps: 4742.24633
+  tps: 3856.45854
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BlackBruise-50692"
  value: {
-  dps: 5008.30845
-  tps: 4090.90401
+  dps: 4893.49698
+  tps: 3974.34796
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BlessedBattlegearofUndeadSlaying"
  value: {
-  dps: 5388.84594
-  tps: 4344.16473
+  dps: 5271.93662
+  tps: 4227.7173
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BlessedGarboftheUndeadSlayer"
  value: {
-  dps: 5315.57686
-  tps: 4281.49162
+  dps: 5222.59026
+  tps: 4193.75014
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 5097.37072
-  tps: 4108.59101
+  dps: 5026.17849
+  tps: 4038.78586
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 6588.02377
-  tps: 5218.54295
+  dps: 6478.84863
+  tps: 5112.88003
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Bryntroll,theBoneArbiter-50415"
  value: {
-  dps: 8625.42745
-  tps: 7032.99268
+  dps: 8016.6666
+  tps: 6494.27378
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Bryntroll,theBoneArbiter-50709"
  value: {
-  dps: 8730.89242
-  tps: 7119.83335
+  dps: 8109.90868
+  tps: 6570.52233
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 6756.72828
-  tps: 5463.2377
+  dps: 6616.85785
+  tps: 5325.93338
  }
 }
 dps_results: {
  key: "TestArms-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
   hps: 64
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 6538.00848
-  tps: 5284.59243
+  dps: 6374.41061
+  tps: 5130.23029
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 6564.50621
-  tps: 5304.87423
+  dps: 6412.14822
+  tps: 5159.52149
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Death'sChoice-47464"
  value: {
-  dps: 6918.19694
-  tps: 5580.28194
+  dps: 6726.16041
+  tps: 5407.81841
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 6469.83394
-  tps: 5227.78913
+  dps: 6335.28796
+  tps: 5097.65887
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 6843.76924
-  tps: 5525.74706
+  dps: 6665.69456
+  tps: 5361.54303
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 6960.67426
-  tps: 5623.80389
+  dps: 6724.67718
+  tps: 5407.8647
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Defender'sCode-40257"
  value: {
-  dps: 6422.85912
-  tps: 5185.94926
+  dps: 6274.81353
+  tps: 5047.61817
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 6620.04877
-  tps: 5349.00093
+  dps: 6501.0713
+  tps: 5235.03075
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DislodgedForeignObject-50348"
  value: {
-  dps: 6541.00488
-  tps: 5284.47045
+  dps: 6350.28984
+  tps: 5105.87855
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 6536.58567
-  tps: 5284.43361
+  dps: 6346.59736
+  tps: 5105.06065
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DreadnaughtBattlegear"
  value: {
-  dps: 6000.16096
-  tps: 4864.19104
+  dps: 5920.25992
+  tps: 4781.31769
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DreadnaughtPlate"
  value: {
-  dps: 5270.20256
-  tps: 4251.79768
+  dps: 5211.25695
+  tps: 4187.74189
  }
 }
 dps_results: {
  key: "TestArms-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 6588.02377
-  tps: 5324.94752
+  dps: 6478.84863
+  tps: 5217.20055
  }
 }
 dps_results: {
  key: "TestArms-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 6588.02377
-  tps: 5324.94752
+  dps: 6478.84863
+  tps: 5217.20055
  }
 }
 dps_results: {
  key: "TestArms-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 6610.9396
-  tps: 5341.97634
+  dps: 6495.57286
+  tps: 5230.5847
  }
 }
 dps_results: {
  key: "TestArms-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 6604.9531
-  tps: 5337.21903
+  dps: 6493.76947
+  tps: 5229.12812
  }
 }
 dps_results: {
  key: "TestArms-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 6462.73667
-  tps: 5216.73975
+  dps: 6306.94826
+  tps: 5070.34131
  }
 }
 dps_results: {
  key: "TestArms-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 6588.02377
-  tps: 5324.94752
+  dps: 6478.84863
+  tps: 5217.20055
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 6573.00582
-  tps: 5312.30228
+  dps: 6415.35315
+  tps: 5162.44931
  }
 }
 dps_results: {
  key: "TestArms-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 6518.02929
-  tps: 5267.89084
+  dps: 6364.14747
+  tps: 5121.48477
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ForgeEmber-37660"
  value: {
-  dps: 6482.54489
-  tps: 5238.39086
+  dps: 6348.70584
+  tps: 5108.3564
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 6588.02377
-  tps: 5324.94752
+  dps: 6478.84863
+  tps: 5217.20055
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 6588.02377
-  tps: 5324.94752
+  dps: 6478.84863
+  tps: 5217.20055
  }
 }
 dps_results: {
  key: "TestArms-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 6638.27271
-  tps: 5355.17518
+  dps: 6464.14941
+  tps: 5197.35269
  }
 }
 dps_results: {
  key: "TestArms-AllItems-FuturesightRune-38763"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Gladiator'sBattlegear"
  value: {
-  dps: 6391.56308
-  tps: 5139.96721
+  dps: 6213.38971
+  tps: 4971.99584
  }
 }
 dps_results: {
  key: "TestArms-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 6542.384
-  tps: 5287.07103
+  dps: 6390.97801
+  tps: 5142.58532
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Heartpierce-49982"
  value: {
-  dps: 7994.21671
-  tps: 6504.30258
+  dps: 7444.30414
+  tps: 6007.8764
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Heartpierce-50641"
  value: {
-  dps: 8082.17352
-  tps: 6572.49427
+  dps: 7475.93535
+  tps: 6035.59142
  }
 }
 dps_results: {
  key: "TestArms-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 6610.9396
-  tps: 5341.97634
+  dps: 6495.57286
+  tps: 5230.5847
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 6604.9531
-  tps: 5337.21903
+  dps: 6493.76947
+  tps: 5229.12812
  }
 }
 dps_results: {
  key: "TestArms-AllItems-IncisorFragment-37723"
  value: {
-  dps: 6619.05428
-  tps: 5343.00426
+  dps: 6455.69968
+  tps: 5193.01856
  }
 }
 dps_results: {
  key: "TestArms-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 6588.02377
-  tps: 5324.94752
+  dps: 6478.84863
+  tps: 5217.20055
  }
 }
 dps_results: {
  key: "TestArms-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 6614.63311
-  tps: 5350.04895
-  hps: 12.91576
+  dps: 6509.46429
+  tps: 5241.50367
+  hps: 13.00101
  }
 }
 dps_results: {
  key: "TestArms-AllItems-LastWord-50179"
  value: {
-  dps: 8092.06147
-  tps: 6585.99095
+  dps: 7563.0174
+  tps: 6106.2864
  }
 }
 dps_results: {
  key: "TestArms-AllItems-LastWord-50708"
  value: {
-  dps: 8207.14873
-  tps: 6678.32817
+  dps: 7618.9608
+  tps: 6151.49898
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 6576.42366
-  tps: 5311.90634
+  dps: 6433.64558
+  tps: 5170.07371
  }
 }
 dps_results: {
  key: "TestArms-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 6560.20246
-  tps: 5306.49729
+  dps: 6439.79417
+  tps: 5187.48571
  }
 }
 dps_results: {
  key: "TestArms-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 6427.44377
-  tps: 5190.8189
+  dps: 6269.28658
+  tps: 5043.25365
  }
 }
 dps_results: {
  key: "TestArms-AllItems-OnslaughtArmor"
  value: {
-  dps: 4275.89386
-  tps: 3454.96674
+  dps: 4249.68079
+  tps: 3420.45651
  }
 }
 dps_results: {
  key: "TestArms-AllItems-OnslaughtBattlegear"
  value: {
-  dps: 4912.08281
-  tps: 3968.95214
+  dps: 4855.76003
+  tps: 3906.0545
  }
 }
 dps_results: {
  key: "TestArms-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 6605.13756
-  tps: 5339.31511
+  dps: 6502.12946
+  tps: 5235.59711
  }
 }
 dps_results: {
  key: "TestArms-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 6615.60889
-  tps: 5348.2666
+  dps: 6507.6073
+  tps: 5239.92558
  }
 }
 dps_results: {
  key: "TestArms-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 6450.7036
-  tps: 5211.08658
+  dps: 6301.19551
+  tps: 5068.45148
  }
 }
 dps_results: {
  key: "TestArms-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 6452.11243
-  tps: 5210.71614
+  dps: 6306.61193
+  tps: 5072.72871
  }
 }
 dps_results: {
  key: "TestArms-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 6588.02377
-  tps: 5324.94752
+  dps: 6478.84863
+  tps: 5217.20055
  }
 }
 dps_results: {
  key: "TestArms-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 6588.02377
-  tps: 5324.94752
+  dps: 6478.84863
+  tps: 5217.20055
  }
 }
 dps_results: {
  key: "TestArms-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 6750.31463
-  tps: 5459.33646
+  dps: 6615.86001
+  tps: 5325.11157
  }
 }
 dps_results: {
  key: "TestArms-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 6588.02377
-  tps: 5324.94752
+  dps: 6478.84863
+  tps: 5217.20055
  }
 }
 dps_results: {
  key: "TestArms-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 6428.76892
-  tps: 5195.23185
+  dps: 6277.61903
+  tps: 5049.92266
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Shadowmourne-49623"
  value: {
-  dps: 9652.97606
-  tps: 7861.34906
+  dps: 9038.57363
+  tps: 7317.90816
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SiegebreakerBattlegear"
  value: {
-  dps: 6186.50477
-  tps: 5003.29305
+  dps: 6056.90359
+  tps: 4885.56365
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SiegebreakerPlate"
  value: {
-  dps: 5424.27098
-  tps: 4379.25955
+  dps: 5354.47251
+  tps: 4302.15286
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SoulPreserver-37111"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SouloftheDead-40382"
  value: {
-  dps: 6524.84379
-  tps: 5273.77265
+  dps: 6366.26913
+  tps: 5123.1821
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SparkofLife-37657"
  value: {
-  dps: 6461.52646
-  tps: 5219.97511
+  dps: 6317.02843
+  tps: 5079.75626
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 6581.50708
-  tps: 5315.95236
+  dps: 6453.84761
+  tps: 5186.09003
  }
 }
 dps_results: {
  key: "TestArms-AllItems-StormshroudArmor"
  value: {
-  dps: 5028.40827
-  tps: 4051.7972
+  dps: 5018.15938
+  tps: 4030.1328
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 6615.60889
-  tps: 5348.2666
+  dps: 6507.6073
+  tps: 5239.92558
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 6605.13756
-  tps: 5339.31511
+  dps: 6502.12946
+  tps: 5235.59711
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 6591.23359
-  tps: 5327.78051
+  dps: 6492.54324
+  tps: 5228.02215
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TheFistsofFury"
  value: {
-  dps: 4805.32956
-  tps: 3995.02084
+  dps: 4568.08818
+  tps: 3767.77424
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TheTwinBladesofAzzinoth"
  value: {
-  dps: 5146.53578
-  tps: 4258.94583
+  dps: 4862.05922
+  tps: 3989.98278
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 6594.14082
-  tps: 5330.03796
+  dps: 6508.13877
+  tps: 5243.09018
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 6726.39326
-  tps: 5433.60078
+  dps: 6584.29713
+  tps: 5296.3194
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 6806.24088
-  tps: 5498.45942
+  dps: 6630.29753
+  tps: 5335.32952
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 6588.02377
-  tps: 5324.94752
+  dps: 6478.84863
+  tps: 5217.20055
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 6588.02377
-  tps: 5324.94752
+  dps: 6478.84863
+  tps: 5217.20055
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 6423.42924
-  tps: 5189.68115
+  dps: 6324.60419
+  tps: 5086.9172
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 6588.02377
-  tps: 5324.94752
+  dps: 6478.84863
+  tps: 5217.20055
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 6588.02377
-  tps: 5324.94752
+  dps: 6478.84863
+  tps: 5217.20055
  }
 }
 dps_results: {
  key: "TestArms-AllItems-UndeadSlayer'sBlessedArmor"
  value: {
-  dps: 5335.20061
-  tps: 4299.41414
+  dps: 5209.00537
+  tps: 4182.56264
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Val'anyr,HammerofAncientKings-46017"
  value: {
-  dps: 3822.06397
-  tps: 3168.78811
+  dps: 3739.49527
+  tps: 3074.84622
  }
 }
 dps_results: {
  key: "TestArms-AllItems-WingedTalisman-37844"
  value: {
-  dps: 6396.86675
-  tps: 5166.94332
+  dps: 6262.82724
+  tps: 5037.96938
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Wrynn'sBattlegear"
  value: {
-  dps: 6890.70917
-  tps: 5566.1016
+  dps: 6714.43279
+  tps: 5406.58323
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Wrynn'sPlate"
  value: {
-  dps: 5640.39847
-  tps: 4546.03583
+  dps: 5548.49888
+  tps: 4454.25
  }
 }
 dps_results: {
  key: "TestArms-AllItems-YmirjarLord'sBattlegear"
  value: {
-  dps: 7494.64482
-  tps: 6071.88535
+  dps: 7349.16566
+  tps: 5931.73881
  }
 }
 dps_results: {
  key: "TestArms-AllItems-YmirjarLord'sPlate"
  value: {
-  dps: 5937.65653
-  tps: 4794.79385
+  dps: 5808.61237
+  tps: 4674.27625
  }
 }
 dps_results: {
  key: "TestArms-Average-Default"
  value: {
-  dps: 6832.06862
-  tps: 5520.34473
+  dps: 6659.68361
+  tps: 5357.47752
  }
 }
 dps_results: {
  key: "TestArms-Settings-Human-p1_arms-Basic-arms-FullBuffs-LongMultiTarget"
  value: {
-  dps: 11924.62575
-  tps: 11028.5597
+  dps: 11184.11318
+  tps: 10321.38091
  }
 }
 dps_results: {
  key: "TestArms-Settings-Human-p1_arms-Basic-arms-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6744.86884
-  tps: 5435.64421
+  dps: 6552.42865
+  tps: 5252.94836
  }
 }
 dps_results: {
  key: "TestArms-Settings-Human-p1_arms-Basic-arms-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7303.9868
-  tps: 5943.01221
+  dps: 6969.43738
+  tps: 5653.47586
  }
 }
 dps_results: {
  key: "TestArms-Settings-Human-p1_arms-Basic-arms-NoBuffs-LongMultiTarget"
  value: {
-  dps: 6201.566
-  tps: 5886.67849
+  dps: 6170.00187
+  tps: 5850.70513
  }
 }
 dps_results: {
  key: "TestArms-Settings-Human-p1_arms-Basic-arms-NoBuffs-LongSingleTarget"
  value: {
-  dps: 3507.48553
-  tps: 2829.64022
+  dps: 3543.41931
+  tps: 2859.62352
  }
 }
 dps_results: {
  key: "TestArms-Settings-Human-p1_arms-Basic-arms-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 3700.71515
-  tps: 3012.98824
+  dps: 3711.29059
+  tps: 3024.56901
  }
 }
 dps_results: {
  key: "TestArms-Settings-Orc-p1_arms-Basic-arms-FullBuffs-LongMultiTarget"
  value: {
-  dps: 11974.1173
-  tps: 11074.57371
+  dps: 11336.28583
+  tps: 10480.36946
  }
 }
 dps_results: {
  key: "TestArms-Settings-Orc-p1_arms-Basic-arms-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6750.31463
-  tps: 5459.33646
+  dps: 6615.86001
+  tps: 5325.11157
  }
 }
 dps_results: {
  key: "TestArms-Settings-Orc-p1_arms-Basic-arms-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7373.00838
-  tps: 6033.37017
+  dps: 7108.58976
+  tps: 5787.99086
  }
 }
 dps_results: {
  key: "TestArms-Settings-Orc-p1_arms-Basic-arms-NoBuffs-LongMultiTarget"
  value: {
-  dps: 6352.90441
-  tps: 6027.91913
+  dps: 6335.42532
+  tps: 6013.61723
  }
 }
 dps_results: {
  key: "TestArms-Settings-Orc-p1_arms-Basic-arms-NoBuffs-LongSingleTarget"
  value: {
-  dps: 3536.91307
-  tps: 2857.80735
+  dps: 3576.94646
+  tps: 2890.00901
  }
 }
 dps_results: {
  key: "TestArms-Settings-Orc-p1_arms-Basic-arms-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 3767.68859
-  tps: 3083.31246
+  dps: 3779.35597
+  tps: 3094.87687
  }
 }
 dps_results: {
  key: "TestArms-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 6322.40706
-  tps: 5086.99074
+  dps: 6140.51847
+  tps: 4918.78951
  }
 }
diff --git a/sim/warrior/dps/TestFury.results b/sim/warrior/dps/TestFury.results
index 2450f6b563..1288ac4796 100644
--- a/sim/warrior/dps/TestFury.results
+++ b/sim/warrior/dps/TestFury.results
@@ -46,935 +46,935 @@ character_stats_results: {
 dps_results: {
  key: "TestFury-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 6864.87671
-  tps: 5103.38904
+  dps: 6285.39617
+  tps: 4617.84989
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 6776.94205
-  tps: 5031.73545
+  dps: 6242.67815
+  tps: 4584.7029
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 6576.11035
-  tps: 4886.41831
+  dps: 6070.00097
+  tps: 4459.82625
   hps: 90.47701
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 6576.11035
-  tps: 4886.41831
+  dps: 6070.00097
+  tps: 4459.82625
   hps: 90.47701
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 6868.88557
-  tps: 5105.07983
+  dps: 6308.37895
+  tps: 4634.43174
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Beast-tamer'sShoulders-30892"
  value: {
-  dps: 6643.31619
-  tps: 4936.52029
+  dps: 6105.97004
+  tps: 4489.27971
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BlackBruise-50035"
  value: {
-  dps: 5912.90398
-  tps: 4415.24024
+  dps: 5437.88412
+  tps: 3997.69695
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BlackBruise-50692"
  value: {
-  dps: 5993.38342
-  tps: 4473.93155
+  dps: 5535.51042
+  tps: 4067.26735
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BlessedBattlegearofUndeadSlaying"
  value: {
-  dps: 5402.16423
-  tps: 4017.57594
+  dps: 5046.84075
+  tps: 3707.15469
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BlessedGarboftheUndeadSlayer"
  value: {
-  dps: 5344.32106
-  tps: 3981.71498
+  dps: 5022.57195
+  tps: 3691.86742
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 5037.55692
-  tps: 3754.11053
+  dps: 4729.19706
+  tps: 3476.65924
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 6885.18545
-  tps: 5017.06984
+  dps: 6282.08305
+  tps: 4523.20391
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Bryntroll,theBoneArbiter-50415"
  value: {
-  dps: 6964.93261
-  tps: 5177.34824
+  dps: 6423.92566
+  tps: 4719.63158
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Bryntroll,theBoneArbiter-50709"
  value: {
-  dps: 6964.93261
-  tps: 5177.34824
+  dps: 6423.92566
+  tps: 4719.63158
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 6964.93261
-  tps: 5177.34824
+  dps: 6423.92566
+  tps: 4719.63158
  }
 }
 dps_results: {
  key: "TestFury-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
   hps: 64
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 6772.21685
-  tps: 5026.11285
+  dps: 6203.41318
+  tps: 4555.80129
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 6766.62895
-  tps: 5024.21231
+  dps: 6236.15781
+  tps: 4578.8361
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Death'sChoice-47464"
  value: {
-  dps: 7160.83725
-  tps: 5311.32515
+  dps: 6602.64112
+  tps: 4849.8309
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 6735.37032
-  tps: 4998.68447
+  dps: 6195.32949
+  tps: 4547.48663
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 7181.01403
-  tps: 5330.98337
+  dps: 6493.06269
+  tps: 4766.0526
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 7248.12327
-  tps: 5377.03041
+  dps: 6551.19253
+  tps: 4809.30975
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Defender'sCode-40257"
  value: {
-  dps: 6596.66988
-  tps: 4898.49101
+  dps: 6084.47406
+  tps: 4470.52004
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 6874.8921
-  tps: 5109.55671
+  dps: 6302.17513
+  tps: 4630.51897
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DislodgedForeignObject-50348"
  value: {
-  dps: 6768.86309
-  tps: 5020.85517
+  dps: 6224.77452
+  tps: 4564.93063
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 6729.96579
-  tps: 4989.43781
+  dps: 6185.24566
+  tps: 4539.96506
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DreadnaughtBattlegear"
  value: {
-  dps: 6227.13442
-  tps: 4636.21994
+  dps: 5719.89221
+  tps: 4210.60192
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DreadnaughtPlate"
  value: {
-  dps: 5328.10898
-  tps: 3971.17866
+  dps: 5029.00135
+  tps: 3700.982
  }
 }
 dps_results: {
  key: "TestFury-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 6885.18545
-  tps: 5119.12412
+  dps: 6282.08305
+  tps: 4615.43576
  }
 }
 dps_results: {
  key: "TestFury-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 6885.18545
-  tps: 5119.12412
+  dps: 6282.08305
+  tps: 4615.43576
  }
 }
 dps_results: {
  key: "TestFury-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 6868.88557
-  tps: 5105.07983
+  dps: 6308.37895
+  tps: 4634.43174
  }
 }
 dps_results: {
  key: "TestFury-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 6874.11509
-  tps: 5110.75442
+  dps: 6308.8863
+  tps: 4634.79924
  }
 }
 dps_results: {
  key: "TestFury-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 6687.25612
-  tps: 4967.56472
+  dps: 6198.69096
+  tps: 4553.2291
  }
 }
 dps_results: {
  key: "TestFury-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 6885.18545
-  tps: 5119.12412
+  dps: 6282.08305
+  tps: 4615.43576
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 6760.84201
-  tps: 5019.53926
+  dps: 6235.09401
+  tps: 4578.01366
  }
 }
 dps_results: {
  key: "TestFury-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 6704.97271
-  tps: 4980.45534
+  dps: 6189.43599
+  tps: 4545.7705
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 6584.90525
-  tps: 4893.12317
+  dps: 6077.42867
+  tps: 4464.82713
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ForgeEmber-37660"
  value: {
-  dps: 6687.15497
-  tps: 4970.63554
+  dps: 6167.90206
+  tps: 4529.76924
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 6885.18545
-  tps: 5119.12412
+  dps: 6282.08305
+  tps: 4615.43576
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 6885.18545
-  tps: 5119.12412
+  dps: 6282.08305
+  tps: 4615.43576
  }
 }
 dps_results: {
  key: "TestFury-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 6784.00223
-  tps: 5039.12821
+  dps: 6282.77567
+  tps: 4614.70076
  }
 }
 dps_results: {
  key: "TestFury-AllItems-FuturesightRune-38763"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Gladiator'sBattlegear"
  value: {
-  dps: 6570.55313
-  tps: 4873.70756
+  dps: 6112.49241
+  tps: 4488.96448
  }
 }
 dps_results: {
  key: "TestFury-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 6732.63239
-  tps: 5001.11317
+  dps: 6214.57201
+  tps: 4563.14193
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Heartpierce-49982"
  value: {
-  dps: 6964.93261
-  tps: 5177.34824
+  dps: 6423.92566
+  tps: 4719.63158
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Heartpierce-50641"
  value: {
-  dps: 6964.93261
-  tps: 5177.34824
+  dps: 6423.92566
+  tps: 4719.63158
  }
 }
 dps_results: {
  key: "TestFury-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 6868.88557
-  tps: 5105.07983
+  dps: 6308.37895
+  tps: 4634.43174
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 6874.11509
-  tps: 5110.75442
+  dps: 6308.8863
+  tps: 4634.79924
  }
 }
 dps_results: {
  key: "TestFury-AllItems-IncisorFragment-37723"
  value: {
-  dps: 6823.56239
-  tps: 5065.85738
+  dps: 6266.13203
+  tps: 4604.24723
  }
 }
 dps_results: {
  key: "TestFury-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 6885.18545
-  tps: 5119.12412
+  dps: 6282.08305
+  tps: 4615.43576
  }
 }
 dps_results: {
  key: "TestFury-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 6901.3274
-  tps: 5130.02631
+  dps: 6311.85119
+  tps: 4637.13045
   hps: 13.7626
  }
 }
 dps_results: {
  key: "TestFury-AllItems-LastWord-50179"
  value: {
-  dps: 6964.93261
-  tps: 5177.34824
+  dps: 6423.92566
+  tps: 4719.63158
  }
 }
 dps_results: {
  key: "TestFury-AllItems-LastWord-50708"
  value: {
-  dps: 6964.93261
-  tps: 5177.34824
+  dps: 6423.92566
+  tps: 4719.63158
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 6806.36961
-  tps: 5051.69152
+  dps: 6251.76275
+  tps: 4593.72518
  }
 }
 dps_results: {
  key: "TestFury-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 6792.6766
-  tps: 5044.42421
+  dps: 6161.9534
+  tps: 4525.70484
  }
 }
 dps_results: {
  key: "TestFury-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 6589.2357
-  tps: 4893.12034
+  dps: 6078.88576
+  tps: 4466.4473
  }
 }
 dps_results: {
  key: "TestFury-AllItems-OnslaughtArmor"
  value: {
-  dps: 4116.35794
-  tps: 3078.01119
+  dps: 3940.83335
+  tps: 2909.62123
  }
 }
 dps_results: {
  key: "TestFury-AllItems-OnslaughtBattlegear"
  value: {
-  dps: 4936.60791
-  tps: 3686.08324
+  dps: 4706.60413
+  tps: 3464.23862
  }
 }
 dps_results: {
  key: "TestFury-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 6887.6853
-  tps: 5120.31582
+  dps: 6305.68102
+  tps: 4632.63106
  }
 }
 dps_results: {
  key: "TestFury-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 6891.5717
-  tps: 5123.87783
+  dps: 6311.23348
+  tps: 4636.67698
  }
 }
 dps_results: {
  key: "TestFury-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 6650.5469
-  tps: 4940.36559
+  dps: 6111.91006
+  tps: 4490.4593
  }
 }
 dps_results: {
  key: "TestFury-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 6653.49639
-  tps: 4940.1973
+  dps: 6117.38687
+  tps: 4494.45067
  }
 }
 dps_results: {
  key: "TestFury-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 6885.18545
-  tps: 5119.12412
+  dps: 6282.08305
+  tps: 4615.43576
  }
 }
 dps_results: {
  key: "TestFury-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 6885.18545
-  tps: 5119.12412
+  dps: 6282.08305
+  tps: 4615.43576
  }
 }
 dps_results: {
  key: "TestFury-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 6960.0882
-  tps: 5172.83324
+  dps: 6425.2442
+  tps: 4720.58779
  }
 }
 dps_results: {
  key: "TestFury-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 6885.18545
-  tps: 5119.12412
+  dps: 6282.08305
+  tps: 4615.43576
  }
 }
 dps_results: {
  key: "TestFury-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 6575.61208
-  tps: 4885.8321
+  dps: 6085.67296
+  tps: 4471.39282
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Shadowmourne-49623"
  value: {
-  dps: 6964.93261
-  tps: 5177.34824
+  dps: 6423.92566
+  tps: 4719.63158
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SiegebreakerBattlegear"
  value: {
-  dps: 6538.17097
-  tps: 4855.89342
+  dps: 6011.43514
+  tps: 4416.30194
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SiegebreakerPlate"
  value: {
-  dps: 5485.8763
-  tps: 4084.22545
+  dps: 5195.06886
+  tps: 3822.59421
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SoulPreserver-37111"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SouloftheDead-40382"
  value: {
-  dps: 6717.34147
-  tps: 4988.78197
+  dps: 6191.58883
+  tps: 4547.35385
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SparkofLife-37657"
  value: {
-  dps: 6638.30084
-  tps: 4923.44284
+  dps: 6127.85832
+  tps: 4496.68067
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 6856.62273
-  tps: 5088.22252
+  dps: 6325.26769
+  tps: 4648.06878
  }
 }
 dps_results: {
  key: "TestFury-AllItems-StormshroudArmor"
  value: {
-  dps: 5041.64657
-  tps: 3761.17074
+  dps: 4734.56855
+  tps: 3486.58656
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 6891.5717
-  tps: 5123.87783
+  dps: 6311.23348
+  tps: 4636.67698
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 6887.6853
-  tps: 5120.31582
+  dps: 6305.68102
+  tps: 4632.63106
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 6871.46861
-  tps: 5109.19871
+  dps: 6295.96421
+  tps: 4625.55079
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TheFistsofFury"
  value: {
-  dps: 4965.9252
-  tps: 3701.06728
+  dps: 4701.8964
+  tps: 3441.99943
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TheTwinBladesofAzzinoth"
  value: {
-  dps: 5099.68017
-  tps: 3798.55202
+  dps: 4945.02638
+  tps: 3617.54645
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 6864.56544
-  tps: 5101.01566
+  dps: 6307.79817
+  tps: 4632.78041
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 6971.6567
-  tps: 5171.68032
+  dps: 6419.39976
+  tps: 4712.21557
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 6969.68545
-  tps: 5166.48933
+  dps: 6473.31079
+  tps: 4750.92795
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 6885.18545
-  tps: 5119.12412
+  dps: 6282.08305
+  tps: 4615.43576
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 6885.18545
-  tps: 5119.12412
+  dps: 6282.08305
+  tps: 4615.43576
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 6683.01256
-  tps: 4964.43213
+  dps: 6202.53723
+  tps: 4553.28874
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 6885.18545
-  tps: 5119.12412
+  dps: 6282.08305
+  tps: 4615.43576
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 6885.18545
-  tps: 5119.12412
+  dps: 6282.08305
+  tps: 4615.43576
  }
 }
 dps_results: {
  key: "TestFury-AllItems-UndeadSlayer'sBlessedArmor"
  value: {
-  dps: 5320.23269
-  tps: 3960.22988
+  dps: 5013.02305
+  tps: 3682.17626
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Val'anyr,HammerofAncientKings-46017"
  value: {
-  dps: 5196.53795
-  tps: 3921.70656
+  dps: 4752.73581
+  tps: 3499.1314
  }
 }
 dps_results: {
  key: "TestFury-AllItems-WingedTalisman-37844"
  value: {
-  dps: 6559.28013
-  tps: 4874.20474
+  dps: 6068.64055
+  tps: 4458.98045
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Wrynn'sBattlegear"
  value: {
-  dps: 7357.98622
-  tps: 5453.94083
+  dps: 6659.09505
+  tps: 4890.56157
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Wrynn'sPlate"
  value: {
-  dps: 5786.67168
-  tps: 4305.73513
+  dps: 5451.43473
+  tps: 4008.11611
  }
 }
 dps_results: {
  key: "TestFury-AllItems-YmirjarLord'sBattlegear"
  value: {
-  dps: 8067.43407
-  tps: 5973.43415
+  dps: 7377.42047
+  tps: 5413.47443
  }
 }
 dps_results: {
  key: "TestFury-AllItems-YmirjarLord'sPlate"
  value: {
-  dps: 6174.83894
-  tps: 4593.70936
+  dps: 5755.86045
+  tps: 4233.42579
  }
 }
 dps_results: {
  key: "TestFury-Average-Default"
  value: {
-  dps: 7015.89861
-  tps: 5207.27726
+  dps: 6423.99337
+  tps: 4721.09766
  }
 }
 dps_results: {
  key: "TestFury-Settings-Human-p1_fury-Basic-fury-FullBuffs-LongMultiTarget"
  value: {
-  dps: 10294.60824
-  tps: 8016.90735
+  dps: 8643.58521
+  tps: 6686.76935
  }
 }
 dps_results: {
  key: "TestFury-Settings-Human-p1_fury-Basic-fury-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6955.40821
-  tps: 5169.2826
+  dps: 6333.29509
+  tps: 4652.58237
  }
 }
 dps_results: {
  key: "TestFury-Settings-Human-p1_fury-Basic-fury-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 8347.45997
-  tps: 6188.65399
+  dps: 7404.71574
+  tps: 5425.96083
  }
 }
 dps_results: {
  key: "TestFury-Settings-Human-p1_fury-Basic-fury-NoBuffs-LongMultiTarget"
  value: {
-  dps: 4394.68747
-  tps: 3569.94332
+  dps: 4307.27459
+  tps: 3451.63161
  }
 }
 dps_results: {
  key: "TestFury-Settings-Human-p1_fury-Basic-fury-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2797.06729
-  tps: 2107.61812
+  dps: 2871.0366
+  tps: 2139.06489
  }
 }
 dps_results: {
  key: "TestFury-Settings-Human-p1_fury-Basic-fury-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 3038.98566
-  tps: 2297.15898
+  dps: 3272.65818
+  tps: 2447.12949
  }
 }
 dps_results: {
  key: "TestFury-Settings-Orc-p1_fury-Basic-fury-FullBuffs-LongMultiTarget"
  value: {
-  dps: 10435.10341
-  tps: 8132.46118
+  dps: 8694.98225
+  tps: 6729.1045
  }
 }
 dps_results: {
  key: "TestFury-Settings-Orc-p1_fury-Basic-fury-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6964.93261
-  tps: 5177.34824
+  dps: 6423.92566
+  tps: 4719.63158
  }
 }
 dps_results: {
  key: "TestFury-Settings-Orc-p1_fury-Basic-fury-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 8385.2664
-  tps: 6218.08828
+  dps: 7516.95896
+  tps: 5506.71715
  }
 }
 dps_results: {
  key: "TestFury-Settings-Orc-p1_fury-Basic-fury-NoBuffs-LongMultiTarget"
  value: {
-  dps: 4418.12696
-  tps: 3589.70524
+  dps: 4334.00204
+  tps: 3472.20866
  }
 }
 dps_results: {
  key: "TestFury-Settings-Orc-p1_fury-Basic-fury-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2797.24399
-  tps: 2107.99386
+  dps: 2910.72975
+  tps: 2168.2564
  }
 }
 dps_results: {
  key: "TestFury-Settings-Orc-p1_fury-Basic-fury-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 3131.51163
-  tps: 2357.99182
+  dps: 3338.8731
+  tps: 2494.623
  }
 }
 dps_results: {
  key: "TestFury-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 6468.4034
-  tps: 4804.77961
+  dps: 5887.98903
+  tps: 4330.99525
  }
 }
diff --git a/sim/warrior/dps/dps_warrior.go b/sim/warrior/dps/dps_warrior.go
index fcc5c19535..0ab0d5c20e 100644
--- a/sim/warrior/dps/dps_warrior.go
+++ b/sim/warrior/dps/dps_warrior.go
@@ -1,9 +1,6 @@
 package dps
 
 import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/common"
 	"github.com/wowsims/wotlk/sim/core"
 	"github.com/wowsims/wotlk/sim/core/proto"
 	"github.com/wowsims/wotlk/sim/warrior"
@@ -29,19 +26,7 @@ func RegisterDpsWarrior() {
 type DpsWarrior struct {
 	*warrior.Warrior
 
-	Options        *proto.Warrior_Options
-	Rotation       *proto.Warrior_Rotation
-	CustomRotation *common.CustomRotation
-
-	// Prevent swapping stances until this time, to account for human reaction time.
-	canSwapStanceAt time.Duration
-	// Last time sunder was applied. Used for maintaining sunder even if sunder is enabled as debuff in individual sim
-	lastSunderAt time.Duration
-
-	maintainSunder  bool
-	thunderClapNext bool
-
-	castSlamAt time.Duration
+	Options *proto.Warrior_Options
 }
 
 func NewDpsWarrior(character *core.Character, options *proto.Player) *DpsWarrior {
@@ -49,13 +34,9 @@ func NewDpsWarrior(character *core.Character, options *proto.Player) *DpsWarrior
 
 	war := &DpsWarrior{
 		Warrior: warrior.NewWarrior(character, options.TalentsString, warrior.WarriorInputs{
-			ShoutType:                   warOptions.Options.Shout,
-			RendCdThreshold:             core.DurationFromSeconds(warOptions.Rotation.RendCdThreshold),
-			BloodsurgeDurationThreshold: core.DurationFromSeconds(warOptions.Rotation.BloodsurgeDurationThreshold),
-			StanceSnapshot:              warOptions.Options.StanceSnapshot,
+			StanceSnapshot: warOptions.Options.StanceSnapshot,
 		}),
-		Rotation: warOptions.Rotation,
-		Options:  warOptions.Options,
+		Options: warOptions.Options,
 	}
 
 	rbo := core.RageBarOptions{
@@ -87,10 +68,6 @@ func (war *DpsWarrior) GetWarrior() *warrior.Warrior {
 func (war *DpsWarrior) Initialize() {
 	war.Warrior.Initialize()
 
-	war.RegisterHSOrCleave(war.Rotation.UseCleave, war.Rotation.HsRageThreshold)
-	war.RegisterRendSpell(war.Rotation.RendRageThresholdBelow, war.Rotation.RendHealthThresholdAbove)
-	war.CustomRotation = war.makeCustomRotation()
-
 	if war.Options.UseRecklessness {
 		war.RegisterRecklessnessCD()
 	}
@@ -99,42 +76,21 @@ func (war *DpsWarrior) Initialize() {
 		war.RegisterShatteringThrowCD()
 	}
 
-	// This makes the behavior of these options more intuitive in the individual sim.
-	if war.Env.Raid.Size() == 1 {
-		if war.Rotation.SunderArmor == proto.Warrior_Rotation_SunderArmorHelpStack {
-			war.SunderArmorAuras.Get(war.CurrentTarget).Duration = core.NeverExpires
-		} else if war.Rotation.SunderArmor == proto.Warrior_Rotation_SunderArmorMaintain {
-			war.SunderArmorAuras.Get(war.CurrentTarget).Duration = time.Second * 30
-		}
-	}
-
-	if war.Rotation.StanceOption == proto.Warrior_Rotation_DefaultStance {
-		if war.Warrior.PrimaryTalentTree == warrior.FuryTree {
-			war.Rotation.StanceOption = proto.Warrior_Rotation_BerserkerStance
-		} else {
-			war.Rotation.StanceOption = proto.Warrior_Rotation_BattleStance
-		}
-	}
-
-	if war.Rotation.StanceOption == proto.Warrior_Rotation_BerserkerStance {
+	if war.PrimaryTalentTree == warrior.FuryTree {
 		war.BerserkerStanceAura.BuildPhase = core.CharacterBuildPhaseTalents
-	} else if war.Rotation.StanceOption == proto.Warrior_Rotation_BattleStance {
+	} else if war.PrimaryTalentTree == warrior.ArmsTree {
 		war.BattleStanceAura.BuildPhase = core.CharacterBuildPhaseTalents
 	}
 }
 
 func (war *DpsWarrior) Reset(sim *core.Simulation) {
-	if war.Rotation.StanceOption == proto.Warrior_Rotation_BerserkerStance {
+	if war.PrimaryTalentTree == warrior.FuryTree {
 		war.Warrior.Reset(sim)
 		war.BerserkerStanceAura.Activate(sim)
 		war.Stance = warrior.BerserkerStance
-	} else if war.Rotation.StanceOption == proto.Warrior_Rotation_BattleStance {
+	} else if war.PrimaryTalentTree == warrior.ArmsTree {
 		war.Warrior.Reset(sim)
 		war.BattleStanceAura.Activate(sim)
 		war.Stance = warrior.BattleStance
 	}
-	war.canSwapStanceAt = 0
-	war.maintainSunder = war.Rotation.SunderArmor != proto.Warrior_Rotation_SunderArmorNone
-	war.castSlamAt = 0
-	war.thunderClapNext = false
 }
diff --git a/sim/warrior/dps/dps_warrior_test.go b/sim/warrior/dps/dps_warrior_test.go
index 798b983f76..dbac975c74 100644
--- a/sim/warrior/dps/dps_warrior_test.go
+++ b/sim/warrior/dps/dps_warrior_test.go
@@ -110,15 +110,13 @@ var ArmsGlyphs = &proto.Glyphs{
 
 var PlayerOptionsArms = &proto.Player_Warrior{
 	Warrior: &proto.Warrior{
-		Options:  warriorOptions,
-		Rotation: &proto.Warrior_Rotation{},
+		Options: warriorOptions,
 	},
 }
 
 var PlayerOptionsFury = &proto.Player_Warrior{
 	Warrior: &proto.Warrior{
-		Options:  warriorOptions,
-		Rotation: &proto.Warrior_Rotation{},
+		Options: warriorOptions,
 	},
 }
 
diff --git a/sim/warrior/dps/rotation.go b/sim/warrior/dps/rotation.go
deleted file mode 100644
index fb469d1236..0000000000
--- a/sim/warrior/dps/rotation.go
+++ /dev/null
@@ -1,451 +0,0 @@
-package dps
-
-import (
-	"time"
-
-	"github.com/wowsims/wotlk/sim/common"
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-	"github.com/wowsims/wotlk/sim/warrior"
-)
-
-const DebuffRefreshWindow = time.Second * 2
-
-func (war *DpsWarrior) OnGCDReady(sim *core.Simulation) {
-	war.doRotation(sim)
-
-	if war.GCD.IsReady(sim) && !war.IsWaiting() {
-		// This means we did nothing
-		war.DoNothing()
-	}
-}
-
-func (war *DpsWarrior) OnAutoAttack(sim *core.Simulation, _ *core.Spell) {
-	war.tryQueueHsCleave(sim)
-}
-
-func (war *DpsWarrior) doRotation(sim *core.Simulation) {
-	if war.thunderClapNext {
-		if war.ThunderClap.CanCast(sim, war.CurrentTarget) {
-			if war.ThunderClap.Cast(sim, war.CurrentTarget) {
-				if war.ThunderClapAuras.Get(war.CurrentTarget).RemainingDuration(sim) > DebuffRefreshWindow {
-					war.thunderClapNext = false
-
-					// Switching back to berserker immediately is unrealistic because the player needs
-					// to visually confirm the TC landed. Instead we add a delay to model that.
-					war.canSwapStanceAt = sim.CurrentTime + time.Millisecond*300
-				}
-				return
-			}
-		}
-	} else {
-		if war.Rotation.StanceOption == proto.Warrior_Rotation_BerserkerStance {
-			war.trySwapToBerserker(sim)
-		} else if war.Rotation.StanceOption == proto.Warrior_Rotation_BattleStance {
-			war.trySwapToBattle(sim)
-		}
-	}
-
-	if war.shouldSunder(sim) {
-		if war.Devastate != nil {
-			war.Devastate.Cast(sim, war.CurrentTarget)
-		} else {
-			war.SunderArmor.Cast(sim, war.CurrentTarget)
-			war.lastSunderAt = sim.CurrentTime
-		}
-		war.tryQueueHsCleave(sim)
-		return
-	}
-
-	IsExecutePhase20 := sim.IsExecutePhase20()
-	if war.Rotation.CustomRotationOption && war.GCD.IsReady(sim) {
-		war.CustomRotation.Cast(sim)
-	} else if IsExecutePhase20 {
-		war.executeRotation(sim)
-	} else {
-		war.normalRotation(sim)
-	}
-
-	if war.GCD.IsReady(sim) && !war.thunderClapNext {
-		// We didn't cast anything, so wait for the next CD.
-		nextCD := war.Whirlwind.CD.ReadyAt()
-		if war.Bloodthirst != nil && war.Bloodthirst.CD.ReadyAt() < nextCD {
-			nextCD = war.Bloodthirst.CD.ReadyAt()
-		} else if war.MortalStrike != nil && war.MortalStrike.CD.ReadyAt() < nextCD {
-			nextCD = war.MortalStrike.CD.ReadyAt()
-		}
-
-		if war.Rotation.SunderArmor == proto.Warrior_Rotation_SunderArmorMaintain {
-			nextSunderAt := war.lastSunderAt + 30*time.Second - sim.CurrentTime - SunderWindow
-			// TODO looks fishy, nextCD is unused
-			nextCD = min(nextCD, nextSunderAt)
-		}
-	}
-}
-
-func (war *DpsWarrior) normalRotation(sim *core.Simulation) {
-	if war.GCD.IsReady(sim) {
-		if war.Warrior.PrimaryTalentTree == warrior.FuryTree {
-			war.furyNormalRotation(sim)
-		} else if war.Warrior.PrimaryTalentTree == warrior.ArmsTree {
-			war.armsNormalRotation(sim)
-		}
-	}
-	war.tryQueueHsCleave(sim)
-}
-
-func (war *DpsWarrior) executeRotation(sim *core.Simulation) {
-	if war.GCD.IsReady(sim) {
-		if war.Warrior.PrimaryTalentTree == warrior.FuryTree {
-			war.furyExecuteRotation(sim)
-		} else if war.Warrior.PrimaryTalentTree == warrior.ArmsTree {
-			war.armsExecuteRotation(sim)
-		}
-	}
-
-	war.tryQueueHsCleave(sim)
-}
-
-func (war *DpsWarrior) furyNormalRotation(sim *core.Simulation) {
-	if war.tryMaintainDebuffs(sim) {
-		war.DoNothing()
-	} else if war.Rotation.MainGcd == proto.Warrior_Rotation_Slam && war.ShouldInstantSlam(sim) {
-		war.Slam.Cast(sim, war.CurrentTarget)
-	} else if war.Rotation.MainGcd == proto.Warrior_Rotation_Bloodthirst && war.Bloodthirst.CanCast(sim, war.CurrentTarget) {
-		war.Bloodthirst.Cast(sim, war.CurrentTarget)
-	} else if war.Rotation.MainGcd == proto.Warrior_Rotation_Whirlwind && war.CanWhirlwind(sim) {
-		war.Whirlwind.Cast(sim, war.CurrentTarget)
-	} else if war.Rotation.MainGcd != proto.Warrior_Rotation_Bloodthirst && war.Bloodthirst.CanCast(sim, war.CurrentTarget) {
-		war.Bloodthirst.Cast(sim, war.CurrentTarget)
-	} else if war.Rotation.MainGcd != proto.Warrior_Rotation_Slam && war.ShouldInstantSlam(sim) {
-		war.Slam.Cast(sim, war.CurrentTarget)
-	} else if war.Rotation.MainGcd != proto.Warrior_Rotation_Whirlwind && war.CanWhirlwind(sim) {
-		war.Whirlwind.Cast(sim, war.CurrentTarget)
-	} else if war.Rotation.UseRend && war.ShouldRend(sim) {
-		if !war.StanceMatches(warrior.BattleStance) {
-			if !war.BattleStance.IsReady(sim) {
-				return
-			}
-			war.BattleStance.Cast(sim, nil)
-		}
-		war.Rend.Cast(sim, war.CurrentTarget)
-	} else if war.Rotation.UseOverpower && war.ShouldOverpower(sim) {
-		if !war.StanceMatches(warrior.BattleStance) {
-			if !war.BattleStance.IsReady(sim) {
-				return
-			}
-			war.BattleStance.Cast(sim, nil)
-		}
-		war.Overpower.Cast(sim, war.CurrentTarget)
-	}
-}
-
-func (war *DpsWarrior) armsNormalRotation(sim *core.Simulation) {
-	if war.tryMaintainDebuffs(sim) {
-		war.DoNothing()
-	} else if war.Execute.CanCast(sim, war.CurrentTarget) {
-		war.CastExecute(sim, war.CurrentTarget)
-	} else if war.Rotation.UseRend && war.ShouldRend(sim) {
-		if !war.StanceMatches(warrior.BattleStance) {
-			if !war.BattleStance.IsReady(sim) {
-				return
-			}
-			war.BattleStance.Cast(sim, nil)
-		}
-		war.Rend.Cast(sim, war.CurrentTarget)
-	} else if war.ShouldOverpower(sim) {
-		if !war.StanceMatches(warrior.BattleStance) {
-			if !war.BattleStance.IsReady(sim) {
-				return
-			}
-			war.BattleStance.Cast(sim, nil)
-		}
-		war.Overpower.Cast(sim, war.CurrentTarget)
-	} else if war.Rotation.UseMs && war.MortalStrike.CanCast(sim, war.CurrentTarget) && war.CurrentRage() >= war.Rotation.MsRageThreshold {
-		war.MortalStrike.Cast(sim, war.CurrentTarget)
-	} else if war.Slam.CanCast(sim, war.CurrentTarget) && war.CurrentRage() >= war.Rotation.SlamRageThreshold {
-		war.Slam.Cast(sim, war.CurrentTarget)
-	}
-}
-
-func (war *DpsWarrior) furyExecuteRotation(sim *core.Simulation) {
-	if war.tryMaintainDebuffs(sim) {
-		war.DoNothing()
-	} else if war.SpamExecute(war.Rotation.SpamExecute) {
-		war.CastExecute(sim, war.CurrentTarget)
-	} else if war.Rotation.MainGcd == proto.Warrior_Rotation_Slam &&
-		war.Rotation.UseSlamOverExecute && war.ShouldInstantSlam(sim) {
-		war.Slam.Cast(sim, war.CurrentTarget)
-	} else if war.Rotation.MainGcd == proto.Warrior_Rotation_Bloodthirst &&
-		war.Rotation.UseBtDuringExecute && war.Bloodthirst.CanCast(sim, war.CurrentTarget) {
-		war.Bloodthirst.Cast(sim, war.CurrentTarget)
-	} else if war.Rotation.MainGcd == proto.Warrior_Rotation_Whirlwind &&
-		war.Rotation.UseWwDuringExecute && war.CanWhirlwind(sim) {
-		war.Whirlwind.Cast(sim, war.CurrentTarget)
-	} else if war.Rotation.MainGcd != proto.Warrior_Rotation_Bloodthirst &&
-		war.Rotation.UseBtDuringExecute && war.Bloodthirst.CanCast(sim, war.CurrentTarget) {
-		war.Bloodthirst.Cast(sim, war.CurrentTarget)
-	} else if war.Rotation.MainGcd != proto.Warrior_Rotation_Slam &&
-		war.Rotation.UseSlamOverExecute && war.ShouldInstantSlam(sim) {
-		war.Slam.Cast(sim, war.CurrentTarget)
-	} else if war.Rotation.MainGcd != proto.Warrior_Rotation_Whirlwind &&
-		war.Rotation.UseWwDuringExecute && war.CanWhirlwind(sim) {
-		war.Whirlwind.Cast(sim, war.CurrentTarget)
-	} else if war.Rotation.UseRend && war.ShouldRend(sim) {
-		if !war.StanceMatches(warrior.BattleStance) {
-			if !war.BattleStance.IsReady(sim) {
-				return
-			}
-			war.BattleStance.Cast(sim, nil)
-		}
-		war.Rend.Cast(sim, war.CurrentTarget)
-	} else if war.Rotation.UseOverpower && war.Rotation.ExecutePhaseOverpower &&
-		war.ShouldOverpower(sim) {
-		if !war.StanceMatches(warrior.BattleStance) {
-			if !war.BattleStance.IsReady(sim) {
-				return
-			}
-			war.BattleStance.Cast(sim, nil)
-		}
-		war.Overpower.Cast(sim, war.CurrentTarget)
-	} else if war.Execute.CanCast(sim, war.CurrentTarget) {
-		war.CastExecute(sim, war.CurrentTarget)
-	}
-}
-
-func (war *DpsWarrior) armsExecuteRotation(sim *core.Simulation) {
-	if war.tryMaintainDebuffs(sim) {
-		war.DoNothing()
-	} else if war.IsSuddenDeathActive() && war.Execute.CanCast(sim, war.CurrentTarget) {
-		war.CastExecute(sim, war.CurrentTarget)
-	} else if war.Rotation.UseRend && war.ShouldRend(sim) {
-		if !war.StanceMatches(warrior.BattleStance) {
-			if !war.BattleStance.IsReady(sim) {
-				return
-			}
-			war.BattleStance.Cast(sim, nil)
-		}
-		war.Rend.Cast(sim, war.CurrentTarget)
-	} else if war.ShouldOverpower(sim) {
-		if !war.StanceMatches(warrior.BattleStance) {
-			if !war.BattleStance.IsReady(sim) {
-				return
-			}
-			war.BattleStance.Cast(sim, nil)
-		}
-		war.Overpower.Cast(sim, war.CurrentTarget)
-	} else if war.SpamExecute(war.Rotation.SpamExecute) {
-		war.CastExecute(sim, war.CurrentTarget)
-	} else if war.Rotation.UseRend && war.ShouldRend(sim) {
-		if !war.StanceMatches(warrior.BattleStance) {
-			if !war.BattleStance.IsReady(sim) {
-				return
-			}
-			war.BattleStance.Cast(sim, nil)
-		}
-		war.Rend.Cast(sim, war.CurrentTarget)
-	} else if war.Rotation.UseMs && war.MortalStrike.CanCast(sim, war.CurrentTarget) && war.CurrentRage() >= war.Rotation.MsRageThreshold {
-		war.MortalStrike.Cast(sim, war.CurrentTarget)
-	} else if war.Execute.CanCast(sim, war.CurrentTarget) {
-		war.CastExecute(sim, war.CurrentTarget)
-	}
-}
-
-func (war *DpsWarrior) trySwapToBerserker(sim *core.Simulation) bool {
-	if !war.StanceMatches(warrior.BerserkerStance) && war.BerserkerStance.IsReady(sim) && sim.CurrentTime >= war.canSwapStanceAt {
-		war.BerserkerStance.Cast(sim, nil)
-		return true
-	}
-	return false
-}
-
-func (war *DpsWarrior) trySwapToBattle(sim *core.Simulation) bool {
-	if !war.StanceMatches(warrior.BattleStance) && war.BattleStance.IsReady(sim) && sim.CurrentTime >= war.canSwapStanceAt {
-		war.BattleStance.Cast(sim, nil)
-		return true
-	}
-	return false
-}
-
-const SunderWindow = time.Second * 3
-
-func (war *DpsWarrior) shouldSunder(sim *core.Simulation) bool {
-	if !war.maintainSunder {
-		return false
-	}
-
-	if !war.SunderArmor.CanCast(sim, war.CurrentTarget) {
-		return false
-	}
-
-	saAura := war.SunderArmorAuras.Get(war.CurrentTarget)
-	stacks := saAura.GetStacks()
-	if war.Rotation.SunderArmor == proto.Warrior_Rotation_SunderArmorHelpStack && stacks == 5 {
-		war.maintainSunder = false
-	}
-
-	return stacks < 5 || (war.lastSunderAt+30*time.Second-sim.CurrentTime) <= SunderWindow
-}
-
-// Returns whether any ability was cast.
-func (war *DpsWarrior) tryMaintainDebuffs(sim *core.Simulation) bool {
-	if war.ShouldShout(sim) {
-		war.Shout.Cast(sim, nil)
-		return true
-	} else if war.Rotation.MaintainDemoShout && war.ShouldDemoralizingShout(sim, war.CurrentTarget, false, true) {
-		war.DemoralizingShout.Cast(sim, war.CurrentTarget)
-		return true
-	} else if war.Rotation.MaintainThunderClap && war.ShouldThunderClap(sim, war.CurrentTarget, false, true, true) {
-		war.thunderClapNext = true
-		if !war.StanceMatches(warrior.BattleStance) {
-			if !war.BattleStance.IsReady(sim) {
-				return false
-			}
-			war.BattleStance.Cast(sim, nil)
-		}
-		// Need to check again because we might have lost rage from switching stances.
-		if war.ThunderClap.CanCast(sim, war.CurrentTarget) {
-			war.ThunderClap.Cast(sim, war.CurrentTarget)
-			if war.ThunderClapAuras.Get(war.CurrentTarget).RemainingDuration(sim) > DebuffRefreshWindow {
-				war.thunderClapNext = false
-			}
-		}
-		return true
-	}
-	return false
-}
-
-func (war *DpsWarrior) tryQueueHsCleave(sim *core.Simulation) {
-	if sim.IsExecutePhase20() && !war.Rotation.UseHsDuringExecute {
-		return
-	}
-
-	if war.ShouldQueueHSOrCleave(sim) {
-		war.QueueHSOrCleave(sim)
-	}
-}
-
-func (war *DpsWarrior) makeCustomRotation() *common.CustomRotation {
-	return common.NewCustomRotation(war.Rotation.CustomRotation, war.GetCharacter(), map[int32]common.CustomSpell{
-		int32(proto.Warrior_Rotation_BloodthirstCustom): {
-			Spell: war.Bloodthirst,
-			Condition: func(sim *core.Simulation) bool {
-				if sim.IsExecutePhase20() && !war.Rotation.UseBtDuringExecute {
-					return false
-				}
-				return war.Bloodthirst.CanCast(sim, war.CurrentTarget)
-			},
-		},
-		int32(proto.Warrior_Rotation_MortalStrike): {
-			Spell: war.MortalStrike,
-			Condition: func(sim *core.Simulation) bool {
-				return war.MortalStrike.CanCast(sim, war.CurrentTarget) && war.CurrentRage() >= war.Rotation.MsRageThreshold
-			},
-		},
-		int32(proto.Warrior_Rotation_WhirlwindCustom): {
-			Spell: war.Whirlwind,
-			Condition: func(sim *core.Simulation) bool {
-				if sim.IsExecutePhase20() && !war.Rotation.UseWwDuringExecute {
-					return false
-				}
-
-				if !war.StanceMatches(warrior.BerserkerStance) {
-					if !war.BerserkerStance.IsReady(sim) {
-						return false
-					}
-					war.BerserkerStance.Cast(sim, nil)
-				}
-				return war.Whirlwind.CanCast(sim, war.CurrentTarget)
-			},
-		},
-		int32(proto.Warrior_Rotation_SlamCustom): {
-			Spell: war.Slam,
-			Condition: func(sim *core.Simulation) bool {
-				if sim.IsExecutePhase20() && !war.Rotation.UseSlamOverExecute {
-					return false
-				}
-
-				if (war.ShouldSlam(sim) && war.CurrentRage() >= war.Rotation.SlamRageThreshold || war.ShouldInstantSlam(sim)) &&
-					war.Slam.CanCast(sim, war.CurrentTarget) {
-					war.AutoAttacks.DelayMeleeBy(sim, war.Slam.CurCast.CastTime)
-					return true
-				}
-				return false
-			},
-		},
-
-		int32(proto.Warrior_Rotation_SlamExpiring): {
-			Spell: war.Slam,
-			Condition: func(sim *core.Simulation) bool {
-				if !war.ShouldInstantSlam(sim) {
-					return false
-				}
-
-				if (war.BloodsurgeValidUntil - sim.CurrentTime) > war.BloodsurgeDurationThreshold {
-					return false
-				}
-
-				if sim.IsExecutePhase20() && !war.Rotation.UseSlamOverExecute {
-					return false
-				}
-
-				if war.CurrentRage() >= war.Rotation.SlamRageThreshold && war.Slam.CanCast(sim, war.CurrentTarget) {
-					war.AutoAttacks.DelayMeleeBy(sim, war.Slam.CurCast.CastTime)
-					return true
-				}
-				return false
-			},
-		},
-
-		int32(proto.Warrior_Rotation_Rend): {
-			Spell: war.Rend,
-			Condition: func(sim *core.Simulation) bool {
-				if !war.ShouldRend(sim) {
-					return false
-				}
-
-				if !war.StanceMatches(warrior.BattleStance) {
-					if !war.BattleStance.IsReady(sim) {
-						return false
-					}
-					war.BattleStance.Cast(sim, nil)
-				}
-				return war.Rend.CanCast(sim, war.CurrentTarget)
-			},
-		},
-		int32(proto.Warrior_Rotation_Overpower): {
-			Spell: war.Overpower,
-			Condition: func(sim *core.Simulation) bool {
-				if !war.ShouldOverpower(sim) {
-					return false
-				}
-				if sim.IsExecutePhase20() && !war.Rotation.ExecutePhaseOverpower && war.PrimaryTalentTree == warrior.FuryTree {
-					return false
-				}
-
-				if !war.StanceMatches(warrior.BattleStance) {
-					if !war.BattleStance.IsReady(sim) {
-						return false
-					}
-					war.BattleStance.Cast(sim, nil)
-				}
-				return war.Overpower.CanCast(sim, war.CurrentTarget)
-			},
-		},
-		int32(proto.Warrior_Rotation_Execute): {
-			Spell: war.Execute,
-		},
-		int32(proto.Warrior_Rotation_ThunderClap): {
-			Spell: war.ThunderClap,
-			Condition: func(sim *core.Simulation) bool {
-				if !war.StanceMatches(warrior.BattleStance) {
-					if !war.BattleStance.IsReady(sim) {
-						return false
-					}
-					war.BattleStance.Cast(sim, nil)
-				}
-				return war.ThunderClap.CanCast(sim, war.CurrentTarget)
-			},
-		},
-	})
-}
diff --git a/sim/warrior/execute.go b/sim/warrior/execute.go
index 1451477a39..8b3e659954 100644
--- a/sim/warrior/execute.go
+++ b/sim/warrior/execute.go
@@ -59,11 +59,3 @@ func (warrior *Warrior) registerExecuteSpell() {
 	})
 	rageMetrics = warrior.Execute.Cost.(*core.RageCost).ResourceMetrics
 }
-
-func (warrior *Warrior) SpamExecute(spam bool) bool {
-	return warrior.CurrentRage() >= warrior.Execute.DefaultCast.Cost && spam && warrior.PrimaryTalentTree == ArmsTree
-}
-
-func (warrior *Warrior) CastExecute(sim *core.Simulation, target *core.Unit) bool {
-	return warrior.Execute.Cast(sim, target)
-}
diff --git a/sim/warrior/heroic_strike_cleave.go b/sim/warrior/heroic_strike_cleave.go
index b75f455c6d..defe4fa625 100644
--- a/sim/warrior/heroic_strike_cleave.go
+++ b/sim/warrior/heroic_strike_cleave.go
@@ -5,14 +5,14 @@ import (
 	"github.com/wowsims/wotlk/sim/core/proto"
 )
 
-func (warrior *Warrior) registerHeroicStrikeSpell() *core.Spell {
+func (warrior *Warrior) registerHeroicStrikeSpell() {
 	hasGlyph := warrior.HasMajorGlyph(proto.WarriorMajorGlyph_GlyphOfHeroicStrike)
 	var rageMetrics *core.ResourceMetrics
 	if hasGlyph {
 		rageMetrics = warrior.NewRageMetrics(core.ActionID{ItemID: 43418})
 	}
 
-	return warrior.RegisterSpell(core.SpellConfig{
+	warrior.HeroicStrike = warrior.RegisterSpell(core.SpellConfig{
 		ActionID:    core.ActionID{SpellID: 47450},
 		SpellSchool: core.SpellSchoolPhysical,
 		ProcMask:    core.ProcMaskMeleeMHSpecial,
@@ -50,14 +50,14 @@ func (warrior *Warrior) registerHeroicStrikeSpell() *core.Spell {
 	})
 }
 
-func (warrior *Warrior) registerCleaveSpell() *core.Spell {
+func (warrior *Warrior) registerCleaveSpell() {
 	flatDamageBonus := 222 * (1 + 0.4*float64(warrior.Talents.ImprovedCleave))
 
 	targets := core.TernaryInt32(warrior.HasMajorGlyph(proto.WarriorMajorGlyph_GlyphOfCleaving), 3, 2)
 	numHits := min(targets, warrior.Env.GetNumTargets())
 	results := make([]*core.SpellResult, numHits)
 
-	return warrior.RegisterSpell(core.SpellConfig{
+	warrior.Cleave = warrior.RegisterSpell(core.SpellConfig{
 		ActionID:    core.ActionID{SpellID: 47520},
 		SpellSchool: core.SpellSchoolPhysical,
 		ProcMask:    core.ProcMaskMeleeMHSpecial,
@@ -136,23 +136,12 @@ func (warrior *Warrior) makeQueueSpellsAndAura(srcSpell *core.Spell) *core.Spell
 	return queueSpell
 }
 
-func (warrior *Warrior) QueueHSOrCleave(sim *core.Simulation) {
-	if warrior.hsOrCleaveQueueSpell.CanCast(sim, warrior.CurrentTarget) {
-		warrior.hsOrCleaveQueueSpell.Cast(sim, warrior.CurrentTarget)
-	}
-}
-
 // Returns true if the regular melee swing should be used, false otherwise.
 func (warrior *Warrior) TryHSOrCleave(sim *core.Simulation, mhSwingSpell *core.Spell) *core.Spell {
 	if !warrior.curQueueAura.IsActive() {
 		return mhSwingSpell
 	}
 
-	if warrior.CurrentRage() < warrior.HSRageThreshold {
-		warrior.curQueueAura.Deactivate(sim)
-		return mhSwingSpell
-	}
-
 	if !warrior.curQueuedAutoSpell.CanCast(sim, warrior.CurrentTarget) {
 		warrior.curQueueAura.Deactivate(sim)
 		return mhSwingSpell
@@ -160,25 +149,3 @@ func (warrior *Warrior) TryHSOrCleave(sim *core.Simulation, mhSwingSpell *core.S
 
 	return warrior.curQueuedAutoSpell
 }
-
-func (warrior *Warrior) ShouldQueueHSOrCleave(sim *core.Simulation) bool {
-	return warrior.CurrentRage() >= warrior.HSRageThreshold && sim.CurrentTime >= warrior.Hardcast.Expires
-}
-
-func (warrior *Warrior) RegisterHSOrCleave(useCleave bool, rageThreshold float64) {
-	warrior.HeroicStrike = warrior.registerCleaveSpell()
-	hsQueueSpell := warrior.makeQueueSpellsAndAura(warrior.HeroicStrike)
-	warrior.Cleave = warrior.registerHeroicStrikeSpell()
-	cleaveQueueSpell := warrior.makeQueueSpellsAndAura(warrior.Cleave)
-
-	var autoSpell *core.Spell
-	if useCleave {
-		autoSpell = warrior.HeroicStrike
-		warrior.hsOrCleaveQueueSpell = hsQueueSpell
-	} else {
-		autoSpell = warrior.Cleave
-		warrior.hsOrCleaveQueueSpell = cleaveQueueSpell
-	}
-
-	warrior.HSRageThreshold = max(autoSpell.DefaultCast.Cost, rageThreshold)
-}
diff --git a/sim/warrior/overpower.go b/sim/warrior/overpower.go
index b4337c53d4..056cc1a3c1 100644
--- a/sim/warrior/overpower.go
+++ b/sim/warrior/overpower.go
@@ -84,27 +84,3 @@ func (warrior *Warrior) registerOverpowerSpell(cdTimer *core.Timer) {
 		},
 	})
 }
-
-func (warrior *Warrior) ShouldOverpower(sim *core.Simulation) bool {
-	return warrior.OverpowerAura.IsActive() && warrior.Overpower.IsReady(sim) &&
-		warrior.CurrentRage() >= warrior.Overpower.DefaultCast.Cost &&
-		sim.CurrentTime > (warrior.lastOverpowerProc+warrior.reactionTime)
-}
-
-// Queue Overpower to be cast at every 6s if talented for 3/3 TfB
-func (warrior *Warrior) CastFullTfbOverpower(sim *core.Simulation, target *core.Unit) bool {
-	if warrior.Talents.TasteForBlood < 3 {
-		return false
-	}
-
-	core.StartDelayedAction(sim, core.DelayedActionOptions{
-		DoAt: sim.CurrentTime + time.Second*6,
-		OnAction: func(_ *core.Simulation) {
-			if warrior.Overpower.CanCast(sim, target) && warrior.ShouldOverpower(sim) {
-				warrior.CastFullTfbOverpower(sim, target)
-			}
-		},
-	})
-
-	return warrior.Overpower.Cast(sim, target)
-}
diff --git a/sim/warrior/protection/TestProtectionWarrior.results b/sim/warrior/protection/TestProtectionWarrior.results
index c86c6c7975..8f624c145c 100644
--- a/sim/warrior/protection/TestProtectionWarrior.results
+++ b/sim/warrior/protection/TestProtectionWarrior.results
@@ -46,7 +46,7 @@ character_stats_results: {
 stat_weights_results: {
  key: "TestProtectionWarrior-StatWeights-Default"
  value: {
-  weights: 0.76367
+  weights: 0.71312
   weights: 0
   weights: 0
   weights: 0
@@ -57,7 +57,7 @@ stat_weights_results: {
   weights: 0
   weights: 0
   weights: 0
-  weights: 0.2638
+  weights: 0.19612
   weights: 0
   weights: 0
   weights: 0
@@ -66,12 +66,12 @@ stat_weights_results: {
   weights: 0
   weights: 0
   weights: 0
-  weights: -3e-05
+  weights: 0.00611
   weights: 0
   weights: 0
   weights: 0
-  weights: 0.43043
-  weights: 0.09932
+  weights: 0.43871
+  weights: 0.05727
   weights: 0
   weights: 0
   weights: 0
@@ -91,874 +91,874 @@ stat_weights_results: {
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 1497.4236
-  tps: 3865.5531
+  dps: 1349.67155
+  tps: 3452.90509
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 1443.61597
-  tps: 3757.74334
-  hps: 82.16554
+  dps: 1289.68012
+  tps: 3328.53102
+  hps: 82.28496
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 1443.61597
-  tps: 3757.74334
-  hps: 82.16554
+  dps: 1289.68012
+  tps: 3328.53102
+  hps: 82.28496
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 1436.29269
-  tps: 3738.87948
+  dps: 1296.93101
+  tps: 3344.72411
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Beast-tamer'sShoulders-30892"
  value: {
-  dps: 1403.06881
-  tps: 3652.51629
+  dps: 1268.62008
+  tps: 3272.93157
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BlackBruise-50035"
  value: {
-  dps: 1502.62813
-  tps: 3840.29252
+  dps: 1366.40693
+  tps: 3480.65126
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BlackBruise-50692"
  value: {
-  dps: 1532.82854
-  tps: 3902.58233
+  dps: 1393.43275
+  tps: 3536.68929
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BlessedBattlegearofUndeadSlaying"
  value: {
-  dps: 1236.90562
-  tps: 3226.4392
+  dps: 1095.91679
+  tps: 2830.7771
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BlessedGarboftheUndeadSlayer"
  value: {
-  dps: 1213.25286
-  tps: 3161.94609
+  dps: 1083.97911
+  tps: 2801.35142
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 1149.45808
-  tps: 3021.01244
+  dps: 1038.79449
+  tps: 2703.60683
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 1429.13677
-  tps: 3649.22353
+  dps: 1288.48948
+  tps: 3259.55894
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Bryntroll,theBoneArbiter-50415"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Bryntroll,theBoneArbiter-50709"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 1464.89347
-  tps: 3806.56276
+  dps: 1312.2867
+  tps: 3382.90933
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
   hps: 64
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 1482.8109
-  tps: 3850.33623
+  dps: 1326.14307
+  tps: 3412.47151
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 1514.2653
-  tps: 3906.78831
+  dps: 1370.16223
+  tps: 3503.06341
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 1475.90374
-  tps: 3829.1951
+  dps: 1320.20348
+  tps: 3397.32538
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Death'sChoice-47464"
  value: {
-  dps: 1516.88738
-  tps: 3908.179
+  dps: 1362.44929
+  tps: 3485.44146
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 1458.84083
-  tps: 3789.00873
+  dps: 1315.30651
+  tps: 3386.90955
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 1550.72349
-  tps: 4003.56481
+  dps: 1379.04866
+  tps: 3530.54996
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 1559.26933
-  tps: 4021.26044
+  dps: 1402.27748
+  tps: 3586.09731
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 1438.34767
-  tps: 3744.86385
+  dps: 1297.77538
+  tps: 3346.47492
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DislodgedForeignObject-50348"
  value: {
-  dps: 1471.50005
-  tps: 3813.02114
+  dps: 1332.44248
+  tps: 3426.37457
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 1470.21311
-  tps: 3812.0458
+  dps: 1327.12982
+  tps: 3414.81696
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DreadnaughtBattlegear"
  value: {
-  dps: 1474.02677
-  tps: 3793.84899
+  dps: 1309.91106
+  tps: 3337.8708
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DreadnaughtPlate"
  value: {
-  dps: 1354.59803
-  tps: 3527.38642
+  dps: 1216.07089
+  tps: 3142.58615
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 1429.13677
-  tps: 3723.6434
+  dps: 1288.48948
+  tps: 3326.07038
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 1429.13677
-  tps: 3723.6434
+  dps: 1288.48948
+  tps: 3326.07038
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 1436.29269
-  tps: 3738.87948
+  dps: 1296.93101
+  tps: 3344.72411
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 1436.28313
-  tps: 3739.17223
+  dps: 1295.62188
+  tps: 3341.52155
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 1452.25212
-  tps: 3775.3647
+  dps: 1314.10173
+  tps: 3386.07585
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 1442.7152
-  tps: 3760.24472
+  dps: 1302.09935
+  tps: 3362.75646
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 1503.39455
-  tps: 3880.58924
+  dps: 1359.5275
+  tps: 3481.35292
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 1466.27182
-  tps: 3802.47188
+  dps: 1323.63574
+  tps: 3406.9386
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ForgeEmber-37660"
  value: {
-  dps: 1464.82611
-  tps: 3806.06243
+  dps: 1315.49823
+  tps: 3387.44624
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 1429.13677
-  tps: 3723.6434
+  dps: 1288.48948
+  tps: 3326.07038
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 1429.13677
-  tps: 3723.6434
+  dps: 1288.48948
+  tps: 3326.07038
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 1473.63265
-  tps: 3820.71002
+  dps: 1318.14001
+  tps: 3387.55053
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-FuturesightRune-38763"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Gladiator'sBattlegear"
  value: {
-  dps: 1546.55353
-  tps: 3917.21448
+  dps: 1390.13929
+  tps: 3488.94895
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 1499.30293
-  tps: 3876.2045
+  dps: 1357.44157
+  tps: 3476.42661
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Heartpierce-49982"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Heartpierce-50641"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 1436.29269
-  tps: 3738.87948
+  dps: 1296.93101
+  tps: 3344.72411
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 1436.28313
-  tps: 3739.17223
+  dps: 1295.62188
+  tps: 3341.52155
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-IncisorFragment-37723"
  value: {
-  dps: 1477.31363
-  tps: 3830.89904
+  dps: 1325.06872
+  tps: 3409.61163
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 1429.13677
-  tps: 3723.6434
+  dps: 1288.48948
+  tps: 3326.07038
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 1439.81394
-  tps: 3750.17262
-  hps: 16.38289
+  dps: 1292.37353
+  tps: 3334.12396
+  hps: 15.35543
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-LastWord-50179"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-LastWord-50708"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 1456.808
-  tps: 3793.30312
+  dps: 1302.08299
+  tps: 3361.96359
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 1487.95987
-  tps: 3858.19024
+  dps: 1342.00137
+  tps: 3451.0587
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 1488.45581
-  tps: 3857.26512
+  dps: 1341.11428
+  tps: 3446.17637
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 1439.06283
-  tps: 3744.35518
+  dps: 1291.10608
+  tps: 3331.4959
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-OnslaughtArmor"
  value: {
-  dps: 1143.96277
-  tps: 3010.38028
+  dps: 1032.96893
+  tps: 2697.01584
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-OnslaughtBattlegear"
  value: {
-  dps: 1212.73705
-  tps: 3132.14296
+  dps: 1073.2847
+  tps: 2744.73876
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 1441.44826
-  tps: 3755.28496
+  dps: 1291.63371
+  tps: 3332.58995
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 1439.81394
-  tps: 3750.17262
+  dps: 1292.37353
+  tps: 3334.12396
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 1438.52633
-  tps: 3741.00002
+  dps: 1295.55527
+  tps: 3340.7213
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 1437.28281
-  tps: 3735.73938
+  dps: 1296.3105
+  tps: 3342.28727
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 1429.13677
-  tps: 3723.6434
+  dps: 1288.48948
+  tps: 3326.07038
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 1429.13677
-  tps: 3723.6434
+  dps: 1288.48948
+  tps: 3326.07038
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 1464.99517
-  tps: 3809.20556
+  dps: 1311.03851
+  tps: 3379.80384
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 1429.13677
-  tps: 3723.6434
+  dps: 1288.48948
+  tps: 3326.07038
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 1432.85618
-  tps: 3727.67586
+  dps: 1291.56894
+  tps: 3332.45565
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Shadowmourne-49623"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SiegebreakerBattlegear"
  value: {
-  dps: 1528.60272
-  tps: 3922.80255
+  dps: 1348.66714
+  tps: 3431.43035
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SiegebreakerPlate"
  value: {
-  dps: 1362.18488
-  tps: 3534.24554
+  dps: 1226.35732
+  tps: 3155.63414
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SoulPreserver-37111"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SouloftheDead-40382"
  value: {
-  dps: 1468.61799
-  tps: 3808.46535
+  dps: 1324.25226
+  tps: 3408.21696
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SparkofLife-37657"
  value: {
-  dps: 1440.66133
-  tps: 3742.16595
+  dps: 1309.36532
+  tps: 3376.66033
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 1478.71065
-  tps: 3846.54338
+  dps: 1322.14884
+  tps: 3404.71972
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-StormshroudArmor"
  value: {
-  dps: 1131.41118
-  tps: 2973.77697
+  dps: 1027.97703
+  tps: 2682.10178
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 1439.81394
-  tps: 3750.17262
+  dps: 1292.37353
+  tps: 3334.12396
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 1441.44826
-  tps: 3755.28496
+  dps: 1291.63371
+  tps: 3332.58995
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 1434.21835
-  tps: 3734.84239
+  dps: 1290.33903
+  tps: 3329.90542
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TheFistsofFury"
  value: {
-  dps: 1002.18698
-  tps: 2269.92269
+  dps: 638.3805
+  tps: 1332.04643
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TheTwinBladesofAzzinoth"
  value: {
-  dps: 1067.10265
-  tps: 2408.07406
+  dps: 672.48703
+  tps: 1402.91131
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 1433.77973
-  tps: 3730.22067
+  dps: 1295.25946
+  tps: 3338.84336
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 1478.24422
-  tps: 3833.2328
+  dps: 1336.15076
+  tps: 3441.80734
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 1467.27925
-  tps: 3802.76201
+  dps: 1350.48168
+  tps: 3475.99802
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 1429.13677
-  tps: 3723.6434
+  dps: 1288.48948
+  tps: 3326.07038
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 1429.13677
-  tps: 3723.6434
+  dps: 1288.48948
+  tps: 3326.07038
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 1436.78984
-  tps: 3728.88041
+  dps: 1308.92313
+  tps: 3378.26678
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 1429.13677
-  tps: 3723.6434
+  dps: 1288.48948
+  tps: 3326.07038
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 1429.13677
-  tps: 3723.6434
+  dps: 1288.48948
+  tps: 3326.07038
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-UndeadSlayer'sBlessedArmor"
  value: {
-  dps: 1207.40792
-  tps: 3148.8019
+  dps: 1086.63762
+  tps: 2805.44607
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Val'anyr,HammerofAncientKings-46017"
  value: {
-  dps: 1320.86781
-  tps: 3455.68965
+  dps: 1216.28045
+  tps: 3164.34375
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-WingedTalisman-37844"
  value: {
-  dps: 1443.50947
-  tps: 3757.45627
+  dps: 1289.69323
+  tps: 3328.56635
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Wrynn'sBattlegear"
  value: {
-  dps: 1715.22713
-  tps: 4343.02026
+  dps: 1501.65236
+  tps: 3779.411
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Wrynn'sPlate"
  value: {
-  dps: 1438.9139
-  tps: 3715.52095
+  dps: 1310.8597
+  tps: 3363.59082
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-YmirjarLord'sBattlegear"
  value: {
-  dps: 1915.35055
-  tps: 4811.31977
+  dps: 1681.60003
+  tps: 4188.20074
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-YmirjarLord'sPlate"
  value: {
-  dps: 1565.56182
-  tps: 4034.16559
+  dps: 1425.09691
+  tps: 3644.74776
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Average-Default"
  value: {
-  dps: 2771.65176
-  tps: 6733.04173
-  dtps: 127.4897
+  dps: 2540.72733
+  tps: 6095.0764
+  dtps: 127.44615
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Human-p1_balanced-Basic-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 1476.36368
-  tps: 3891.49394
+  dps: 1353.97589
+  tps: 3511.30182
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Human-p1_balanced-Basic-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 1476.36368
-  tps: 3840.82728
+  dps: 1353.97589
+  tps: 3501.81765
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Human-p1_balanced-Basic-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 1639.76544
-  tps: 4236.26058
+  dps: 1561.83404
+  tps: 4024.60825
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Human-p1_balanced-Basic-default-NoBuffs-LongMultiTarget"
  value: {
-  dps: 1084.94322
-  tps: 3407.20547
+  dps: 1094.86734
+  tps: 3435.07618
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Human-p1_balanced-Basic-default-NoBuffs-LongSingleTarget"
  value: {
-  dps: 866.97075
-  tps: 2487.10038
+  dps: 882.0064
+  tps: 2533.03893
  }
 }
 dps_results: {
@@ -971,36 +971,36 @@ dps_results: {
 dps_results: {
  key: "TestProtectionWarrior-Settings-Orc-p1_balanced-Basic-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 1493.10216
-  tps: 3930.30504
+  dps: 1360.20142
+  tps: 3524.55292
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Orc-p1_balanced-Basic-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 1493.10216
-  tps: 3879.63837
+  dps: 1360.20142
+  tps: 3515.09003
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Orc-p1_balanced-Basic-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 1681.29892
-  tps: 4344.50046
+  dps: 1573.75672
+  tps: 4049.75167
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Orc-p1_balanced-Basic-default-NoBuffs-LongMultiTarget"
  value: {
-  dps: 1097.36592
-  tps: 3443.56821
+  dps: 1104.93219
+  tps: 3468.9695
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Orc-p1_balanced-Basic-default-NoBuffs-LongSingleTarget"
  value: {
-  dps: 881.93425
-  tps: 2531.41294
+  dps: 885.75406
+  tps: 2542.0882
  }
 }
 dps_results: {
@@ -1013,8 +1013,8 @@ dps_results: {
 dps_results: {
  key: "TestProtectionWarrior-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 2890.35455
-  tps: 7022.68811
-  dtps: 120.81343
+  dps: 2658.90648
+  tps: 6391.54637
+  dtps: 120.61498
  }
 }
diff --git a/sim/warrior/protection/protection_warrior.go b/sim/warrior/protection/protection_warrior.go
index d8c4ce30fb..7b9ab5899d 100644
--- a/sim/warrior/protection/protection_warrior.go
+++ b/sim/warrior/protection/protection_warrior.go
@@ -1,7 +1,6 @@
 package protection
 
 import (
-	"github.com/wowsims/wotlk/sim/common"
 	"github.com/wowsims/wotlk/sim/core"
 	"github.com/wowsims/wotlk/sim/core/proto"
 	"github.com/wowsims/wotlk/sim/warrior"
@@ -27,21 +26,15 @@ func RegisterProtectionWarrior() {
 type ProtectionWarrior struct {
 	*warrior.Warrior
 
-	Rotation *proto.ProtectionWarrior_Rotation
-	Options  *proto.ProtectionWarrior_Options
-
-	CustomRotation *common.CustomRotation
+	Options *proto.ProtectionWarrior_Options
 }
 
 func NewProtectionWarrior(character *core.Character, options *proto.Player) *ProtectionWarrior {
 	warOptions := options.GetProtectionWarrior()
 
 	war := &ProtectionWarrior{
-		Warrior: warrior.NewWarrior(character, options.TalentsString, warrior.WarriorInputs{
-			ShoutType: warOptions.Options.Shout,
-		}),
-		Rotation: warOptions.Rotation,
-		Options:  warOptions.Options,
+		Warrior: warrior.NewWarrior(character, options.TalentsString, warrior.WarriorInputs{}),
+		Options: warOptions.Options,
 	}
 
 	rbo := core.RageBarOptions{
@@ -80,8 +73,6 @@ func (war *ProtectionWarrior) GetWarrior() *warrior.Warrior {
 func (war *ProtectionWarrior) Initialize() {
 	war.Warrior.Initialize()
 
-	war.RegisterHSOrCleave(false, war.Rotation.HsRageThreshold)
-	war.RegisterRendSpell(0, 0)
 	war.RegisterShieldWallCD()
 	war.RegisterShieldBlockCD()
 	war.DefensiveStanceAura.BuildPhase = core.CharacterBuildPhaseTalents
diff --git a/sim/warrior/protection/rotation.go b/sim/warrior/protection/rotation.go
deleted file mode 100644
index eac4131895..0000000000
--- a/sim/warrior/protection/rotation.go
+++ /dev/null
@@ -1,113 +0,0 @@
-package protection
-
-import (
-	"github.com/wowsims/wotlk/sim/common"
-	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-	"github.com/wowsims/wotlk/sim/warrior"
-)
-
-func (war *ProtectionWarrior) OnGCDReady(sim *core.Simulation) {
-	war.doRotation(sim)
-}
-
-func (war *ProtectionWarrior) OnAutoAttack(sim *core.Simulation, spell *core.Spell) {
-	war.tryQueueHsCleave(sim)
-}
-
-func (war *ProtectionWarrior) doRotation(sim *core.Simulation) {
-	war.trySwapToDefensive(sim)
-	war.CustomRotation.Cast(sim)
-
-	// if we did nothing else, mark we intentionally did nothing here.
-	if war.GCD.IsReady(sim) {
-		war.DoNothing()
-	}
-}
-
-func (war *ProtectionWarrior) tryQueueHsCleave(sim *core.Simulation) {
-	if war.ShouldQueueHSOrCleave(sim) {
-		war.QueueHSOrCleave(sim)
-	}
-}
-
-func (war *ProtectionWarrior) shouldDemoShout(sim *core.Simulation) bool {
-	return war.ShouldDemoralizingShout(sim, war.CurrentTarget,
-		war.Rotation.DemoShoutChoice == proto.ProtectionWarrior_Rotation_DemoShoutChoiceFiller,
-		war.Rotation.DemoShoutChoice == proto.ProtectionWarrior_Rotation_DemoShoutChoiceMaintain)
-}
-
-func (war *ProtectionWarrior) shouldThunderClap(sim *core.Simulation) bool {
-	return war.ShouldThunderClap(sim, war.CurrentTarget,
-		war.Rotation.ThunderClapChoice == proto.ProtectionWarrior_Rotation_ThunderClapChoiceOnCD,
-		war.Rotation.ThunderClapChoice == proto.ProtectionWarrior_Rotation_ThunderClapChoiceMaintain,
-		false)
-}
-
-func (war *ProtectionWarrior) trySwapToDefensive(sim *core.Simulation) bool {
-	if !war.StanceMatches(warrior.DefensiveStance) && war.DefensiveStance.IsReady(sim) {
-		war.DefensiveStance.Cast(sim, nil)
-		return true
-	}
-	return false
-}
-
-func (war *ProtectionWarrior) makeCustomRotation() *common.CustomRotation {
-	return common.NewCustomRotation(war.Rotation.CustomRotation, war.GetCharacter(), map[int32]common.CustomSpell{
-		int32(proto.ProtectionWarrior_Rotation_Revenge): {
-			Spell: war.Revenge,
-			Condition: func(sim *core.Simulation) bool {
-				if !war.Rotation.PrioSslamOnShieldBlock {
-					return war.Revenge.CanCast(sim, war.CurrentTarget)
-				}
-
-				if war.ShieldBlockAura.IsActive() {
-					return !war.ShieldSlam.CanCast(sim, war.CurrentTarget) && war.Revenge.CanCast(sim, war.CurrentTarget)
-				} else {
-					return war.Revenge.CanCast(sim, war.CurrentTarget)
-				}
-			},
-		},
-		int32(proto.ProtectionWarrior_Rotation_ShieldSlam): {
-			Spell: war.ShieldSlam,
-			Condition: func(sim *core.Simulation) bool {
-				if !war.Rotation.PrioSslamOnShieldBlock {
-					return war.ShieldSlam.CanCast(sim, war.CurrentTarget)
-				}
-
-				if war.ShieldBlockAura.IsActive() {
-					return war.ShieldSlam.CanCast(sim, war.CurrentTarget)
-				} else {
-					return !war.Revenge.CanCast(sim, war.CurrentTarget) && war.ShieldSlam.CanCast(sim, war.CurrentTarget)
-				}
-			},
-		},
-		int32(proto.ProtectionWarrior_Rotation_Devastate): {
-			Spell: war.Devastate,
-		},
-		int32(proto.ProtectionWarrior_Rotation_SunderArmor): {
-			Spell: war.SunderArmor,
-		},
-		int32(proto.ProtectionWarrior_Rotation_DemoralizingShout): {
-			Spell:     war.DemoralizingShout,
-			Condition: war.shouldDemoShout,
-		},
-		int32(proto.ProtectionWarrior_Rotation_ThunderClap): {
-			Spell:     war.ThunderClap,
-			Condition: war.shouldThunderClap,
-		},
-		int32(proto.ProtectionWarrior_Rotation_Shout): {
-			Spell:     war.Shout,
-			Condition: war.ShouldShout,
-		},
-		int32(proto.ProtectionWarrior_Rotation_MortalStrike): {
-			Spell: war.MortalStrike,
-		},
-		int32(proto.ProtectionWarrior_Rotation_ConcussionBlow): {
-			Spell: war.ConcussionBlow,
-		},
-		int32(proto.ProtectionWarrior_Rotation_Shockwave): {
-			Spell: war.Shockwave,
-		},
-	})
-}
diff --git a/sim/warrior/rend.go b/sim/warrior/rend.go
index 06df46ca63..9efc680e2e 100644
--- a/sim/warrior/rend.go
+++ b/sim/warrior/rend.go
@@ -8,7 +8,7 @@ import (
 )
 
 // TODO (maybe) https://github.com/magey/wotlk-warrior/issues/23 - Rend is not benefitting from Two-Handed Weapon Specialization
-func (warrior *Warrior) RegisterRendSpell(rageThreshold float64, healthThreshold float64) {
+func (warrior *Warrior) RegisterRendSpell() {
 	dotDuration := time.Second * 15
 	dotTicks := int32(5)
 	if warrior.HasMajorGlyph(proto.WarriorMajorGlyph_GlyphOfRending) {
@@ -70,27 +70,6 @@ func (warrior *Warrior) RegisterRendSpell(rageThreshold float64, healthThreshold
 			}
 
 			spell.DealOutcome(sim, result)
-
-			// Queue Overpower to be cast at 3s after rend if talented for 3/3 TfB
-			if warrior.Talents.TasteForBlood == 3 {
-				core.StartDelayedAction(sim, core.DelayedActionOptions{
-					DoAt: sim.CurrentTime + time.Second*3,
-					OnAction: func(_ *core.Simulation) {
-						// Force to use OP on first 3s due to AM ticks that happens before TfB procs and break that
-						if warrior.Overpower.CanCast(sim, target) && (warrior.ShouldOverpower(sim) || sim.CurrentTime >= time.Second*3 && sim.CurrentTime <= time.Second*4) {
-							warrior.CastFullTfbOverpower(sim, target)
-						}
-					},
-				})
-			}
 		},
 	})
-
-	warrior.RendHealthThresholdAbove = healthThreshold / 100
-	warrior.RendRageThresholdBelow = max(warrior.Rend.DefaultCast.Cost, rageThreshold)
-}
-
-func (warrior *Warrior) ShouldRend(sim *core.Simulation) bool {
-	return warrior.Rend.IsReady(sim) && sim.CurrentTime >= (warrior.RendValidUntil-warrior.RendCdThreshold) &&
-		warrior.CurrentRage() >= warrior.Rend.DefaultCast.Cost && warrior.RendHealthThresholdAbove < sim.GetRemainingDurationPercent()
 }
diff --git a/sim/warrior/shouts.go b/sim/warrior/shouts.go
index 8bf929945a..e592990b1b 100644
--- a/sim/warrior/shouts.go
+++ b/sim/warrior/shouts.go
@@ -36,24 +36,12 @@ func (warrior *Warrior) makeShoutSpellHelper(actionID core.ActionID, allyAuras c
 	})
 }
 
-func (warrior *Warrior) makeShoutSpell() *core.Spell {
-	battleShout := warrior.makeShoutSpellHelper(core.ActionID{SpellID: 47436}, warrior.NewAllyAuraArray(func(unit *core.Unit) *core.Aura {
+func (warrior *Warrior) registerShouts() {
+	warrior.BattleShout = warrior.makeShoutSpellHelper(core.ActionID{SpellID: 47436}, warrior.NewAllyAuraArray(func(unit *core.Unit) *core.Aura {
 		return core.BattleShoutAura(unit, warrior.Talents.CommandingPresence, warrior.Talents.BoomingVoice, warrior.HasMinorGlyph(proto.WarriorMinorGlyph_GlyphOfBattle))
 	}))
 
-	commandingShout := warrior.makeShoutSpellHelper(core.ActionID{SpellID: 47440}, warrior.NewAllyAuraArray(func(unit *core.Unit) *core.Aura {
+	warrior.CommandingShout = warrior.makeShoutSpellHelper(core.ActionID{SpellID: 47440}, warrior.NewAllyAuraArray(func(unit *core.Unit) *core.Aura {
 		return core.CommandingShoutAura(unit, warrior.Talents.CommandingPresence, warrior.Talents.BoomingVoice, warrior.HasMinorGlyph(proto.WarriorMinorGlyph_GlyphOfCommand))
 	}))
-
-	if warrior.ShoutType == proto.WarriorShout_WarriorShoutBattle {
-		return battleShout
-	} else if warrior.ShoutType == proto.WarriorShout_WarriorShoutCommanding {
-		return commandingShout
-	} else {
-		return nil
-	}
-}
-
-func (warrior *Warrior) ShouldShout(sim *core.Simulation) bool {
-	return warrior.Shout != nil && warrior.CurrentRage() >= warrior.Shout.DefaultCast.Cost && warrior.Shout.ShouldRefreshExclusiveEffects(sim, &warrior.Unit, ShoutExpirationThreshold)
 }
diff --git a/sim/warrior/warrior.go b/sim/warrior/warrior.go
index 0075ca70bb..17d48abdf7 100644
--- a/sim/warrior/warrior.go
+++ b/sim/warrior/warrior.go
@@ -11,13 +11,7 @@ import (
 var TalentTreeSizes = [3]int{31, 27, 27}
 
 type WarriorInputs struct {
-	ShoutType                   proto.WarriorShout
-	PrecastShout                bool
-	PrecastShoutSapphire        bool
-	PrecastShoutT2              bool
-	RendCdThreshold             time.Duration
-	BloodsurgeDurationThreshold time.Duration
-	StanceSnapshot              bool
+	StanceSnapshot bool
 }
 
 const (
@@ -48,7 +42,8 @@ type Warrior struct {
 	lastOverpowerProc  time.Duration
 	LastAMTick         time.Duration
 
-	Shout           *core.Spell
+	BattleShout     *core.Spell
+	CommandingShout *core.Spell
 	BattleStance    *core.Spell
 	DefensiveStance *core.Spell
 	BerserkerStance *core.Spell
@@ -76,16 +71,12 @@ type Warrior struct {
 	Bladestorm           *core.Spell
 	BladestormOH         *core.Spell
 
-	HeroicStrike         *core.Spell
-	Cleave               *core.Spell
-	hsOrCleaveQueueSpell *core.Spell
-	curQueueAura         *core.Aura
-	curQueuedAutoSpell   *core.Spell
+	HeroicStrike       *core.Spell
+	Cleave             *core.Spell
+	curQueueAura       *core.Aura
+	curQueuedAutoSpell *core.Spell
 
-	OverpowerAura            *core.Aura
-	HSRageThreshold          float64
-	RendRageThresholdBelow   float64
-	RendHealthThresholdAbove float64
+	OverpowerAura *core.Aura
 
 	BattleStanceAura    *core.Aura
 	DefensiveStanceAura *core.Aura
@@ -117,19 +108,20 @@ func (warrior *Warrior) Initialize() {
 	warrior.AutoAttacks.MHConfig().CritMultiplier = warrior.autoCritMultiplier(mh)
 	warrior.AutoAttacks.OHConfig().CritMultiplier = warrior.autoCritMultiplier(oh)
 
-	warrior.Shout = warrior.makeShoutSpell()
-
 	primaryTimer := warrior.NewTimer()
 	overpowerRevengeTimer := warrior.NewTimer()
 
 	warrior.reactionTime = time.Millisecond * 500
 
+	warrior.registerShouts()
 	warrior.registerStances()
 	warrior.registerBerserkerRageSpell()
 	warrior.registerBloodthirstSpell(primaryTimer)
+	warrior.registerCleaveSpell()
 	warrior.registerDemoralizingShoutSpell()
 	warrior.registerDevastateSpell()
 	warrior.registerExecuteSpell()
+	warrior.registerHeroicStrikeSpell()
 	warrior.registerMortalStrikeSpell(primaryTimer)
 	warrior.registerOverpowerSpell(overpowerRevengeTimer)
 	warrior.registerRevengeSpell(overpowerRevengeTimer)
@@ -140,17 +132,12 @@ func (warrior *Warrior) Initialize() {
 	warrior.registerShockwaveSpell()
 	warrior.registerConcussionBlowSpell()
 	warrior.RegisterHeroicThrow()
+	warrior.RegisterRendSpell()
 
 	warrior.SunderArmor = warrior.newSunderArmorSpell(false)
 	warrior.SunderArmorDevastate = warrior.newSunderArmorSpell(true)
 
 	warrior.registerBloodrageCD()
-
-	if !warrior.IsUsingAPL && warrior.Shout != nil && warrior.PrecastShout {
-		warrior.RegisterPrepullAction(-10*time.Second, func(sim *core.Simulation) {
-			warrior.Shout.SkipCastAndApplyEffects(sim, nil)
-		})
-	}
 }
 
 func (warrior *Warrior) Reset(_ *core.Simulation) {

From 10f8370260a73181c8719aefca4efba5e50f0c13 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Sat, 6 Jan 2024 15:21:56 -0800
Subject: [PATCH 15/28] Fix ui compile error

---
 ui/core/components/individual_sim_ui/apl_values.ts | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/ui/core/components/individual_sim_ui/apl_values.ts b/ui/core/components/individual_sim_ui/apl_values.ts
index a74c7a6aca..85cecb6e28 100644
--- a/ui/core/components/individual_sim_ui/apl_values.ts
+++ b/ui/core/components/individual_sim_ui/apl_values.ts
@@ -48,7 +48,6 @@ import {
 	APLValueSpellTimeToReady,
 	APLValueSpellCastTime,
 	APLValueSpellTravelTime,
-	APLValueSpellChannelTime,
 	APLValueSpellCPM,
 	APLValueSpellIsChanneling,
 	APLValueSpellChanneledTicks,
@@ -825,15 +824,6 @@ const valueKindFactories: {[f in NonNullable<APLValueKind>]: ValueKindConfig<APL
 			AplHelpers.actionIdFieldConfig('spellId', 'castable_spells', ''),
 		],
 	}),
-	'spellChannelTime': inputBuilder({
-		label: 'Channel Time',
-		submenu: ['Spell'],
-		shortDescription: 'Amount of time to fully channel the spell including any haste and spell cast time adjustments.',
-		newValue: APLValueSpellChannelTime.create,
-		fields: [
-			AplHelpers.actionIdFieldConfig('spellId', 'channel_spells', ''),
-		],
-	}),
 	'spellIsChanneling': inputBuilder({
 		label: 'Is Channeling',
 		submenu: ['Spell'],

From faff70cfba2e30321d2bd1dccec12a1a60e8cd31 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Sat, 6 Jan 2024 19:23:48 -0800
Subject: [PATCH 16/28] Commit before making another DK fix on master

---
 proto/deathknight.proto                       | 133 ------------------
 sim/deathknight/anti_magic_shell.go           |   6 +-
 sim/deathknight/deathknight.go                |  48 ++-----
 sim/deathknight/dps/dps_deathknight.go        |  26 ++--
 sim/deathknight/dps/dps_deathknight_test.go   |  56 ++------
 sim/deathknight/horn_of_winter.go             |  27 ----
 sim/deathknight/summon_gargoyle.go            |  27 ++--
 sim/deathknight/tank/tank_deathknight.go      |   2 +-
 sim/deathknight/tank/tank_deathknight_test.go |   1 -
 9 files changed, 42 insertions(+), 284 deletions(-)

diff --git a/proto/deathknight.proto b/proto/deathknight.proto
index 4f9d2955ff..feeaa4083c 100644
--- a/proto/deathknight.proto
+++ b/proto/deathknight.proto
@@ -143,143 +143,12 @@ enum DeathknightMinorGlyph {
 
 message Deathknight {
 	message Rotation {
-		enum DrwDiseases {
-			DoNotApply = 0;
-			Normal = 1;
-			Pestilence = 2;
-		}
-
-		enum ArmyOfTheDead {
-			DoNotUse = 0;
-			PreCast = 1;
-			AsMajorCd = 2;
-		}
-
-		enum BloodTap {
-			GhoulFrenzy = 0;
-			IcyTouch = 1;
-			BloodStrikeBT = 2;
-			BloodBoilBT = 3;
-		}
-
-		enum DeathAndDecayPrio {
-			MaxRuneDowntime = 0;
-			MaxDndUptime = 1;
-		}
-
-		enum FirstDisease {
-			FrostFever = 0;
-			BloodPlague = 1;
-		}
-
-		enum BloodRuneFiller {
-			BloodStrike = 0;
-			BloodBoil = 1;
-		}
-
-		enum Presence {
-			Blood = 0;
-			Unholy = 1;
-			Frost = 2;
-		}
-
-		enum FrostRotationType {
-			UnknownType = 0;
-			SingleTarget = 1;
-			Custom = 2;
-		}
-		FrostRotationType frost_rotation_type = 19;
-
-		enum BloodOpener {
-			Standard = 0;
-			Experimental_1 = 1;
-			Experimental_2 = 2;
-		}
-
-		enum BloodSpell {
-			HS = 0;
-			BB = 1;
-			BS = 2;
-		}
-		
-		enum CustomSpellOption {
-			CustomNoSpell = 0;
-			CustomIcyTouch = 1;
-			CustomPlagueStrike = 2;
-			CustomPestilence = 3;
-			CustomObliterate = 4;
-			CustomHowlingBlast = 5;
-			CustomHowlingBlastRime = 6;
-			CustomBloodBoil = 7;
-			CustomBloodStrike = 8;
-			CustomDeathAndDecay = 9;
-			CustomHornOfWinter = 10;
-			CustomUnbreakableArmor = 11;
-			CustomBloodTap = 12;
-			CustomEmpoweredRuneWeapon = 13;
-			CustomFrostStrike = 14;
-		}
-		CustomRotation frost_custom_rotation = 20;
-
-		ArmyOfTheDead army_of_the_dead = 1;
-		bool use_death_and_decay = 2;
-		
-		bool bt_ghoul_frenzy = 3;
-		
-		double disease_refresh_duration = 4;
-		bool refresh_horn_of_winter = 5;
-		
-		FirstDisease first_disease = 6;
-		
-		DeathAndDecayPrio death_and_decay_prio = 7;
-
-		bool use_empower_rune_weapon = 8;
-
-		BloodRuneFiller blood_rune_filler = 9;
-
-		Presence starting_presence = 10;
-		BloodTap blood_tap = 11;
-
-		bool use_ams = 13 [deprecated = true];
-		double avg_ams_success_rate = 14 [deprecated = true];
-		double avg_ams_hit = 15 [deprecated = true];
-
-		bool auto_rotation = 16;
-
-		bool desync_rotation = 17;
-
-		Presence bl_presence = 18;
-
-		Presence presence = 21;
-
-		bool hold_erw_army = 22;
-		
-		bool use_gargoyle = 23;
-
-		bool pre_nerfed_gargoyle = 24;
-
-		Presence gargoyle_presence = 25;
-
-		DrwDiseases drw_diseases = 26;
-		BloodOpener blood_opener = 27;
-
-		bool enable_weapon_swap = 28;
-		ItemSwap weapon_swap = 29;
-
-		bool use_dancing_rune_weapon = 30;
-
-		BloodSpell blood_spender = 31;
-
-		double virulence_refresh = 32;
 	}
 	Rotation rotation = 1;
 	
 	message Options {
 		double starting_runic_power = 1;
 		double pet_uptime = 2;
-		
-		bool precast_ghoul_frenzy = 3;
-		bool precast_horn_of_winter = 4;
 
 		UnitReference unholy_frenzy_target = 5;
 
@@ -287,8 +156,6 @@ message Deathknight {
 
 		bool new_drw = 7;
 
-		double disease_downtime = 8;
-
 		bool use_ams = 9;
 		double avg_ams_success_rate = 10;
 		double avg_ams_hit = 11;
diff --git a/sim/deathknight/anti_magic_shell.go b/sim/deathknight/anti_magic_shell.go
index 79a70d47d8..d46cf66bc3 100644
--- a/sim/deathknight/anti_magic_shell.go
+++ b/sim/deathknight/anti_magic_shell.go
@@ -10,8 +10,6 @@ import (
 
 func (dk *Deathknight) registerAntiMagicShellSpell() {
 	actionID := core.ActionID{SpellID: 48707}
-	cdTimer := dk.NewTimer()
-	cd := time.Second * 45
 
 	dk.AntiMagicShell = dk.RegisterSpell(core.SpellConfig{
 		ActionID: actionID,
@@ -22,8 +20,8 @@ func (dk *Deathknight) registerAntiMagicShellSpell() {
 		},
 		Cast: core.CastConfig{
 			CD: core.Cooldown{
-				Timer:    cdTimer,
-				Duration: cd,
+				Timer:    dk.NewTimer(),
+				Duration: time.Second * 45,
 			},
 			IgnoreHaste: true,
 		},
diff --git a/sim/deathknight/deathknight.go b/sim/deathknight/deathknight.go
index 9c05192037..61c53fe706 100644
--- a/sim/deathknight/deathknight.go
+++ b/sim/deathknight/deathknight.go
@@ -10,14 +10,6 @@ import (
 	"github.com/wowsims/wotlk/sim/core/stats"
 )
 
-type Rotation_FuStrike int32
-
-const (
-	FuStrike_DeathStrike   Rotation_FuStrike = 0
-	FuStrike_ScourgeStrike Rotation_FuStrike = 1
-	FuStrike_Obliterate    Rotation_FuStrike = 2
-)
-
 const (
 	PetSpellHitScale   = 17.0 / 8.0 * core.SpellHitRatingPerHitChance / core.MeleeHitRatingPerHitChance    // 1.7
 	PetExpertiseScale  = 3.25 * core.ExpertisePerQuarterPercentReduction / core.MeleeHitRatingPerHitChance // 0.8125
@@ -33,23 +25,14 @@ type DeathknightInputs struct {
 
 	UnholyFrenzyTarget *proto.UnitReference
 
-	StartingRunicPower  float64
-	PrecastGhoulFrenzy  bool
-	PrecastHornOfWinter bool
-	PetUptime           float64
-	DrwPestiApply       bool
-	BloodOpener         proto.Deathknight_Rotation_BloodOpener
+	StartingRunicPower float64
+	PetUptime          float64
+	DrwPestiApply      bool
 
 	// Rotation Vars
-	RefreshHornOfWinter bool
-	ArmyOfTheDeadType   proto.Deathknight_Rotation_ArmyOfTheDead
-	StartingPresence    proto.Deathknight_Rotation_Presence
-	UseAMS              bool
-	AvgAMSSuccessRate   float64
-	AvgAMSHit           float64
-	FuStrike            Rotation_FuStrike
-	DiseaseDowntime     float64
-	VirulenceRefresh    float64
+	UseAMS            bool
+	AvgAMSSuccessRate float64
+	AvgAMSHit         float64
 }
 
 type DeathknightCoeffs struct {
@@ -143,10 +126,8 @@ type Deathknight struct {
 
 	HowlingBlast *core.Spell
 
-	HasDraeneiHitAura         bool
-	OtherRelevantStrAgiActive bool
-	HornOfWinter              *core.Spell
-	HornOfWinterAura          *core.Aura
+	HasDraeneiHitAura bool
+	HornOfWinter      *core.Spell
 
 	// "CDs"
 	RuneTap     *core.Spell
@@ -257,14 +238,7 @@ func (dk *Deathknight) AddRaidBuffs(raidBuffs *proto.RaidBuffs) {
 		raidBuffs.IcyTalons = true
 	}
 
-	raidBuffs.HornOfWinter = !dk.Inputs.RefreshHornOfWinter
-
-	if raidBuffs.StrengthOfEarthTotem == proto.TristateEffect_TristateEffectImproved ||
-		raidBuffs.StrengthOfEarthTotem == proto.TristateEffect_TristateEffectRegular {
-		dk.OtherRelevantStrAgiActive = true
-	} else {
-		dk.OtherRelevantStrAgiActive = false
-	}
+	raidBuffs.HornOfWinter = true
 }
 
 func (dk *Deathknight) ApplyTalents() {
@@ -379,7 +353,7 @@ func (dk *Deathknight) HasMinorGlyph(glyph proto.DeathknightMinorGlyph) bool {
 	return dk.HasGlyph(int32(glyph))
 }
 
-func NewDeathknight(character *core.Character, inputs DeathknightInputs, talents string, preNerfedGargoyle bool) *Deathknight {
+func NewDeathknight(character *core.Character, inputs DeathknightInputs, talents string) *Deathknight {
 	dk := &Deathknight{
 		Character:  *character,
 		Talents:    &proto.DeathknightTalents{},
@@ -421,7 +395,7 @@ func NewDeathknight(character *core.Character, inputs DeathknightInputs, talents
 	dk.PseudoStats.MeleeHasteRatingPerHastePercent /= 1.3
 
 	if dk.Talents.SummonGargoyle {
-		dk.Gargoyle = dk.NewGargoyle(!preNerfedGargoyle)
+		dk.Gargoyle = dk.NewGargoyle()
 	}
 
 	dk.Ghoul = dk.NewGhoulPet(dk.Talents.MasterOfGhouls)
diff --git a/sim/deathknight/dps/dps_deathknight.go b/sim/deathknight/dps/dps_deathknight.go
index b5d8c369d1..de021426d0 100644
--- a/sim/deathknight/dps/dps_deathknight.go
+++ b/sim/deathknight/dps/dps_deathknight.go
@@ -32,24 +32,16 @@ func NewDpsDeathknight(character *core.Character, player *proto.Player) *DpsDeat
 
 	dpsDk := &DpsDeathknight{
 		Deathknight: deathknight.NewDeathknight(character, deathknight.DeathknightInputs{
-			StartingRunicPower:  dk.Options.StartingRunicPower,
-			PrecastGhoulFrenzy:  dk.Options.PrecastGhoulFrenzy,
-			PrecastHornOfWinter: dk.Options.PrecastHornOfWinter,
-			PetUptime:           dk.Options.PetUptime,
-			DrwPestiApply:       dk.Options.DrwPestiApply,
-			BloodOpener:         dk.Rotation.BloodOpener,
-			IsDps:               true,
-			NewDrw:              true,
-			DiseaseDowntime:     dk.Options.DiseaseDowntime,
-			VirulenceRefresh:    dk.Rotation.VirulenceRefresh,
+			StartingRunicPower: dk.Options.StartingRunicPower,
+			PetUptime:          dk.Options.PetUptime,
+			DrwPestiApply:      dk.Options.DrwPestiApply,
+			IsDps:              true,
+			NewDrw:             true,
 
-			RefreshHornOfWinter: dk.Rotation.RefreshHornOfWinter,
-			ArmyOfTheDeadType:   dk.Rotation.ArmyOfTheDead,
-			StartingPresence:    dk.Rotation.StartingPresence,
-			UseAMS:              dk.Options.UseAms,
-			AvgAMSSuccessRate:   dk.Options.AvgAmsSuccessRate,
-			AvgAMSHit:           dk.Options.AvgAmsHit,
-		}, player.TalentsString, dk.Rotation.PreNerfedGargoyle),
+			UseAMS:            dk.Options.UseAms,
+			AvgAMSSuccessRate: dk.Options.AvgAmsSuccessRate,
+			AvgAMSHit:         dk.Options.AvgAmsHit,
+		}, player.TalentsString),
 	}
 
 	dpsDk.Inputs.UnholyFrenzyTarget = dk.Options.UnholyFrenzyTarget
diff --git a/sim/deathknight/dps/dps_deathknight_test.go b/sim/deathknight/dps/dps_deathknight_test.go
index 4aff6eb371..f0db8878b3 100644
--- a/sim/deathknight/dps/dps_deathknight_test.go
+++ b/sim/deathknight/dps/dps_deathknight_test.go
@@ -126,73 +126,33 @@ var UnholyDefaultGlyphs = &proto.Glyphs{
 
 var PlayerOptionsBlood = &proto.Player_Deathknight{
 	Deathknight: &proto.Deathknight{
-		Options:  deathKnightOptions,
-		Rotation: bloodRotation,
+		Options: deathKnightOptions,
 	},
 }
 
 var PlayerOptionsUnholy = &proto.Player_Deathknight{
 	Deathknight: &proto.Deathknight{
-		Options:  deathKnightOptions,
-		Rotation: unholyRotation,
+		Options: deathKnightOptions,
 	},
 }
 
 var PlayerOptionsFrost = &proto.Player_Deathknight{
 	Deathknight: &proto.Deathknight{
-		Options:  deathKnightOptions,
-		Rotation: frostRotation,
+		Options: deathKnightOptions,
 	},
 }
 
 var PlayerOptionsDesyncFrost = &proto.Player_Deathknight{
 	Deathknight: &proto.Deathknight{
-		Options:  deathKnightOptions,
-		Rotation: frostDesyncRotation,
+		Options: deathKnightOptions,
 	},
 }
 
-var bloodRotation = &proto.Deathknight_Rotation{
-	ArmyOfTheDead:        proto.Deathknight_Rotation_PreCast,
-	DrwDiseases:          proto.Deathknight_Rotation_Pestilence,
-	UseEmpowerRuneWeapon: true,
-	PreNerfedGargoyle:    false,
-	UseDancingRuneWeapon: true,
-	BloodSpender:         proto.Deathknight_Rotation_HS,
-}
-
-var unholyRotation = &proto.Deathknight_Rotation{
-	UseDeathAndDecay:     true,
-	StartingPresence:     proto.Deathknight_Rotation_Unholy,
-	BlPresence:           proto.Deathknight_Rotation_Blood,
-	Presence:             proto.Deathknight_Rotation_Blood,
-	GargoylePresence:     proto.Deathknight_Rotation_Unholy,
-	UseEmpowerRuneWeapon: true,
-	UseGargoyle:          true,
-	BtGhoulFrenzy:        false,
-	HoldErwArmy:          false,
-	PreNerfedGargoyle:    false,
-	BloodRuneFiller:      proto.Deathknight_Rotation_BloodBoil,
-	ArmyOfTheDead:        proto.Deathknight_Rotation_AsMajorCd,
-	BloodTap:             proto.Deathknight_Rotation_GhoulFrenzy,
-}
-
-var frostRotation = &proto.Deathknight_Rotation{
-	UseEmpowerRuneWeapon: true,
-}
-
-var frostDesyncRotation = &proto.Deathknight_Rotation{
-	UseEmpowerRuneWeapon: true,
-	DesyncRotation:       true,
-}
-
 var deathKnightOptions = &proto.Deathknight_Options{
-	UnholyFrenzyTarget:  &proto.UnitReference{Type: proto.UnitReference_Player, Index: 0},
-	DrwPestiApply:       true,
-	StartingRunicPower:  0,
-	PetUptime:           1,
-	PrecastGhoulFrenzy:  false,
-	PrecastHornOfWinter: true,
+	UnholyFrenzyTarget: &proto.UnitReference{Type: proto.UnitReference_Player, Index: 0},
+	DrwPestiApply:      true,
+	StartingRunicPower: 0,
+	PetUptime:          1,
 }
 
 var FullConsumes = &proto.Consumes{
diff --git a/sim/deathknight/horn_of_winter.go b/sim/deathknight/horn_of_winter.go
index 8554b8a488..1ef67ae6e6 100644
--- a/sim/deathknight/horn_of_winter.go
+++ b/sim/deathknight/horn_of_winter.go
@@ -4,35 +4,12 @@ import (
 	"time"
 
 	"github.com/wowsims/wotlk/sim/core"
-	"github.com/wowsims/wotlk/sim/core/proto"
-	"github.com/wowsims/wotlk/sim/core/stats"
 )
 
 func (dk *Deathknight) registerHornOfWinterSpell() {
 	actionID := core.ActionID{SpellID: 57623}
-	duration := time.Minute * time.Duration((2.0 + core.TernaryFloat64(dk.HasMinorGlyph(proto.DeathknightMinorGlyph_GlyphOfHornOfWinter), 1.0, 0.0)))
 	rpMetrics := dk.NewRunicPowerMetrics(actionID)
 
-	bonusStats := stats.Stats{stats.Strength: 155.0, stats.Agility: 155.0}
-	negativeStats := bonusStats.Invert()
-
-	dk.HornOfWinterAura = dk.RegisterAura(core.Aura{
-		Label:    "Horn of Winter",
-		ActionID: actionID,
-		Duration: duration,
-		OnGain: func(aura *core.Aura, sim *core.Simulation) {
-			if !dk.OtherRelevantStrAgiActive {
-				dk.HornOfWinterAura.Unit.AddStatsDynamic(sim, bonusStats)
-			}
-		},
-
-		OnExpire: func(aura *core.Aura, sim *core.Simulation) {
-			if !dk.OtherRelevantStrAgiActive {
-				dk.HornOfWinterAura.Unit.AddStatsDynamic(sim, negativeStats)
-			}
-		},
-	})
-
 	dk.HornOfWinter = dk.RegisterSpell(core.SpellConfig{
 		ActionID: actionID,
 		Flags:    core.SpellFlagAPL,
@@ -51,7 +28,3 @@ func (dk *Deathknight) registerHornOfWinterSpell() {
 		},
 	})
 }
-
-func (dk *Deathknight) ShouldHornOfWinter(sim *core.Simulation) bool {
-	return dk.Inputs.RefreshHornOfWinter && dk.HornOfWinter.IsReady(sim) && !dk.HornOfWinterAura.IsActive()
-}
diff --git a/sim/deathknight/summon_gargoyle.go b/sim/deathknight/summon_gargoyle.go
index cbcd0016c8..47dcb507be 100644
--- a/sim/deathknight/summon_gargoyle.go
+++ b/sim/deathknight/summon_gargoyle.go
@@ -72,11 +72,9 @@ type GargoylePet struct {
 	dkOwner *Deathknight
 
 	GargoyleStrike *core.Spell
-
-	isNerfedGargoyle bool
 }
 
-func (dk *Deathknight) NewGargoyle(nerfedGargoyle bool) *GargoylePet {
+func (dk *Deathknight) NewGargoyle() *GargoylePet {
 	// Remove any hit that would be given by NocS as it does not translate to pets
 	var nocsHit float64
 	if dk.nervesOfColdSteelActive() {
@@ -97,8 +95,7 @@ func (dk *Deathknight) NewGargoyle(nerfedGargoyle bool) *GargoylePet {
 				stats.SpellHaste:  ownerStats[stats.MeleeHaste] * PetSpellHasteScale,
 			}
 		}, false, true),
-		dkOwner:          dk,
-		isNerfedGargoyle: nerfedGargoyle,
+		dkOwner: dk,
 	}
 
 	// NightOfTheDead
@@ -109,17 +106,15 @@ func (dk *Deathknight) NewGargoyle(nerfedGargoyle bool) *GargoylePet {
 		gargoyle.MultiplyCastSpeed(dk.PseudoStats.MeleeSpeedMultiplier)
 
 		// "Nerfed Gargoyle" dynamically updates with owner's haste and melee speed
-		if gargoyle.isNerfedGargoyle {
-			gargoyle.EnableDynamicMeleeSpeed(func(amount float64) {
-				gargoyle.MultiplyCastSpeed(amount)
-			})
-
-			gargoyle.EnableDynamicStats(func(ownerStats stats.Stats) stats.Stats {
-				return stats.Stats{
-					stats.SpellHaste: ownerStats[stats.MeleeHaste] * PetSpellHasteScale,
-				}
-			})
-		}
+		gargoyle.EnableDynamicMeleeSpeed(func(amount float64) {
+			gargoyle.MultiplyCastSpeed(amount)
+		})
+
+		gargoyle.EnableDynamicStats(func(ownerStats stats.Stats) stats.Stats {
+			return stats.Stats{
+				stats.SpellHaste: ownerStats[stats.MeleeHaste] * PetSpellHasteScale,
+			}
+		})
 	}
 
 	dk.AddPet(gargoyle)
diff --git a/sim/deathknight/tank/tank_deathknight.go b/sim/deathknight/tank/tank_deathknight.go
index 6ede0bec8e..e4db62df4f 100644
--- a/sim/deathknight/tank/tank_deathknight.go
+++ b/sim/deathknight/tank/tank_deathknight.go
@@ -38,7 +38,7 @@ func NewTankDeathknight(character *core.Character, options *proto.Player) *TankD
 		Deathknight: deathknight.NewDeathknight(character, deathknight.DeathknightInputs{
 			IsDps:              false,
 			StartingRunicPower: dkOptions.Options.StartingRunicPower,
-		}, options.TalentsString, false),
+		}, options.TalentsString),
 	}
 
 	tankDk.Inputs.UnholyFrenzyTarget = dkOptions.Options.UnholyFrenzyTarget
diff --git a/sim/deathknight/tank/tank_deathknight_test.go b/sim/deathknight/tank/tank_deathknight_test.go
index 3f308a73e8..70e68f03a6 100644
--- a/sim/deathknight/tank/tank_deathknight_test.go
+++ b/sim/deathknight/tank/tank_deathknight_test.go
@@ -55,7 +55,6 @@ var PlayerOptionsBloodTank = &proto.Player_TankDeathknight{
 		Options: &proto.TankDeathknight_Options{
 			StartingRunicPower: 0,
 		},
-		Rotation: &proto.TankDeathknight_Rotation{},
 	},
 }
 

From fd6d8ec9e3923b9c5f86758a6c37bd4249a7cc29 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Sat, 6 Jan 2024 20:11:40 -0800
Subject: [PATCH 17/28] Cleanup dk code

---
 proto/apl.proto                               |  2 -
 proto/common.proto                            |  4 -
 proto/deathknight.proto                       |  3 -
 sim/deathknight/blood_boil.go                 |  5 --
 sim/deathknight/blood_strike.go               |  5 --
 sim/deathknight/dancing_rune_weapon.go        | 25 ++----
 sim/deathknight/death_coil.go                 |  5 --
 sim/deathknight/death_strike.go               |  5 --
 sim/deathknight/deathknight.go                |  3 +-
 sim/deathknight/diseases.go                   | 10 ---
 sim/deathknight/dps/dps_deathknight.go        |  1 -
 sim/deathknight/heart_strike.go               |  4 -
 sim/deathknight/icy_touch.go                  |  5 --
 sim/deathknight/plague_strike.go              |  5 --
 sim/deathknight/rune_strike.go                |  5 --
 sim/deathknight/tank/tank_deathknight.go      |  7 --
 .../individual_sim_ui/rotation_tab.ts         |  1 -
 ui/core/player.ts                             | 86 +------------------
 ui/core/proto_utils/utils.ts                  | 64 --------------
 ui/core/raid.ts                               |  4 -
 ui/deathknight/inputs.ts                      | 29 -------
 ui/deathknight/presets.ts                     | 13 +--
 ui/deathknight/sim.ts                         |  6 +-
 ui/feral_druid/inputs.ts                      |  3 +-
 24 files changed, 16 insertions(+), 284 deletions(-)

diff --git a/proto/apl.proto b/proto/apl.proto
index 2f683604b1..f380dcaee9 100644
--- a/proto/apl.proto
+++ b/proto/apl.proto
@@ -10,8 +10,6 @@ import "druid.proto";
 // Rotation options are based heavily on APL. See https://github.com/simulationcraft/simc/wiki/ActionLists.
 
 message APLRotation {
-    bool enabled = 20 [deprecated = true]; // If false, use old rotation options.
-
     enum Type {
         TypeUnknown = 0;
         TypeAuto = 1;
diff --git a/proto/common.proto b/proto/common.proto
index 8d48b909c1..7bba98594d 100644
--- a/proto/common.proto
+++ b/proto/common.proto
@@ -785,10 +785,6 @@ message UnitReference {
 
 	// Reference to the owner, only used iff this is a pet.
 	UnitReference owner = 4;
-
-	// Raid index of the player to target. A value of -1 indicates no target.
-	// TODO: Delete this after 2 months (on or after 9/19/2023)
-	int32 target_index = 1 [deprecated = true];
 }
 
 // ID for actions that aren't spells or items.
diff --git a/proto/deathknight.proto b/proto/deathknight.proto
index feeaa4083c..000d5c784f 100644
--- a/proto/deathknight.proto
+++ b/proto/deathknight.proto
@@ -144,7 +144,6 @@ enum DeathknightMinorGlyph {
 message Deathknight {
 	message Rotation {
 	}
-	Rotation rotation = 1;
 	
 	message Options {
 		double starting_runic_power = 1;
@@ -154,8 +153,6 @@ message Deathknight {
 
 		bool drw_pesti_apply = 6;
 
-		bool new_drw = 7;
-
 		bool use_ams = 9;
 		double avg_ams_success_rate = 10;
 		double avg_ams_hit = 11;
diff --git a/sim/deathknight/blood_boil.go b/sim/deathknight/blood_boil.go
index 943aa60057..62f5dcb046 100644
--- a/sim/deathknight/blood_boil.go
+++ b/sim/deathknight/blood_boil.go
@@ -64,9 +64,4 @@ func (dk *Deathknight) registerDrwBloodBoilSpell() {
 			}
 		},
 	})
-
-	if !dk.Inputs.NewDrw {
-		dk.RuneWeapon.BloodBoil.DamageMultiplier *= 0.5
-		dk.RuneWeapon.BloodBoil.Flags |= core.SpellFlagIgnoreAttackerModifiers
-	}
 }
diff --git a/sim/deathknight/blood_strike.go b/sim/deathknight/blood_strike.go
index 96d53a6f16..11eefb5708 100644
--- a/sim/deathknight/blood_strike.go
+++ b/sim/deathknight/blood_strike.go
@@ -115,9 +115,4 @@ func (dk *Deathknight) registerDrwBloodStrikeSpell() {
 			spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMeleeWeaponSpecialHitAndCrit)
 		},
 	})
-
-	if !dk.Inputs.NewDrw {
-		dk.RuneWeapon.BloodStrike.DamageMultiplier *= 0.5
-		dk.RuneWeapon.BloodStrike.Flags |= core.SpellFlagIgnoreAttackerModifiers
-	}
 }
diff --git a/sim/deathknight/dancing_rune_weapon.go b/sim/deathknight/dancing_rune_weapon.go
index 99f6914817..17a2232610 100644
--- a/sim/deathknight/dancing_rune_weapon.go
+++ b/sim/deathknight/dancing_rune_weapon.go
@@ -116,13 +116,7 @@ func (runeWeapon *RuneWeaponPet) Initialize() {
 }
 
 func (dk *Deathknight) DrwWeaponDamage(sim *core.Simulation, spell *core.Spell) float64 {
-	if dk.Inputs.NewDrw {
-		return spell.Unit.MHWeaponDamage(sim, spell.MeleeAttackPower()) +
-			spell.BonusWeaponDamage()
-	} else {
-		return spell.Unit.MHNormalizedWeaponDamage(sim, spell.MeleeAttackPower()) +
-			spell.BonusWeaponDamage()
-	}
+	return spell.Unit.MHWeaponDamage(sim, spell.MeleeAttackPower()) + spell.BonusWeaponDamage()
 }
 
 func (dk *Deathknight) NewRuneWeapon() *RuneWeaponPet {
@@ -163,11 +157,9 @@ func (dk *Deathknight) NewRuneWeapon() *RuneWeaponPet {
 
 	mhWeapon := dk.WeaponFromMainHand(dk.DefaultMeleeCritMultiplier())
 
-	if dk.Inputs.NewDrw {
-		baseDamage := mhWeapon.AverageDamage() / mhWeapon.SwingSpeed * 3.5
-		mhWeapon.BaseDamageMin = baseDamage - 150
-		mhWeapon.BaseDamageMax = baseDamage + 150
-	}
+	baseDamage := mhWeapon.AverageDamage() / mhWeapon.SwingSpeed * 3.5
+	mhWeapon.BaseDamageMin = baseDamage - 150
+	mhWeapon.BaseDamageMax = baseDamage + 150
 
 	mhWeapon.SwingSpeed = 3.5
 	mhWeapon.NormalizedSwingSpeed = 3.3
@@ -200,13 +192,8 @@ func (runeWeapon *RuneWeaponPet) enable(sim *core.Simulation) {
 	runeWeapon.PseudoStats.MeleeSpeedMultiplier = 1
 	runeWeapon.MultiplyMeleeSpeed(sim, runeWeapon.dkOwner.PseudoStats.MeleeSpeedMultiplier)
 
-	if runeWeapon.dkOwner.Inputs.NewDrw {
-		runeWeapon.dkOwner.drwDmgSnapshot = runeWeapon.dkOwner.PseudoStats.DamageDealtMultiplier * 0.5
-		runeWeapon.dkOwner.RuneWeapon.PseudoStats.DamageDealtMultiplier *= runeWeapon.dkOwner.drwDmgSnapshot
-	} else {
-		runeWeapon.dkOwner.drwDmgSnapshot = runeWeapon.dkOwner.PseudoStats.DamageDealtMultiplier - 0.5
-		runeWeapon.dkOwner.RuneWeapon.PseudoStats.DamageDealtMultiplier *= runeWeapon.dkOwner.drwDmgSnapshot
-	}
+	runeWeapon.dkOwner.drwDmgSnapshot = runeWeapon.dkOwner.PseudoStats.DamageDealtMultiplier * 0.5
+	runeWeapon.dkOwner.RuneWeapon.PseudoStats.DamageDealtMultiplier *= runeWeapon.dkOwner.drwDmgSnapshot
 
 	runeWeapon.dkOwner.drwPhysSnapshot = runeWeapon.dkOwner.PseudoStats.SchoolDamageDealtMultiplier[stats.SchoolIndexPhysical]
 	runeWeapon.PseudoStats.SchoolDamageDealtMultiplier[stats.SchoolIndexPhysical] *= runeWeapon.dkOwner.drwPhysSnapshot
diff --git a/sim/deathknight/death_coil.go b/sim/deathknight/death_coil.go
index da233c52f5..b2c6b777f9 100644
--- a/sim/deathknight/death_coil.go
+++ b/sim/deathknight/death_coil.go
@@ -60,9 +60,4 @@ func (dk *Deathknight) registerDrwDeathCoilSpell() {
 			spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMagicHitAndCrit)
 		},
 	})
-
-	if !dk.Inputs.NewDrw {
-		dk.RuneWeapon.DeathCoil.DamageMultiplier *= 0.5
-		dk.RuneWeapon.DeathCoil.Flags |= core.SpellFlagIgnoreAttackerModifiers
-	}
 }
diff --git a/sim/deathknight/death_strike.go b/sim/deathknight/death_strike.go
index de85bd7a11..c26d7811ad 100644
--- a/sim/deathknight/death_strike.go
+++ b/sim/deathknight/death_strike.go
@@ -120,9 +120,4 @@ func (dk *Deathknight) registerDrwDeathStrikeSpell() {
 			spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMeleeWeaponSpecialHitAndCrit)
 		},
 	})
-
-	if !dk.Inputs.NewDrw {
-		dk.RuneWeapon.DeathStrike.DamageMultiplier *= 0.5
-		dk.RuneWeapon.DeathStrike.Flags |= core.SpellFlagIgnoreAttackerModifiers
-	}
 }
diff --git a/sim/deathknight/deathknight.go b/sim/deathknight/deathknight.go
index 61c53fe706..673004d4df 100644
--- a/sim/deathknight/deathknight.go
+++ b/sim/deathknight/deathknight.go
@@ -20,8 +20,7 @@ var TalentTreeSizes = [3]int{28, 29, 31}
 
 type DeathknightInputs struct {
 	// Option Vars
-	IsDps  bool
-	NewDrw bool
+	IsDps bool
 
 	UnholyFrenzyTarget *proto.UnitReference
 
diff --git a/sim/deathknight/diseases.go b/sim/deathknight/diseases.go
index 4006b43011..7a8b02cb5e 100644
--- a/sim/deathknight/diseases.go
+++ b/sim/deathknight/diseases.go
@@ -224,11 +224,6 @@ func (dk *Deathknight) registerDrwFrostFever() {
 			spell.Dot(target).Apply(sim)
 		},
 	})
-
-	if !dk.Inputs.NewDrw {
-		dk.RuneWeapon.FrostFeverSpell.DamageMultiplier *= 0.5
-		dk.RuneWeapon.FrostFeverSpell.Flags |= core.SpellFlagIgnoreAttackerModifiers
-	}
 }
 
 func (dk *Deathknight) registerDrwBloodPlague() {
@@ -276,11 +271,6 @@ func (dk *Deathknight) registerDrwBloodPlague() {
 			spell.Dot(target).Apply(sim)
 		},
 	})
-
-	if !dk.Inputs.NewDrw {
-		dk.RuneWeapon.BloodPlagueSpell.DamageMultiplier *= 0.5
-		dk.RuneWeapon.BloodPlagueSpell.Flags |= core.SpellFlagIgnoreAttackerModifiers
-	}
 }
 
 func (dk *Deathknight) doWanderingPlague(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
diff --git a/sim/deathknight/dps/dps_deathknight.go b/sim/deathknight/dps/dps_deathknight.go
index de021426d0..8f0cfbce50 100644
--- a/sim/deathknight/dps/dps_deathknight.go
+++ b/sim/deathknight/dps/dps_deathknight.go
@@ -36,7 +36,6 @@ func NewDpsDeathknight(character *core.Character, player *proto.Player) *DpsDeat
 			PetUptime:          dk.Options.PetUptime,
 			DrwPestiApply:      dk.Options.DrwPestiApply,
 			IsDps:              true,
-			NewDrw:             true,
 
 			UseAMS:            dk.Options.UseAms,
 			AvgAMSSuccessRate: dk.Options.AvgAmsSuccessRate,
diff --git a/sim/deathknight/heart_strike.go b/sim/deathknight/heart_strike.go
index 1fa9ebf983..92dbdde6b2 100644
--- a/sim/deathknight/heart_strike.go
+++ b/sim/deathknight/heart_strike.go
@@ -79,10 +79,6 @@ func (dk *Deathknight) newHeartStrikeSpell(isMainTarget bool, isDrw bool) *core.
 	}
 
 	if isDrw {
-		if !dk.Inputs.NewDrw {
-			conf.DamageMultiplier *= 0.5
-			conf.Flags |= core.SpellFlagIgnoreAttackerModifiers
-		}
 		return dk.RuneWeapon.RegisterSpell(conf)
 	} else {
 		return dk.RegisterSpell(conf)
diff --git a/sim/deathknight/icy_touch.go b/sim/deathknight/icy_touch.go
index e622ee282b..41a793d5c0 100644
--- a/sim/deathknight/icy_touch.go
+++ b/sim/deathknight/icy_touch.go
@@ -73,9 +73,4 @@ func (dk *Deathknight) registerDrwIcyTouchSpell() {
 			spell.DealDamage(sim, result)
 		},
 	})
-
-	if !dk.Inputs.NewDrw {
-		dk.RuneWeapon.IcyTouch.DamageMultiplier *= 0.5
-		dk.RuneWeapon.IcyTouch.Flags |= core.SpellFlagIgnoreAttackerModifiers
-	}
 }
diff --git a/sim/deathknight/plague_strike.go b/sim/deathknight/plague_strike.go
index d6635013c5..f8d26b25ab 100644
--- a/sim/deathknight/plague_strike.go
+++ b/sim/deathknight/plague_strike.go
@@ -100,9 +100,4 @@ func (dk *Deathknight) registerDrwPlagueStrikeSpell() {
 			}
 		},
 	})
-
-	if !dk.Inputs.NewDrw {
-		dk.RuneWeapon.PlagueStrike.DamageMultiplier *= 0.5
-		dk.RuneWeapon.PlagueStrike.Flags |= core.SpellFlagIgnoreAttackerModifiers
-	}
 }
diff --git a/sim/deathknight/rune_strike.go b/sim/deathknight/rune_strike.go
index 6d22e707cd..d74c270fd2 100644
--- a/sim/deathknight/rune_strike.go
+++ b/sim/deathknight/rune_strike.go
@@ -137,9 +137,4 @@ func (dk *Deathknight) registerDrwRuneStrikeSpell() {
 			spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMeleeSpecialNoBlockDodgeParry)
 		},
 	})
-
-	if !dk.Inputs.NewDrw {
-		dk.RuneWeapon.RuneStrike.DamageMultiplier *= 0.5
-		dk.RuneWeapon.RuneStrike.Flags |= core.SpellFlagIgnoreAttackerModifiers
-	}
 }
diff --git a/sim/deathknight/tank/tank_deathknight.go b/sim/deathknight/tank/tank_deathknight.go
index e8b35054fa..8a5643e8dc 100644
--- a/sim/deathknight/tank/tank_deathknight.go
+++ b/sim/deathknight/tank/tank_deathknight.go
@@ -25,10 +25,6 @@ func RegisterTankDeathknight() {
 
 type TankDeathknight struct {
 	*deathknight.Deathknight
-
-	switchIT   bool
-	BloodSpell *core.Spell
-	FuSpell    *core.Spell
 }
 
 func NewTankDeathknight(character *core.Character, options *proto.Player) *TankDeathknight {
@@ -37,7 +33,6 @@ func NewTankDeathknight(character *core.Character, options *proto.Player) *TankD
 	tankDk := &TankDeathknight{
 		Deathknight: deathknight.NewDeathknight(character, deathknight.DeathknightInputs{
 			IsDps:              false,
-			NewDrw:             true,
 			StartingRunicPower: dkOptions.Options.StartingRunicPower,
 		}, options.TalentsString),
 	}
@@ -78,8 +73,6 @@ func (dk *TankDeathknight) Initialize() {
 func (dk *TankDeathknight) Reset(sim *core.Simulation) {
 	dk.Deathknight.Reset(sim)
 
-	dk.switchIT = false
-
 	dk.Presence = deathknight.UnsetPresence
 	dk.Deathknight.PseudoStats.Stunned = false
 }
diff --git a/ui/core/components/individual_sim_ui/rotation_tab.ts b/ui/core/components/individual_sim_ui/rotation_tab.ts
index 81742a4f7b..70a09d43f7 100644
--- a/ui/core/components/individual_sim_ui/rotation_tab.ts
+++ b/ui/core/components/individual_sim_ui/rotation_tab.ts
@@ -100,7 +100,6 @@ export class RotationTab extends SimTab {
 			changedEvent: (player: Player<any>) => player.rotationChangeEmitter,
 			getValue: (player: Player<any>) => player.getRotationType(),
 			setValue: (eventID: EventID, player: Player<any>, newValue: number) => {
-				player.aplRotation.enabled = false;
 				player.aplRotation.type = newValue;
 				player.rotationChangeEmitter.emit(eventID);
 			},
diff --git a/ui/core/player.ts b/ui/core/player.ts
index 7690346209..ee9d44f6fb 100644
--- a/ui/core/player.ts
+++ b/ui/core/player.ts
@@ -780,10 +780,8 @@ export class Player<SpecType extends Spec> {
 	}
 
 	getRotationType(): APLRotationType {
-		if (this.aplRotation.enabled) {
+		if (this.aplRotation.type == APLRotationType.TypeUnknown) {
 			return APLRotationType.TypeAPL;
-		} else if (this.aplRotation.type == APLRotationType.TypeUnknown) {
-			return APLRotationType.TypeLegacy;
 		} else {
 			return this.aplRotation.type;
 		}
@@ -1374,40 +1372,6 @@ export class Player<SpecType extends Spec> {
 				|| includeCategories.length == 0
 				|| includeCategories.includes(cat);
 
-		if (proto.rotation) {
-			proto.rotation.prepullActions.forEach(ppa => {
-				if (ppa.doAt) {
-					ppa.doAtValue = APLValue.create({
-						value: {oneofKind: 'const', const: { val: ppa.doAt }}
-					});
-					ppa.doAt = '';
-				}
-			});
-			if (proto.rotation.enabled) {
-				proto.rotation.enabled = false;
-				proto.rotation.type = APLRotationType.TypeAPL;
-			}
-		}
-
-		const rot = this.specTypeFunctions.rotationFromPlayer(proto);
-		if (rot && !this.specTypeFunctions.rotationEquals(rot, this.specTypeFunctions.rotationCreate())) {
-			if (proto.rotation?.type == APLRotationType.TypeAPL) {
-				// Do nothing
-			} else if (this.simpleRotationGenerator) {
-				proto.rotation = APLRotation.create({
-					type: APLRotationType.TypeSimple,
-					simple: {
-						specRotationJson: JSON.stringify(this.specTypeFunctions.rotationToJson(rot)),
-						cooldowns: proto.cooldowns,
-					},
-				});
-			} else {
-				proto.rotation = APLRotation.create({
-					type: APLRotationType.TypeAuto,
-				});
-			}
-		}
-
 		TypedEvent.freezeAllAndDo(() => {
 			if (loadCategory(SimSettingCategories.Gear)) {
 				this.setGear(eventID, proto.equipment ? this.sim.db.lookupEquipmentSpec(proto.equipment) : new Gear({}));
@@ -1452,50 +1416,6 @@ export class Player<SpecType extends Spec> {
 				this.setBuffs(eventID, proto.buffs || IndividualBuffs.create());
 			}
 
-			const options = this.getSpecOptions();
-			for (let key in options) {
-				if ((options[key] as any)?.['targetIndex']) {
-					const targetIndex = (options[key] as any)['targetIndex'] as number;
-					if (targetIndex == -1) {
-						(options[key] as any) = UnitReference.create();
-					} else {
-						(options[key] as any) = UnitReference.create({type: UnitReference_Type.Player, index: targetIndex});
-					}
-					this.setSpecOptions(eventID, options);
-					break;
-				}
-			}
-
-			if (this.spec == Spec.SpecBalanceDruid) {
-				const rot = this.getSimpleRotation() as SpecRotation<Spec.SpecBalanceDruid>;
-				if (rot.okfPpm) {
-					rot.okfPpm = 0;
-					this.setSimpleRotation(eventID, rot as SpecRotation<SpecType>);
-				}
-			}
-
-			if (this.spec == Spec.SpecHunter) {
-				const rot = this.getSimpleRotation() as SpecRotation<Spec.SpecHunter>;
-				if (rot.timeToTrapWeaveMs) {
-					const options = this.getSpecOptions() as SpecOptions<Spec.SpecHunter>;
-					options.timeToTrapWeaveMs = rot.timeToTrapWeaveMs;
-					this.setSpecOptions(eventID, options as SpecOptions<SpecType>);
-					rot.timeToTrapWeaveMs = 0;
-					this.setSimpleRotation(eventID, rot as SpecRotation<SpecType>);
-				}
-			}
-
-			if (this.spec == Spec.SpecMage) {
-				const rot = this.getSimpleRotation() as SpecRotation<Spec.SpecMage>;
-				if (rot.waterElementalDisobeyChance) {
-					const options = this.getSpecOptions() as SpecOptions<Spec.SpecMage>;
-					options.waterElementalDisobeyChance = rot.waterElementalDisobeyChance;
-					rot.waterElementalDisobeyChance = 0;
-					this.setSpecOptions(eventID, options as SpecOptions<SpecType>);
-					this.setSimpleRotation(eventID, rot as SpecRotation<SpecType>);
-				}
-			}
-
 			if (this.spec == Spec.SpecShadowPriest) {
 				const options = this.getSpecOptions() as SpecOptions<Spec.SpecShadowPriest>;
 				if (options.latency) {
@@ -1546,8 +1466,8 @@ export class Player<SpecType extends Spec> {
 				}
 			}
 
-			if (this.spec == Spec.SpecWarlock || this.spec == Spec.SpecDeathknight) {
-				const rot = this.getSimpleRotation() as SpecRotation<Spec.SpecWarlock | Spec.SpecDeathknight>;
+			if (this.spec == Spec.SpecWarlock) {
+				const rot = this.getSimpleRotation() as SpecRotation<Spec.SpecWarlock>;
 				if (rot.enableWeaponSwap) {
 					this.setEnableItemSwap(eventID, rot.enableWeaponSwap);
 					rot.enableWeaponSwap = false;
diff --git a/ui/core/proto_utils/utils.ts b/ui/core/proto_utils/utils.ts
index 8a6be3a96a..3e9d8d20c8 100644
--- a/ui/core/proto_utils/utils.ts
+++ b/ui/core/proto_utils/utils.ts
@@ -548,7 +548,6 @@ export type SpecTypeFunctions<SpecType extends Spec> = {
 	rotationCopy: (a: SpecRotation<SpecType>) => SpecRotation<SpecType>;
 	rotationToJson: (a: SpecRotation<SpecType>) => any;
 	rotationFromJson: (obj: any) => SpecRotation<SpecType>;
-	rotationFromPlayer: (player: Player) => SpecRotation<SpecType>;
 
 	talentsCreate: () => SpecTalents<SpecType>;
 	talentsEquals: (a: SpecTalents<SpecType>, b: SpecTalents<SpecType>) => boolean;
@@ -571,9 +570,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => BalanceDruidRotation.clone(a as BalanceDruidRotation),
 		rotationToJson: (a) => BalanceDruidRotation.toJson(a as BalanceDruidRotation),
 		rotationFromJson: (obj) => BalanceDruidRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'balanceDruid'
-			? player.spec.balanceDruid.rotation || BalanceDruidRotation.create()
-			: BalanceDruidRotation.create(),
 
 		talentsCreate: () => DruidTalents.create(),
 		talentsEquals: (a, b) => DruidTalents.equals(a as DruidTalents, b as DruidTalents),
@@ -596,9 +592,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => FeralDruidRotation.clone(a as FeralDruidRotation),
 		rotationToJson: (a) => FeralDruidRotation.toJson(a as FeralDruidRotation),
 		rotationFromJson: (obj) => FeralDruidRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'feralDruid'
-			? player.spec.feralDruid.rotation || FeralDruidRotation.create()
-			: FeralDruidRotation.create(),
 
 		talentsCreate: () => DruidTalents.create(),
 		talentsEquals: (a, b) => DruidTalents.equals(a as DruidTalents, b as DruidTalents),
@@ -621,9 +614,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => FeralTankDruidRotation.clone(a as FeralTankDruidRotation),
 		rotationToJson: (a) => FeralTankDruidRotation.toJson(a as FeralTankDruidRotation),
 		rotationFromJson: (obj) => FeralTankDruidRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'feralTankDruid'
-			? player.spec.feralTankDruid.rotation || FeralTankDruidRotation.create()
-			: FeralTankDruidRotation.create(),
 
 		talentsCreate: () => DruidTalents.create(),
 		talentsEquals: (a, b) => DruidTalents.equals(a as DruidTalents, b as DruidTalents),
@@ -646,9 +636,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => RestorationDruidRotation.clone(a as RestorationDruidRotation),
 		rotationToJson: (a) => RestorationDruidRotation.toJson(a as RestorationDruidRotation),
 		rotationFromJson: (obj) => RestorationDruidRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'restorationDruid'
-			? player.spec.restorationDruid.rotation || RestorationDruidRotation.create()
-			: RestorationDruidRotation.create(),
 
 		talentsCreate: () => DruidTalents.create(),
 		talentsEquals: (a, b) => DruidTalents.equals(a as DruidTalents, b as DruidTalents),
@@ -671,9 +658,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => ElementalShamanRotation.clone(a as ElementalShamanRotation),
 		rotationToJson: (a) => ElementalShamanRotation.toJson(a as ElementalShamanRotation),
 		rotationFromJson: (obj) => ElementalShamanRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'elementalShaman'
-			? player.spec.elementalShaman.rotation || ElementalShamanRotation.create()
-			: ElementalShamanRotation.create(),
 
 		talentsCreate: () => ShamanTalents.create(),
 		talentsEquals: (a, b) => ShamanTalents.equals(a as ShamanTalents, b as ShamanTalents),
@@ -696,9 +680,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => EnhancementShamanRotation.clone(a as EnhancementShamanRotation),
 		rotationToJson: (a) => EnhancementShamanRotation.toJson(a as EnhancementShamanRotation),
 		rotationFromJson: (obj) => EnhancementShamanRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'enhancementShaman'
-			? player.spec.enhancementShaman.rotation || EnhancementShamanRotation.create()
-			: EnhancementShamanRotation.create(),
 
 		talentsCreate: () => ShamanTalents.create(),
 		talentsEquals: (a, b) => ShamanTalents.equals(a as ShamanTalents, b as ShamanTalents),
@@ -721,9 +702,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => RestorationShamanRotation.clone(a as RestorationShamanRotation),
 		rotationToJson: (a) => RestorationShamanRotation.toJson(a as RestorationShamanRotation),
 		rotationFromJson: (obj) => RestorationShamanRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'restorationShaman'
-			? player.spec.restorationShaman.rotation || RestorationShamanRotation.create()
-			: RestorationShamanRotation.create(),
 
 		talentsCreate: () => ShamanTalents.create(),
 		talentsEquals: (a, b) => ShamanTalents.equals(a as ShamanTalents, b as ShamanTalents),
@@ -746,9 +724,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => HunterRotation.clone(a as HunterRotation),
 		rotationToJson: (a) => HunterRotation.toJson(a as HunterRotation),
 		rotationFromJson: (obj) => HunterRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'hunter'
-			? player.spec.hunter.rotation || HunterRotation.create()
-			: HunterRotation.create(),
 
 		talentsCreate: () => HunterTalents.create(),
 		talentsEquals: (a, b) => HunterTalents.equals(a as HunterTalents, b as HunterTalents),
@@ -771,9 +746,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => MageRotation.clone(a as MageRotation),
 		rotationToJson: (a) => MageRotation.toJson(a as MageRotation),
 		rotationFromJson: (obj) => MageRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'mage'
-			? player.spec.mage.rotation || MageRotation.create()
-			: MageRotation.create(),
 
 		talentsCreate: () => MageTalents.create(),
 		talentsEquals: (a, b) => MageTalents.equals(a as MageTalents, b as MageTalents),
@@ -796,9 +768,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => HolyPaladinRotation.clone(a as HolyPaladinRotation),
 		rotationToJson: (a) => HolyPaladinRotation.toJson(a as HolyPaladinRotation),
 		rotationFromJson: (obj) => HolyPaladinRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'holyPaladin'
-			? player.spec.holyPaladin.rotation || HolyPaladinRotation.create()
-			: HolyPaladinRotation.create(),
 
 		talentsCreate: () => PaladinTalents.create(),
 		talentsEquals: (a, b) => PaladinTalents.equals(a as PaladinTalents, b as PaladinTalents),
@@ -821,9 +790,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => ProtectionPaladinRotation.clone(a as ProtectionPaladinRotation),
 		rotationToJson: (a) => ProtectionPaladinRotation.toJson(a as ProtectionPaladinRotation),
 		rotationFromJson: (obj) => ProtectionPaladinRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'protectionPaladin'
-			? player.spec.protectionPaladin.rotation || ProtectionPaladinRotation.create()
-			: ProtectionPaladinRotation.create(),
 
 		talentsCreate: () => PaladinTalents.create(),
 		talentsEquals: (a, b) => PaladinTalents.equals(a as PaladinTalents, b as PaladinTalents),
@@ -846,9 +812,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => RetributionPaladinRotation.clone(a as RetributionPaladinRotation),
 		rotationToJson: (a) => RetributionPaladinRotation.toJson(a as RetributionPaladinRotation),
 		rotationFromJson: (obj) => RetributionPaladinRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'retributionPaladin'
-			? player.spec.retributionPaladin.rotation || RetributionPaladinRotation.create()
-			: RetributionPaladinRotation.create(),
 
 		talentsCreate: () => PaladinTalents.create(),
 		talentsEquals: (a, b) => PaladinTalents.equals(a as PaladinTalents, b as PaladinTalents),
@@ -871,9 +834,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => RogueRotation.clone(a as RogueRotation),
 		rotationToJson: (a) => RogueRotation.toJson(a as RogueRotation),
 		rotationFromJson: (obj) => RogueRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'rogue'
-			? player.spec.rogue.rotation || RogueRotation.create()
-			: RogueRotation.create(),
 
 		talentsCreate: () => RogueTalents.create(),
 		talentsEquals: (a, b) => RogueTalents.equals(a as RogueTalents, b as RogueTalents),
@@ -896,9 +856,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => HealingPriestRotation.clone(a as HealingPriestRotation),
 		rotationToJson: (a) => HealingPriestRotation.toJson(a as HealingPriestRotation),
 		rotationFromJson: (obj) => HealingPriestRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'healingPriest'
-			? player.spec.healingPriest.rotation || HealingPriestRotation.create()
-			: HealingPriestRotation.create(),
 
 		talentsCreate: () => PriestTalents.create(),
 		talentsEquals: (a, b) => PriestTalents.equals(a as PriestTalents, b as PriestTalents),
@@ -921,9 +878,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => ShadowPriestRotation.clone(a as ShadowPriestRotation),
 		rotationToJson: (a) => ShadowPriestRotation.toJson(a as ShadowPriestRotation),
 		rotationFromJson: (obj) => ShadowPriestRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'shadowPriest'
-			? player.spec.shadowPriest.rotation || ShadowPriestRotation.create()
-			: ShadowPriestRotation.create(),
 
 		talentsCreate: () => PriestTalents.create(),
 		talentsEquals: (a, b) => PriestTalents.equals(a as PriestTalents, b as PriestTalents),
@@ -946,9 +900,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => SmitePriestRotation.clone(a as SmitePriestRotation),
 		rotationToJson: (a) => SmitePriestRotation.toJson(a as SmitePriestRotation),
 		rotationFromJson: (obj) => SmitePriestRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'smitePriest'
-			? player.spec.smitePriest.rotation || SmitePriestRotation.create()
-			: SmitePriestRotation.create(),
 
 		talentsCreate: () => PriestTalents.create(),
 		talentsEquals: (a, b) => PriestTalents.equals(a as PriestTalents, b as PriestTalents),
@@ -971,9 +922,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => WarlockRotation.clone(a as WarlockRotation),
 		rotationToJson: (a) => WarlockRotation.toJson(a as WarlockRotation),
 		rotationFromJson: (obj) => WarlockRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'warlock'
-			? player.spec.warlock.rotation || WarlockRotation.create()
-			: WarlockRotation.create(),
 
 		talentsCreate: () => WarlockTalents.create(),
 		talentsEquals: (a, b) => WarlockTalents.equals(a as WarlockTalents, b as WarlockTalents),
@@ -996,9 +944,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => WarriorRotation.clone(a as WarriorRotation),
 		rotationToJson: (a) => WarriorRotation.toJson(a as WarriorRotation),
 		rotationFromJson: (obj) => WarriorRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'warrior'
-			? player.spec.warrior.rotation || WarriorRotation.create()
-			: WarriorRotation.create(),
 
 		talentsCreate: () => WarriorTalents.create(),
 		talentsEquals: (a, b) => WarriorTalents.equals(a as WarriorTalents, b as WarriorTalents),
@@ -1021,9 +966,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => ProtectionWarriorRotation.clone(a as ProtectionWarriorRotation),
 		rotationToJson: (a) => ProtectionWarriorRotation.toJson(a as ProtectionWarriorRotation),
 		rotationFromJson: (obj) => ProtectionWarriorRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'protectionWarrior'
-			? player.spec.protectionWarrior.rotation || ProtectionWarriorRotation.create()
-			: ProtectionWarriorRotation.create(),
 
 		talentsCreate: () => WarriorTalents.create(),
 		talentsEquals: (a, b) => WarriorTalents.equals(a as WarriorTalents, b as WarriorTalents),
@@ -1046,9 +988,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => DeathknightRotation.clone(a as DeathknightRotation),
 		rotationToJson: (a) => DeathknightRotation.toJson(a as DeathknightRotation),
 		rotationFromJson: (obj) => DeathknightRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'deathknight'
-			? player.spec.deathknight.rotation || DeathknightRotation.create()
-			: DeathknightRotation.create(),
 
 		talentsCreate: () => DeathknightTalents.create(),
 		talentsEquals: (a, b) => DeathknightTalents.equals(a as DeathknightTalents, b as DeathknightTalents),
@@ -1071,9 +1010,6 @@ export const specTypeFunctions: Record<Spec, SpecTypeFunctions<any>> = {
 		rotationCopy: (a) => TankDeathknightRotation.clone(a as TankDeathknightRotation),
 		rotationToJson: (a) => TankDeathknightRotation.toJson(a as TankDeathknightRotation),
 		rotationFromJson: (obj) => TankDeathknightRotation.fromJson(obj),
-		rotationFromPlayer: (player) => player.spec.oneofKind == 'tankDeathknight'
-			? player.spec.tankDeathknight.rotation || TankDeathknightRotation.create()
-			: TankDeathknightRotation.create(),
 
 		talentsCreate: () => DeathknightTalents.create(),
 		talentsEquals: (a, b) => DeathknightTalents.equals(a as DeathknightTalents, b as DeathknightTalents),
diff --git a/ui/core/raid.ts b/ui/core/raid.ts
index 1dae6e4849..d42215b1ad 100644
--- a/ui/core/raid.ts
+++ b/ui/core/raid.ts
@@ -234,10 +234,6 @@ export class Raid {
 
 	fromProto(eventID: EventID, proto: RaidProto) {
 		TypedEvent.freezeAllAndDo(() => {
-			if (proto.tanks) {
-				proto.tanks = proto.tanks.map(tank => (tank.type == 0 && tank.targetIndex != -1) ? UnitReference.create({type: UnitType.Player, index: tank.targetIndex}) : tank);
-			}
-
 			if (proto.buffs) {
 				if (proto.buffs.demonicPact > 0 && proto.buffs.demonicPactSp == 0) {
 					proto.buffs.demonicPactSp = proto.buffs.demonicPact;
diff --git a/ui/deathknight/inputs.ts b/ui/deathknight/inputs.ts
index 94183ef92b..f20dd4c859 100644
--- a/ui/deathknight/inputs.ts
+++ b/ui/deathknight/inputs.ts
@@ -46,20 +46,6 @@ export const PetUptime = InputHelpers.makeSpecOptionsNumberInput<Spec.SpecDeathk
 	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().masterOfGhouls,
 });
 
-export const PrecastGhoulFrenzy = InputHelpers.makeSpecOptionsBooleanInput<Spec.SpecDeathknight>({
-	fieldName: 'precastGhoulFrenzy',
-	label: 'Pre-Cast Ghoul Frenzy',
-	labelTooltip: 'Cast Ghoul Frenzy 10 seconds before combat starts.',
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().summonGargoyle && player.getTalents().ghoulFrenzy,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.specOptionsChangeEmitter, player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
-export const PrecastHornOfWinter = InputHelpers.makeSpecOptionsBooleanInput<Spec.SpecDeathknight>({
-	fieldName: 'precastHornOfWinter',
-	label: 'Pre-Cast Horn of Winter',
-	labelTooltip: 'Precast Horn of Winter for 10 extra runic power before fight.',
-});
-
 export const DrwPestiApply = InputHelpers.makeSpecOptionsBooleanInput<Spec.SpecDeathknight>({
 	fieldName: 'drwPestiApply',
 	label: 'DRW Pestilence Add',
@@ -68,21 +54,6 @@ export const DrwPestiApply = InputHelpers.makeSpecOptionsBooleanInput<Spec.SpecD
 	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.specOptionsChangeEmitter, player.rotationChangeEmitter, player.talentsChangeEmitter]),
 });
 
-export const NewDrwInput = InputHelpers.makeSpecOptionsBooleanInput<Spec.SpecDeathknight>({
-	fieldName: 'newDrw',
-	label: 'PTR DRW Scaling',
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalents().dancingRuneWeapon,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-})
-
-export const DiseaseDowntime = InputHelpers.makeSpecOptionsNumberInput<Spec.SpecDeathknight>({
-	fieldName: 'diseaseDowntime',
-	label: 'Disease Downtime',
-	labelTooltip: 'Maximum allowed downtime between disease applications.',
-	showWhen: (player: Player<Spec.SpecDeathknight>) => player.getTalentTree() == 2,
-	changeEmitter: (player: Player<Spec.SpecDeathknight>) => TypedEvent.onAny([player.rotationChangeEmitter, player.talentsChangeEmitter]),
-});
-
 export const UseAMSInput = InputHelpers.makeSpecOptionsBooleanInput<Spec.SpecDeathknight>({
 	fieldName: 'useAms',
 	label: 'Use AMS',
diff --git a/ui/deathknight/presets.ts b/ui/deathknight/presets.ts
index d73bd8b94d..70f3c6c517 100644
--- a/ui/deathknight/presets.ts
+++ b/ui/deathknight/presets.ts
@@ -182,31 +182,24 @@ export const BloodTalents = {
 };
 
 export const DefaultUnholyOptions = DeathKnightOptions.create({
-	drwPestiApply: true,
 	startingRunicPower: 0,
 	petUptime: 1,
-	precastGhoulFrenzy: false,
-	precastHornOfWinter: true,
 	unholyFrenzyTarget: UnitReference.create(),
-	diseaseDowntime: 2,
+	drwPestiApply: true,
 });
 
 export const DefaultFrostOptions = DeathKnightOptions.create({
-	drwPestiApply: true,
 	startingRunicPower: 0,
 	petUptime: 1,
-	precastHornOfWinter: true,
 	unholyFrenzyTarget: UnitReference.create(),
-	diseaseDowntime: 0,
+	drwPestiApply: true,
 });
 
 export const DefaultBloodOptions = DeathKnightOptions.create({
-	drwPestiApply: true,
 	startingRunicPower: 0,
 	petUptime: 1,
-	precastHornOfWinter: true,
 	unholyFrenzyTarget: UnitReference.create(),
-	diseaseDowntime: 0,
+	drwPestiApply: true,
 });
 
 export const OtherDefaults = {
diff --git a/ui/deathknight/sim.ts b/ui/deathknight/sim.ts
index 9aa93de575..ee214815e2 100644
--- a/ui/deathknight/sim.ts
+++ b/ui/deathknight/sim.ts
@@ -182,18 +182,14 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecDeathknight, {
 	// Inputs to include in the 'Other' section on the settings tab.
 	otherInputs: {
 		inputs: [
-			DeathKnightInputs.DiseaseDowntime,
-			DeathKnightInputs.DrwPestiApply,
 			DeathKnightInputs.SelfUnholyFrenzy,
 			DeathKnightInputs.StartingRunicPower,
 			DeathKnightInputs.PetUptime,
+			DeathKnightInputs.DrwPestiApply,
 			DeathKnightInputs.UseAMSInput,
 			DeathKnightInputs.AvgAMSSuccessRateInput,
 			DeathKnightInputs.AvgAMSHitInput,
 
-			DeathKnightInputs.PrecastGhoulFrenzy,
-			DeathKnightInputs.PrecastHornOfWinter,
-
 			OtherInputs.TankAssignment,
 			OtherInputs.InFrontOfTarget,
 		],
diff --git a/ui/feral_druid/inputs.ts b/ui/feral_druid/inputs.ts
index 88b482a56a..2f16efda08 100644
--- a/ui/feral_druid/inputs.ts
+++ b/ui/feral_druid/inputs.ts
@@ -1,5 +1,6 @@
 import { UnitReference, UnitReference_Type as UnitType } from '../core/proto/common.js';
 import { Spec } from '../core/proto/common.js';
+import { APLRotation_Type } from '../core/proto/apl.js';
 import { ActionId } from '../core/proto_utils/action_id.js';
 import { Player } from '../core/player.js';
 import { EventID, TypedEvent } from '../core/typed_event.js';
@@ -133,7 +134,7 @@ export const FeralDruidRotationConfig = {
 			fieldName: 'raidTargets',
 			label: 'GotW Raid Targets',
 			labelTooltip: 'Raid size to assume for clearcast proc chance (can include pets as well, so 25 man raid potentically can be ~30)',
-			showWhen: (player: Player<Spec.SpecFeralDruid>) => player.aplRotation.enabled || (ShouldShowAdvParamAoe(player) && player.getSimpleRotation().flowerWeave == true),
+			showWhen: (player: Player<Spec.SpecFeralDruid>) => player.aplRotation.type != APLRotation_Type.TypeSimple || (ShouldShowAdvParamAoe(player) && player.getSimpleRotation().flowerWeave == true),
 		}),
 		// Can be uncommented if/when analytical bite mode is added
 		//InputHelpers.makeRotationEnumInput<Spec.SpecFeralDruid, BiteModeType>({

From e0dc3bcae5fa92c48f44148de38474b29fef2c58 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Sat, 6 Jan 2024 21:32:03 -0800
Subject: [PATCH 18/28] Fix priest test change

---
 sim/priest/healing/TestHoly.results | 4 ++--
 sim/priest/hymn_of_hope.go          | 2 +-
 sim/priest/smite/TestSmite.results  | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/sim/priest/healing/TestHoly.results b/sim/priest/healing/TestHoly.results
index 7de62a4da8..388a12beda 100644
--- a/sim/priest/healing/TestHoly.results
+++ b/sim/priest/healing/TestHoly.results
@@ -172,8 +172,8 @@ dps_results: {
 dps_results: {
  key: "TestHoly-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  tps: 35.9717
-  hps: 4983.05245
+  tps: 36.04595
+  hps: 4986.56935
  }
 }
 dps_results: {
diff --git a/sim/priest/hymn_of_hope.go b/sim/priest/hymn_of_hope.go
index f88c6ecafe..995b1a541f 100644
--- a/sim/priest/hymn_of_hope.go
+++ b/sim/priest/hymn_of_hope.go
@@ -30,7 +30,7 @@ func (priest *Priest) RegisterHymnOfHopeCD() {
 
 		ApplyEffects: func(sim *core.Simulation, _ *core.Unit, spell *core.Spell) {
 			core.StartPeriodicAction(sim, core.PeriodicActionOptions{
-				Period:   time.Second * 2,
+				Period:   spell.Unit.ApplyCastSpeedForSpell(time.Second*2, spell),
 				NumTicks: int(numTicks),
 				OnAction: func(sim *core.Simulation) {
 					// This is 3%, but it increases the target's max mana by 20% for the duration
diff --git a/sim/priest/smite/TestSmite.results b/sim/priest/smite/TestSmite.results
index 110aae8059..ae55fcdfe8 100644
--- a/sim/priest/smite/TestSmite.results
+++ b/sim/priest/smite/TestSmite.results
@@ -855,7 +855,7 @@ dps_results: {
  key: "TestSmite-Settings-Undead-p1-Basic-default-NoBuffs-ShortSingleTarget"
  value: {
   dps: 2520.9093
-  tps: 2126.72097
+  tps: 2130.11373
  }
 }
 dps_results: {

From 0938396717aee3aa206c84eed1472b02a420941d Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Sat, 6 Jan 2024 22:37:18 -0800
Subject: [PATCH 19/28] Remove unused protos and fields

---
 proto/druid.proto                             |  61 +---------
 proto/hunter.proto                            |  25 -----
 proto/mage.proto                              |   4 -
 proto/paladin.proto                           |  55 ---------
 proto/priest.proto                            |  43 -------
 proto/rogue.proto                             |  65 -----------
 proto/shaman.proto                            | 106 ------------------
 proto/warlock.proto                           |  47 --------
 proto/warrior.proto                           | 105 -----------------
 sim/druid/balance/balance_test.go             |   1 -
 sim/druid/restoration/restoration_test.go     |   1 -
 sim/druid/tank/tank_test.go                   |   1 -
 sim/mage/mage_test.go                         |   3 -
 sim/paladin/avenging_wrath.go                 |   8 --
 sim/paladin/holy/holy.go                      |   8 +-
 sim/paladin/holy/holy_test.go                 |   3 +-
 sim/paladin/paladin.go                        |   4 -
 sim/paladin/protection/protection_test.go     |   7 +-
 sim/paladin/retribution/retribution.go        |   4 -
 sim/paladin/retribution/retribution_test.go   |  11 --
 sim/priest/healing/healing_priest_test.go     |   2 -
 sim/priest/shadow/shadow_priest_test.go       |  11 --
 sim/priest/smite/smite_priest_test.go         |   1 -
 sim/raid_bench_test.go                        |  26 +----
 sim/shaman/elemental/elemental.go             |   3 +-
 sim/shaman/elemental/elemental_test.go        |  10 +-
 sim/shaman/enhancement/enhancement.go         |   7 +-
 sim/shaman/enhancement/enhancement_test.go    |  27 ++---
 sim/shaman/restoration/restoration.go         |   3 +-
 sim/shaman/restoration/restoration_test.go    |   5 +-
 sim/shaman/shaman.go                          |   7 +-
 sim/warlock/warlock_test.go                   |  10 --
 .../protection/protection_warrior_test.go     |   3 +-
 sim/web/main_test.go                          |   3 -
 ui/core/player.ts                             |  81 -------------
 ui/elemental_shaman/presets.ts                |   1 -
 ui/enhancement_shaman/presets.ts              |   1 -
 ui/raid/raid_stats.ts                         |   2 +-
 ui/restoration_shaman/presets.ts              |   1 -
 39 files changed, 37 insertions(+), 729 deletions(-)

diff --git a/proto/druid.proto b/proto/druid.proto
index a5e8215341..61429b6059 100644
--- a/proto/druid.proto
+++ b/proto/druid.proto
@@ -146,64 +146,7 @@ enum DruidMinorGlyph {
 
 message BalanceDruid {
   message Rotation {
-		enum Type {
-			Unknown = 0;
-			Default = 1;
-			Manual = 2;
-		}
-		Type type = 1;
-
-		enum MfUsage {
-			NoMf = 0;
-			BeforeLunar = 1;
-			MaximizeMf = 2;
-			MultidotMf = 3;
-		}
-		MfUsage mf_usage = 2;
-
-		enum IsUsage {
-			NoIs = 0;
-			BeforeSolar = 1;
-			OptimizeIs = 2;
-			MultidotIs = 3;
-		}
-		IsUsage is_usage = 3;
-
-		bool use_battle_res = 4;
-		enum WrathUsage {
-			NoWrath = 0;
-			FishingForLunar= 1;
-			RegularWrath = 2;
-		}
-		WrathUsage wrath_usage = 5;
-		bool use_starfire = 6;
-		bool use_typhoon = 7;
-		bool use_hurricane = 8;
-		bool use_smart_cooldowns = 9;
-		bool maintain_faerie_fire = 10;
-		int32 player_latency = 11;
-		float okf_ppm = 14 [deprecated = true];
-
-		enum EclipsePrio {
-			Lunar = 0;
-			Solar = 1;
-		}
-		EclipsePrio eclipse_prio = 12;
-		bool eclipse_shuffling = 13;
-
-		enum MfExtension {
-			ExtendFishingForLunar = 0;
-			ExtendFishingForSolar = 1;
-			ExtendDuringLunar = 2;
-			ExtendDuringSolar = 3;
-			ExtendAlways = 4;
-			ExtendOutsideSolar = 5;
-			DontExtend = 6;
-		}
-		MfExtension mf_extension = 15;
-		bool snapshot_mf = 16;
-	}
-  Rotation rotation = 1;
+  }
 
   message Options {
     UnitReference innervate_target = 1;
@@ -269,7 +212,6 @@ message FeralTankDruid {
 		bool maintain_demoralizing_roar = 2;
 		double lacerate_time = 3;
   }
-  Rotation rotation = 1;
 
   message Options {
     UnitReference innervate_target = 1;
@@ -281,7 +223,6 @@ message FeralTankDruid {
 message RestorationDruid {
   message Rotation {
   }
-  Rotation rotation = 1;
 
   message Options {
     UnitReference innervate_target = 1;
diff --git a/proto/hunter.proto b/proto/hunter.proto
index f470dd0c4d..ae3753fa07 100644
--- a/proto/hunter.proto
+++ b/proto/hunter.proto
@@ -3,8 +3,6 @@ package proto;
 
 option go_package = "./proto";
 
-import "common.proto";
-
 message HunterTalents {
 	// Beast Mastery
 	int32 improved_aspect_of_the_hawk = 1;
@@ -198,27 +196,6 @@ message Hunter {
 
 		bool trap_weave = 1;
 
-		// TODO: Safe to delete after 1 month (after 8/26/2023).
-		double time_to_trap_weave_ms = 2 [deprecated = true];
-
-		enum SpellOption {
-			NoSpell = 0;
-			SteadyShot = 1;
-			ArcaneShot = 2;
-			AimedShot = 3;
-			MultiShot = 4;
-			SerpentStingSpell = 5;
-			ScorpidStingSpell = 6;
-			KillShot = 7;
-			BlackArrow = 8;
-			ChimeraShot = 9;
-			ExplosiveShot = 10;
-			ExplosiveShotDownrank = 13;
-			ExplosiveTrap = 11;
-			Volley = 12;
-		}
-		CustomRotation custom_rotation = 8 [deprecated = true];
-
 		// Switch to Aspect of the Viper when mana goes below this percent.
 		double viper_start_mana_percent = 6;
 		// Switch back to Aspect of the Hawk when mana goes above this percent.
@@ -228,9 +205,7 @@ message Hunter {
 		bool allow_explosive_shot_downrank = 10;
 
 		bool multi_dot_serpent_sting = 11;
-		double steady_shot_max_delay = 12 [deprecated = true];
 	}
-	Rotation rotation = 1;
 	
 	message Options {
 		enum Ammo {
diff --git a/proto/mage.proto b/proto/mage.proto
index 9cf136b1ac..9c75cb42f3 100644
--- a/proto/mage.proto
+++ b/proto/mage.proto
@@ -145,8 +145,6 @@ enum MageMinorGlyph {
 message Mage {
 	message Rotation {
 		// Arcane Options.
-		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;
@@ -164,9 +162,7 @@ message Mage {
 
 		// Frost Options.
 		bool use_ice_lance = 18;
-		double water_elemental_disobey_chance = 6 [deprecated = true];
 	}
-	Rotation rotation = 1;
 	
 	message Options {
 		enum ArmorType {
diff --git a/proto/paladin.proto b/proto/paladin.proto
index 30aab70b4a..27aec34243 100644
--- a/proto/paladin.proto
+++ b/proto/paladin.proto
@@ -3,8 +3,6 @@ package proto;
 
 option go_package = "./proto";
 
- import "common.proto";
-
 message PaladinTalents {
 	// Holy
 	int32 spiritual_focus = 1;
@@ -163,40 +161,7 @@ enum PaladinJudgement {
 
 message RetributionPaladin {
 	message Rotation {
-		int32 exo_slack = 1;
-		int32 cons_slack = 2;
-		double divine_plea_percentage = 3;
-		int32 holy_wrath_threshold = 4;
-		int32 sov_targets = 5;
-
-		enum SpellOption {
-			NoSpell = 0;
-			JudgementOfWisdom = 1;
-			DivineStorm = 2;
-			HammerOfWrath = 3;
-			Consecration = 4;
-			HolyWrath = 5;
-			CrusaderStrike = 6;
-			Exorcism = 7;
-			DivinePlea = 8;
-		}
-		CustomRotation custom_rotation = 8;
-		CustomRotation custom_cast_sequence = 10;
-
-		enum RotationType {
-			UnknownType = 0;
-			Standard = 1;
-			Custom = 2;
-			CastSequence = 3;
-		}
-		RotationType type = 9;
-
-		bool use_divine_plea = 11;
-		bool avoid_clipping_consecration = 12;
-		bool hold_last_avenging_wrath_until_execution = 13;
-		bool cancel_chaos_bane = 14;
 	}
-	Rotation rotation = 1;
 	
 	message Options {
 		PaladinJudgement judgement = 1;
@@ -209,26 +174,7 @@ message RetributionPaladin {
 
 message ProtectionPaladin {
 	message Rotation {
-		bool hammer_first = 1;
-		bool use_custom_prio = 2;
-		bool squeeze_holy_wrath = 4;
-		double wait_slack = 5;
-
-		enum SpellOption {
-			NoSpell = 0;
-			JudgementOfWisdom = 1;
-			HammerOfWrath = 2;
-			Consecration = 3;
-			HolyWrath = 4;
-			Exorcism = 5;
-			ShieldOfRighteousness = 6;
-			AvengersShield = 7;
-			HammerOfTheRighteous = 8;
-			HolyShield = 9;
-		}
-		CustomRotation custom_rotation = 3;
 	}
-	Rotation rotation = 1;
 
 	message Options {
 		PaladinJudgement judgement = 1;
@@ -242,7 +188,6 @@ message ProtectionPaladin {
 message HolyPaladin {
 	message Rotation {
 	}
-	Rotation rotation = 1;
 
 	message Options {
 		PaladinJudgement judgement = 1;
diff --git a/proto/priest.proto b/proto/priest.proto
index d275e525bd..9aad900732 100644
--- a/proto/priest.proto
+++ b/proto/priest.proto
@@ -138,23 +138,7 @@ enum PriestMinorGlyph {
 
 message ShadowPriest {
 	message Rotation {
-		enum RotationType {
-			Unknown = 0;
-			Basic = 1;
-			Clipping = 2;
-			Ideal = 3;
-			AoE = 4;
-		}
-		enum PreCastOption {
-			Nothing = 0;
-			PrecastVt = 1;
-			PrecastMb = 2;
-		}
-		RotationType rotation_type = 1;
-		PreCastOption precast_type = 2;
-		double latency = 3 [deprecated = true]; // Latency between actions
 	}
-	Rotation rotation = 1;
 	
 	message Options {
 		enum Armor {
@@ -174,13 +158,7 @@ message ShadowPriest {
 
 message SmitePriest {
 	message Rotation {
-		bool use_mind_blast = 2;
-		bool use_shadow_word_death = 3;
-		bool use_devouring_plague = 4;
-		bool meme_dream = 5;
-		double allowed_holy_fire_delay_ms = 6;
 	}
-	Rotation rotation = 1;
 	
 	message Options {
 		bool use_inner_fire = 3;
@@ -192,28 +170,7 @@ message SmitePriest {
 
 message HealingPriest {
 	message Rotation {
-		enum RotationType {
-			UnknownType = 0;
-			Cycle = 1;
-			Custom = 2;
-		}
-		RotationType type = 1;
-
-		enum SpellOption {
-			NoSpell = 0;
-			GreaterHeal = 1;
-			FlashHeal = 2;
-			Renew = 3;
-			PowerWordShield = 4;
-			CircleOfHealing = 5;
-			PrayerOfHealing = 6;
-			PrayerOfMending = 7;
-			Penance = 8;
-			BindingHeal = 9;
-		}
-		CustomRotation custom_rotation = 2;
 	}
-	Rotation rotation = 1;
 	
 	message Options {
 		bool use_inner_fire = 3;
diff --git a/proto/rogue.proto b/proto/rogue.proto
index c9661fe73c..3d191bcd50 100644
--- a/proto/rogue.proto
+++ b/proto/rogue.proto
@@ -140,72 +140,7 @@ enum RogueMinorGlyph {
 
 message Rogue {
 	message Rotation {
-		enum Frequency {
-			Never = 0;
-			Once = 1;
-			Maintain = 2;
-			FrequencyUnknown = 5;
-		}
-		Frequency expose_armor_frequency = 1;
-
-		int32 minimum_combo_points_expose_armor = 2;
-
-		Frequency tricks_of_the_trade_frequency = 3;
-
-		enum CombatPriority {
-			RuptureEviscerate = 0;
-			EviscerateRupture = 1;
-			CombatPriorityUnknown = 2;
-		}
-		CombatPriority combat_finisher_priority = 4;
-
-		enum CombatBuilder {
-			SinisterStrike = 0;
-			Backstab = 1;
-			HemorrhageCombat = 2;
-		}
-		CombatBuilder combat_builder = 25;
-
-		enum AssassinationPriority {
-			EnvenomRupture = 0;
-			RuptureEnvenom = 1;
-			AssassinationPriorityUnknown = 2;
-		}
-		AssassinationPriority assassination_finisher_priority = 5;
-
-		enum SubtletyBuilder {
-			Hemorrhage = 0;
-			BackstabSub = 1;
-		}
-		SubtletyBuilder subtlety_builder = 26;
-
-		enum SubtletyPriority {
-			SubtletyEviscerate = 0;
-			SubtletyEnvenom = 1;
-			SubtletyPriorityUnknown = 2;
-		}
-		SubtletyPriority subtlety_finisher_priority = 6;
-
-		int32 minimum_combo_points_primary_finisher = 7;
-		int32 minimum_combo_points_secondary_finisher = 8;
-
-		Frequency MultiTargetSliceFrequency = 9;
-		int32 minimum_combo_points_multi_target_slice = 10;
-
-		reserved 11, 13, 14, 19, 21, 22, 23; // the various envenom pooling parameters
-
-		bool use_feint = 12;
-
-		bool open_with_garrote = 15;
-		bool open_with_premeditation = 16;
-		bool open_with_shadowstep = 17;
-
-		bool rupture_for_bleed = 18;
-
-		bool hemo_with_dagger = 20;
-		bool use_ghostly_strike = 24;
 	}
-	Rotation rotation = 1;
 	
 	message Options {
 		UnitReference tricks_of_the_trade_target = 1;
diff --git a/proto/shaman.proto b/proto/shaman.proto
index b5fde9f161..83413a897c 100644
--- a/proto/shaman.proto
+++ b/proto/shaman.proto
@@ -2,7 +2,6 @@ syntax = "proto3";
 package proto;
 
 option go_package = "./proto";
-import "common.proto";
 
 message ShamanTalents {
 	// Elemental
@@ -210,39 +209,10 @@ enum ShamanSyncType {
 
 message ElementalShaman {
 	message Rotation {
-		// Deprecated as of 2023-08-29
-		ShamanTotems totems = 3 [deprecated = true];
-
-		enum RotationType {
-			Unknown = 0;
-			Adaptive = 1;
-			Manual = 2;
-		}
-		RotationType type = 1;
-		bool in_thunderstorm_range = 2 [deprecated = true];
-
-		// These options are used for the manual rotation.
-		bool use_fire_nova = 4;
-		double fn_min_mana_per = 9;
-		bool use_chain_lightning = 5;
-		bool use_cl_only_gap = 11;
-		double cl_min_mana_per = 10;
-		bool overwrite_flameshock = 6;
-		bool always_crit_lvb = 7;
-		bool use_thunderstorm = 8;
-		double lvb_fs_wait_ms = 12;
-		
-		enum BloodlustUse {
-			UnsetBloodlust = 0;
-			UseBloodlust = 1;
-			NoBloodlust = 2;
-		}
-		BloodlustUse bloodlust = 13;
 	}
 
 	message Options {
 		ShamanShield shield = 1;
-		bool bloodlust = 2 [deprecated = true];
 		ShamanTotems totems = 3;
 		
 		enum ThunderstormRange {
@@ -253,83 +223,21 @@ message ElementalShaman {
 		ThunderstormRange thunderstormRange = 4;
 	}
 
-	Rotation rotation = 1;
 	Options options = 3;
 }
 
 message EnhancementShaman {
 	message Rotation {
-		// Deprecated as of 2023-08-29
-		ShamanTotems totems = 1 [deprecated = true];
-		
-		enum RotationType {
-			Unknown = 0;
-			Priority = 1;
-			Custom = 2;
-		}
-		RotationType rotation_type = 2;
-
-		//TODO: add spells here for custom rotation (if necessary?)
-		enum CustomRotationSpell {
-			NoSpell = 0;
-			Stormstrike = 1;
-			StormstrikeDebuffMissing = 2; //find a way to differentiate tooltips for this, lightning bolt weaves, and downranked flametongue
-			LightningBolt = 3;
-			LightningBoltWeave = 4;
-			FlameShock = 5;
-			EarthShock = 6;
-			LavaLash = 7;
-			LavaBurst = 8;
-			LightningShield = 9;
-			FireNova = 10;
-			ChainLightning = 11;
-			FrostShock = 12;
-			MagmaTotem = 13;
-			LightningBoltDelayedWeave = 14;
-		}
-		CustomRotation custom_rotation = 3;
-
-		//weaving options
-		bool lavaburst_weave = 4;
-		bool lightningbolt_weave = 5;
-		int32 maelstromweapon_min_stack = 6;
-		double auto_weave_delay = 7;
-
-		//other general rotation config options
-		double firenova_mana_threshold = 8;
-		double shamanistic_rage_mana_threshold = 9;
-
-		enum PrimaryShock {
-			None = 0;
-			Earth = 1;
-			Frost = 2;
-		}
-
-		bool weave_flame_shock = 10;
-		PrimaryShock primary_shock = 11;
-		int32 flame_shock_clip_ticks = 12;
-		double delay_gcd_weave = 13;
-		ItemSwap item_swap = 14;
-		bool enable_item_swap = 15;
-
-		enum BloodlustUse {
-			UnsetBloodlust = 0;
-			UseBloodlust = 1;
-			NoBloodlust = 2;
-		}
-		BloodlustUse bloodlust = 16;
 	}
 
 	message Options {
 		ShamanShield shield = 1;
-		bool bloodlust = 2 [deprecated = true];
 		ShamanSyncType sync_type = 3;
 		ShamanImbue imbue_mh = 4;
 		ShamanImbue imbue_oh = 5;
 		ShamanTotems totems = 6;
 	}
 
-	Rotation rotation = 1;
 	Options options = 3;
 }
 
@@ -342,28 +250,14 @@ enum ShamanHealSpell {
 
 message RestorationShaman {
 	message Rotation {
-		// Deprecated as of 2023-08-29
-		ShamanTotems totems = 1 [deprecated = true];
-		bool use_earth_shield = 2;
-		ShamanHealSpell primary_heal = 3;
-		bool use_riptide = 4;
-
-		enum BloodlustUse {
-			UnsetBloodlust = 0;
-			UseBloodlust = 1;
-			NoBloodlust = 2;
-		}
-		BloodlustUse bloodlust = 5;
 	}
 
 	message Options {
 		ShamanShield shield = 1;
-		bool bloodlust = 2 [deprecated = true];
 		ShamanImbue imbue_mh = 4;
 		int32 earth_shield_p_p_m = 5;
 		ShamanTotems totems = 6;
 	}
 
-	Rotation rotation = 1;
 	Options options = 3;
 }
diff --git a/proto/warlock.proto b/proto/warlock.proto
index 9e387cca6c..21f95aa0f2 100644
--- a/proto/warlock.proto
+++ b/proto/warlock.proto
@@ -2,7 +2,6 @@ syntax = "proto3";
 package proto;
 
 option go_package = "./proto";
-import "common.proto";
 
 // WotLK talents
 message WarlockTalents {
@@ -138,53 +137,7 @@ enum WarlockMinorGlyph {
 
 message Warlock {
 	message Rotation {
-		enum Preset {
-			Manual = 0;
-			Automatic = 1;
-		}
-		enum Type {
-			Affliction = 0;
-			Demonology = 1;
-			Destruction = 2;
-		}
-		enum PrimarySpell {
-			UnknownSpell = 0;
-			ShadowBolt = 1;
-			Incinerate = 2;
-			Seed = 3;
-		}
-		enum Curse {
-			NoCurse = 0;
-			Elements = 1;
-			Weakness = 2;
-			Doom = 3;
-			Agony = 4;
-			Tongues = 5;
-		}
-		enum SecondaryDot {
-			NoSecondaryDot = 0;
-			Immolate = 1;
-			UnstableAffliction = 2;
-		}
-		enum SpecSpell {
-			NoSpecSpell = 0;
-			ChaosBolt = 1;
-			Haunt = 2;
-		}
-		Preset preset = 1;
-		Curse curse = 2;
-		PrimarySpell primary_spell = 3;
-		SecondaryDot secondary_dot = 4;
-		bool corruption = 5;
-		bool detonate_seed = 6;
-		SpecSpell spec_spell = 7;
-		Type type = 8;
-		bool use_infernal = 9;
-
-		bool enable_weapon_swap = 10;
-		ItemSwap weapon_swap = 11;
 	}
-	Rotation rotation = 1;
 	
 	message Options {
 		enum Summon {
diff --git a/proto/warrior.proto b/proto/warrior.proto
index 8886e52313..51a6cd3e65 100644
--- a/proto/warrior.proto
+++ b/proto/warrior.proto
@@ -3,8 +3,6 @@ package proto;
 
 option go_package = "./proto";
 
-import "common.proto";
-
 message WarriorTalents {
 	// Arms
 	int32 improved_heroic_strike = 1;
@@ -148,78 +146,7 @@ enum WarriorShout {
 
 message Warrior {
 	message Rotation {
-		bool use_cleave = 1;
-		bool use_rend = 2;
-		bool use_ms = 18;
-		bool use_slam = 3;
-
-		enum MainGcd {
-			None = 0;
-			Slam = 1;
-			Bloodthirst = 2;
-			Whirlwind = 3;
-		}
-		MainGcd main_gcd = 4;
-
-		enum SunderArmor {
-			SunderArmorNone = 0;
-			SunderArmorHelpStack = 1;
-			SunderArmorMaintain = 2;
-		}
-		SunderArmor sunderArmor = 5;
-		bool maintain_demo_shout = 6;
-		bool maintain_thunder_clap = 7;
-
-		// Queue HS or Cleave when over this threshold.
-		double hs_rage_threshold = 8;
-
-		// Use Mortal Strike when over this threshold.
-		double ms_rage_threshold = 9;
-
-		// Use Rend when below this threshold.
-		double rend_rage_threshold_below = 10;
-
-		// Use Slam when over this threshold.
-		double slam_rage_threshold = 11;
-
-		// Refresh Rend when remaining duration is less than this threshold.
-		double rend_cd_threshold = 12;
-
-		bool use_hs_during_execute = 13;
-		bool use_bt_during_execute = 14;
-		bool spam_execute = 15;
-		bool use_ww_during_execute = 16;
-		bool use_slam_over_execute = 17;
-
-		enum StanceOption {
-			DefaultStance = 0;
-			BattleStance = 1;
-			BerserkerStance = 2;
-		}
-		StanceOption stance_option = 19;
-		double rend_health_threshold_above = 20;
-
-		// Only used for Fury
-		bool use_overpower = 21;
-		bool execute_phase_overpower = 22;
-		double bloodsurge_duration_threshold = 25;
-
-		enum SpellOption {
-			NoSpell = 0;
-			BloodthirstCustom = 1;
-			MortalStrike = 2;
-			WhirlwindCustom = 3;
-			SlamCustom = 4;
-			Rend = 5;
-			Overpower = 6;
-			Execute = 7;
-			ThunderClap = 8;
-			SlamExpiring = 9;
-		}
-		CustomRotation custom_rotation = 23;
-		bool custom_rotation_option = 24;
 	}
-	Rotation rotation = 1;
 
 	message Options {
 		double starting_rage = 1;
@@ -234,39 +161,7 @@ message Warrior {
 
 message ProtectionWarrior {
 	message Rotation {
-		enum DemoShoutChoice {
-			DemoShoutChoiceNone = 0;
-			DemoShoutChoiceMaintain = 1;
-			DemoShoutChoiceFiller = 2;
-		}
-		DemoShoutChoice demo_shout_choice = 1;
-
-		enum ThunderClapChoice {
-			ThunderClapChoiceNone = 0;
-			ThunderClapChoiceMaintain = 1;
-			ThunderClapChoiceOnCD = 2;
-		}
-		ThunderClapChoice thunder_clap_choice = 2;
-
-		enum SpellOption {
-			NoSpell = 0;
-			Revenge = 1;
-			ShieldSlam = 2;
-			Devastate = 3;
-			SunderArmor = 4;
-			Shout = 5;
-			DemoralizingShout = 6;
-			ThunderClap = 7;
-			MortalStrike = 8;
-			ConcussionBlow = 9;
-			Shockwave = 10;
-		}
-		CustomRotation custom_rotation = 3;
-
-		double hs_rage_threshold = 4;
-		bool prio_sslam_on_shield_block = 5;
 	}
-	Rotation rotation = 1;
 
 	message Options {
 		double starting_rage = 1;
diff --git a/sim/druid/balance/balance_test.go b/sim/druid/balance/balance_test.go
index 5e5d2d2b2e..6d4faf9b5b 100644
--- a/sim/druid/balance/balance_test.go
+++ b/sim/druid/balance/balance_test.go
@@ -85,7 +85,6 @@ var PlayerOptionsAdaptive = &proto.Player_BalanceDruid{
 		Options: &proto.BalanceDruid_Options{
 			OkfUptime: 0.2,
 		},
-		Rotation: &proto.BalanceDruid_Rotation{},
 	},
 }
 
diff --git a/sim/druid/restoration/restoration_test.go b/sim/druid/restoration/restoration_test.go
index cd6edfb296..9860678554 100644
--- a/sim/druid/restoration/restoration_test.go
+++ b/sim/druid/restoration/restoration_test.go
@@ -60,7 +60,6 @@ var PlayerOptionsStandard = &proto.Player_RestorationDruid{
 		Options: &proto.RestorationDruid_Options{
 			InnervateTarget: &proto.UnitReference{Type: proto.UnitReference_Player, Index: 0}, // self innervate
 		},
-		Rotation: &proto.RestorationDruid_Rotation{},
 	},
 }
 
diff --git a/sim/druid/tank/tank_test.go b/sim/druid/tank/tank_test.go
index 1424efb46f..b9788176e7 100644
--- a/sim/druid/tank/tank_test.go
+++ b/sim/druid/tank/tank_test.go
@@ -83,7 +83,6 @@ var PlayerOptionsDefault = &proto.Player_FeralTankDruid{
 			InnervateTarget: &proto.UnitReference{}, // no Innervate
 			StartingRage:    20,
 		},
-		Rotation: &proto.FeralTankDruid_Rotation{},
 	},
 }
 
diff --git a/sim/mage/mage_test.go b/sim/mage/mage_test.go
index 3f9e9b4145..b5e777f68c 100644
--- a/sim/mage/mage_test.go
+++ b/sim/mage/mage_test.go
@@ -128,7 +128,6 @@ var PlayerOptionsFire = &proto.Player_Mage{
 		Options: &proto.Mage_Options{
 			Armor: proto.Mage_Options_MoltenArmor,
 		},
-		Rotation: &proto.Mage_Rotation{},
 	},
 }
 
@@ -137,7 +136,6 @@ var PlayerOptionsFrost = &proto.Player_Mage{
 		Options: &proto.Mage_Options{
 			Armor: proto.Mage_Options_MageArmor,
 		},
-		Rotation: &proto.Mage_Rotation{},
 	},
 }
 
@@ -146,7 +144,6 @@ var PlayerOptionsArcane = &proto.Player_Mage{
 		Options: &proto.Mage_Options{
 			Armor: proto.Mage_Options_MoltenArmor,
 		},
-		Rotation: &proto.Mage_Rotation{},
 	},
 }
 
diff --git a/sim/paladin/avenging_wrath.go b/sim/paladin/avenging_wrath.go
index b853e05f08..018012a8f3 100644
--- a/sim/paladin/avenging_wrath.go
+++ b/sim/paladin/avenging_wrath.go
@@ -56,14 +56,6 @@ func (paladin *Paladin) RegisterAvengingWrathCD() {
 		Type:  core.CooldownTypeDPS,
 		// modify this logic if it should ever not be spammed on CD / maybe should synced with other CDs
 		ShouldActivate: func(sim *core.Simulation, character *core.Character) bool {
-			if paladin.HoldLastAvengingWrathUntilExecution && float64(sim.CurrentTime+paladin.AvengingWrath.CD.Duration) >= float64(sim.Duration) {
-				if float64(sim.CurrentTime+paladin.AvengingWrathAura.Duration) >= float64(sim.Duration) {
-					// If we're cutting it close on iteration end time, pop AW anyways to try and get full duration.
-					return true
-				}
-				return false
-			}
-
 			if paladin.CurrentSeal == paladin.SealOfVengeanceAura {
 				if paladin.SovDotSpell.Dot(paladin.CurrentTarget).GetStacks() < 5 {
 					return false
diff --git a/sim/paladin/holy/holy.go b/sim/paladin/holy/holy.go
index 7230b32915..535c1484b1 100644
--- a/sim/paladin/holy/holy.go
+++ b/sim/paladin/holy/holy.go
@@ -27,9 +27,8 @@ func NewHolyPaladin(character *core.Character, options *proto.Player) *HolyPalad
 	holyOptions := options.GetHolyPaladin()
 
 	holy := &HolyPaladin{
-		Paladin:  paladin.NewPaladin(character, options.TalentsString),
-		Rotation: holyOptions.Rotation,
-		Options:  holyOptions.Options,
+		Paladin: paladin.NewPaladin(character, options.TalentsString),
+		Options: holyOptions.Options,
 	}
 
 	holy.PaladinAura = holyOptions.Options.Aura
@@ -40,8 +39,7 @@ func NewHolyPaladin(character *core.Character, options *proto.Player) *HolyPalad
 type HolyPaladin struct {
 	*paladin.Paladin
 
-	Rotation *proto.HolyPaladin_Rotation
-	Options  *proto.HolyPaladin_Options
+	Options *proto.HolyPaladin_Options
 }
 
 func (holy *HolyPaladin) GetPaladin() *paladin.Paladin {
diff --git a/sim/paladin/holy/holy_test.go b/sim/paladin/holy/holy_test.go
index 81bbcf1835..5550a226a4 100644
--- a/sim/paladin/holy/holy_test.go
+++ b/sim/paladin/holy/holy_test.go
@@ -87,8 +87,7 @@ var defaultProtOptions = &proto.HolyPaladin_Options{
 
 var BasicOptions = &proto.Player_HolyPaladin{
 	HolyPaladin: &proto.HolyPaladin{
-		Options:  defaultProtOptions,
-		Rotation: &proto.HolyPaladin_Rotation{},
+		Options: defaultProtOptions,
 	},
 }
 
diff --git a/sim/paladin/paladin.go b/sim/paladin/paladin.go
index 8b4c5aa17b..d91ca9bb13 100644
--- a/sim/paladin/paladin.go
+++ b/sim/paladin/paladin.go
@@ -70,10 +70,6 @@ type Paladin struct {
 
 	DemonAndUndeadTargetCount int32
 
-	AvoidClippingConsecration           bool
-	HoldLastAvengingWrathUntilExecution bool
-	CancelChaosBane                     bool
-
 	mutualLockoutDPAW *core.Timer
 }
 
diff --git a/sim/paladin/protection/protection_test.go b/sim/paladin/protection/protection_test.go
index dcfb1f388e..5b38e0bdbb 100644
--- a/sim/paladin/protection/protection_test.go
+++ b/sim/paladin/protection/protection_test.go
@@ -33,7 +33,6 @@ func TestProtection(t *testing.T) {
 							Seal:      proto.PaladinSeal_Command,
 							Aura:      proto.PaladinAura_RetributionAura,
 						},
-						Rotation: defaultProtRotation,
 					},
 				},
 			},
@@ -46,7 +45,6 @@ func TestProtection(t *testing.T) {
 							Seal:      proto.PaladinSeal_Righteousness,
 							Aura:      proto.PaladinAura_RetributionAura,
 						},
-						Rotation: defaultProtRotation,
 					},
 				},
 			},
@@ -106,8 +104,6 @@ var StandardGlyphs = &proto.Glyphs{
 	Minor2: int32(proto.PaladinMinorGlyph_GlyphOfSenseUndead),
 }
 
-var defaultProtRotation = &proto.ProtectionPaladin_Rotation{}
-
 var defaultProtOptions = &proto.ProtectionPaladin_Options{
 	Judgement: proto.PaladinJudgement_JudgementOfWisdom,
 	Seal:      proto.PaladinSeal_Vengeance,
@@ -116,8 +112,7 @@ var defaultProtOptions = &proto.ProtectionPaladin_Options{
 
 var DefaultOptions = &proto.Player_ProtectionPaladin{
 	ProtectionPaladin: &proto.ProtectionPaladin{
-		Options:  defaultProtOptions,
-		Rotation: defaultProtRotation,
+		Options: defaultProtOptions,
 	},
 }
 
diff --git a/sim/paladin/retribution/retribution.go b/sim/paladin/retribution/retribution.go
index a21aca2089..0af33a5d40 100644
--- a/sim/paladin/retribution/retribution.go
+++ b/sim/paladin/retribution/retribution.go
@@ -33,10 +33,6 @@ func NewRetributionPaladin(character *core.Character, options *proto.Player) *Re
 		Seal:    retOptions.Options.Seal,
 	}
 
-	pal.AvoidClippingConsecration = retOptions.Rotation.AvoidClippingConsecration
-	pal.HoldLastAvengingWrathUntilExecution = retOptions.Rotation.HoldLastAvengingWrathUntilExecution
-	pal.CancelChaosBane = retOptions.Rotation.CancelChaosBane
-
 	ret.PaladinAura = retOptions.Options.Aura
 
 	ret.EnableAutoAttacks(ret, core.AutoAttackOptions{
diff --git a/sim/paladin/retribution/retribution_test.go b/sim/paladin/retribution/retribution_test.go
index b4eb5a47ce..a0d790ec77 100644
--- a/sim/paladin/retribution/retribution_test.go
+++ b/sim/paladin/retribution/retribution_test.go
@@ -33,7 +33,6 @@ func TestRetribution(t *testing.T) {
 							Seal:      proto.PaladinSeal_Command,
 							Aura:      proto.PaladinAura_RetributionAura,
 						},
-						Rotation: &proto.RetributionPaladin_Rotation{},
 					},
 				},
 			},
@@ -46,7 +45,6 @@ func TestRetribution(t *testing.T) {
 							Seal:      proto.PaladinSeal_Righteousness,
 							Aura:      proto.PaladinAura_RetributionAura,
 						},
-						Rotation: &proto.RetributionPaladin_Rotation{},
 					},
 				},
 			},
@@ -59,14 +57,6 @@ func TestRetribution(t *testing.T) {
 							Seal:      proto.PaladinSeal_Vengeance,
 							Aura:      proto.PaladinAura_RetributionAura,
 						},
-						Rotation: &proto.RetributionPaladin_Rotation{
-							ConsSlack:            500,
-							ExoSlack:             500,
-							UseDivinePlea:        true,
-							DivinePleaPercentage: 0.75,
-							HolyWrathThreshold:   4,
-							SovTargets:           2,
-						},
 					},
 				},
 			},
@@ -133,7 +123,6 @@ var DefaultOptions = &proto.Player_RetributionPaladin{
 			Seal:      proto.PaladinSeal_Vengeance,
 			Aura:      proto.PaladinAura_RetributionAura,
 		},
-		Rotation: &proto.RetributionPaladin_Rotation{},
 	},
 }
 
diff --git a/sim/priest/healing/healing_priest_test.go b/sim/priest/healing/healing_priest_test.go
index c283397c98..0ff0f4907e 100644
--- a/sim/priest/healing/healing_priest_test.go
+++ b/sim/priest/healing/healing_priest_test.go
@@ -106,7 +106,6 @@ var PlayerOptionsDisc = &proto.Player_HealingPriest{
 			UseShadowfiend:    true,
 			RapturesPerMinute: 5,
 		},
-		Rotation: &proto.HealingPriest_Rotation{},
 	},
 }
 
@@ -116,6 +115,5 @@ var PlayerOptionsHoly = &proto.Player_HealingPriest{
 			UseInnerFire:   true,
 			UseShadowfiend: true,
 		},
-		Rotation: &proto.HealingPriest_Rotation{},
 	},
 }
diff --git a/sim/priest/shadow/shadow_priest_test.go b/sim/priest/shadow/shadow_priest_test.go
index 3fb8fae729..edd33176d8 100644
--- a/sim/priest/shadow/shadow_priest_test.go
+++ b/sim/priest/shadow/shadow_priest_test.go
@@ -70,9 +70,6 @@ var PlayerOptionsBasic = &proto.Player_ShadowPriest{
 			UseMindBlast:       true,
 			UseShadowWordDeath: true,
 		},
-		Rotation: &proto.ShadowPriest_Rotation{
-			RotationType: proto.ShadowPriest_Rotation_Basic,
-		},
 	},
 }
 var PlayerOptionsClipping = &proto.Player_ShadowPriest{
@@ -83,10 +80,6 @@ var PlayerOptionsClipping = &proto.Player_ShadowPriest{
 			UseMindBlast:       true,
 			UseShadowWordDeath: true,
 		},
-		Rotation: &proto.ShadowPriest_Rotation{
-			RotationType: proto.ShadowPriest_Rotation_Clipping,
-			PrecastType:  1,
-		},
 	},
 }
 var PlayerOptionsIdeal = &proto.Player_ShadowPriest{
@@ -97,9 +90,5 @@ var PlayerOptionsIdeal = &proto.Player_ShadowPriest{
 			UseMindBlast:       true,
 			UseShadowWordDeath: true,
 		},
-		Rotation: &proto.ShadowPriest_Rotation{
-			RotationType: proto.ShadowPriest_Rotation_Ideal,
-			PrecastType:  1,
-		},
 	},
 }
diff --git a/sim/priest/smite/smite_priest_test.go b/sim/priest/smite/smite_priest_test.go
index a4a067f70d..5ed9e42767 100644
--- a/sim/priest/smite/smite_priest_test.go
+++ b/sim/priest/smite/smite_priest_test.go
@@ -60,6 +60,5 @@ var PlayerOptionsBasic = &proto.Player_SmitePriest{
 			UseInnerFire:   true,
 			UseShadowfiend: true,
 		},
-		Rotation: &proto.SmitePriest_Rotation{},
 	},
 }
diff --git a/sim/raid_bench_test.go b/sim/raid_bench_test.go
index d159a20cc1..41e9ef3411 100644
--- a/sim/raid_bench_test.go
+++ b/sim/raid_bench_test.go
@@ -18,9 +18,6 @@ var castersWithElemental = &proto.Party{
 			Equipment: MoonkinEquipment,
 			Spec: &proto.Player_BalanceDruid{
 				BalanceDruid: &proto.BalanceDruid{
-					Rotation: &proto.BalanceDruid_Rotation{
-						Type: proto.BalanceDruid_Rotation_Default,
-					},
 					Options: &proto.BalanceDruid_Options{
 						InnervateTarget: &proto.UnitReference{},
 					},
@@ -41,9 +38,6 @@ var castersWithElemental = &proto.Party{
 			Equipment: ShadowEquipment,
 			Spec: &proto.Player_ShadowPriest{
 				ShadowPriest: &proto.ShadowPriest{
-					Rotation: &proto.ShadowPriest_Rotation{
-						RotationType: proto.ShadowPriest_Rotation_Ideal,
-					},
 					Options: &proto.ShadowPriest_Options{
 						UseShadowfiend: true,
 					},
@@ -64,12 +58,8 @@ var castersWithElemental = &proto.Party{
 			Equipment: ElementalEquipment,
 			Spec: &proto.Player_ElementalShaman{
 				ElementalShaman: &proto.ElementalShaman{
-					Rotation: &proto.ElementalShaman_Rotation{
-						Type: proto.ElementalShaman_Rotation_Adaptive,
-					},
 					Options: &proto.ElementalShaman_Options{
-						Shield:    proto.ShamanShield_WaterShield,
-						Bloodlust: true,
+						Shield: proto.ShamanShield_WaterShield,
 						Totems: &proto.ShamanTotems{
 							Earth: proto.EarthTotem_TremorTotem,
 							Air:   proto.AirTotem_WrathOfAirTotem,
@@ -97,7 +87,6 @@ var castersWithElemental = &proto.Party{
 					Options: &proto.Mage_Options{
 						Armor: proto.Mage_Options_MageArmor,
 					},
-					Rotation: &proto.Mage_Rotation{},
 				},
 			},
 			Consumes: &proto.Consumes{
@@ -122,9 +111,6 @@ var castersWithResto = &proto.Party{
 			Equipment: MoonkinEquipment,
 			Spec: &proto.Player_BalanceDruid{
 				BalanceDruid: &proto.BalanceDruid{
-					Rotation: &proto.BalanceDruid_Rotation{
-						Type: proto.BalanceDruid_Rotation_Default,
-					},
 					Options: &proto.BalanceDruid_Options{
 						InnervateTarget: &proto.UnitReference{
 							Type:  proto.UnitReference_Player,
@@ -148,9 +134,6 @@ var castersWithResto = &proto.Party{
 			Equipment: ShadowEquipment,
 			Spec: &proto.Player_ShadowPriest{
 				ShadowPriest: &proto.ShadowPriest{
-					Rotation: &proto.ShadowPriest_Rotation{
-						RotationType: proto.ShadowPriest_Rotation_Ideal,
-					},
 					Options: &proto.ShadowPriest_Options{
 						UseShadowfiend: true,
 					},
@@ -174,7 +157,6 @@ var castersWithResto = &proto.Party{
 					Options: &proto.Mage_Options{
 						Armor: proto.Mage_Options_MageArmor,
 					},
-					Rotation: &proto.Mage_Rotation{},
 				},
 			},
 			Consumes: &proto.Consumes{
@@ -206,11 +188,9 @@ func BenchmarkSimulate(b *testing.B) {
 							Equipment: EnhancementEquipment,
 							Spec: &proto.Player_EnhancementShaman{
 								EnhancementShaman: &proto.EnhancementShaman{
-									Rotation: &proto.EnhancementShaman_Rotation{},
 									Options: &proto.EnhancementShaman_Options{
-										Shield:    proto.ShamanShield_LightningShield,
-										Bloodlust: true,
-										SyncType:  proto.ShamanSyncType_SyncMainhandOffhandSwings,
+										Shield:   proto.ShamanShield_LightningShield,
+										SyncType: proto.ShamanSyncType_SyncMainhandOffhandSwings,
 										Totems: &proto.ShamanTotems{
 											Earth: proto.EarthTotem_TremorTotem,
 											Air:   proto.AirTotem_WrathOfAirTotem,
diff --git a/sim/shaman/elemental/elemental.go b/sim/shaman/elemental/elemental.go
index b33058633b..5e63cd3756 100644
--- a/sim/shaman/elemental/elemental.go
+++ b/sim/shaman/elemental/elemental.go
@@ -28,8 +28,7 @@ func NewElementalShaman(character *core.Character, options *proto.Player) *Eleme
 	eleShamOptions := options.GetElementalShaman()
 
 	selfBuffs := shaman.SelfBuffs{
-		Bloodlust: eleShamOptions.Options.Bloodlust,
-		Shield:    eleShamOptions.Options.Shield,
+		Shield: eleShamOptions.Options.Shield,
 	}
 
 	totems := &proto.ShamanTotems{}
diff --git a/sim/shaman/elemental/elemental_test.go b/sim/shaman/elemental/elemental_test.go
index 5a20559d19..d47892c4a3 100644
--- a/sim/shaman/elemental/elemental_test.go
+++ b/sim/shaman/elemental/elemental_test.go
@@ -111,9 +111,8 @@ var FireElementalBasicTotems = &proto.ShamanTotems{
 var PlayerOptionsAdaptive = &proto.Player_ElementalShaman{
 	ElementalShaman: &proto.ElementalShaman{
 		Options: &proto.ElementalShaman_Options{
-			Shield:    proto.ShamanShield_WaterShield,
-			Bloodlust: true,
-			Totems:    FireElementalBasicTotems,
+			Shield: proto.ShamanShield_WaterShield,
+			Totems: FireElementalBasicTotems,
 		},
 	},
 }
@@ -121,9 +120,8 @@ var PlayerOptionsAdaptive = &proto.Player_ElementalShaman{
 var PlayerOptionsAdaptiveFireElemental = &proto.Player_ElementalShaman{
 	ElementalShaman: &proto.ElementalShaman{
 		Options: &proto.ElementalShaman_Options{
-			Shield:    proto.ShamanShield_WaterShield,
-			Bloodlust: true,
-			Totems:    FireElementalBasicTotems,
+			Shield: proto.ShamanShield_WaterShield,
+			Totems: FireElementalBasicTotems,
 		},
 	},
 }
diff --git a/sim/shaman/enhancement/enhancement.go b/sim/shaman/enhancement/enhancement.go
index e1653492f2..5dba89bcda 100644
--- a/sim/shaman/enhancement/enhancement.go
+++ b/sim/shaman/enhancement/enhancement.go
@@ -29,10 +29,9 @@ func NewEnhancementShaman(character *core.Character, options *proto.Player) *Enh
 	enhOptions := options.GetEnhancementShaman()
 
 	selfBuffs := shaman.SelfBuffs{
-		Bloodlust: enhOptions.Options.Bloodlust,
-		Shield:    enhOptions.Options.Shield,
-		ImbueMH:   enhOptions.Options.ImbueMh,
-		ImbueOH:   enhOptions.Options.ImbueOh,
+		Shield:  enhOptions.Options.Shield,
+		ImbueMH: enhOptions.Options.ImbueMh,
+		ImbueOH: enhOptions.Options.ImbueOh,
 	}
 
 	totems := &proto.ShamanTotems{}
diff --git a/sim/shaman/enhancement/enhancement_test.go b/sim/shaman/enhancement/enhancement_test.go
index 14da520aef..11be85311a 100644
--- a/sim/shaman/enhancement/enhancement_test.go
+++ b/sim/shaman/enhancement/enhancement_test.go
@@ -98,19 +98,17 @@ var PlayerOptionsFTFT = &proto.Player_EnhancementShaman{
 }
 
 var enhShamWFWF = &proto.EnhancementShaman_Options{
-	Shield:    proto.ShamanShield_WaterShield,
-	Bloodlust: true,
-	SyncType:  proto.ShamanSyncType_DelayOffhandSwings,
-	ImbueMh:   proto.ShamanImbue_WindfuryWeapon,
-	ImbueOh:   proto.ShamanImbue_WindfuryWeapon,
+	Shield:   proto.ShamanShield_WaterShield,
+	SyncType: proto.ShamanSyncType_DelayOffhandSwings,
+	ImbueMh:  proto.ShamanImbue_WindfuryWeapon,
+	ImbueOh:  proto.ShamanImbue_WindfuryWeapon,
 }
 
 var enhShamFTFT = &proto.EnhancementShaman_Options{
-	Shield:    proto.ShamanShield_LightningShield,
-	Bloodlust: true,
-	SyncType:  proto.ShamanSyncType_Auto,
-	ImbueMh:   proto.ShamanImbue_FlametongueWeaponDownrank,
-	ImbueOh:   proto.ShamanImbue_FlametongueWeapon,
+	Shield:   proto.ShamanShield_LightningShield,
+	SyncType: proto.ShamanSyncType_Auto,
+	ImbueMh:  proto.ShamanImbue_FlametongueWeaponDownrank,
+	ImbueOh:  proto.ShamanImbue_FlametongueWeapon,
 	Totems: &proto.ShamanTotems{
 		Earth:            proto.EarthTotem_StrengthOfEarthTotem,
 		Air:              proto.AirTotem_WindfuryTotem,
@@ -121,11 +119,10 @@ var enhShamFTFT = &proto.EnhancementShaman_Options{
 }
 
 var enhShamWFFT = &proto.EnhancementShaman_Options{
-	Shield:    proto.ShamanShield_LightningShield,
-	Bloodlust: true,
-	SyncType:  proto.ShamanSyncType_NoSync,
-	ImbueMh:   proto.ShamanImbue_WindfuryWeapon,
-	ImbueOh:   proto.ShamanImbue_FlametongueWeapon,
+	Shield:   proto.ShamanShield_LightningShield,
+	SyncType: proto.ShamanSyncType_NoSync,
+	ImbueMh:  proto.ShamanImbue_WindfuryWeapon,
+	ImbueOh:  proto.ShamanImbue_FlametongueWeapon,
 }
 
 var FullConsumes = &proto.Consumes{
diff --git a/sim/shaman/restoration/restoration.go b/sim/shaman/restoration/restoration.go
index a4a2d0b706..20bb441821 100644
--- a/sim/shaman/restoration/restoration.go
+++ b/sim/shaman/restoration/restoration.go
@@ -27,8 +27,7 @@ func NewRestorationShaman(character *core.Character, options *proto.Player) *Res
 	restoShamOptions := options.GetRestorationShaman()
 
 	selfBuffs := shaman.SelfBuffs{
-		Bloodlust: restoShamOptions.Options.Bloodlust,
-		Shield:    restoShamOptions.Options.Shield,
+		Shield: restoShamOptions.Options.Shield,
 	}
 
 	totems := &proto.ShamanTotems{}
diff --git a/sim/shaman/restoration/restoration_test.go b/sim/shaman/restoration/restoration_test.go
index 629b68812a..f39cd5a715 100644
--- a/sim/shaman/restoration/restoration_test.go
+++ b/sim/shaman/restoration/restoration_test.go
@@ -85,9 +85,8 @@ var BasicTotems = &proto.ShamanTotems{
 }
 
 var restoShamOptions = &proto.RestorationShaman_Options{
-	Shield:    proto.ShamanShield_WaterShield,
-	Bloodlust: true,
-	Totems:    BasicTotems,
+	Shield: proto.ShamanShield_WaterShield,
+	Totems: BasicTotems,
 }
 var PlayerOptionsStandard = &proto.Player_RestorationShaman{
 	RestorationShaman: &proto.RestorationShaman{
diff --git a/sim/shaman/shaman.go b/sim/shaman/shaman.go
index ca370a75ba..06ae6ea461 100644
--- a/sim/shaman/shaman.go
+++ b/sim/shaman/shaman.go
@@ -60,10 +60,9 @@ func NewShaman(character *core.Character, talents string, totems *proto.ShamanTo
 
 // Which buffs this shaman is using.
 type SelfBuffs struct {
-	Bloodlust bool
-	Shield    proto.ShamanShield
-	ImbueMH   proto.ShamanImbue
-	ImbueOH   proto.ShamanImbue
+	Shield  proto.ShamanShield
+	ImbueMH proto.ShamanImbue
+	ImbueOH proto.ShamanImbue
 }
 
 // Indexes into NextTotemDrops for self buffs
diff --git a/sim/warlock/warlock_test.go b/sim/warlock/warlock_test.go
index 1e8e56443e..74cf2555af 100644
--- a/sim/warlock/warlock_test.go
+++ b/sim/warlock/warlock_test.go
@@ -92,16 +92,6 @@ var DestructionGlyphs = &proto.Glyphs{
 	Major3: int32(proto.WarlockMajorGlyph_GlyphOfIncinerate),
 }
 
-var defaultDestroRotation = &proto.Warlock_Rotation{
-	Type:         proto.Warlock_Rotation_Destruction,
-	PrimarySpell: proto.Warlock_Rotation_Incinerate,
-	SecondaryDot: proto.Warlock_Rotation_Immolate,
-	SpecSpell:    proto.Warlock_Rotation_ChaosBolt,
-	Curse:        proto.Warlock_Rotation_Doom,
-	Corruption:   false,
-	DetonateSeed: true,
-}
-
 var defaultDestroOptions = &proto.Warlock_Options{
 	Armor:       proto.Warlock_Options_FelArmor,
 	Summon:      proto.Warlock_Options_Imp,
diff --git a/sim/warrior/protection/protection_warrior_test.go b/sim/warrior/protection/protection_warrior_test.go
index 3d01bba754..e4fad71719 100644
--- a/sim/warrior/protection/protection_warrior_test.go
+++ b/sim/warrior/protection/protection_warrior_test.go
@@ -91,8 +91,7 @@ var DefaultGlyphs = &proto.Glyphs{
 
 var PlayerOptionsBasic = &proto.Player_ProtectionWarrior{
 	ProtectionWarrior: &proto.ProtectionWarrior{
-		Options:  warriorOptions,
-		Rotation: &proto.ProtectionWarrior_Rotation{},
+		Options: warriorOptions,
 	},
 }
 
diff --git a/sim/web/main_test.go b/sim/web/main_test.go
index 84695ae0f7..53e9d95cea 100644
--- a/sim/web/main_test.go
+++ b/sim/web/main_test.go
@@ -18,9 +18,6 @@ import (
 
 var basicSpec = &proto.Player_ElementalShaman{
 	ElementalShaman: &proto.ElementalShaman{
-		Rotation: &proto.ElementalShaman_Rotation{
-			Type: proto.ElementalShaman_Rotation_Adaptive,
-		},
 		Options: &proto.ElementalShaman_Options{
 			Shield: proto.ShamanShield_WaterShield,
 		},
diff --git a/ui/core/player.ts b/ui/core/player.ts
index ee9d44f6fb..7e6fde5a22 100644
--- a/ui/core/player.ts
+++ b/ui/core/player.ts
@@ -17,7 +17,6 @@ import {
 	Spec,
 	Stat,
 	UnitStats,
-	UnitReference_Type,
 } from './proto/common.js';
 import {
 	AuraStats as AuraStatsProto,
@@ -27,7 +26,6 @@ import {
 import {
 	APLRotation,
 	APLRotation_Type as APLRotationType,
-	APLValue,
 	SimpleRotation,
 } from './proto/apl.js';
 import {
@@ -78,7 +76,6 @@ import {
 	specToEligibleRaces,
 	specTypeFunctions,
 	withSpecProto,
-	ShamanSpecs,
 } from './proto_utils/utils.js';
 
 import * as Mechanics from './constants/mechanics.js';
@@ -88,7 +85,6 @@ import { Party, MAX_PARTY_SIZE } from './party.js';
 import { Raid } from './raid.js';
 import { Sim, SimSettingCategories } from './sim.js';
 import { stringComparator, sum } from './utils.js';
-import { ElementalShaman_Options, ElementalShaman_Options_ThunderstormRange, ElementalShaman_Rotation, ElementalShaman_Rotation_BloodlustUse, EnhancementShaman_Rotation, EnhancementShaman_Rotation_BloodlustUse, RestorationShaman_Rotation, RestorationShaman_Rotation_BloodlustUse } from './proto/shaman.js';
 import { Database } from './proto_utils/database.js';
 
 export interface AuraStats {
@@ -1415,83 +1411,6 @@ export class Player<SpecType extends Spec> {
 			if (loadCategory(SimSettingCategories.External)) {
 				this.setBuffs(eventID, proto.buffs || IndividualBuffs.create());
 			}
-
-			if (this.spec == Spec.SpecShadowPriest) {
-				const options = this.getSpecOptions() as SpecOptions<Spec.SpecShadowPriest>;
-				if (options.latency) {
-					this.setChannelClipDelay(eventID, options.latency);
-					options.latency = 0;
-					this.setSpecOptions(eventID, options as SpecOptions<SpecType>)
-				}
-			}
-
-			if ([Spec.SpecEnhancementShaman, Spec.SpecRestorationShaman, Spec.SpecElementalShaman].includes(this.spec)) {
-				const rot = this.getSimpleRotation() as SpecRotation<ShamanSpecs>;
-				if (rot.totems) {
-					const options = this.getSpecOptions() as SpecOptions<ShamanSpecs>;
-					options.totems = rot.totems;
-					this.setSpecOptions(eventID, options as SpecOptions<SpecType>);
-					rot.totems = undefined;
-					this.setSimpleRotation(eventID, rot as SpecRotation<SpecType>);
-				}
-				const opt = this.getSpecOptions() as SpecOptions<ShamanSpecs>;
-
-				// Update Bloodlust to be part of rotation instead of options to support APL casting bloodlust.
-				if (opt.bloodlust) {
-					opt.bloodlust = false;
-
-					var tRot = this.getSimpleRotation();
-					if (this.spec == Spec.SpecElementalShaman) {
-						(tRot as ElementalShaman_Rotation).bloodlust = ElementalShaman_Rotation_BloodlustUse.UseBloodlust;
-					} else if (this.spec == Spec.SpecEnhancementShaman) {
-						(tRot as EnhancementShaman_Rotation).bloodlust = EnhancementShaman_Rotation_BloodlustUse.UseBloodlust;
-					} else if (this.spec == Spec.SpecRestorationShaman) {
-						(tRot as RestorationShaman_Rotation).bloodlust = RestorationShaman_Rotation_BloodlustUse.UseBloodlust;
-					}
-
-					this.setSimpleRotation(eventID, tRot as SpecRotation<SpecType>);
-					this.setSpecOptions(eventID, opt as SpecOptions<SpecType>);
-				}
-
-				// Update Ele TS range option.
-				if (this.spec == Spec.SpecElementalShaman) {
-					var eleOpt = this.getSpecOptions() as ElementalShaman_Options;
-					var eleRot = this.getSimpleRotation() as ElementalShaman_Rotation;
-					if (eleRot.inThunderstormRange) {
-						eleOpt.thunderstormRange = ElementalShaman_Options_ThunderstormRange.TSInRange;
-						eleRot.inThunderstormRange = false;
-						this.setSimpleRotation(eventID, eleRot as SpecRotation<SpecType>);
-						this.setSpecOptions(eventID, eleOpt as SpecOptions<SpecType>);
-					}
-				}
-			}
-
-			if (this.spec == Spec.SpecWarlock) {
-				const rot = this.getSimpleRotation() as SpecRotation<Spec.SpecWarlock>;
-				if (rot.enableWeaponSwap) {
-					this.setEnableItemSwap(eventID, rot.enableWeaponSwap);
-					rot.enableWeaponSwap = false;
-					this.setSimpleRotation(eventID, rot as SpecRotation<SpecType>)
-				}
-				if (rot.weaponSwap) {
-					this.setItemSwapGear(eventID, this.sim.db.lookupItemSwap(rot.weaponSwap));
-					rot.weaponSwap = undefined;
-					this.setSimpleRotation(eventID, rot as SpecRotation<SpecType>)
-				}
-			}
-			if (this.spec == Spec.SpecEnhancementShaman) {
-				const rot = this.getSimpleRotation() as SpecRotation<Spec.SpecEnhancementShaman>;
-				if (rot.enableItemSwap) {
-					this.setEnableItemSwap(eventID, rot.enableItemSwap);
-					rot.enableItemSwap = false;
-					this.setSimpleRotation(eventID, rot as SpecRotation<SpecType>)
-				}
-				if (rot.itemSwap) {
-					this.setItemSwapGear(eventID, this.sim.db.lookupItemSwap(rot.itemSwap));
-					rot.itemSwap = undefined;
-					this.setSimpleRotation(eventID, rot as SpecRotation<SpecType>)
-				}
-			}
 		});
 	}
 
diff --git a/ui/elemental_shaman/presets.ts b/ui/elemental_shaman/presets.ts
index d41b815709..929f3826d0 100644
--- a/ui/elemental_shaman/presets.ts
+++ b/ui/elemental_shaman/presets.ts
@@ -64,7 +64,6 @@ export const StandardTalents = {
 
 export const DefaultOptions = ElementalShamanOptions.create({
   shield: ShamanShield.WaterShield,
-  bloodlust: true,
   totems: ShamanTotems.create({
     earth: EarthTotem.StrengthOfEarthTotem,
     air: AirTotem.WrathOfAirTotem,
diff --git a/ui/enhancement_shaman/presets.ts b/ui/enhancement_shaman/presets.ts
index 1d4174bec1..7069a9ac9f 100644
--- a/ui/enhancement_shaman/presets.ts
+++ b/ui/enhancement_shaman/presets.ts
@@ -84,7 +84,6 @@ export const Phase3Talents = {
 
 export const DefaultOptions = EnhancementShamanOptions.create({
 	shield: ShamanShield.LightningShield,
-	bloodlust: true,
 	imbueMh: ShamanImbue.WindfuryWeapon,
 	imbueOh: ShamanImbue.FlametongueWeapon,
 	syncType: ShamanSyncType.Auto,
diff --git a/ui/raid/raid_stats.ts b/ui/raid/raid_stats.ts
index 516105ffb0..38d4f91dd2 100644
--- a/ui/raid/raid_stats.ts
+++ b/ui/raid/raid_stats.ts
@@ -295,7 +295,7 @@ const RAID_STATS_OPTIONS: RaidStatsOptions = {
 						{
 							label: 'Bloodlust',
 							actionId: ActionId.fromSpellId(2825),
-							playerData: playerClass(Class.ClassShaman, player => player.getSpecOptions().bloodlust),
+							playerData: playerClass(Class.ClassShaman),
 						},
 					],
 				},
diff --git a/ui/restoration_shaman/presets.ts b/ui/restoration_shaman/presets.ts
index c1b8631e04..bf332e295e 100644
--- a/ui/restoration_shaman/presets.ts
+++ b/ui/restoration_shaman/presets.ts
@@ -64,7 +64,6 @@ export const RaidHealingTalents = {
 
 export const DefaultOptions = RestorationShamanOptions.create({
 	shield: ShamanShield.WaterShield,
-	bloodlust: true,
 	earthShieldPPM: 0,
 });
 

From 08c7406706be5ce1a0d7bfbec2e43695649f04fc Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Sat, 6 Jan 2024 22:40:26 -0800
Subject: [PATCH 20/28] Remove README reference to talents and rotation

---
 README.md | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 8a63d13624..e232c04675 100644
--- a/README.md
+++ b/README.md
@@ -149,10 +149,7 @@ This project uses [Google Protocol Buffers](https://developers.google.com/protoc
 
 For a new sim, make the following changes:
   - Add a new value to the `Spec` enum in proto/common.proto. __NOTE: The name you give to this enum value is not just a name, it is used in our templating system. This guide will refer to this name as `$SPEC` elsewhere.__
-  - Add a 'proto/YOUR_CLASS.proto' file if it doesn't already exist and add data messages containing all the class/spec-specific information needed to run your sim. In general, there will be 3 pieces of information you need:
-    - Talents
-    - Rotation (the order in which your sim will use spells/abilities)
-    - Options (additional choices your sim needs to make)
+  - Add a 'proto/YOUR_CLASS.proto' file if it doesn't already exist and add data messages containing all the class/spec-specific information needed to run your sim.
   - Update the `PlayerOptions.spec` field in `proto/api.proto` to include your shiny new message as an option.
 
 That's it! Now when you run `make` there will be generated .go and .ts code in `sim/core/proto` and `ui/core/proto` respectively. If you aren't familiar with protos, take a quick look at them to see what's happening.

From dd9c6dc0f50e4cfcbd5742a7b78d0480b6ccd77a Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Sat, 6 Jan 2024 22:59:59 -0800
Subject: [PATCH 21/28] Tiny cleanups

---
 sim/shaman/enhancement/enhancement.go |  4 ----
 ui/core/components/totem_inputs.ts    | 11 +++--------
 2 files changed, 3 insertions(+), 12 deletions(-)

diff --git a/sim/shaman/enhancement/enhancement.go b/sim/shaman/enhancement/enhancement.go
index 5dba89bcda..e13e8cffbe 100644
--- a/sim/shaman/enhancement/enhancement.go
+++ b/sim/shaman/enhancement/enhancement.go
@@ -83,9 +83,6 @@ func (enh *EnhancementShaman) getImbueProcMask(imbue proto.ShamanImbue) core.Pro
 
 type EnhancementShaman struct {
 	*shaman.Shaman
-
-	// for weaving Lava Burst or Lightning Bolt
-	previousSwingAt time.Duration
 }
 
 func (enh *EnhancementShaman) GetShaman() *shaman.Shaman {
@@ -112,7 +109,6 @@ func (enh *EnhancementShaman) Initialize() {
 }
 
 func (enh *EnhancementShaman) Reset(sim *core.Simulation) {
-	enh.previousSwingAt = 0
 	enh.Shaman.Reset(sim)
 }
 
diff --git a/ui/core/components/totem_inputs.ts b/ui/core/components/totem_inputs.ts
index 3b9f5770b4..db1316d107 100644
--- a/ui/core/components/totem_inputs.ts
+++ b/ui/core/components/totem_inputs.ts
@@ -1,24 +1,19 @@
-import { BooleanPicker } from '../components/boolean_picker.js';
-import { EnumPicker } from '../components/enum_picker.js';
-import { IconEnumPicker, IconEnumPickerConfig } from '../components/icon_enum_picker.js';
+import { IconEnumPicker } from '../components/icon_enum_picker.js';
 import { IconPicker } from '../components/icon_picker.js';
-import { IconPickerConfig } from '../components/icon_picker.js';
 import {
 	AirTotem,
 	EarthTotem,
 	FireTotem,
 	WaterTotem,
 	ShamanTotems,
-	EnhancementShaman,
 } from '../proto/shaman.js';
 import { Spec } from '../proto/common.js';
 import { ActionId } from '../proto_utils/action_id.js';
 import { Player } from '../player.js';
-import { Sim } from '../sim.js';
 import { IndividualSimUI } from '../individual_sim_ui.js';
-import { EventID, TypedEvent } from '../typed_event.js';
+import { EventID } from '../typed_event.js';
 import * as InputHelpers from '../components/input_helpers.js';
-import { SpecRotation, ShamanSpecs } from '../proto_utils/utils.js';
+import { ShamanSpecs } from '../proto_utils/utils.js';
 import { ContentBlock } from './content_block.js';
 import { Input } from './input.js';
 

From e07657f840fa3bf744d2d388a80b9ceebbaa4a88 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Sat, 6 Jan 2024 23:24:41 -0800
Subject: [PATCH 22/28] Fix ele tests

---
 sim/shaman/elemental/TestElemental.results | 642 ++++++++++-----------
 sim/shaman/elemental/elemental_test.go     |   2 +-
 2 files changed, 322 insertions(+), 322 deletions(-)

diff --git a/sim/shaman/elemental/TestElemental.results b/sim/shaman/elemental/TestElemental.results
index eb13cce09e..ad70d93501 100644
--- a/sim/shaman/elemental/TestElemental.results
+++ b/sim/shaman/elemental/TestElemental.results
@@ -49,12 +49,12 @@ stat_weights_results: {
   weights: 0
   weights: 0
   weights: 0
-  weights: 4.52675
+  weights: 4.44299
   weights: 0
-  weights: 1.60973
+  weights: 1.57057
   weights: 0
   weights: 0
-  weights: 1.43945
+  weights: 1.63513
   weights: 0
   weights: 0
   weights: 0
@@ -91,1023 +91,1023 @@ stat_weights_results: {
 dps_results: {
  key: "TestElemental-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 6863.28823
-  tps: 3844.36465
+  dps: 6650.62639
+  tps: 3908.93838
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 6896.84001
-  tps: 3863.05201
+  dps: 6683.44081
+  tps: 3927.92265
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 6970.06348
-  tps: 3869.24998
+  dps: 6650.78587
+  tps: 3892.51837
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 7073.31686
-  tps: 3931.20584
+  dps: 6749.85373
+  tps: 3950.83736
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Beast-tamer'sShoulders-30892"
  value: {
-  dps: 6637.19889
-  tps: 3686.64787
+  dps: 6284.7992
+  tps: 3692.43192
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Bizuri'sTotemofShatteredIce-50458"
  value: {
-  dps: 6957.45214
-  tps: 3860.74595
+  dps: 6721.95171
+  tps: 3946.75474
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-BlackBruise-50035"
  value: {
-  dps: 5934.84303
-  tps: 3289.58182
+  dps: 5647.12346
+  tps: 3312.02078
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-BlackBruise-50692"
  value: {
-  dps: 5934.84303
-  tps: 3289.58182
+  dps: 5647.12346
+  tps: 3312.02078
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-BlessedGarboftheUndeadSlayer"
  value: {
-  dps: 4849.76749
-  tps: 2661.84341
+  dps: 4621.6797
+  tps: 2713.57301
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 5087.30576
-  tps: 2788.85887
+  dps: 4847.11817
+  tps: 2844.06021
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 7008.89733
-  tps: 3813.18697
+  dps: 6688.4352
+  tps: 3836.11596
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Bryntroll,theBoneArbiter-50415"
  value: {
-  dps: 7164.80442
-  tps: 3989.22636
+  dps: 6878.48069
+  tps: 4029.20239
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Bryntroll,theBoneArbiter-50709"
  value: {
-  dps: 7164.80442
-  tps: 3989.22636
+  dps: 6878.48069
+  tps: 4029.20239
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 7164.80442
-  tps: 3989.22636
+  dps: 6878.48069
+  tps: 4029.20239
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 6732.02618
-  tps: 3773.01076
+  dps: 6553.94093
+  tps: 3849.61191
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 6860.7293
-  tps: 3880.09818
+  dps: 6570.39262
+  tps: 3890.05855
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 7040.20523
-  tps: 3959.90556
+  dps: 6793.15133
+  tps: 3996.29522
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-DeadlyGladiator'sTotemofSurvival-42602"
  value: {
-  dps: 7105.93371
-  tps: 3949.85562
+  dps: 6815.79901
+  tps: 3989.27266
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Death'sChoice-47464"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 6654.44856
-  tps: 3731.08418
+  dps: 6424.08497
+  tps: 3776.14708
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 6608.92553
-  tps: 3701.80012
+  dps: 6362.13263
+  tps: 3742.1294
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 6615.30831
-  tps: 3701.43181
+  dps: 6344.09943
+  tps: 3729.51539
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Defender'sCode-40257"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 6983.31752
-  tps: 3875.81743
+  dps: 6690.07926
+  tps: 3915.78762
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-DislodgedForeignObject-50348"
  value: {
-  dps: 6966.64527
-  tps: 3900.5792
+  dps: 6703.78396
+  tps: 3932.26253
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 6942.61816
-  tps: 3893.17893
+  dps: 6663.50664
+  tps: 3914.71277
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-EarthshatterBattlegear"
  value: {
-  dps: 5240.68746
-  tps: 2874.25887
+  dps: 5008.10765
+  tps: 2942.16728
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-EarthshatterGarb"
  value: {
-  dps: 6674.94094
-  tps: 3717.32881
+  dps: 6179.49141
+  tps: 3627.02244
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 6970.06348
-  tps: 3869.24998
+  dps: 6650.78587
+  tps: 3892.51837
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 7109.25352
-  tps: 3952.04187
+  dps: 6780.0939
+  tps: 3969.04623
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 6981.25486
-  tps: 3875.42442
+  dps: 6688.54472
+  tps: 3915.04084
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 6978.29223
-  tps: 3873.56016
+  dps: 6685.93319
+  tps: 3913.7067
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 6970.06348
-  tps: 3869.24998
+  dps: 6650.78587
+  tps: 3892.51837
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 6796.89239
-  tps: 3823.35342
+  dps: 6590.60935
+  tps: 3895.85528
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 6921.47785
-  tps: 3875.31782
+  dps: 6729.4714
+  tps: 3950.89645
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 6752.7015
-  tps: 3787.08978
+  dps: 6575.3696
+  tps: 3870.55974
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 6759.58274
-  tps: 3786.60372
+  dps: 6549.19998
+  tps: 3850.25971
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-ForgeEmber-37660"
  value: {
-  dps: 6857.94549
-  tps: 3833.97125
+  dps: 6643.77803
+  tps: 3900.27038
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 7008.89733
-  tps: 3890.80376
+  dps: 6688.4352
+  tps: 3914.20069
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 7001.13056
-  tps: 3886.493
+  dps: 6680.90533
+  tps: 3909.86422
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-FrostWitch'sBattlegear"
  value: {
-  dps: 6251.22911
-  tps: 3478.8872
+  dps: 5945.37615
+  tps: 3481.78408
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-FrostWitch'sRegalia"
  value: {
-  dps: 8181.04054
-  tps: 4550.42527
+  dps: 7844.07938
+  tps: 4580.49628
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-FuriousGladiator'sTotemofSurvival-42603"
  value: {
-  dps: 7120.43152
-  tps: 3957.75317
+  dps: 6830.00984
+  tps: 3997.28389
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-FuturesightRune-38763"
  value: {
-  dps: 6684.85377
-  tps: 3744.98187
+  dps: 6476.1133
+  tps: 3807.97655
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Gladiator'sEarthshaker"
  value: {
-  dps: 5674.43948
-  tps: 3169.07236
+  dps: 5335.58213
+  tps: 3145.50078
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Gladiator'sWartide"
  value: {
-  dps: 6486.91113
-  tps: 3600.91125
+  dps: 6112.79044
+  tps: 3592.17518
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 6880.06412
-  tps: 3853.70833
+  dps: 6667.0336
+  tps: 3918.43051
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 6918.19114
-  tps: 3874.94396
+  dps: 6704.32272
+  tps: 3940.00355
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 6720.43279
-  tps: 3765.32555
+  dps: 6521.92552
+  tps: 3829.99373
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-HatefulGladiator'sTotemofSurvival-42601"
  value: {
-  dps: 7068.6805
-  tps: 3929.02562
+  dps: 6777.4012
+  tps: 3967.65244
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Heartpierce-49982"
  value: {
-  dps: 7164.80442
-  tps: 3989.22636
+  dps: 6878.48069
+  tps: 4029.20239
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Heartpierce-50641"
  value: {
-  dps: 7164.80442
-  tps: 3989.22636
+  dps: 6878.48069
+  tps: 4029.20239
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 6981.25486
-  tps: 3875.42442
+  dps: 6688.54472
+  tps: 3915.04084
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 6978.29223
-  tps: 3873.56016
+  dps: 6685.93319
+  tps: 3913.7067
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-IncisorFragment-37723"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 7290.49152
-  tps: 4066.63225
+  dps: 6913.42681
+  tps: 4049.97369
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 6970.06348
-  tps: 3869.24998
+  dps: 6650.78587
+  tps: 3892.51837
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-LastWord-50179"
  value: {
-  dps: 7164.80442
-  tps: 3989.22636
+  dps: 6878.48069
+  tps: 4029.20239
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-LastWord-50708"
  value: {
-  dps: 7164.80442
-  tps: 3989.22636
+  dps: 6878.48069
+  tps: 4029.20239
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 6703.01641
-  tps: 3757.5639
+  dps: 6509.07319
+  tps: 3823.05387
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 6759.58274
-  tps: 3786.60372
+  dps: 6549.19998
+  tps: 3850.25971
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Nibelung-49992"
  value: {
-  dps: 7164.80442
-  tps: 3989.22636
+  dps: 6878.48069
+  tps: 4029.20239
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Nibelung-50648"
  value: {
-  dps: 7164.80442
-  tps: 3989.22636
+  dps: 6878.48069
+  tps: 4029.20239
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 6970.06348
-  tps: 3869.24998
+  dps: 6650.78587
+  tps: 3892.51837
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 6970.06348
-  tps: 3869.24998
+  dps: 6650.78587
+  tps: 3892.51837
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 6970.06348
-  tps: 3869.24998
+  dps: 6650.78587
+  tps: 3892.51837
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 6970.06348
-  tps: 3869.24998
+  dps: 6650.78587
+  tps: 3892.51837
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 7052.24444
-  tps: 4039.06046
+  dps: 6785.44036
+  tps: 4061.50904
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 7103.52117
-  tps: 4078.12459
+  dps: 6835.69769
+  tps: 4100.44394
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 7152.70434
-  tps: 3982.54241
+  dps: 6838.52532
+  tps: 4005.44364
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-RelentlessGladiator'sTotemofSurvival-42604"
  value: {
-  dps: 7138.036
-  tps: 3967.34306
+  dps: 6847.26584
+  tps: 4007.0118
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 7046.129
-  tps: 3916.29081
+  dps: 6732.8001
+  tps: 3942.32307
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-SavageGladiator'sTotemofSurvival-42594"
  value: {
-  dps: 7062.9973
-  tps: 3926.0163
+  dps: 6772.13405
+  tps: 3964.67891
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Shadowmourne-49623"
  value: {
-  dps: 7164.80442
-  tps: 3989.22636
+  dps: 6878.48069
+  tps: 4029.20239
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-SkycallTotem-33506"
  value: {
-  dps: 6996.67757
-  tps: 3883.18757
+  dps: 6682.61033
+  tps: 3913.80687
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-SkyshatterHarness"
  value: {
-  dps: 4242.77008
-  tps: 2309.658
+  dps: 4002.585
+  tps: 2357.7596
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-SkyshatterRegalia"
  value: {
-  dps: 5207.79808
-  tps: 2868.59121
+  dps: 4883.58338
+  tps: 2859.69339
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 6831.26153
-  tps: 3826.52671
+  dps: 6619.30353
+  tps: 3890.81702
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 6861.76315
-  tps: 3843.51522
+  dps: 6649.13482
+  tps: 3908.07545
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-SoulPreserver-37111"
  value: {
-  dps: 6704.67982
-  tps: 3756.0244
+  dps: 6495.50364
+  tps: 3819.19453
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-SouloftheDead-40382"
  value: {
-  dps: 7069.54719
-  tps: 3985.56698
+  dps: 6825.82188
+  tps: 4022.98812
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-SparkofLife-37657"
  value: {
-  dps: 7025.73924
-  tps: 3971.04629
+  dps: 6650.00647
+  tps: 3915.17261
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 6837.78129
-  tps: 3823.43591
+  dps: 6623.79551
+  tps: 3891.42765
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Stonebreaker'sTotem-33507"
  value: {
-  dps: 7033.44467
-  tps: 3910.36784
+  dps: 6744.74488
+  tps: 3949.21654
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-StormshroudArmor"
  value: {
-  dps: 4709.32376
-  tps: 2574.18851
+  dps: 4355.70242
+  tps: 2550.73391
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 6970.06348
-  tps: 3869.24998
+  dps: 6650.78587
+  tps: 3892.51837
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 6970.06348
-  tps: 3869.24998
+  dps: 6650.78587
+  tps: 3892.51837
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 6970.06348
-  tps: 3869.24998
+  dps: 6650.78587
+  tps: 3892.51837
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 6909.61723
-  tps: 3881.54719
+  dps: 6689.01917
+  tps: 3935.29542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-TheFistsofFury"
  value: {
-  dps: 5891.32972
-  tps: 3249.59732
+  dps: 5625.57829
+  tps: 3285.97169
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Thrall'sBattlegear"
  value: {
-  dps: 5796.83651
-  tps: 3216.81418
+  dps: 5516.27414
+  tps: 3246.61244
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Thrall'sRegalia"
  value: {
-  dps: 6929.19955
-  tps: 3885.68471
+  dps: 6598.34827
+  tps: 3909.84157
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 6970.06348
-  tps: 3869.24998
+  dps: 6650.78587
+  tps: 3892.51837
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-TidefuryRaiment"
  value: {
-  dps: 4718.56457
-  tps: 2574.31116
+  dps: 4423.20082
+  tps: 2596.84574
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 6988.22174
-  tps: 3926.40867
+  dps: 6655.54584
+  tps: 3899.95103
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 6988.22174
-  tps: 3926.40867
+  dps: 6655.54584
+  tps: 3899.95103
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 7008.89733
-  tps: 3890.80376
+  dps: 6688.4352
+  tps: 3914.20069
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 7001.13056
-  tps: 3886.493
+  dps: 6680.90533
+  tps: 3909.86422
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 6694.00426
-  tps: 3750.07842
+  dps: 6485.06269
+  tps: 3813.15408
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-TotemofElectrifyingWind-47666"
  value: {
-  dps: 6976.71388
-  tps: 3877.7192
+  dps: 6736.64674
+  tps: 3958.31392
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-TotemofQuakingEarth-47667"
  value: {
-  dps: 7033.44467
-  tps: 3910.36784
+  dps: 6744.74488
+  tps: 3949.21654
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-TotemoftheAvalanche-50463"
  value: {
-  dps: 7033.44467
-  tps: 3910.36784
+  dps: 6744.74488
+  tps: 3949.21654
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-TotemoftheElementalPlane-40708"
  value: {
-  dps: 7016.26152
-  tps: 3891.61216
+  dps: 6674.7515
+  tps: 3911.78201
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 7001.13056
-  tps: 3886.493
+  dps: 6680.90533
+  tps: 3909.86422
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 7008.89733
-  tps: 3890.80376
+  dps: 6688.4352
+  tps: 3914.20069
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-UndeadSlayer'sBlessedArmor"
  value: {
-  dps: 4569.19693
-  tps: 2487.70946
+  dps: 4323.7801
+  tps: 2529.23393
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-Val'anyr,HammerofAncientKings-46017"
  value: {
-  dps: 7390.12914
-  tps: 4115.93764
+  dps: 7128.01624
+  tps: 4183.98496
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-WingedTalisman-37844"
  value: {
-  dps: 6590.29876
-  tps: 3692.31749
+  dps: 6383.63628
+  tps: 3754.47542
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-WorldbreakerBattlegear"
  value: {
-  dps: 5369.52365
-  tps: 2952.12673
+  dps: 5093.03619
+  tps: 2978.14057
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-WorldbreakerGarb"
  value: {
-  dps: 6993.19748
-  tps: 3958.54516
+  dps: 6644.51389
+  tps: 3953.39731
  }
 }
 dps_results: {
  key: "TestElemental-AllItems-WrathfulGladiator'sTotemofSurvival-51513"
  value: {
-  dps: 7156.67604
-  tps: 3977.49706
+  dps: 6865.5369
+  tps: 4017.31195
  }
 }
 dps_results: {
  key: "TestElemental-Average-Default"
  value: {
-  dps: 7276.45932
-  tps: 4041.15477
+  dps: 6975.13169
+  tps: 4083.69205
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Orc-p1-Adaptive-advanced-FullBuffs-LongMultiTarget"
  value: {
-  dps: 9771.21018
-  tps: 4705.22913
+  dps: 7158.1636
+  tps: 4631.22912
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Orc-p1-Adaptive-advanced-FullBuffs-LongSingleTarget"
  value: {
-  dps: 7454.50067
-  tps: 4140.478
+  dps: 7051.59197
+  tps: 4152.01262
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Orc-p1-Adaptive-advanced-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 8964.30154
-  tps: 4708.79854
+  dps: 8048.02778
+  tps: 4774.36143
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Orc-p1-Adaptive-advanced-NoBuffs-LongMultiTarget"
  value: {
-  dps: 5521.03184
-  tps: 2325.24307
+  dps: 3548.58923
+  tps: 2374.57903
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Orc-p1-Adaptive-advanced-NoBuffs-LongSingleTarget"
  value: {
-  dps: 3837.05884
-  tps: 2026.4407
+  dps: 3555.63224
+  tps: 2074.92756
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Orc-p1-Adaptive-advanced-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 6515.97761
-  tps: 3373.88626
+  dps: 5696.34418
+  tps: 3366.5265
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Orc-p1-Adaptive-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 8579.99908
-  tps: 3796.63806
+  dps: 6069.22264
+  tps: 3746.61997
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Orc-p1-Adaptive-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 7313.94007
-  tps: 4056.90464
+  dps: 6948.65991
+  tps: 4079.46271
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Orc-p1-Adaptive-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 9410.66293
-  tps: 4967.07992
+  dps: 8298.85912
+  tps: 4893.97673
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Orc-p1-Adaptive-default-NoBuffs-LongMultiTarget"
  value: {
-  dps: 4748.6775
-  tps: 1590.44032
+  dps: 2796.89148
+  tps: 1627.34252
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Orc-p1-Adaptive-default-NoBuffs-LongSingleTarget"
  value: {
-  dps: 3294.32029
-  tps: 1688.70458
+  dps: 3020.48753
+  tps: 1734.1061
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Orc-p1-Adaptive-default-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 6610.40307
-  tps: 3428.92065
+  dps: 5846.9485
+  tps: 3464.62776
  }
 }
 dps_results: {
@@ -1197,85 +1197,85 @@ dps_results: {
 dps_results: {
  key: "TestElemental-Settings-Troll-p1-Adaptive-advanced-FullBuffs-LongMultiTarget"
  value: {
-  dps: 9488.36304
-  tps: 4655.75987
+  dps: 7188.71322
+  tps: 4648.91616
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Troll-p1-Adaptive-advanced-FullBuffs-LongSingleTarget"
  value: {
-  dps: 7367.11741
-  tps: 4114.77864
+  dps: 7020.67592
+  tps: 4141.24482
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Troll-p1-Adaptive-advanced-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 9035.33243
-  tps: 4858.19017
+  dps: 8241.24547
+  tps: 4948.94681
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Troll-p1-Adaptive-advanced-NoBuffs-LongMultiTarget"
  value: {
-  dps: 5368.23343
-  tps: 2330.5972
+  dps: 3505.62587
+  tps: 2350.14464
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Troll-p1-Adaptive-advanced-NoBuffs-LongSingleTarget"
  value: {
-  dps: 3821.62337
-  tps: 2048.21029
+  dps: 3509.36245
+  tps: 2042.74932
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Troll-p1-Adaptive-advanced-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 6522.405
-  tps: 3434.46393
+  dps: 5750.36161
+  tps: 3412.18966
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Troll-p1-Adaptive-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 8339.91467
-  tps: 3764.5368
+  dps: 6135.51805
+  tps: 3798.17278
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Troll-p1-Adaptive-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 7164.80442
-  tps: 3989.22636
+  dps: 6878.48069
+  tps: 4029.20239
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Troll-p1-Adaptive-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 9014.56519
-  tps: 4792.02486
+  dps: 8150.21209
+  tps: 4790.65713
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Troll-p1-Adaptive-default-NoBuffs-LongMultiTarget"
  value: {
-  dps: 4642.58591
-  tps: 1623.02948
+  dps: 2815.63936
+  tps: 1642.0156
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Troll-p1-Adaptive-default-NoBuffs-LongSingleTarget"
  value: {
-  dps: 3256.28858
-  tps: 1685.78523
+  dps: 2959.15746
+  tps: 1688.05159
  }
 }
 dps_results: {
  key: "TestElemental-Settings-Troll-p1-Adaptive-default-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 6403.21606
-  tps: 3333.61161
+  dps: 5711.12961
+  tps: 3354.03045
  }
 }
 dps_results: {
@@ -1365,7 +1365,7 @@ dps_results: {
 dps_results: {
  key: "TestElemental-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 7144.09519
-  tps: 3989.22636
+  dps: 6878.48069
+  tps: 4029.20239
  }
 }
diff --git a/sim/shaman/elemental/elemental_test.go b/sim/shaman/elemental/elemental_test.go
index d47892c4a3..469836c68b 100644
--- a/sim/shaman/elemental/elemental_test.go
+++ b/sim/shaman/elemental/elemental_test.go
@@ -112,7 +112,7 @@ var PlayerOptionsAdaptive = &proto.Player_ElementalShaman{
 	ElementalShaman: &proto.ElementalShaman{
 		Options: &proto.ElementalShaman_Options{
 			Shield: proto.ShamanShield_WaterShield,
-			Totems: FireElementalBasicTotems,
+			Totems: BasicTotems,
 		},
 	},
 }

From 2cd9e30a729534b6cffa497b5ba524735e8426d0 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Sun, 7 Jan 2024 02:55:05 -0800
Subject: [PATCH 23/28] Add back detonate_seed rotation field, as an option

---
 proto/warlock.proto         |  1 +
 sim/warlock/seed.go         |  6 +++++-
 sim/warlock/warlock_test.go | 21 ++++++++++++---------
 ui/warlock/inputs.ts        |  6 ++++++
 ui/warlock/sim.ts           |  1 +
 5 files changed, 25 insertions(+), 10 deletions(-)

diff --git a/proto/warlock.proto b/proto/warlock.proto
index 21f95aa0f2..b4b778fa9e 100644
--- a/proto/warlock.proto
+++ b/proto/warlock.proto
@@ -165,6 +165,7 @@ message Warlock {
 		Summon summon = 2;
 		WeaponImbue weaponImbue = 3;
 		bool _oldDPBehaviourOption = 4;
+		bool detonate_seed = 5;
 	}
 	Options options = 3;
 }
diff --git a/sim/warlock/seed.go b/sim/warlock/seed.go
index d404d16861..bf479799d8 100644
--- a/sim/warlock/seed.go
+++ b/sim/warlock/seed.go
@@ -108,7 +108,11 @@ func (warlock *Warlock) registerSeedSpell() {
 					// seed is mutually exclusive with corruption
 					warlock.Corruption.Dot(target).Deactivate(sim)
 
-					spell.Dot(target).Apply(sim)
+					if warlock.Options.DetonateSeed {
+						seedExplosion.Cast(sim, target)
+					} else {
+						spell.Dot(target).Apply(sim)
+					}
 				}
 			})
 		},
diff --git a/sim/warlock/warlock_test.go b/sim/warlock/warlock_test.go
index 74cf2555af..288d7cac2a 100644
--- a/sim/warlock/warlock_test.go
+++ b/sim/warlock/warlock_test.go
@@ -93,9 +93,10 @@ var DestructionGlyphs = &proto.Glyphs{
 }
 
 var defaultDestroOptions = &proto.Warlock_Options{
-	Armor:       proto.Warlock_Options_FelArmor,
-	Summon:      proto.Warlock_Options_Imp,
-	WeaponImbue: proto.Warlock_Options_GrandFirestone,
+	Armor:        proto.Warlock_Options_FelArmor,
+	Summon:       proto.Warlock_Options_Imp,
+	WeaponImbue:  proto.Warlock_Options_GrandFirestone,
+	DetonateSeed: true,
 }
 
 var DefaultDestroWarlock = &proto.Player_Warlock{
@@ -112,9 +113,10 @@ var DefaultAfflictionWarlock = &proto.Player_Warlock{
 }
 
 var defaultAfflictionOptions = &proto.Warlock_Options{
-	Armor:       proto.Warlock_Options_FelArmor,
-	Summon:      proto.Warlock_Options_Felhunter,
-	WeaponImbue: proto.Warlock_Options_GrandSpellstone,
+	Armor:        proto.Warlock_Options_FelArmor,
+	Summon:       proto.Warlock_Options_Felhunter,
+	WeaponImbue:  proto.Warlock_Options_GrandSpellstone,
+	DetonateSeed: true,
 }
 
 // ---------------------------------------
@@ -125,9 +127,10 @@ var DefaultDemonologyWarlock = &proto.Player_Warlock{
 }
 
 var defaultDemonologyOptions = &proto.Warlock_Options{
-	Armor:       proto.Warlock_Options_FelArmor,
-	Summon:      proto.Warlock_Options_Felguard,
-	WeaponImbue: proto.Warlock_Options_GrandSpellstone,
+	Armor:        proto.Warlock_Options_FelArmor,
+	Summon:       proto.Warlock_Options_Felguard,
+	WeaponImbue:  proto.Warlock_Options_GrandSpellstone,
+	DetonateSeed: true,
 }
 
 // ---------------------------------------------------------
diff --git a/ui/warlock/inputs.ts b/ui/warlock/inputs.ts
index f0763c80bc..1c0380f778 100644
--- a/ui/warlock/inputs.ts
+++ b/ui/warlock/inputs.ts
@@ -45,3 +45,9 @@ export const PetInput = InputHelpers.makeSpecOptionsEnumIconInput<Spec.SpecWarlo
 	],
 	changeEmitter: (player: Player<Spec.SpecWarlock>) => player.changeEmitter,
 });
+
+export const DetonateSeed = InputHelpers.makeSpecOptionsBooleanInput<Spec.SpecWarlock>({
+	fieldName: 'detonateSeed',
+	label: 'Detonate Seed on Cast',
+	labelTooltip: 'Simulates raid doing damage to targets such that seed detonates immediately on cast.',
+});
diff --git a/ui/warlock/sim.ts b/ui/warlock/sim.ts
index c35da9c98c..e40c13932e 100644
--- a/ui/warlock/sim.ts
+++ b/ui/warlock/sim.ts
@@ -116,6 +116,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWarlock, {
 	// Inputs to include in the 'Other' section on the settings tab.
 	otherInputs: {
 		inputs: [
+			WarlockInputs.DetonateSeed,
 			OtherInputs.DistanceFromTarget,
 			OtherInputs.TankAssignment,
 			OtherInputs.ChannelClipDelay,

From ff7d01e6045e5354b79b23934da6732615fbf0c9 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Sun, 7 Jan 2024 03:23:45 -0800
Subject: [PATCH 24/28] Update warlock demo tests

---
 sim/shaman/fire_elemental_totem.go |   2 +-
 sim/warlock/TestDemonology.results | 424 ++++++++++++++---------------
 sim/warlock/inferno.go             |   5 -
 ui/warlock/presets.ts              |   3 +
 4 files changed, 216 insertions(+), 218 deletions(-)

diff --git a/sim/shaman/fire_elemental_totem.go b/sim/shaman/fire_elemental_totem.go
index 893fc5ab8c..b9afd73072 100644
--- a/sim/shaman/fire_elemental_totem.go
+++ b/sim/shaman/fire_elemental_totem.go
@@ -60,6 +60,6 @@ func (shaman *Shaman) registerFireElementalTotem() {
 
 	shaman.AddMajorCooldown(core.MajorCooldown{
 		Spell: shaman.FireElementalTotem,
-		Type:  core.CooldownTypeUnknown,
+		Type:  core.CooldownTypeDPS,
 	})
 }
diff --git a/sim/warlock/TestDemonology.results b/sim/warlock/TestDemonology.results
index 536e7e6fb0..8279f7033d 100644
--- a/sim/warlock/TestDemonology.results
+++ b/sim/warlock/TestDemonology.results
@@ -46,738 +46,738 @@ character_stats_results: {
 dps_results: {
  key: "TestDemonology-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 11029.86291
-  tps: 10767.28533
+  dps: 13362.9926
+  tps: 11605.06694
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 11055.04985
-  tps: 10791.31034
+  dps: 13408.47575
+  tps: 11646.08451
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-AshtongueTalismanofShadows-32493"
  value: {
-  dps: 10875.72599
-  tps: 10616.72071
+  dps: 13223.59596
+  tps: 11481.52309
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 11073.18055
-  tps: 10814.91661
+  dps: 13573.58115
+  tps: 11798.10008
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.05853
+  tps: 11278.57276
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 10722.16856
-  tps: 10465.85596
-  hps: 101.80272
+  dps: 13006.0771
+  tps: 11284.03967
+  hps: 101.08784
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 10722.16856
-  tps: 10465.85596
-  hps: 101.80272
+  dps: 13006.0771
+  tps: 11284.03967
+  hps: 101.08784
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 11099.41588
-  tps: 10841.25543
+  dps: 13563.75667
+  tps: 11788.03405
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 8005.70237
-  tps: 7802.7555
+  dps: 9902.89183
+  tps: 8440.13945
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 11119.66976
-  tps: 10645.99435
+  dps: 13599.47078
+  tps: 11585.96681
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 11410.3423
-  tps: 11152.09819
+  dps: 13937.65732
+  tps: 12163.36117
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.05853
+  tps: 11278.57276
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.05853
+  tps: 11278.57276
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 10770.42571
-  tps: 10513.39859
+  dps: 13073.06117
+  tps: 11345.1642
   hps: 64
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DarkCoven'sRegalia"
  value: {
-  dps: 10425.18922
-  tps: 10175.94046
+  dps: 12728.43281
+  tps: 11034.66574
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 10864.3927
-  tps: 10608.00856
+  dps: 13169.18682
+  tps: 11446.70105
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 10914.04201
-  tps: 10657.69731
+  dps: 13205.69172
+  tps: 11483.33552
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 10827.49237
-  tps: 10566.12402
+  dps: 13123.06301
+  tps: 11391.45655
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Death'sChoice-47464"
  value: {
-  dps: 10720.49094
-  tps: 10464.1068
+  dps: 13001.05853
+  tps: 11278.57276
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 10768.14946
-  tps: 10511.76532
+  dps: 13054.31942
+  tps: 11331.83365
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.05853
+  tps: 11278.57276
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.05853
+  tps: 11278.57276
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DeathbringerGarb"
  value: {
-  dps: 8750.45172
-  tps: 8513.7745
+  dps: 10829.58867
+  tps: 9256.94195
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Defender'sCode-40257"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.09262
+  tps: 11278.60685
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 11104.72076
-  tps: 10846.47665
+  dps: 13604.8557
+  tps: 11830.55955
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 11327.35486
-  tps: 11069.91946
+  dps: 13835.25615
+  tps: 12063.59581
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 11073.18055
-  tps: 10814.91661
+  dps: 13573.58115
+  tps: 11798.10008
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 11134.3961
-  tps: 10875.5855
+  dps: 13584.42686
+  tps: 11804.01851
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 11100.49894
-  tps: 10842.25483
+  dps: 13597.95754
+  tps: 11823.66138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 11092.43436
-  tps: 10834.19025
+  dps: 13588.9541
+  tps: 11814.65795
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 10879.10858
-  tps: 10625.58514
+  dps: 13200.05256
+  tps: 11476.85655
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 10742.56294
-  tps: 10485.89317
+  dps: 13039.12931
+  tps: 11313.92687
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 11067.81225
-  tps: 10809.56813
+  dps: 13562.44986
+  tps: 11788.1537
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 10953.30393
-  tps: 10696.91978
+  dps: 13232.58503
+  tps: 11510.37767
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 11069.28
-  tps: 10808.5171
+  dps: 13416.73309
+  tps: 11669.56719
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 10754.22395
-  tps: 10497.0465
+  dps: 13041.91339
+  tps: 11315.17499
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 10918.00416
-  tps: 10657.22787
+  dps: 13211.86872
+  tps: 11468.52981
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ForgeEmber-37660"
  value: {
-  dps: 10966.72513
-  tps: 10707.20814
+  dps: 13362.65503
+  tps: 11617.2622
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 11119.66976
-  tps: 10860.98248
+  dps: 13599.47078
+  tps: 11820.25
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 11105.65831
-  tps: 10847.10119
+  dps: 13588.30536
+  tps: 11809.90411
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.05853
+  tps: 11278.57276
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-FuturesightRune-38763"
  value: {
-  dps: 10877.79011
-  tps: 10618.64538
+  dps: 13205.12887
+  tps: 11464.5391
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Gladiator'sFelshroud"
  value: {
-  dps: 8575.50793
-  tps: 8343.43479
+  dps: 10677.0966
+  tps: 9098.70061
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 11034.67418
-  tps: 10771.5057
+  dps: 13387.6928
+  tps: 11626.99352
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 11080.46799
-  tps: 10816.04753
+  dps: 13438.36879
+  tps: 11673.25119
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 10896.47429
-  tps: 10640.15174
+  dps: 13184.64163
+  tps: 11462.7986
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Gul'dan'sRegalia"
  value: {
-  dps: 8893.35742
-  tps: 8648.53034
+  dps: 11170.31233
+  tps: 9425.53097
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
-  dps: 11019.97936
-  tps: 10757.4527
+  dps: 13395.10575
+  tps: 11633.43219
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 11100.49894
-  tps: 10842.25483
+  dps: 13597.95754
+  tps: 11823.66138
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 11092.43436
-  tps: 10834.19025
+  dps: 13588.9541
+  tps: 11814.65795
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-IncisorFragment-37723"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.09262
+  tps: 11278.60685
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 11111.06498
-  tps: 10852.30362
+  dps: 13576.20154
+  tps: 11802.41958
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 11067.81225
-  tps: 10809.56813
+  dps: 13562.44986
+  tps: 11788.1537
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.05853
+  tps: 11278.57276
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 10912.86436
-  tps: 10652.26582
+  dps: 13229.87428
+  tps: 11485.10666
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-MaleficRaiment"
  value: {
-  dps: 6854.13966
-  tps: 6659.45182
+  dps: 8500.92025
+  tps: 7137.00467
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 10825.62748
-  tps: 10569.24333
+  dps: 13121.26418
+  tps: 11398.77841
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 10983.322
-  tps: 10722.54571
+  dps: 13276.44705
+  tps: 11533.10814
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.05853
+  tps: 11278.57276
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 11067.81225
-  tps: 10809.56813
+  dps: 13562.44986
+  tps: 11788.1537
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 11067.81225
-  tps: 10809.56813
+  dps: 13562.44986
+  tps: 11788.1537
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.05853
+  tps: 11278.57276
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.05853
+  tps: 11278.57276
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.05853
+  tps: 11278.57276
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PlagueheartGarb"
  value: {
-  dps: 8326.41706
-  tps: 8103.48409
+  dps: 10353.38967
+  tps: 8813.1671
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 11071.97708
-  tps: 10813.72807
+  dps: 13569.99782
+  tps: 11794.76338
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 11073.18055
-  tps: 10814.91661
+  dps: 13573.58115
+  tps: 11798.10008
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.05853
+  tps: 11278.57276
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 11182.591
-  tps: 10920.47378
+  dps: 13578.66848
+  tps: 11826.60815
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 11238.80768
-  tps: 10976.33941
+  dps: 13625.62539
+  tps: 11869.82399
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 11374.7138
-  tps: 11116.46969
+  dps: 13898.95395
+  tps: 12124.6578
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 11055.59429
-  tps: 10797.48576
+  dps: 13555.88187
+  tps: 11780.03817
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.09262
+  tps: 11278.60685
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.09262
+  tps: 11278.60685
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.05853
+  tps: 11278.57276
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 10770.41707
-  tps: 10513.38995
+  dps: 13073.04367
+  tps: 11345.1467
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 11018.87465
-  tps: 10759.96064
+  dps: 13367.39811
+  tps: 11613.85091
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 11045.70793
-  tps: 10785.47065
+  dps: 13358.48994
+  tps: 11601.09505
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SoulPreserver-37111"
  value: {
-  dps: 10853.08201
-  tps: 10594.90656
+  dps: 13163.13832
+  tps: 11426.59504
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SouloftheDead-40382"
  value: {
-  dps: 10904.13536
-  tps: 10648.91037
+  dps: 13196.76389
+  tps: 11475.18871
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SparkofLife-37657"
  value: {
-  dps: 10875.52441
-  tps: 10614.81598
+  dps: 13203.57651
+  tps: 11476.11353
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 10727.3943
-  tps: 10464.17138
+  dps: 13025.12425
+  tps: 11287.66844
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 11067.81225
-  tps: 10809.56813
+  dps: 13562.44986
+  tps: 11788.1537
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 11067.81225
-  tps: 10809.56813
+  dps: 13562.44986
+  tps: 11788.1537
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 11067.81225
-  tps: 10809.56813
+  dps: 13562.44986
+  tps: 11788.1537
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 10798.36596
-  tps: 10541.0729
+  dps: 13117.12603
+  tps: 11386.11813
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 10776.36851
-  tps: 10515.78096
+  dps: 13083.28612
+  tps: 11354.52228
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 10720.55552
-  tps: 10464.17138
+  dps: 13001.05853
+  tps: 11278.57276
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 11067.81225
-  tps: 10809.56813
+  dps: 13562.44986
+  tps: 11788.1537
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 10729.22572
-  tps: 10464.17138
+  dps: 13036.21327
+  tps: 11291.58017
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 10729.22572
-  tps: 10464.17138
+  dps: 13036.21327
+  tps: 11291.58017
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 11119.66976
-  tps: 10860.98248
+  dps: 13599.47078
+  tps: 11820.25
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 11105.65831
-  tps: 10847.10119
+  dps: 13588.30536
+  tps: 11809.90411
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 10917.10386
-  tps: 10661.13027
+  dps: 13269.52013
+  tps: 11535.64917
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 11105.65831
-  tps: 10847.10119
+  dps: 13588.30536
+  tps: 11809.90411
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 11119.66976
-  tps: 10860.98248
+  dps: 13599.47078
+  tps: 11820.25
  }
 }
 dps_results: {
  key: "TestDemonology-AllItems-WingedTalisman-37844"
  value: {
-  dps: 10843.78785
-  tps: 10585.1107
+  dps: 13220.90237
+  tps: 11480.34933
  }
 }
 dps_results: {
  key: "TestDemonology-Average-Default"
  value: {
-  dps: 11508.98797
-  tps: 11252.67784
+  dps: 14094.85717
+  tps: 12300.56245
  }
 }
 dps_results: {
  key: "TestDemonology-Settings-Orc-p4_demo-Demonology Warlock-demo-FullBuffs-LongMultiTarget"
  value: {
-  dps: 14569.48158
-  tps: 16422.33574
+  dps: 17327.69771
+  tps: 17164.80693
  }
 }
 dps_results: {
  key: "TestDemonology-Settings-Orc-p4_demo-Demonology Warlock-demo-FullBuffs-LongSingleTarget"
  value: {
-  dps: 11410.3423
-  tps: 11152.09819
+  dps: 13937.65732
+  tps: 12163.36117
  }
 }
 dps_results: {
  key: "TestDemonology-Settings-Orc-p4_demo-Demonology Warlock-demo-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 14524.59149
-  tps: 12818.53577
+  dps: 15997.67468
+  tps: 13932.25441
  }
 }
 dps_results: {
  key: "TestDemonology-Settings-Orc-p4_demo-Demonology Warlock-demo-NoBuffs-LongMultiTarget"
  value: {
-  dps: 6562.7132
-  tps: 8970.11008
+  dps: 10670.49322
+  tps: 12397.56516
  }
 }
 dps_results: {
  key: "TestDemonology-Settings-Orc-p4_demo-Demonology Warlock-demo-NoBuffs-LongSingleTarget"
  value: {
-  dps: 6266.27386
-  tps: 6225.81786
+  dps: 8066.21241
+  tps: 7450.9108
  }
 }
 dps_results: {
  key: "TestDemonology-Settings-Orc-p4_demo-Demonology Warlock-demo-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 6929.63748
-  tps: 6137.98157
+  dps: 8263.86975
+  tps: 7507.09706
  }
 }
 dps_results: {
  key: "TestDemonology-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 11359.99577
-  tps: 11138.46347
+  dps: 13827.77154
+  tps: 12196.0304
  }
 }
diff --git a/sim/warlock/inferno.go b/sim/warlock/inferno.go
index da4b7740f3..f08c271c1e 100644
--- a/sim/warlock/inferno.go
+++ b/sim/warlock/inferno.go
@@ -56,11 +56,6 @@ func (warlock *Warlock) registerInfernoSpell() {
 			summonInfernalAura.Activate(sim)
 		},
 	})
-
-	warlock.AddMajorCooldown(core.MajorCooldown{
-		Spell: warlock.Inferno,
-		Type:  core.CooldownTypeUnknown,
-	})
 }
 
 type InfernalPet struct {
diff --git a/ui/warlock/presets.ts b/ui/warlock/presets.ts
index fef9006aa3..e9ca7de51a 100644
--- a/ui/warlock/presets.ts
+++ b/ui/warlock/presets.ts
@@ -119,18 +119,21 @@ export const AfflictionOptions = WarlockOptions.create({
 	armor: Armor.FelArmor,
 	summon: Summon.Felhunter,
 	weaponImbue: WeaponImbue.GrandSpellstone,
+	detonateSeed: true,
 });
 
 export const DemonologyOptions = WarlockOptions.create({
 	armor: Armor.FelArmor,
 	summon: Summon.Felguard,
 	weaponImbue: WeaponImbue.GrandSpellstone,
+	detonateSeed: true,
 });
 
 export const DestructionOptions = WarlockOptions.create({
 	armor: Armor.FelArmor,
 	summon: Summon.Imp,
 	weaponImbue: WeaponImbue.GrandFirestone,
+	detonateSeed: true,
 });
 
 export const DefaultConsumes = Consumes.create({

From d0910e78d23a872ee48617e7a06b9776feb12a2f Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Mon, 8 Jan 2024 17:49:35 -0800
Subject: [PATCH 25/28] Fix hs+cleave bug

---
 sim/warrior/dps/TestArms.results              | 534 ++++++++---------
 sim/warrior/dps/TestFury.results              | 532 ++++++++---------
 sim/warrior/heroic_strike_cleave.go           |   2 +
 .../protection/TestProtectionWarrior.results  | 540 +++++++++---------
 4 files changed, 805 insertions(+), 803 deletions(-)

diff --git a/sim/warrior/dps/TestArms.results b/sim/warrior/dps/TestArms.results
index 8c4f33cd79..8dba53729a 100644
--- a/sim/warrior/dps/TestArms.results
+++ b/sim/warrior/dps/TestArms.results
@@ -46,935 +46,935 @@ character_stats_results: {
 dps_results: {
  key: "TestArms-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 6482.41744
-  tps: 5220.02072
+  dps: 6578.76707
+  tps: 5317.04783
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 6431.93917
-  tps: 5172.33986
+  dps: 6602.04105
+  tps: 5330.43346
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 6264.67869
-  tps: 5039.7388
+  dps: 6417.35518
+  tps: 5185.5382
   hps: 89.11518
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 6264.67869
-  tps: 5039.7388
+  dps: 6417.35518
+  tps: 5185.5382
   hps: 89.11518
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 6495.57286
-  tps: 5230.5847
+  dps: 6610.9396
+  tps: 5341.97634
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Beast-tamer'sShoulders-30892"
  value: {
-  dps: 6301.65452
-  tps: 5073.33708
+  dps: 6412.14717
+  tps: 5185.57512
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BlackBruise-50035"
  value: {
-  dps: 4742.24633
-  tps: 3856.45854
+  dps: 4866.62556
+  tps: 3976.19228
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BlackBruise-50692"
  value: {
-  dps: 4893.49698
-  tps: 3974.34796
+  dps: 5008.28346
+  tps: 4090.87462
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BlessedBattlegearofUndeadSlaying"
  value: {
-  dps: 5271.93662
-  tps: 4227.7173
+  dps: 5391.55621
+  tps: 4346.35392
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BlessedGarboftheUndeadSlayer"
  value: {
-  dps: 5222.59026
-  tps: 4193.75014
+  dps: 5306.16771
+  tps: 4274.09658
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 5026.17849
-  tps: 4038.78586
+  dps: 5092.62896
+  tps: 4104.40432
  }
 }
 dps_results: {
  key: "TestArms-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 6478.84863
-  tps: 5112.88003
+  dps: 6588.02377
+  tps: 5218.54295
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Bryntroll,theBoneArbiter-50415"
  value: {
-  dps: 8016.6666
-  tps: 6494.27378
+  dps: 8625.42745
+  tps: 7032.99268
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Bryntroll,theBoneArbiter-50709"
  value: {
-  dps: 8109.90868
-  tps: 6570.52233
+  dps: 8730.89242
+  tps: 7119.83335
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 6616.85785
-  tps: 5325.93338
+  dps: 6756.72828
+  tps: 5463.2377
  }
 }
 dps_results: {
  key: "TestArms-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
   hps: 64
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 6374.41061
-  tps: 5130.23029
+  dps: 6537.93935
+  tps: 5284.55832
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 6412.14822
-  tps: 5159.52149
+  dps: 6564.43708
+  tps: 5304.84013
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Death'sChoice-47464"
  value: {
-  dps: 6726.16041
-  tps: 5407.81841
+  dps: 6919.08161
+  tps: 5580.98556
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 6335.28796
-  tps: 5097.65887
+  dps: 6469.76481
+  tps: 5227.75503
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 6665.69456
-  tps: 5361.54303
+  dps: 6851.65251
+  tps: 5532.825
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 6724.67718
-  tps: 5407.8647
+  dps: 6960.67426
+  tps: 5623.80389
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Defender'sCode-40257"
  value: {
-  dps: 6274.81353
-  tps: 5047.61817
+  dps: 6422.78999
+  tps: 5185.91516
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 6501.0713
-  tps: 5235.03075
+  dps: 6620.04877
+  tps: 5349.00093
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DislodgedForeignObject-50348"
  value: {
-  dps: 6350.28984
-  tps: 5105.87855
+  dps: 6541.00488
+  tps: 5284.47045
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 6346.59736
-  tps: 5105.06065
+  dps: 6542.30837
+  tps: 5288.85649
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DreadnaughtBattlegear"
  value: {
-  dps: 5920.25992
-  tps: 4781.31769
+  dps: 6000.16096
+  tps: 4864.19104
  }
 }
 dps_results: {
  key: "TestArms-AllItems-DreadnaughtPlate"
  value: {
-  dps: 5211.25695
-  tps: 4187.74189
+  dps: 5270.24824
+  tps: 4251.82732
  }
 }
 dps_results: {
  key: "TestArms-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 6478.84863
-  tps: 5217.20055
+  dps: 6588.02377
+  tps: 5324.94752
  }
 }
 dps_results: {
  key: "TestArms-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 6478.84863
-  tps: 5217.20055
+  dps: 6588.02377
+  tps: 5324.94752
  }
 }
 dps_results: {
  key: "TestArms-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 6495.57286
-  tps: 5230.5847
+  dps: 6610.9396
+  tps: 5341.97634
  }
 }
 dps_results: {
  key: "TestArms-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 6493.76947
-  tps: 5229.12812
+  dps: 6604.9531
+  tps: 5337.21903
  }
 }
 dps_results: {
  key: "TestArms-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 6306.94826
-  tps: 5070.34131
+  dps: 6462.73846
+  tps: 5216.74118
  }
 }
 dps_results: {
  key: "TestArms-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 6478.84863
-  tps: 5217.20055
+  dps: 6588.02377
+  tps: 5324.94752
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 6415.35315
-  tps: 5162.44931
+  dps: 6572.93669
+  tps: 5312.26817
  }
 }
 dps_results: {
  key: "TestArms-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 6364.14747
-  tps: 5121.48477
+  dps: 6517.96016
+  tps: 5267.85674
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ForgeEmber-37660"
  value: {
-  dps: 6348.70584
-  tps: 5108.3564
+  dps: 6482.47577
+  tps: 5238.35676
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 6478.84863
-  tps: 5217.20055
+  dps: 6588.02377
+  tps: 5324.94752
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 6478.84863
-  tps: 5217.20055
+  dps: 6588.02377
+  tps: 5324.94752
  }
 }
 dps_results: {
  key: "TestArms-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 6464.14941
-  tps: 5197.35269
+  dps: 6638.27271
+  tps: 5355.17518
  }
 }
 dps_results: {
  key: "TestArms-AllItems-FuturesightRune-38763"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Gladiator'sBattlegear"
  value: {
-  dps: 6213.38971
-  tps: 4971.99584
+  dps: 6400.63471
+  tps: 5147.67442
  }
 }
 dps_results: {
  key: "TestArms-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 6390.97801
-  tps: 5142.58532
+  dps: 6542.31487
+  tps: 5287.03693
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Heartpierce-49982"
  value: {
-  dps: 7444.30414
-  tps: 6007.8764
+  dps: 7994.21671
+  tps: 6504.30258
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Heartpierce-50641"
  value: {
-  dps: 7475.93535
-  tps: 6035.59142
+  dps: 8082.17352
+  tps: 6572.49427
  }
 }
 dps_results: {
  key: "TestArms-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 6495.57286
-  tps: 5230.5847
+  dps: 6610.9396
+  tps: 5341.97634
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 6493.76947
-  tps: 5229.12812
+  dps: 6604.9531
+  tps: 5337.21903
  }
 }
 dps_results: {
  key: "TestArms-AllItems-IncisorFragment-37723"
  value: {
-  dps: 6455.69968
-  tps: 5193.01856
+  dps: 6619.05428
+  tps: 5343.00426
  }
 }
 dps_results: {
  key: "TestArms-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 6478.84863
-  tps: 5217.20055
+  dps: 6588.02377
+  tps: 5324.94752
  }
 }
 dps_results: {
  key: "TestArms-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 6509.46429
-  tps: 5241.50367
-  hps: 13.00101
+  dps: 6614.63311
+  tps: 5350.04895
+  hps: 12.91576
  }
 }
 dps_results: {
  key: "TestArms-AllItems-LastWord-50179"
  value: {
-  dps: 7563.0174
-  tps: 6106.2864
+  dps: 8087.93558
+  tps: 6582.07627
  }
 }
 dps_results: {
  key: "TestArms-AllItems-LastWord-50708"
  value: {
-  dps: 7618.9608
-  tps: 6151.49898
+  dps: 8207.14873
+  tps: 6678.32817
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 6433.64558
-  tps: 5170.07371
+  dps: 6576.42366
+  tps: 5311.90634
  }
 }
 dps_results: {
  key: "TestArms-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 6439.79417
-  tps: 5187.48571
+  dps: 6559.25088
+  tps: 5305.75722
  }
 }
 dps_results: {
  key: "TestArms-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 6269.28658
-  tps: 5043.25365
+  dps: 6427.37465
+  tps: 5190.7848
  }
 }
 dps_results: {
  key: "TestArms-AllItems-OnslaughtArmor"
  value: {
-  dps: 4249.68079
-  tps: 3420.45651
+  dps: 4291.99379
+  tps: 3468.58551
  }
 }
 dps_results: {
  key: "TestArms-AllItems-OnslaughtBattlegear"
  value: {
-  dps: 4855.76003
-  tps: 3906.0545
+  dps: 4912.08281
+  tps: 3968.95214
  }
 }
 dps_results: {
  key: "TestArms-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 6502.12946
-  tps: 5235.59711
+  dps: 6605.13756
+  tps: 5339.31511
  }
 }
 dps_results: {
  key: "TestArms-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 6507.6073
-  tps: 5239.92558
+  dps: 6615.60889
+  tps: 5348.2666
  }
 }
 dps_results: {
  key: "TestArms-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 6301.19551
-  tps: 5068.45148
+  dps: 6450.7036
+  tps: 5211.08658
  }
 }
 dps_results: {
  key: "TestArms-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 6306.61193
-  tps: 5072.72871
+  dps: 6452.11243
+  tps: 5210.71614
  }
 }
 dps_results: {
  key: "TestArms-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 6478.84863
-  tps: 5217.20055
+  dps: 6588.02377
+  tps: 5324.94752
  }
 }
 dps_results: {
  key: "TestArms-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 6478.84863
-  tps: 5217.20055
+  dps: 6588.02377
+  tps: 5324.94752
  }
 }
 dps_results: {
  key: "TestArms-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 6615.86001
-  tps: 5325.11157
+  dps: 6750.31463
+  tps: 5459.33646
  }
 }
 dps_results: {
  key: "TestArms-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 6478.84863
-  tps: 5217.20055
+  dps: 6588.02377
+  tps: 5324.94752
  }
 }
 dps_results: {
  key: "TestArms-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 6277.61903
-  tps: 5049.92266
+  dps: 6428.76892
+  tps: 5195.23185
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Shadowmourne-49623"
  value: {
-  dps: 9038.57363
-  tps: 7317.90816
+  dps: 9653.04384
+  tps: 7861.38457
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SiegebreakerBattlegear"
  value: {
-  dps: 6056.90359
-  tps: 4885.56365
+  dps: 6186.50477
+  tps: 5003.29305
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SiegebreakerPlate"
  value: {
-  dps: 5354.47251
-  tps: 4302.15286
+  dps: 5424.27098
+  tps: 4379.25955
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SoulPreserver-37111"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SouloftheDead-40382"
  value: {
-  dps: 6366.26913
-  tps: 5123.1821
+  dps: 6524.77466
+  tps: 5273.73855
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SparkofLife-37657"
  value: {
-  dps: 6317.02843
-  tps: 5079.75626
+  dps: 6461.52646
+  tps: 5219.97511
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 6453.84761
-  tps: 5186.09003
+  dps: 6581.50708
+  tps: 5315.95236
  }
 }
 dps_results: {
  key: "TestArms-AllItems-StormshroudArmor"
  value: {
-  dps: 5018.15938
-  tps: 4030.1328
+  dps: 5028.43701
+  tps: 4051.08268
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 6507.6073
-  tps: 5239.92558
+  dps: 6615.60889
+  tps: 5348.2666
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 6502.12946
-  tps: 5235.59711
+  dps: 6605.13756
+  tps: 5339.31511
  }
 }
 dps_results: {
  key: "TestArms-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 6492.54324
-  tps: 5228.02215
+  dps: 6591.23359
+  tps: 5327.78051
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TheFistsofFury"
  value: {
-  dps: 4568.08818
-  tps: 3767.77424
+  dps: 4805.27477
+  tps: 3994.52922
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TheTwinBladesofAzzinoth"
  value: {
-  dps: 4862.05922
-  tps: 3989.98278
+  dps: 5146.53578
+  tps: 4258.94583
  }
 }
 dps_results: {
  key: "TestArms-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 6508.13877
-  tps: 5243.09018
+  dps: 6594.14082
+  tps: 5330.03796
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 6584.29713
-  tps: 5296.3194
+  dps: 6726.39326
+  tps: 5433.60078
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 6630.29753
-  tps: 5335.32952
+  dps: 6806.24088
+  tps: 5498.45942
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 6478.84863
-  tps: 5217.20055
+  dps: 6588.02377
+  tps: 5324.94752
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 6478.84863
-  tps: 5217.20055
+  dps: 6588.02377
+  tps: 5324.94752
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 6324.60419
-  tps: 5086.9172
+  dps: 6423.29498
+  tps: 5189.66479
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 6478.84863
-  tps: 5217.20055
+  dps: 6588.02377
+  tps: 5324.94752
  }
 }
 dps_results: {
  key: "TestArms-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 6478.84863
-  tps: 5217.20055
+  dps: 6588.02377
+  tps: 5324.94752
  }
 }
 dps_results: {
  key: "TestArms-AllItems-UndeadSlayer'sBlessedArmor"
  value: {
-  dps: 5209.00537
-  tps: 4182.56264
+  dps: 5334.191
+  tps: 4298.58943
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Val'anyr,HammerofAncientKings-46017"
  value: {
-  dps: 3739.49527
-  tps: 3074.84622
+  dps: 3822.06397
+  tps: 3168.78811
  }
 }
 dps_results: {
  key: "TestArms-AllItems-WingedTalisman-37844"
  value: {
-  dps: 6262.82724
-  tps: 5037.96938
+  dps: 6396.79762
+  tps: 5166.90921
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Wrynn'sBattlegear"
  value: {
-  dps: 6714.43279
-  tps: 5406.58323
+  dps: 6890.70917
+  tps: 5566.1016
  }
 }
 dps_results: {
  key: "TestArms-AllItems-Wrynn'sPlate"
  value: {
-  dps: 5548.49888
-  tps: 4454.25
+  dps: 5640.39847
+  tps: 4546.03583
  }
 }
 dps_results: {
  key: "TestArms-AllItems-YmirjarLord'sBattlegear"
  value: {
-  dps: 7349.16566
-  tps: 5931.73881
+  dps: 7494.64482
+  tps: 6071.88535
  }
 }
 dps_results: {
  key: "TestArms-AllItems-YmirjarLord'sPlate"
  value: {
-  dps: 5808.61237
-  tps: 4674.27625
+  dps: 5937.65653
+  tps: 4794.79385
  }
 }
 dps_results: {
  key: "TestArms-Average-Default"
  value: {
-  dps: 6659.68361
-  tps: 5357.47752
+  dps: 6831.86673
+  tps: 5520.13027
  }
 }
 dps_results: {
  key: "TestArms-Settings-Human-p1_arms-Basic-arms-FullBuffs-LongMultiTarget"
  value: {
-  dps: 11184.11318
-  tps: 10321.38091
+  dps: 11927.56542
+  tps: 11029.81694
  }
 }
 dps_results: {
  key: "TestArms-Settings-Human-p1_arms-Basic-arms-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6552.42865
-  tps: 5252.94836
+  dps: 6744.96972
+  tps: 5435.72093
  }
 }
 dps_results: {
  key: "TestArms-Settings-Human-p1_arms-Basic-arms-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6969.43738
-  tps: 5653.47586
+  dps: 7303.9868
+  tps: 5943.01221
  }
 }
 dps_results: {
  key: "TestArms-Settings-Human-p1_arms-Basic-arms-NoBuffs-LongMultiTarget"
  value: {
-  dps: 6170.00187
-  tps: 5850.70513
+  dps: 6201.566
+  tps: 5886.67849
  }
 }
 dps_results: {
  key: "TestArms-Settings-Human-p1_arms-Basic-arms-NoBuffs-LongSingleTarget"
  value: {
-  dps: 3543.41931
-  tps: 2859.62352
+  dps: 3507.48553
+  tps: 2829.64022
  }
 }
 dps_results: {
  key: "TestArms-Settings-Human-p1_arms-Basic-arms-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 3711.29059
-  tps: 3024.56901
+  dps: 3700.71515
+  tps: 3012.98824
  }
 }
 dps_results: {
  key: "TestArms-Settings-Orc-p1_arms-Basic-arms-FullBuffs-LongMultiTarget"
  value: {
-  dps: 11336.28583
-  tps: 10480.36946
+  dps: 11974.1173
+  tps: 11074.57371
  }
 }
 dps_results: {
  key: "TestArms-Settings-Orc-p1_arms-Basic-arms-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6615.86001
-  tps: 5325.11157
+  dps: 6750.31463
+  tps: 5459.33646
  }
 }
 dps_results: {
  key: "TestArms-Settings-Orc-p1_arms-Basic-arms-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7108.58976
-  tps: 5787.99086
+  dps: 7373.00838
+  tps: 6033.37017
  }
 }
 dps_results: {
  key: "TestArms-Settings-Orc-p1_arms-Basic-arms-NoBuffs-LongMultiTarget"
  value: {
-  dps: 6335.42532
-  tps: 6013.61723
+  dps: 6352.90441
+  tps: 6027.91913
  }
 }
 dps_results: {
  key: "TestArms-Settings-Orc-p1_arms-Basic-arms-NoBuffs-LongSingleTarget"
  value: {
-  dps: 3576.94646
-  tps: 2890.00901
+  dps: 3536.91307
+  tps: 2857.80735
  }
 }
 dps_results: {
  key: "TestArms-Settings-Orc-p1_arms-Basic-arms-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 3779.35597
-  tps: 3094.87687
+  dps: 3767.68859
+  tps: 3083.31246
  }
 }
 dps_results: {
  key: "TestArms-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 6140.51847
-  tps: 4918.78951
+  dps: 6324.6294
+  tps: 5088.14444
  }
 }
diff --git a/sim/warrior/dps/TestFury.results b/sim/warrior/dps/TestFury.results
index 1288ac4796..2450f6b563 100644
--- a/sim/warrior/dps/TestFury.results
+++ b/sim/warrior/dps/TestFury.results
@@ -46,935 +46,935 @@ character_stats_results: {
 dps_results: {
  key: "TestFury-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 6285.39617
-  tps: 4617.84989
+  dps: 6864.87671
+  tps: 5103.38904
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 6242.67815
-  tps: 4584.7029
+  dps: 6776.94205
+  tps: 5031.73545
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 6070.00097
-  tps: 4459.82625
+  dps: 6576.11035
+  tps: 4886.41831
   hps: 90.47701
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 6070.00097
-  tps: 4459.82625
+  dps: 6576.11035
+  tps: 4886.41831
   hps: 90.47701
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 6308.37895
-  tps: 4634.43174
+  dps: 6868.88557
+  tps: 5105.07983
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Beast-tamer'sShoulders-30892"
  value: {
-  dps: 6105.97004
-  tps: 4489.27971
+  dps: 6643.31619
+  tps: 4936.52029
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BlackBruise-50035"
  value: {
-  dps: 5437.88412
-  tps: 3997.69695
+  dps: 5912.90398
+  tps: 4415.24024
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BlackBruise-50692"
  value: {
-  dps: 5535.51042
-  tps: 4067.26735
+  dps: 5993.38342
+  tps: 4473.93155
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BlessedBattlegearofUndeadSlaying"
  value: {
-  dps: 5046.84075
-  tps: 3707.15469
+  dps: 5402.16423
+  tps: 4017.57594
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BlessedGarboftheUndeadSlayer"
  value: {
-  dps: 5022.57195
-  tps: 3691.86742
+  dps: 5344.32106
+  tps: 3981.71498
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 4729.19706
-  tps: 3476.65924
+  dps: 5037.55692
+  tps: 3754.11053
  }
 }
 dps_results: {
  key: "TestFury-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 6282.08305
-  tps: 4523.20391
+  dps: 6885.18545
+  tps: 5017.06984
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Bryntroll,theBoneArbiter-50415"
  value: {
-  dps: 6423.92566
-  tps: 4719.63158
+  dps: 6964.93261
+  tps: 5177.34824
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Bryntroll,theBoneArbiter-50709"
  value: {
-  dps: 6423.92566
-  tps: 4719.63158
+  dps: 6964.93261
+  tps: 5177.34824
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 6423.92566
-  tps: 4719.63158
+  dps: 6964.93261
+  tps: 5177.34824
  }
 }
 dps_results: {
  key: "TestFury-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
   hps: 64
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 6203.41318
-  tps: 4555.80129
+  dps: 6772.21685
+  tps: 5026.11285
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 6236.15781
-  tps: 4578.8361
+  dps: 6766.62895
+  tps: 5024.21231
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Death'sChoice-47464"
  value: {
-  dps: 6602.64112
-  tps: 4849.8309
+  dps: 7160.83725
+  tps: 5311.32515
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 6195.32949
-  tps: 4547.48663
+  dps: 6735.37032
+  tps: 4998.68447
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 6493.06269
-  tps: 4766.0526
+  dps: 7181.01403
+  tps: 5330.98337
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 6551.19253
-  tps: 4809.30975
+  dps: 7248.12327
+  tps: 5377.03041
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Defender'sCode-40257"
  value: {
-  dps: 6084.47406
-  tps: 4470.52004
+  dps: 6596.66988
+  tps: 4898.49101
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 6302.17513
-  tps: 4630.51897
+  dps: 6874.8921
+  tps: 5109.55671
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DislodgedForeignObject-50348"
  value: {
-  dps: 6224.77452
-  tps: 4564.93063
+  dps: 6768.86309
+  tps: 5020.85517
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 6185.24566
-  tps: 4539.96506
+  dps: 6729.96579
+  tps: 4989.43781
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DreadnaughtBattlegear"
  value: {
-  dps: 5719.89221
-  tps: 4210.60192
+  dps: 6227.13442
+  tps: 4636.21994
  }
 }
 dps_results: {
  key: "TestFury-AllItems-DreadnaughtPlate"
  value: {
-  dps: 5029.00135
-  tps: 3700.982
+  dps: 5328.10898
+  tps: 3971.17866
  }
 }
 dps_results: {
  key: "TestFury-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 6282.08305
-  tps: 4615.43576
+  dps: 6885.18545
+  tps: 5119.12412
  }
 }
 dps_results: {
  key: "TestFury-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 6282.08305
-  tps: 4615.43576
+  dps: 6885.18545
+  tps: 5119.12412
  }
 }
 dps_results: {
  key: "TestFury-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 6308.37895
-  tps: 4634.43174
+  dps: 6868.88557
+  tps: 5105.07983
  }
 }
 dps_results: {
  key: "TestFury-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 6308.8863
-  tps: 4634.79924
+  dps: 6874.11509
+  tps: 5110.75442
  }
 }
 dps_results: {
  key: "TestFury-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 6198.69096
-  tps: 4553.2291
+  dps: 6687.25612
+  tps: 4967.56472
  }
 }
 dps_results: {
  key: "TestFury-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 6282.08305
-  tps: 4615.43576
+  dps: 6885.18545
+  tps: 5119.12412
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 6235.09401
-  tps: 4578.01366
+  dps: 6760.84201
+  tps: 5019.53926
  }
 }
 dps_results: {
  key: "TestFury-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 6189.43599
-  tps: 4545.7705
+  dps: 6704.97271
+  tps: 4980.45534
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 6077.42867
-  tps: 4464.82713
+  dps: 6584.90525
+  tps: 4893.12317
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ForgeEmber-37660"
  value: {
-  dps: 6167.90206
-  tps: 4529.76924
+  dps: 6687.15497
+  tps: 4970.63554
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 6282.08305
-  tps: 4615.43576
+  dps: 6885.18545
+  tps: 5119.12412
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 6282.08305
-  tps: 4615.43576
+  dps: 6885.18545
+  tps: 5119.12412
  }
 }
 dps_results: {
  key: "TestFury-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 6282.77567
-  tps: 4614.70076
+  dps: 6784.00223
+  tps: 5039.12821
  }
 }
 dps_results: {
  key: "TestFury-AllItems-FuturesightRune-38763"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Gladiator'sBattlegear"
  value: {
-  dps: 6112.49241
-  tps: 4488.96448
+  dps: 6570.55313
+  tps: 4873.70756
  }
 }
 dps_results: {
  key: "TestFury-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 6214.57201
-  tps: 4563.14193
+  dps: 6732.63239
+  tps: 5001.11317
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Heartpierce-49982"
  value: {
-  dps: 6423.92566
-  tps: 4719.63158
+  dps: 6964.93261
+  tps: 5177.34824
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Heartpierce-50641"
  value: {
-  dps: 6423.92566
-  tps: 4719.63158
+  dps: 6964.93261
+  tps: 5177.34824
  }
 }
 dps_results: {
  key: "TestFury-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 6308.37895
-  tps: 4634.43174
+  dps: 6868.88557
+  tps: 5105.07983
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 6308.8863
-  tps: 4634.79924
+  dps: 6874.11509
+  tps: 5110.75442
  }
 }
 dps_results: {
  key: "TestFury-AllItems-IncisorFragment-37723"
  value: {
-  dps: 6266.13203
-  tps: 4604.24723
+  dps: 6823.56239
+  tps: 5065.85738
  }
 }
 dps_results: {
  key: "TestFury-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 6282.08305
-  tps: 4615.43576
+  dps: 6885.18545
+  tps: 5119.12412
  }
 }
 dps_results: {
  key: "TestFury-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 6311.85119
-  tps: 4637.13045
+  dps: 6901.3274
+  tps: 5130.02631
   hps: 13.7626
  }
 }
 dps_results: {
  key: "TestFury-AllItems-LastWord-50179"
  value: {
-  dps: 6423.92566
-  tps: 4719.63158
+  dps: 6964.93261
+  tps: 5177.34824
  }
 }
 dps_results: {
  key: "TestFury-AllItems-LastWord-50708"
  value: {
-  dps: 6423.92566
-  tps: 4719.63158
+  dps: 6964.93261
+  tps: 5177.34824
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 6251.76275
-  tps: 4593.72518
+  dps: 6806.36961
+  tps: 5051.69152
  }
 }
 dps_results: {
  key: "TestFury-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 6161.9534
-  tps: 4525.70484
+  dps: 6792.6766
+  tps: 5044.42421
  }
 }
 dps_results: {
  key: "TestFury-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 6078.88576
-  tps: 4466.4473
+  dps: 6589.2357
+  tps: 4893.12034
  }
 }
 dps_results: {
  key: "TestFury-AllItems-OnslaughtArmor"
  value: {
-  dps: 3940.83335
-  tps: 2909.62123
+  dps: 4116.35794
+  tps: 3078.01119
  }
 }
 dps_results: {
  key: "TestFury-AllItems-OnslaughtBattlegear"
  value: {
-  dps: 4706.60413
-  tps: 3464.23862
+  dps: 4936.60791
+  tps: 3686.08324
  }
 }
 dps_results: {
  key: "TestFury-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 6305.68102
-  tps: 4632.63106
+  dps: 6887.6853
+  tps: 5120.31582
  }
 }
 dps_results: {
  key: "TestFury-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 6311.23348
-  tps: 4636.67698
+  dps: 6891.5717
+  tps: 5123.87783
  }
 }
 dps_results: {
  key: "TestFury-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 6111.91006
-  tps: 4490.4593
+  dps: 6650.5469
+  tps: 4940.36559
  }
 }
 dps_results: {
  key: "TestFury-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 6117.38687
-  tps: 4494.45067
+  dps: 6653.49639
+  tps: 4940.1973
  }
 }
 dps_results: {
  key: "TestFury-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 6282.08305
-  tps: 4615.43576
+  dps: 6885.18545
+  tps: 5119.12412
  }
 }
 dps_results: {
  key: "TestFury-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 6282.08305
-  tps: 4615.43576
+  dps: 6885.18545
+  tps: 5119.12412
  }
 }
 dps_results: {
  key: "TestFury-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 6425.2442
-  tps: 4720.58779
+  dps: 6960.0882
+  tps: 5172.83324
  }
 }
 dps_results: {
  key: "TestFury-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 6282.08305
-  tps: 4615.43576
+  dps: 6885.18545
+  tps: 5119.12412
  }
 }
 dps_results: {
  key: "TestFury-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 6085.67296
-  tps: 4471.39282
+  dps: 6575.61208
+  tps: 4885.8321
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Shadowmourne-49623"
  value: {
-  dps: 6423.92566
-  tps: 4719.63158
+  dps: 6964.93261
+  tps: 5177.34824
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SiegebreakerBattlegear"
  value: {
-  dps: 6011.43514
-  tps: 4416.30194
+  dps: 6538.17097
+  tps: 4855.89342
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SiegebreakerPlate"
  value: {
-  dps: 5195.06886
-  tps: 3822.59421
+  dps: 5485.8763
+  tps: 4084.22545
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SoulPreserver-37111"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SouloftheDead-40382"
  value: {
-  dps: 6191.58883
-  tps: 4547.35385
+  dps: 6717.34147
+  tps: 4988.78197
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SparkofLife-37657"
  value: {
-  dps: 6127.85832
-  tps: 4496.68067
+  dps: 6638.30084
+  tps: 4923.44284
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 6325.26769
-  tps: 4648.06878
+  dps: 6856.62273
+  tps: 5088.22252
  }
 }
 dps_results: {
  key: "TestFury-AllItems-StormshroudArmor"
  value: {
-  dps: 4734.56855
-  tps: 3486.58656
+  dps: 5041.64657
+  tps: 3761.17074
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 6311.23348
-  tps: 4636.67698
+  dps: 6891.5717
+  tps: 5123.87783
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 6305.68102
-  tps: 4632.63106
+  dps: 6887.6853
+  tps: 5120.31582
  }
 }
 dps_results: {
  key: "TestFury-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 6295.96421
-  tps: 4625.55079
+  dps: 6871.46861
+  tps: 5109.19871
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TheFistsofFury"
  value: {
-  dps: 4701.8964
-  tps: 3441.99943
+  dps: 4965.9252
+  tps: 3701.06728
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TheTwinBladesofAzzinoth"
  value: {
-  dps: 4945.02638
-  tps: 3617.54645
+  dps: 5099.68017
+  tps: 3798.55202
  }
 }
 dps_results: {
  key: "TestFury-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 6307.79817
-  tps: 4632.78041
+  dps: 6864.56544
+  tps: 5101.01566
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 6419.39976
-  tps: 4712.21557
+  dps: 6971.6567
+  tps: 5171.68032
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 6473.31079
-  tps: 4750.92795
+  dps: 6969.68545
+  tps: 5166.48933
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 6282.08305
-  tps: 4615.43576
+  dps: 6885.18545
+  tps: 5119.12412
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 6282.08305
-  tps: 4615.43576
+  dps: 6885.18545
+  tps: 5119.12412
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 6202.53723
-  tps: 4553.28874
+  dps: 6683.01256
+  tps: 4964.43213
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 6282.08305
-  tps: 4615.43576
+  dps: 6885.18545
+  tps: 5119.12412
  }
 }
 dps_results: {
  key: "TestFury-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 6282.08305
-  tps: 4615.43576
+  dps: 6885.18545
+  tps: 5119.12412
  }
 }
 dps_results: {
  key: "TestFury-AllItems-UndeadSlayer'sBlessedArmor"
  value: {
-  dps: 5013.02305
-  tps: 3682.17626
+  dps: 5320.23269
+  tps: 3960.22988
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Val'anyr,HammerofAncientKings-46017"
  value: {
-  dps: 4752.73581
-  tps: 3499.1314
+  dps: 5196.53795
+  tps: 3921.70656
  }
 }
 dps_results: {
  key: "TestFury-AllItems-WingedTalisman-37844"
  value: {
-  dps: 6068.64055
-  tps: 4458.98045
+  dps: 6559.28013
+  tps: 4874.20474
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Wrynn'sBattlegear"
  value: {
-  dps: 6659.09505
-  tps: 4890.56157
+  dps: 7357.98622
+  tps: 5453.94083
  }
 }
 dps_results: {
  key: "TestFury-AllItems-Wrynn'sPlate"
  value: {
-  dps: 5451.43473
-  tps: 4008.11611
+  dps: 5786.67168
+  tps: 4305.73513
  }
 }
 dps_results: {
  key: "TestFury-AllItems-YmirjarLord'sBattlegear"
  value: {
-  dps: 7377.42047
-  tps: 5413.47443
+  dps: 8067.43407
+  tps: 5973.43415
  }
 }
 dps_results: {
  key: "TestFury-AllItems-YmirjarLord'sPlate"
  value: {
-  dps: 5755.86045
-  tps: 4233.42579
+  dps: 6174.83894
+  tps: 4593.70936
  }
 }
 dps_results: {
  key: "TestFury-Average-Default"
  value: {
-  dps: 6423.99337
-  tps: 4721.09766
+  dps: 7015.89861
+  tps: 5207.27726
  }
 }
 dps_results: {
  key: "TestFury-Settings-Human-p1_fury-Basic-fury-FullBuffs-LongMultiTarget"
  value: {
-  dps: 8643.58521
-  tps: 6686.76935
+  dps: 10294.60824
+  tps: 8016.90735
  }
 }
 dps_results: {
  key: "TestFury-Settings-Human-p1_fury-Basic-fury-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6333.29509
-  tps: 4652.58237
+  dps: 6955.40821
+  tps: 5169.2826
  }
 }
 dps_results: {
  key: "TestFury-Settings-Human-p1_fury-Basic-fury-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7404.71574
-  tps: 5425.96083
+  dps: 8347.45997
+  tps: 6188.65399
  }
 }
 dps_results: {
  key: "TestFury-Settings-Human-p1_fury-Basic-fury-NoBuffs-LongMultiTarget"
  value: {
-  dps: 4307.27459
-  tps: 3451.63161
+  dps: 4394.68747
+  tps: 3569.94332
  }
 }
 dps_results: {
  key: "TestFury-Settings-Human-p1_fury-Basic-fury-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2871.0366
-  tps: 2139.06489
+  dps: 2797.06729
+  tps: 2107.61812
  }
 }
 dps_results: {
  key: "TestFury-Settings-Human-p1_fury-Basic-fury-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 3272.65818
-  tps: 2447.12949
+  dps: 3038.98566
+  tps: 2297.15898
  }
 }
 dps_results: {
  key: "TestFury-Settings-Orc-p1_fury-Basic-fury-FullBuffs-LongMultiTarget"
  value: {
-  dps: 8694.98225
-  tps: 6729.1045
+  dps: 10435.10341
+  tps: 8132.46118
  }
 }
 dps_results: {
  key: "TestFury-Settings-Orc-p1_fury-Basic-fury-FullBuffs-LongSingleTarget"
  value: {
-  dps: 6423.92566
-  tps: 4719.63158
+  dps: 6964.93261
+  tps: 5177.34824
  }
 }
 dps_results: {
  key: "TestFury-Settings-Orc-p1_fury-Basic-fury-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 7516.95896
-  tps: 5506.71715
+  dps: 8385.2664
+  tps: 6218.08828
  }
 }
 dps_results: {
  key: "TestFury-Settings-Orc-p1_fury-Basic-fury-NoBuffs-LongMultiTarget"
  value: {
-  dps: 4334.00204
-  tps: 3472.20866
+  dps: 4418.12696
+  tps: 3589.70524
  }
 }
 dps_results: {
  key: "TestFury-Settings-Orc-p1_fury-Basic-fury-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2910.72975
-  tps: 2168.2564
+  dps: 2797.24399
+  tps: 2107.99386
  }
 }
 dps_results: {
  key: "TestFury-Settings-Orc-p1_fury-Basic-fury-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 3338.8731
-  tps: 2494.623
+  dps: 3131.51163
+  tps: 2357.99182
  }
 }
 dps_results: {
  key: "TestFury-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 5887.98903
-  tps: 4330.99525
+  dps: 6468.4034
+  tps: 4804.77961
  }
 }
diff --git a/sim/warrior/heroic_strike_cleave.go b/sim/warrior/heroic_strike_cleave.go
index defe4fa625..94e532653c 100644
--- a/sim/warrior/heroic_strike_cleave.go
+++ b/sim/warrior/heroic_strike_cleave.go
@@ -48,6 +48,7 @@ func (warrior *Warrior) registerHeroicStrikeSpell() {
 			}
 		},
 	})
+	warrior.makeQueueSpellsAndAura(warrior.HeroicStrike)
 }
 
 func (warrior *Warrior) registerCleaveSpell() {
@@ -94,6 +95,7 @@ func (warrior *Warrior) registerCleaveSpell() {
 			}
 		},
 	})
+	warrior.makeQueueSpellsAndAura(warrior.Cleave)
 }
 
 func (warrior *Warrior) makeQueueSpellsAndAura(srcSpell *core.Spell) *core.Spell {
diff --git a/sim/warrior/protection/TestProtectionWarrior.results b/sim/warrior/protection/TestProtectionWarrior.results
index 8f624c145c..c86c6c7975 100644
--- a/sim/warrior/protection/TestProtectionWarrior.results
+++ b/sim/warrior/protection/TestProtectionWarrior.results
@@ -46,7 +46,7 @@ character_stats_results: {
 stat_weights_results: {
  key: "TestProtectionWarrior-StatWeights-Default"
  value: {
-  weights: 0.71312
+  weights: 0.76367
   weights: 0
   weights: 0
   weights: 0
@@ -57,7 +57,7 @@ stat_weights_results: {
   weights: 0
   weights: 0
   weights: 0
-  weights: 0.19612
+  weights: 0.2638
   weights: 0
   weights: 0
   weights: 0
@@ -66,12 +66,12 @@ stat_weights_results: {
   weights: 0
   weights: 0
   weights: 0
-  weights: 0.00611
+  weights: -3e-05
   weights: 0
   weights: 0
   weights: 0
-  weights: 0.43871
-  weights: 0.05727
+  weights: 0.43043
+  weights: 0.09932
   weights: 0
   weights: 0
   weights: 0
@@ -91,874 +91,874 @@ stat_weights_results: {
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Althor'sAbacus-50359"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Althor'sAbacus-50366"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-AustereEarthsiegeDiamond"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Bandit'sInsignia-40371"
  value: {
-  dps: 1349.67155
-  tps: 3452.90509
+  dps: 1497.4236
+  tps: 3865.5531
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BaubleofTrueBlood-50354"
  value: {
-  dps: 1289.68012
-  tps: 3328.53102
-  hps: 82.28496
+  dps: 1443.61597
+  tps: 3757.74334
+  hps: 82.16554
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BaubleofTrueBlood-50726"
  value: {
-  dps: 1289.68012
-  tps: 3328.53102
-  hps: 82.28496
+  dps: 1443.61597
+  tps: 3757.74334
+  hps: 82.16554
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BeamingEarthsiegeDiamond"
  value: {
-  dps: 1296.93101
-  tps: 3344.72411
+  dps: 1436.29269
+  tps: 3738.87948
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Beast-tamer'sShoulders-30892"
  value: {
-  dps: 1268.62008
-  tps: 3272.93157
+  dps: 1403.06881
+  tps: 3652.51629
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BlackBruise-50035"
  value: {
-  dps: 1366.40693
-  tps: 3480.65126
+  dps: 1502.62813
+  tps: 3840.29252
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BlackBruise-50692"
  value: {
-  dps: 1393.43275
-  tps: 3536.68929
+  dps: 1532.82854
+  tps: 3902.58233
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BlessedBattlegearofUndeadSlaying"
  value: {
-  dps: 1095.91679
-  tps: 2830.7771
+  dps: 1236.90562
+  tps: 3226.4392
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BlessedGarboftheUndeadSlayer"
  value: {
-  dps: 1083.97911
-  tps: 2801.35142
+  dps: 1213.25286
+  tps: 3161.94609
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BlessedRegaliaofUndeadCleansing"
  value: {
-  dps: 1038.79449
-  tps: 2703.60683
+  dps: 1149.45808
+  tps: 3021.01244
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-BracingEarthsiegeDiamond"
  value: {
-  dps: 1288.48948
-  tps: 3259.55894
+  dps: 1429.13677
+  tps: 3649.22353
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Bryntroll,theBoneArbiter-50415"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Bryntroll,theBoneArbiter-50709"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ChaoticSkyflareDiamond"
  value: {
-  dps: 1312.2867
-  tps: 3382.90933
+  dps: 1464.89347
+  tps: 3806.56276
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-CorpseTongueCoin-50349"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-CorpseTongueCoin-50352"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-CorrodedSkeletonKey-50356"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
   hps: 64
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DarkmoonCard:Berserker!-42989"
  value: {
-  dps: 1326.14307
-  tps: 3412.47151
+  dps: 1482.8109
+  tps: 3850.33623
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DarkmoonCard:Death-42990"
  value: {
-  dps: 1370.16223
-  tps: 3503.06341
+  dps: 1514.2653
+  tps: 3906.78831
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DarkmoonCard:Greatness-44255"
  value: {
-  dps: 1320.20348
-  tps: 3397.32538
+  dps: 1475.90374
+  tps: 3829.1951
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Death'sChoice-47464"
  value: {
-  dps: 1362.44929
-  tps: 3485.44146
+  dps: 1516.88738
+  tps: 3908.179
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DeathKnight'sAnguish-38212"
  value: {
-  dps: 1315.30651
-  tps: 3386.90955
+  dps: 1458.84083
+  tps: 3789.00873
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Deathbringer'sWill-50362"
  value: {
-  dps: 1379.04866
-  tps: 3530.54996
+  dps: 1550.72349
+  tps: 4003.56481
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Deathbringer'sWill-50363"
  value: {
-  dps: 1402.27748
-  tps: 3586.09731
+  dps: 1559.26933
+  tps: 4021.26044
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DestructiveSkyflareDiamond"
  value: {
-  dps: 1297.77538
-  tps: 3346.47492
+  dps: 1438.34767
+  tps: 3744.86385
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DislodgedForeignObject-50348"
  value: {
-  dps: 1332.44248
-  tps: 3426.37457
+  dps: 1471.50005
+  tps: 3813.02114
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DislodgedForeignObject-50353"
  value: {
-  dps: 1327.12982
-  tps: 3414.81696
+  dps: 1470.21311
+  tps: 3812.0458
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DreadnaughtBattlegear"
  value: {
-  dps: 1309.91106
-  tps: 3337.8708
+  dps: 1474.02677
+  tps: 3793.84899
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-DreadnaughtPlate"
  value: {
-  dps: 1216.07089
-  tps: 3142.58615
+  dps: 1354.59803
+  tps: 3527.38642
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-EffulgentSkyflareDiamond"
  value: {
-  dps: 1288.48948
-  tps: 3326.07038
+  dps: 1429.13677
+  tps: 3723.6434
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-EmberSkyflareDiamond"
  value: {
-  dps: 1288.48948
-  tps: 3326.07038
+  dps: 1429.13677
+  tps: 3723.6434
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-EnigmaticSkyflareDiamond"
  value: {
-  dps: 1296.93101
-  tps: 3344.72411
+  dps: 1436.29269
+  tps: 3738.87948
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-EnigmaticStarflareDiamond"
  value: {
-  dps: 1295.62188
-  tps: 3341.52155
+  dps: 1436.28313
+  tps: 3739.17223
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-EphemeralSnowflake-50260"
  value: {
-  dps: 1314.10173
-  tps: 3386.07585
+  dps: 1452.25212
+  tps: 3775.3647
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-EssenceofGossamer-37220"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-EternalEarthsiegeDiamond"
  value: {
-  dps: 1302.09935
-  tps: 3362.75646
+  dps: 1442.7152
+  tps: 3760.24472
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ExtractofNecromanticPower-40373"
  value: {
-  dps: 1359.5275
-  tps: 3481.35292
+  dps: 1503.39455
+  tps: 3880.58924
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-EyeoftheBroodmother-45308"
  value: {
-  dps: 1323.63574
-  tps: 3406.9386
+  dps: 1466.27182
+  tps: 3802.47188
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Figurine-SapphireOwl-42413"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ForethoughtTalisman-40258"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ForgeEmber-37660"
  value: {
-  dps: 1315.49823
-  tps: 3387.44624
+  dps: 1464.82611
+  tps: 3806.06243
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ForlornSkyflareDiamond"
  value: {
-  dps: 1288.48948
-  tps: 3326.07038
+  dps: 1429.13677
+  tps: 3723.6434
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ForlornStarflareDiamond"
  value: {
-  dps: 1288.48948
-  tps: 3326.07038
+  dps: 1429.13677
+  tps: 3723.6434
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-FuryoftheFiveFlights-40431"
  value: {
-  dps: 1318.14001
-  tps: 3387.55053
+  dps: 1473.63265
+  tps: 3820.71002
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-FuturesightRune-38763"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Gladiator'sBattlegear"
  value: {
-  dps: 1390.13929
-  tps: 3488.94895
+  dps: 1546.55353
+  tps: 3917.21448
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-GlowingTwilightScale-54573"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-GlowingTwilightScale-54589"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-GnomishLightningGenerator-41121"
  value: {
-  dps: 1357.44157
-  tps: 3476.42661
+  dps: 1499.30293
+  tps: 3876.2045
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Heartpierce-49982"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Heartpierce-50641"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-IllustrationoftheDragonSoul-40432"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ImpassiveSkyflareDiamond"
  value: {
-  dps: 1296.93101
-  tps: 3344.72411
+  dps: 1436.29269
+  tps: 3738.87948
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ImpassiveStarflareDiamond"
  value: {
-  dps: 1295.62188
-  tps: 3341.52155
+  dps: 1436.28313
+  tps: 3739.17223
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-IncisorFragment-37723"
  value: {
-  dps: 1325.06872
-  tps: 3409.61163
+  dps: 1477.31363
+  tps: 3830.89904
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-InsightfulEarthsiegeDiamond"
  value: {
-  dps: 1288.48948
-  tps: 3326.07038
+  dps: 1429.13677
+  tps: 3723.6434
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-InvigoratingEarthsiegeDiamond"
  value: {
-  dps: 1292.37353
-  tps: 3334.12396
-  hps: 15.35543
+  dps: 1439.81394
+  tps: 3750.17262
+  hps: 16.38289
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-LastWord-50179"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-LastWord-50708"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Lavanthor'sTalisman-37872"
  value: {
-  dps: 1302.08299
-  tps: 3361.96359
+  dps: 1456.808
+  tps: 3793.30312
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-MajesticDragonFigurine-40430"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-MeteoriteWhetstone-37390"
  value: {
-  dps: 1342.00137
-  tps: 3451.0587
+  dps: 1487.95987
+  tps: 3858.19024
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-NevermeltingIceCrystal-50259"
  value: {
-  dps: 1341.11428
-  tps: 3446.17637
+  dps: 1488.45581
+  tps: 3857.26512
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-OfferingofSacrifice-37638"
  value: {
-  dps: 1291.10608
-  tps: 3331.4959
+  dps: 1439.06283
+  tps: 3744.35518
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-OnslaughtArmor"
  value: {
-  dps: 1032.96893
-  tps: 2697.01584
+  dps: 1143.96277
+  tps: 3010.38028
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-OnslaughtBattlegear"
  value: {
-  dps: 1073.2847
-  tps: 2744.73876
+  dps: 1212.73705
+  tps: 3132.14296
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-PersistentEarthshatterDiamond"
  value: {
-  dps: 1291.63371
-  tps: 3332.58995
+  dps: 1441.44826
+  tps: 3755.28496
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-PersistentEarthsiegeDiamond"
  value: {
-  dps: 1292.37353
-  tps: 3334.12396
+  dps: 1439.81394
+  tps: 3750.17262
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-PetrifiedScarab-21685"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-PetrifiedTwilightScale-54571"
  value: {
-  dps: 1295.55527
-  tps: 3340.7213
+  dps: 1438.52633
+  tps: 3741.00002
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-PetrifiedTwilightScale-54591"
  value: {
-  dps: 1296.3105
-  tps: 3342.28727
+  dps: 1437.28281
+  tps: 3735.73938
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-PowerfulEarthshatterDiamond"
  value: {
-  dps: 1288.48948
-  tps: 3326.07038
+  dps: 1429.13677
+  tps: 3723.6434
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-PowerfulEarthsiegeDiamond"
  value: {
-  dps: 1288.48948
-  tps: 3326.07038
+  dps: 1429.13677
+  tps: 3723.6434
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-PurifiedShardoftheGods"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ReignoftheDead-47316"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ReignoftheDead-47477"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-RelentlessEarthsiegeDiamond"
  value: {
-  dps: 1311.03851
-  tps: 3379.80384
+  dps: 1464.99517
+  tps: 3809.20556
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-RevitalizingSkyflareDiamond"
  value: {
-  dps: 1288.48948
-  tps: 3326.07038
+  dps: 1429.13677
+  tps: 3723.6434
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-RuneofRepulsion-40372"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SealofthePantheon-36993"
  value: {
-  dps: 1291.56894
-  tps: 3332.45565
+  dps: 1432.85618
+  tps: 3727.67586
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Shadowmourne-49623"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ShinyShardoftheGods"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SiegebreakerBattlegear"
  value: {
-  dps: 1348.66714
-  tps: 3431.43035
+  dps: 1528.60272
+  tps: 3922.80255
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SiegebreakerPlate"
  value: {
-  dps: 1226.35732
-  tps: 3155.63414
+  dps: 1362.18488
+  tps: 3534.24554
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Sindragosa'sFlawlessFang-50361"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SliverofPureIce-50339"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SliverofPureIce-50346"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SoulPreserver-37111"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SouloftheDead-40382"
  value: {
-  dps: 1324.25226
-  tps: 3408.21696
+  dps: 1468.61799
+  tps: 3808.46535
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SparkofLife-37657"
  value: {
-  dps: 1309.36532
-  tps: 3376.66033
+  dps: 1440.66133
+  tps: 3742.16595
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SphereofRedDragon'sBlood-37166"
  value: {
-  dps: 1322.14884
-  tps: 3404.71972
+  dps: 1478.71065
+  tps: 3846.54338
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-StormshroudArmor"
  value: {
-  dps: 1027.97703
-  tps: 2682.10178
+  dps: 1131.41118
+  tps: 2973.77697
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SwiftSkyflareDiamond"
  value: {
-  dps: 1292.37353
-  tps: 3334.12396
+  dps: 1439.81394
+  tps: 3750.17262
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SwiftStarflareDiamond"
  value: {
-  dps: 1291.63371
-  tps: 3332.58995
+  dps: 1441.44826
+  tps: 3755.28496
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-SwiftWindfireDiamond"
  value: {
-  dps: 1290.33903
-  tps: 3329.90542
+  dps: 1434.21835
+  tps: 3734.84239
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TalismanofTrollDivinity-37734"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TearsoftheVanquished-47215"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TheFistsofFury"
  value: {
-  dps: 638.3805
-  tps: 1332.04643
+  dps: 1002.18698
+  tps: 2269.92269
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TheGeneral'sHeart-45507"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TheTwinBladesofAzzinoth"
  value: {
-  dps: 672.48703
-  tps: 1402.91131
+  dps: 1067.10265
+  tps: 2408.07406
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-ThunderingSkyflareDiamond"
  value: {
-  dps: 1295.25946
-  tps: 3338.84336
+  dps: 1433.77973
+  tps: 3730.22067
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TinyAbominationinaJar-50351"
  value: {
-  dps: 1336.15076
-  tps: 3441.80734
+  dps: 1478.24422
+  tps: 3833.2328
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TinyAbominationinaJar-50706"
  value: {
-  dps: 1350.48168
-  tps: 3475.99802
+  dps: 1467.27925
+  tps: 3802.76201
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TirelessSkyflareDiamond"
  value: {
-  dps: 1288.48948
-  tps: 3326.07038
+  dps: 1429.13677
+  tps: 3723.6434
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TirelessStarflareDiamond"
  value: {
-  dps: 1288.48948
-  tps: 3326.07038
+  dps: 1429.13677
+  tps: 3723.6434
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TomeofArcanePhenomena-36972"
  value: {
-  dps: 1308.92313
-  tps: 3378.26678
+  dps: 1436.78984
+  tps: 3728.88041
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TrenchantEarthshatterDiamond"
  value: {
-  dps: 1288.48948
-  tps: 3326.07038
+  dps: 1429.13677
+  tps: 3723.6434
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-TrenchantEarthsiegeDiamond"
  value: {
-  dps: 1288.48948
-  tps: 3326.07038
+  dps: 1429.13677
+  tps: 3723.6434
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-UndeadSlayer'sBlessedArmor"
  value: {
-  dps: 1086.63762
-  tps: 2805.44607
+  dps: 1207.40792
+  tps: 3148.8019
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Val'anyr,HammerofAncientKings-46017"
  value: {
-  dps: 1216.28045
-  tps: 3164.34375
+  dps: 1320.86781
+  tps: 3455.68965
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-WingedTalisman-37844"
  value: {
-  dps: 1289.69323
-  tps: 3328.56635
+  dps: 1443.50947
+  tps: 3757.45627
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Wrynn'sBattlegear"
  value: {
-  dps: 1501.65236
-  tps: 3779.411
+  dps: 1715.22713
+  tps: 4343.02026
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-Wrynn'sPlate"
  value: {
-  dps: 1310.8597
-  tps: 3363.59082
+  dps: 1438.9139
+  tps: 3715.52095
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-YmirjarLord'sBattlegear"
  value: {
-  dps: 1681.60003
-  tps: 4188.20074
+  dps: 1915.35055
+  tps: 4811.31977
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-AllItems-YmirjarLord'sPlate"
  value: {
-  dps: 1425.09691
-  tps: 3644.74776
+  dps: 1565.56182
+  tps: 4034.16559
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Average-Default"
  value: {
-  dps: 2540.72733
-  tps: 6095.0764
-  dtps: 127.44615
+  dps: 2771.65176
+  tps: 6733.04173
+  dtps: 127.4897
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Human-p1_balanced-Basic-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 1353.97589
-  tps: 3511.30182
+  dps: 1476.36368
+  tps: 3891.49394
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Human-p1_balanced-Basic-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 1353.97589
-  tps: 3501.81765
+  dps: 1476.36368
+  tps: 3840.82728
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Human-p1_balanced-Basic-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 1561.83404
-  tps: 4024.60825
+  dps: 1639.76544
+  tps: 4236.26058
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Human-p1_balanced-Basic-default-NoBuffs-LongMultiTarget"
  value: {
-  dps: 1094.86734
-  tps: 3435.07618
+  dps: 1084.94322
+  tps: 3407.20547
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Human-p1_balanced-Basic-default-NoBuffs-LongSingleTarget"
  value: {
-  dps: 882.0064
-  tps: 2533.03893
+  dps: 866.97075
+  tps: 2487.10038
  }
 }
 dps_results: {
@@ -971,36 +971,36 @@ dps_results: {
 dps_results: {
  key: "TestProtectionWarrior-Settings-Orc-p1_balanced-Basic-default-FullBuffs-LongMultiTarget"
  value: {
-  dps: 1360.20142
-  tps: 3524.55292
+  dps: 1493.10216
+  tps: 3930.30504
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Orc-p1_balanced-Basic-default-FullBuffs-LongSingleTarget"
  value: {
-  dps: 1360.20142
-  tps: 3515.09003
+  dps: 1493.10216
+  tps: 3879.63837
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Orc-p1_balanced-Basic-default-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 1573.75672
-  tps: 4049.75167
+  dps: 1681.29892
+  tps: 4344.50046
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Orc-p1_balanced-Basic-default-NoBuffs-LongMultiTarget"
  value: {
-  dps: 1104.93219
-  tps: 3468.9695
+  dps: 1097.36592
+  tps: 3443.56821
  }
 }
 dps_results: {
  key: "TestProtectionWarrior-Settings-Orc-p1_balanced-Basic-default-NoBuffs-LongSingleTarget"
  value: {
-  dps: 885.75406
-  tps: 2542.0882
+  dps: 881.93425
+  tps: 2531.41294
  }
 }
 dps_results: {
@@ -1013,8 +1013,8 @@ dps_results: {
 dps_results: {
  key: "TestProtectionWarrior-SwitchInFrontOfTarget-Default"
  value: {
-  dps: 2658.90648
-  tps: 6391.54637
-  dtps: 120.61498
+  dps: 2890.35455
+  tps: 7022.68811
+  dtps: 120.81343
  }
 }

From e138207915a7dff4917bc36e2226035ee9b25a63 Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Tue, 9 Jan 2024 02:09:41 -0800
Subject: [PATCH 26/28] Fix small shaman bug

---
 .../enhancement/TestEnhancement.results       | 144 +++++++++---------
 sim/shaman/totems.go                          |   7 +-
 2 files changed, 78 insertions(+), 73 deletions(-)

diff --git a/sim/shaman/enhancement/TestEnhancement.results b/sim/shaman/enhancement/TestEnhancement.results
index c72deb447e..e1003be241 100644
--- a/sim/shaman/enhancement/TestEnhancement.results
+++ b/sim/shaman/enhancement/TestEnhancement.results
@@ -1114,127 +1114,127 @@ dps_results: {
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_ft-FullBuffs-LongMultiTarget"
  value: {
-  dps: 4944.24822
-  tps: 4008.12786
+  dps: 22232.41084
+  tps: 14032.63156
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_ft-FullBuffs-LongSingleTarget"
  value: {
-  dps: 4944.24822
-  tps: 3210.89845
+  dps: 6310.29822
+  tps: 3894.21075
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_ft-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6208.66309
-  tps: 3584.79758
+  dps: 7625.09438
+  tps: 4292.73614
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_ft-NoBuffs-LongMultiTarget"
  value: {
-  dps: 2440.10311
-  tps: 2574.46437
+  dps: 10601.75571
+  tps: 7528.77421
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_ft-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2440.10311
-  tps: 1639.91575
+  dps: 2818.31739
+  tps: 1776.97179
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_ft-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2708.33712
-  tps: 1602.97462
+  dps: 3477.88715
+  tps: 2033.81312
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_wf-FullBuffs-LongMultiTarget"
  value: {
-  dps: 5091.26951
-  tps: 4166.23495
+  dps: 21690.21929
+  tps: 13696.7373
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_wf-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5091.26951
-  tps: 3317.31698
+  dps: 6407.59639
+  tps: 3963.37725
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_wf-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6396.28053
-  tps: 3720.37032
+  dps: 7688.0309
+  tps: 4341.98635
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_wf-NoBuffs-LongMultiTarget"
  value: {
-  dps: 2568.28624
-  tps: 2810.23722
+  dps: 9884.6133
+  tps: 7003.63947
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_wf-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2568.28624
-  tps: 1736.89243
+  dps: 2854.92489
+  tps: 1807.16084
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-default_wf-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2834.49133
-  tps: 1691.14902
+  dps: 3553.21327
+  tps: 2087.56986
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-phase_3-FullBuffs-LongMultiTarget"
  value: {
-  dps: 4866.76982
-  tps: 3900.96896
+  dps: 22332.92499
+  tps: 14053.10037
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-phase_3-FullBuffs-LongSingleTarget"
  value: {
-  dps: 4866.76982
-  tps: 3154.8846
+  dps: 6333.04736
+  tps: 3909.91624
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-phase_3-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6155.11998
-  tps: 3552.33946
+  dps: 7565.25151
+  tps: 4246.92698
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-phase_3-NoBuffs-LongMultiTarget"
  value: {
-  dps: 2357.03434
-  tps: 2371.87381
+  dps: 10577.713
+  tps: 7462.8381
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-phase_3-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2357.03434
-  tps: 1574.49498
+  dps: 2829.31347
+  tps: 1783.88363
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Orc-p1-WF-phase_3-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2620.98068
-  tps: 1541.92994
+  dps: 3469.99663
+  tps: 2009.30926
  }
 }
 dps_results: {
@@ -1366,127 +1366,127 @@ dps_results: {
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_ft-FullBuffs-LongMultiTarget"
  value: {
-  dps: 4987.1598
-  tps: 4061.9771
+  dps: 22214.34484
+  tps: 14071.30753
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_ft-FullBuffs-LongSingleTarget"
  value: {
-  dps: 4987.1598
-  tps: 3258.62378
+  dps: 6317.56193
+  tps: 3916.88417
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_ft-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6308.24779
-  tps: 3709.32128
+  dps: 7607.20836
+  tps: 4327.10898
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_ft-NoBuffs-LongMultiTarget"
  value: {
-  dps: 2444.83649
-  tps: 2561.92902
+  dps: 10508.5794
+  tps: 7459.33318
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_ft-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2444.83649
-  tps: 1648.29155
+  dps: 2822.54419
+  tps: 1786.97077
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_ft-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2711.86435
-  tps: 1621.83785
+  dps: 3538.91671
+  tps: 2092.00848
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_wf-FullBuffs-LongMultiTarget"
  value: {
-  dps: 5115.23319
-  tps: 4209.88967
+  dps: 21464.69877
+  tps: 13553.35701
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_wf-FullBuffs-LongSingleTarget"
  value: {
-  dps: 5115.23319
-  tps: 3351.39836
+  dps: 6374.41933
+  tps: 3951.79147
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_wf-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6447.86216
-  tps: 3809.26534
+  dps: 7722.14196
+  tps: 4403.68463
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_wf-NoBuffs-LongMultiTarget"
  value: {
-  dps: 2558.15125
-  tps: 2852.05761
+  dps: 9866.63739
+  tps: 7014.52359
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_wf-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2558.15125
-  tps: 1737.43083
+  dps: 2853.62154
+  tps: 1813.24619
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-default_wf-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2841.24541
-  tps: 1712.38615
+  dps: 3567.55283
+  tps: 2111.33287
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-phase_3-FullBuffs-LongMultiTarget"
  value: {
-  dps: 4868.01714
-  tps: 3921.40877
+  dps: 22209.29686
+  tps: 14032.30074
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-phase_3-FullBuffs-LongSingleTarget"
  value: {
-  dps: 4868.01714
-  tps: 3172.16547
+  dps: 6316.60001
+  tps: 3912.07724
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-phase_3-FullBuffs-ShortSingleTarget"
  value: {
-  dps: 6181.81733
-  tps: 3618.34966
+  dps: 7562.8648
+  tps: 4284.24708
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-phase_3-NoBuffs-LongMultiTarget"
  value: {
-  dps: 2362.5928
-  tps: 2365.21563
+  dps: 10640.8485
+  tps: 7505.58378
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-phase_3-NoBuffs-LongSingleTarget"
  value: {
-  dps: 2362.5928
-  tps: 1583.3199
+  dps: 2817.18144
+  tps: 1783.40572
  }
 }
 dps_results: {
  key: "TestEnhancement-Settings-Troll-p1-WF-phase_3-NoBuffs-ShortSingleTarget"
  value: {
-  dps: 2633.31988
-  tps: 1567.36545
+  dps: 3543.74483
+  tps: 2079.12484
  }
 }
 dps_results: {
diff --git a/sim/shaman/totems.go b/sim/shaman/totems.go
index 9b7e51ed6e..7e9b08550d 100644
--- a/sim/shaman/totems.go
+++ b/sim/shaman/totems.go
@@ -135,17 +135,22 @@ func (shaman *Shaman) registerCallOfTheElements() {
 	waterTotem := shaman.getWaterTotemSpell(shaman.Totems.Water)
 
 	totalManaCost := 0.0
+	anyTotems := false
 	if airTotem != nil {
 		totalManaCost += airTotem.DefaultCast.Cost
+		anyTotems = true
 	}
 	if earthTotem != nil {
 		totalManaCost += earthTotem.DefaultCast.Cost
+		anyTotems = true
 	}
 	if fireTotem != nil {
 		totalManaCost += fireTotem.DefaultCast.Cost
+		anyTotems = true
 	}
 	if waterTotem != nil {
 		totalManaCost += waterTotem.DefaultCast.Cost
+		anyTotems = true
 	}
 
 	shaman.RegisterSpell(core.SpellConfig{
@@ -158,7 +163,7 @@ func (shaman *Shaman) registerCallOfTheElements() {
 			},
 		},
 		ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool {
-			return shaman.CurrentMana() >= totalManaCost
+			return anyTotems && shaman.CurrentMana() >= totalManaCost
 		},
 
 		ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {

From 9b8987bd4e72edb30fa90d17f8b77fee39213dbb Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Tue, 9 Jan 2024 03:40:44 -0800
Subject: [PATCH 27/28] Remove deprecated protos

---
 proto/apl.proto                               |  3 --
 proto/common.proto                            |  6 ----
 proto/priest.proto                            |  4 ---
 proto/ui.proto                                |  8 -----
 sim/core/buffs.go                             | 10 +------
 sim/core/test_utils.go                        |  2 +-
 sim/priest/shadow/shadow_priest.go            |  2 +-
 sim/priest/shadow/shadow_priest_test.go       | 15 ++--------
 sim/raid_bench_test.go                        |  8 ++---
 ui/balance_druid/presets.ts                   |  2 +-
 ui/core/components/icon_inputs.ts             |  5 ++--
 .../components/individual_sim_ui/gear_tab.ts  |  6 +---
 .../individual_sim_ui/rotation_tab.ts         |  5 ----
 ui/core/individual_sim_ui.ts                  |  4 +--
 ui/core/preset_utils.ts                       |  8 -----
 ui/core/raid.ts                               | 29 -------------------
 ui/elemental_shaman/sim.ts                    |  2 +-
 ui/enhancement_shaman/presets.ts              |  2 +-
 ui/healing_priest/presets.ts                  |  2 +-
 ui/holy_paladin/sim.ts                        |  2 +-
 ui/mage/sim.ts                                |  2 +-
 ui/protection_paladin/sim.ts                  |  2 +-
 ui/retribution_paladin/sim.ts                 |  2 +-
 ui/shadow_priest/presets.ts                   |  5 +---
 ui/warlock/presets.ts                         |  2 +-
 25 files changed, 22 insertions(+), 116 deletions(-)

diff --git a/proto/apl.proto b/proto/apl.proto
index f380dcaee9..072ead59fa 100644
--- a/proto/apl.proto
+++ b/proto/apl.proto
@@ -34,9 +34,6 @@ message APLPrepullAction {
     APLAction action = 1;
     APLValue do_at_value = 4; // When to perform this prepull action. Should be a negative value.
     bool hide = 3;            // Causes this item to be ignored.
-
-    // TODO: Remove after 1 month (on or after 8/30/2023).
-    string do_at = 2 [deprecated = true];
 }
 
 message APLListItem {
diff --git a/proto/common.proto b/proto/common.proto
index 7bba98594d..a41176b01e 100644
--- a/proto/common.proto
+++ b/proto/common.proto
@@ -431,8 +431,6 @@ message RaidBuffs {
 	// +Spell Power
 	bool totem_of_wrath = 18;
 	bool flametongue_totem = 19;
-	int32 demonic_pact_old = 20; // old demo SP 
-	int32 demonic_pact = 43; // SP buff -- deprecated.
 	int32 demonic_pact_sp = 44; // SP buff NEW CORRECT
 
 	// +5% Spell Crit and/or +3% Haste
@@ -658,7 +656,6 @@ message Target {
 	bool dual_wield = 9;
 	bool dual_wield_penalty = 10;
 	bool parry_haste = 12;
-	bool tight_enemy_damage = 17; // deprecated as of 07/12/23
 	bool suppress_dodge = 16; // Sunwell Radiance
 	SpellSchool spell_school = 13; // Allows elemental attacks.
 
@@ -848,9 +845,6 @@ message Cooldowns {
 
 	// % HP threshold, below which defensive cooldowns can be used.
 	double hp_percent_for_defensives = 2;
-
-	int32 desync_proc_trinket1_seconds = 3 [deprecated = true];
-	int32 desync_proc_trinket2_seconds = 4 [deprecated = true];
 }
 
 message HealingModel {
diff --git a/proto/priest.proto b/proto/priest.proto
index 9aad900732..3066bda4f1 100644
--- a/proto/priest.proto
+++ b/proto/priest.proto
@@ -146,12 +146,8 @@ message ShadowPriest {
 			InnerFire = 1;
 		}
 
-		bool use_shadowfiend = 1 [deprecated = true];
 		Armor armor = 2;
-		bool use_mind_blast = 4 [deprecated = true];
-		bool use_shadow_word_death = 5 [deprecated = true];
 		UnitReference power_infusion_target = 6;
-		double latency = 7 [deprecated = true]; // Latency between actions
 	}
 	Options options = 3;
 }
diff --git a/proto/ui.proto b/proto/ui.proto
index ae1b928691..343ba90593 100644
--- a/proto/ui.proto
+++ b/proto/ui.proto
@@ -265,8 +265,6 @@ message IndividualSimSettings {
 	Player player = 3;
 	Encounter encounter = 4;
 	int32 target_dummies = 9;
-	// Deprecate after 2 months (on 2023/02/13)
-	repeated double ep_weights = 6;
 	UnitStats ep_weights_stats = 10;
 	repeated double ep_ratios = 11;
 	Stat dps_ref_stat = 12;
@@ -277,8 +275,6 @@ message IndividualSimSettings {
 // Local storage data for gear settings.
 message SavedGearSet {
 	EquipmentSpec gear = 1;
-	// Deprecate after 2 months (on 2023/02/13)
-	repeated double bonus_stats = 2;
 	UnitStats bonus_stats_stats = 3;
 }
 
@@ -290,8 +286,6 @@ message SavedSettings {
 	IndividualBuffs player_buffs = 3;
 	Consumes consumes = 4;
 	Race race = 5;
-	Cooldowns cooldowns = 6 [deprecated = true];
-	string rotation_json = 8 [deprecated = true];
 	repeated Profession professions = 9;
 	bool enable_item_swap = 18;
 	ItemSwap item_swap = 17;
@@ -313,8 +307,6 @@ message SavedTalents {
 
 message SavedRotation {
 	APLRotation rotation = 1;
-	string spec_rotation_options_json = 2 [deprecated = true];
-	Cooldowns cooldowns = 3 [deprecated = true];
 }
 
 message BlessingsAssignment {
diff --git a/sim/core/buffs.go b/sim/core/buffs.go
index 36107457d5..66d59134fd 100644
--- a/sim/core/buffs.go
+++ b/sim/core/buffs.go
@@ -224,16 +224,8 @@ func applyBuffEffects(agent Agent, raidBuffs *proto.RaidBuffs, partyBuffs *proto
 	if raidBuffs.TotemOfWrath {
 		MakePermanent(TotemOfWrathAura(character))
 	}
-	if raidBuffs.DemonicPactOld > 0 || raidBuffs.DemonicPact > 0 || raidBuffs.DemonicPactSp > 0 {
-		// Use DemonicPactSp if set.
+	if raidBuffs.DemonicPactSp > 0 {
 		power := raidBuffs.DemonicPactSp
-		if power == 0 {
-			power = raidBuffs.DemonicPact // fallback to old setting.
-		}
-		if power == 0 {
-			power = raidBuffs.DemonicPactOld
-		}
-
 		dpAura := DemonicPactAura(character)
 		dpAura.ExclusiveEffects[0].Priority = float64(power)
 		MakePermanent(dpAura)
diff --git a/sim/core/test_utils.go b/sim/core/test_utils.go
index 1c0c2cbced..f37983362a 100644
--- a/sim/core/test_utils.go
+++ b/sim/core/test_utils.go
@@ -53,7 +53,7 @@ var FullRaidBuffs = &proto.RaidBuffs{
 	ArcaneEmpowerment:     true,
 	BattleShout:           proto.TristateEffect_TristateEffectImproved,
 	Bloodlust:             true,
-	DemonicPact:           500,
+	DemonicPactSp:         500,
 	DevotionAura:          proto.TristateEffect_TristateEffectImproved,
 	DivineSpirit:          true,
 	ElementalOath:         true,
diff --git a/sim/priest/shadow/shadow_priest.go b/sim/priest/shadow/shadow_priest.go
index 36a2ae9c6e..735f84f1bb 100644
--- a/sim/priest/shadow/shadow_priest.go
+++ b/sim/priest/shadow/shadow_priest.go
@@ -28,7 +28,7 @@ func NewShadowPriest(character *core.Character, options *proto.Player) *ShadowPr
 	shadowOptions := options.GetShadowPriest()
 
 	selfBuffs := priest.SelfBuffs{
-		UseShadowfiend: shadowOptions.Options.UseShadowfiend,
+		UseShadowfiend: true,
 		UseInnerFire:   shadowOptions.Options.Armor == proto.ShadowPriest_Options_InnerFire,
 	}
 
diff --git a/sim/priest/shadow/shadow_priest_test.go b/sim/priest/shadow/shadow_priest_test.go
index edd33176d8..ac5003879b 100644
--- a/sim/priest/shadow/shadow_priest_test.go
+++ b/sim/priest/shadow/shadow_priest_test.go
@@ -65,30 +65,21 @@ var FullConsumes = &proto.Consumes{
 var PlayerOptionsBasic = &proto.Player_ShadowPriest{
 	ShadowPriest: &proto.ShadowPriest{
 		Options: &proto.ShadowPriest_Options{
-			Armor:              proto.ShadowPriest_Options_InnerFire,
-			UseShadowfiend:     true,
-			UseMindBlast:       true,
-			UseShadowWordDeath: true,
+			Armor: proto.ShadowPriest_Options_InnerFire,
 		},
 	},
 }
 var PlayerOptionsClipping = &proto.Player_ShadowPriest{
 	ShadowPriest: &proto.ShadowPriest{
 		Options: &proto.ShadowPriest_Options{
-			Armor:              proto.ShadowPriest_Options_InnerFire,
-			UseShadowfiend:     true,
-			UseMindBlast:       true,
-			UseShadowWordDeath: true,
+			Armor: proto.ShadowPriest_Options_InnerFire,
 		},
 	},
 }
 var PlayerOptionsIdeal = &proto.Player_ShadowPriest{
 	ShadowPriest: &proto.ShadowPriest{
 		Options: &proto.ShadowPriest_Options{
-			Armor:              proto.ShadowPriest_Options_InnerFire,
-			UseShadowfiend:     true,
-			UseMindBlast:       true,
-			UseShadowWordDeath: true,
+			Armor: proto.ShadowPriest_Options_InnerFire,
 		},
 	},
 }
diff --git a/sim/raid_bench_test.go b/sim/raid_bench_test.go
index 41e9ef3411..01026d3438 100644
--- a/sim/raid_bench_test.go
+++ b/sim/raid_bench_test.go
@@ -38,9 +38,7 @@ var castersWithElemental = &proto.Party{
 			Equipment: ShadowEquipment,
 			Spec: &proto.Player_ShadowPriest{
 				ShadowPriest: &proto.ShadowPriest{
-					Options: &proto.ShadowPriest_Options{
-						UseShadowfiend: true,
-					},
+					Options: &proto.ShadowPriest_Options{},
 				},
 			},
 			Consumes: &proto.Consumes{
@@ -134,9 +132,7 @@ var castersWithResto = &proto.Party{
 			Equipment: ShadowEquipment,
 			Spec: &proto.Player_ShadowPriest{
 				ShadowPriest: &proto.ShadowPriest{
-					Options: &proto.ShadowPriest_Options{
-						UseShadowfiend: true,
-					},
+					Options: &proto.ShadowPriest_Options{},
 				},
 			},
 			Consumes: &proto.Consumes{
diff --git a/ui/balance_druid/presets.ts b/ui/balance_druid/presets.ts
index b11e50dd42..b4ca4b6aaa 100644
--- a/ui/balance_druid/presets.ts
+++ b/ui/balance_druid/presets.ts
@@ -137,7 +137,7 @@ export const DefaultRaidBuffs = RaidBuffs.create({
 	strengthOfEarthTotem: TristateEffect.TristateEffectImproved,
 	trueshotAura: true,
 	wrathOfAirTotem: true,
-	demonicPact: 500,
+	demonicPactSp: 500,
 });
 
 export const DefaultIndividualBuffs = IndividualBuffs.create({
diff --git a/ui/core/components/icon_inputs.ts b/ui/core/components/icon_inputs.ts
index 0182abd75f..f2fcf70fe8 100644
--- a/ui/core/components/icon_inputs.ts
+++ b/ui/core/components/icon_inputs.ts
@@ -20,11 +20,10 @@ import { Player } from '../player.js';
 import { Raid } from '../raid.js';
 import { EventID, TypedEvent } from '../typed_event.js';
 
-import { IconPicker, IconPickerConfig } from './icon_picker.js';
-import { IconEnumPicker, IconEnumPickerConfig, IconEnumValueConfig } from './icon_enum_picker.js';
+import { IconPicker, } from './icon_picker.js';
+import { IconEnumPicker, IconEnumValueConfig } from './icon_enum_picker.js';
 
 import * as InputHelpers from './input_helpers.js';
-import { Tooltip } from 'bootstrap';
 
 // Component Functions
 
diff --git a/ui/core/components/individual_sim_ui/gear_tab.ts b/ui/core/components/individual_sim_ui/gear_tab.ts
index db11ae7f7c..8698b3582b 100644
--- a/ui/core/components/individual_sim_ui/gear_tab.ts
+++ b/ui/core/components/individual_sim_ui/gear_tab.ts
@@ -61,11 +61,7 @@ export class GearTab extends SimTab {
 			setData: (eventID: EventID, player: Player<any>, newSavedGear: SavedGearSet) => {
 				TypedEvent.freezeAllAndDo(() => {
 					player.setGear(eventID, this.simUI.sim.db.lookupEquipmentSpec(newSavedGear.gear || EquipmentSpec.create()));
-					if (newSavedGear.bonusStats && newSavedGear.bonusStats.some(s => s != 0)) {
-						player.setBonusStats(eventID, new Stats(newSavedGear.bonusStats));
-					} else {
-						player.setBonusStats(eventID, Stats.fromProto(newSavedGear.bonusStatsStats || UnitStats.create()));
-					}
+					player.setBonusStats(eventID, Stats.fromProto(newSavedGear.bonusStatsStats || UnitStats.create()));
 				});
 			},
 			changeEmitters: [this.simUI.player.changeEmitter],
diff --git a/ui/core/components/individual_sim_ui/rotation_tab.ts b/ui/core/components/individual_sim_ui/rotation_tab.ts
index 70a09d43f7..2bfe14199a 100644
--- a/ui/core/components/individual_sim_ui/rotation_tab.ts
+++ b/ui/core/components/individual_sim_ui/rotation_tab.ts
@@ -1,6 +1,5 @@
 import { IndividualSimUI, InputSection } from "../../individual_sim_ui";
 import {
-	Cooldowns,
 	Spec,
 } from "../../proto/common";
 import {
@@ -188,8 +187,6 @@ export class RotationTab extends SimTab {
 			storageKey: this.simUI.getSavedRotationStorageKey(),
 			getData: (player: Player<any>) => SavedRotation.create({
 				rotation: APLRotation.clone(player.aplRotation),
-				specRotationOptionsJson: '{}',
-				cooldowns: Cooldowns.create(),
 			}),
 			setData: (eventID: EventID, player: Player<any>, newRotation: SavedRotation) => {
 				TypedEvent.freezeAllAndDo(() => {
@@ -211,8 +208,6 @@ export class RotationTab extends SimTab {
 			(this.simUI.individualConfig.presets.rotations || []).forEach(presetRotation => {
 				const rotData = presetRotation.rotation;
 				// Fill default values so the equality checks always work.
-				if (!rotData.cooldowns) rotData.cooldowns = Cooldowns.create();
-				if (!rotData.specRotationOptionsJson) rotData.specRotationOptionsJson = '{}';
 				if (!rotData.rotation) rotData.rotation = APLRotation.create();
 
 				savedRotationsManager.addSavedData({
diff --git a/ui/core/individual_sim_ui.ts b/ui/core/individual_sim_ui.ts
index 08502d6d36..32c1ed0fe9 100644
--- a/ui/core/individual_sim_ui.ts
+++ b/ui/core/individual_sim_ui.ts
@@ -542,9 +542,7 @@ export abstract class IndividualSimUI<SpecType extends Spec> extends SimUI {
 				this.sim.encounter.fromProto(eventID, settings.encounter || EncounterProto.create());
 			}
 			if (loadCategory(SimSettingCategories.UISettings)) {
-				if (settings.epWeights?.length > 0) {
-					this.player.setEpWeights(eventID, new Stats(settings.epWeights));
-				} else if (settings.epWeightsStats) {
+				if (settings.epWeightsStats) {
 					this.player.setEpWeights(eventID, Stats.fromProto(settings.epWeightsStats));
 				} else {
 					this.player.setEpWeights(eventID, this.individualConfig.defaults.epWeights);
diff --git a/ui/core/preset_utils.ts b/ui/core/preset_utils.ts
index a62bb11136..f9d2689d99 100644
--- a/ui/core/preset_utils.ts
+++ b/ui/core/preset_utils.ts
@@ -78,7 +78,6 @@ function makePresetGearHelper(name: string, gear: EquipmentSpec, options: Preset
 
 export function makePresetAPLRotation(name: string, rotationJson: any, options?: PresetRotationOptions): PresetRotation {
     const rotation = SavedRotation.create({
-        specRotationOptionsJson: '{}',
         rotation: APLRotation.fromJson(rotationJson),
     });
     return makePresetRotationHelper(name, rotation, options);
@@ -96,13 +95,6 @@ export function makePresetSimpleRotation<SpecType extends Spec>(name: string, sp
     return makePresetRotationHelper(name, rotation, options);
 }
 
-export function makePresetLegacyRotation<SpecType extends Spec>(name: string, spec: SpecType, simpleRotation: SpecRotation<SpecType>, options?: PresetRotationOptions): PresetRotation {
-    const rotation = SavedRotation.create({
-        specRotationOptionsJson: JSON.stringify(specTypeFunctions[spec].rotationToJson(simpleRotation)),
-    });
-    return makePresetRotationHelper(name, rotation, options);
-}
-
 function makePresetRotationHelper(name: string, rotation: SavedRotation, options?: PresetRotationOptions): PresetRotation {
     let conditions: Array<(player: Player<any>) => boolean> = [];
     if (options?.talentTree != undefined) {
diff --git a/ui/core/raid.ts b/ui/core/raid.ts
index d42215b1ad..6079d3398a 100644
--- a/ui/core/raid.ts
+++ b/ui/core/raid.ts
@@ -131,20 +131,6 @@ export class Raid {
 		// Make a defensive copy
 		this.buffs = RaidBuffs.clone(newBuffs);
 
-		if (newBuffs.demonicPact > 0 && newBuffs.demonicPactSp == 0) {
-			this.buffs.demonicPactSp = this.buffs.demonicPact;
-			if (this.buffs.demonicPactSp > 1000) {
-				this.buffs.demonicPactSp /= 10;
-			}
-			this.buffs.demonicPact = 0;
-		} else if (newBuffs.demonicPactOld > 0 && newBuffs.demonicPactSp == 0) {
-			this.buffs.demonicPactSp = this.buffs.demonicPactOld;
-			if (this.buffs.demonicPactSp > 1000) {
-				this.buffs.demonicPactSp /= 10;
-			}
-			this.buffs.demonicPactOld = 0;
-		}
-
 		// Special handle ToW since it crosses buffs/debuffs.
 		if (this.debuffs.totemOfWrath != this.buffs.totemOfWrath) {
 			var newDebuff = Debuffs.clone(this.debuffs);
@@ -234,21 +220,6 @@ export class Raid {
 
 	fromProto(eventID: EventID, proto: RaidProto) {
 		TypedEvent.freezeAllAndDo(() => {
-			if (proto.buffs) {
-				if (proto.buffs.demonicPact > 0 && proto.buffs.demonicPactSp == 0) {
-					proto.buffs.demonicPactSp = proto.buffs.demonicPact;
-					if (proto.buffs.demonicPactSp > 1000) {
-						proto.buffs.demonicPactSp /= 10;
-					}
-					proto.buffs.demonicPact = 0;
-				} else if (proto.buffs.demonicPactOld > 0 && proto.buffs.demonicPactSp == 0) {
-					proto.buffs.demonicPactSp = proto.buffs.demonicPactOld;
-					if (proto.buffs.demonicPactSp > 1000) {
-						proto.buffs.demonicPactSp /= 10;
-					}
-					proto.buffs.demonicPactOld = 0;
-				}
-			}
 			this.setBuffs(eventID, proto.buffs || RaidBuffs.create());
 			this.setDebuffs(eventID, proto.debuffs || Debuffs.create());
 			this.setTanks(eventID, proto.tanks);
diff --git a/ui/elemental_shaman/sim.ts b/ui/elemental_shaman/sim.ts
index 6bbaf3bef6..46e36502e2 100644
--- a/ui/elemental_shaman/sim.ts
+++ b/ui/elemental_shaman/sim.ts
@@ -107,7 +107,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecElementalShaman, {
 			giftOfTheWild: TristateEffect.TristateEffectImproved,
 			moonkinAura: TristateEffect.TristateEffectImproved,
 			sanctifiedRetribution: true,
-			demonicPact: 500,
+			demonicPactSp: 500,
 			wrathOfAirTotem: true,
 		}),
 		partyBuffs: PartyBuffs.create({
diff --git a/ui/enhancement_shaman/presets.ts b/ui/enhancement_shaman/presets.ts
index 7069a9ac9f..4a1cda843f 100644
--- a/ui/enhancement_shaman/presets.ts
+++ b/ui/enhancement_shaman/presets.ts
@@ -111,7 +111,7 @@ export const DefaultRaidBuffs = RaidBuffs.create({
 	sanctifiedRetribution: true,
 	divineSpirit: true,
 	battleShout: TristateEffect.TristateEffectImproved,
-	demonicPact: 500,
+	demonicPactSp: 500,
 });
 
 export const DefaultDebuffs = Debuffs.create({
diff --git a/ui/healing_priest/presets.ts b/ui/healing_priest/presets.ts
index 8bb3ab74bc..e634974fdb 100644
--- a/ui/healing_priest/presets.ts
+++ b/ui/healing_priest/presets.ts
@@ -110,7 +110,7 @@ export const DefaultRaidBuffs = RaidBuffs.create({
 	wrathOfAirTotem: true,
 	sanctifiedRetribution: true,
 	bloodlust: true,
-	demonicPact: 500,
+	demonicPactSp: 500,
 });
 
 export const DefaultIndividualBuffs = IndividualBuffs.create({
diff --git a/ui/holy_paladin/sim.ts b/ui/holy_paladin/sim.ts
index 726cf95491..d73dc75409 100644
--- a/ui/holy_paladin/sim.ts
+++ b/ui/holy_paladin/sim.ts
@@ -81,7 +81,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecHolyPaladin, {
 			leaderOfThePack: TristateEffect.TristateEffectRegular,
 			icyTalons: true,
 			totemOfWrath: true,
-			demonicPact: 500,
+			demonicPactSp: 500,
 			swiftRetribution: true,
 			moonkinAura: TristateEffect.TristateEffectRegular,
 			sanctifiedRetribution: true,
diff --git a/ui/mage/sim.ts b/ui/mage/sim.ts
index ff702df22d..fff37fb5f0 100644
--- a/ui/mage/sim.ts
+++ b/ui/mage/sim.ts
@@ -89,7 +89,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecMage, {
 			divineSpirit: true,
 			swiftRetribution: true,
 			sanctifiedRetribution: true,
-			demonicPact: 500,
+			demonicPactSp: 500,
 			moonkinAura: TristateEffect.TristateEffectImproved,
 			arcaneBrilliance: true,
 		}),
diff --git a/ui/protection_paladin/sim.ts b/ui/protection_paladin/sim.ts
index 89161b19f3..11b8ae3b70 100644
--- a/ui/protection_paladin/sim.ts
+++ b/ui/protection_paladin/sim.ts
@@ -150,7 +150,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionPaladin, {
 			leaderOfThePack: TristateEffect.TristateEffectRegular,
 			icyTalons: true,
 			totemOfWrath: true,
-			demonicPact: 500,
+			demonicPactSp: 500,
 			swiftRetribution: true,
 			moonkinAura: TristateEffect.TristateEffectRegular,
 			sanctifiedRetribution: true,
diff --git a/ui/retribution_paladin/sim.ts b/ui/retribution_paladin/sim.ts
index d70127cba4..1959bc5061 100644
--- a/ui/retribution_paladin/sim.ts
+++ b/ui/retribution_paladin/sim.ts
@@ -135,7 +135,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRetributionPaladin, {
 			icyTalons: true,
 			totemOfWrath: true,
 			wrathOfAirTotem: true,
-			demonicPact: 500,
+			demonicPactSp: 500,
 		}),
 		partyBuffs: PartyBuffs.create({
 		}),
diff --git a/ui/shadow_priest/presets.ts b/ui/shadow_priest/presets.ts
index f58b46cb1e..fa2cf145b8 100644
--- a/ui/shadow_priest/presets.ts
+++ b/ui/shadow_priest/presets.ts
@@ -76,9 +76,6 @@ export const EnlightenmentTalents = {
 };
 
 export const DefaultOptions = Options.create({
-	useShadowfiend: true,
-	useMindBlast: true,
-	useShadowWordDeath: true,
 	armor: Armor.InnerFire,
 });
 
@@ -103,7 +100,7 @@ export const DefaultRaidBuffs = RaidBuffs.create({
 	wrathOfAirTotem: true,
 	sanctifiedRetribution: true,
 	bloodlust: true,
-	demonicPact: 500,
+	demonicPactSp: 500,
 });
 
 export const DefaultIndividualBuffs = IndividualBuffs.create({
diff --git a/ui/warlock/presets.ts b/ui/warlock/presets.ts
index e9ca7de51a..ac09cc6229 100644
--- a/ui/warlock/presets.ts
+++ b/ui/warlock/presets.ts
@@ -158,7 +158,7 @@ export const DefaultRaidBuffs = RaidBuffs.create({
 	wrathOfAirTotem: true,
 	sanctifiedRetribution: true,
 	bloodlust: true,
-	demonicPact: 500,
+	demonicPactSp: 500,
 });
 
 export const DefaultIndividualBuffs = IndividualBuffs.create({

From d8bd035d651b85eebd9ac48253856abb2852580f Mon Sep 17 00:00:00 2001
From: James Tanner <jimmyt857@gmail.com>
Date: Mon, 15 Jan 2024 08:32:06 -0800
Subject: [PATCH 28/28] Add comment for potential proto refactoring

---
 proto/api.proto   | 3 +++
 ui/core/player.ts | 5 +++++
 2 files changed, 8 insertions(+)

diff --git a/proto/api.proto b/proto/api.proto
index 3b4e9b3e05..21e5536bfc 100644
--- a/proto/api.proto
+++ b/proto/api.proto
@@ -65,6 +65,9 @@ message Player {
 
 	APLRotation rotation = 40;
 
+	// TODO: Move most of the remaining fields into a 'MiscellaneousPlayerOptions' message.
+	// This will remove a lot of the boilerplate code in the UI for each new field.
+
 	int32 reaction_time_ms = 41;
 	int32 channel_clip_delay_ms = 42;
 	bool in_front_of_target = 23;
diff --git a/ui/core/player.ts b/ui/core/player.ts
index 7e6fde5a22..b9e1b95c02 100644
--- a/ui/core/player.ts
+++ b/ui/core/player.ts
@@ -1368,6 +1368,11 @@ export class Player<SpecType extends Spec> {
 				|| includeCategories.length == 0
 				|| includeCategories.includes(cat);
 
+		// For backwards compatibility with legacy rotations (removed on 2024/01/15).
+		if (proto.rotation?.type == APLRotationType.TypeLegacy) {
+			proto.rotation.type = APLRotationType.TypeAuto;
+		}
+
 		TypedEvent.freezeAllAndDo(() => {
 			if (loadCategory(SimSettingCategories.Gear)) {
 				this.setGear(eventID, proto.equipment ? this.sim.db.lookupEquipmentSpec(proto.equipment) : new Gear({}));