Skip to content

Commit

Permalink
Merge pull request #763 from wowsims/feature/mage-reforge-optimizer
Browse files Browse the repository at this point in the history
[MAGE] Improved Reforge Optimizer
  • Loading branch information
1337LutZ authored Jun 23, 2024
2 parents 1fc5fb7 + e426eb5 commit 7431e0b
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 41 deletions.
1 change: 0 additions & 1 deletion ui/core/components/character_stats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ export class CharacterStats extends Component {

private updateStats(player: Player<any>) {
const playerStats = player.getCurrentStats();

const statMods = this.modifyDisplayStats ? this.modifyDisplayStats(this.player) : {};

const baseStats = Stats.fromProto(playerStats.baseStats);
Expand Down
20 changes: 16 additions & 4 deletions ui/core/components/suggest_reforges_action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,28 @@ const INCLUDED_STATS = [
Stat.StatParry,
];

const STAT_TOOLTIP: { [key in Stat]?: () => Element | string } = {
type StatTooltips = { [key in Stat]?: () => Element | string };

const STAT_TOOLTIPS: StatTooltips = {
[Stat.StatMastery]: () => (
<>
Rating: Excludes your base mastery
Rating: <strong>excluding</strong> your base mastery
<br />
%: <strong>including</strong> your base mastery
</>
),
[Stat.StatSpellHaste]: () => (
<>
Rating: final rating <strong>including</strong> all buffs/gear.
<br />
%: Includes base mastery
%: final percentage value <strong>including</strong> all buffs/gear.
</>
),
};

export type ReforgeOptimizerOptions = {
experimental?: true;
statTooltips?: StatTooltips;
// Allows you to modify the stats before they are returned for the calculations
// For example: Adding class specific Glyphs/Talents that are not added by the backend
updateGearStatsModifier?: (baseStats: Stats) => Stats;
Expand All @@ -67,6 +77,7 @@ export class ReforgeOptimizer {
protected _statCaps: Stats;
protected updateGearStatsModifier: ReforgeOptimizerOptions['updateGearStatsModifier'];
protected softCapsConfig: StatCapConfig[];
protected statTooltips: StatTooltips = {};

constructor(simUI: IndividualSimUI<any>, options?: ReforgeOptimizerOptions) {
this.simUI = simUI;
Expand All @@ -78,6 +89,7 @@ export class ReforgeOptimizer {
this.defaults = simUI.individualConfig.defaults;
this.updateGearStatsModifier = options?.updateGearStatsModifier;
this.softCapsConfig = this.defaults.softCapBreakpoints || [];
this.statTooltips = { ...STAT_TOOLTIPS, ...options?.statTooltips };
this._statCaps = this.statCaps;

const startReforgeOptimizationEntry: ActionGroupItem = {
Expand Down Expand Up @@ -331,7 +343,7 @@ export class ReforgeOptimizer {
},
});

const tooltipText = STAT_TOOLTIP[stat];
const tooltipText = this.statTooltips[stat];
const statTooltipRef = ref<HTMLButtonElement>();

const row = (
Expand Down
7 changes: 5 additions & 2 deletions ui/core/proto_utils/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,10 @@ export class Stats {

getHasteMultipliers(playerClass: Class): number[] {
const baseMeleeHasteMultiplier = 1 + this.getStat(Stat.StatMeleeHaste) / (Mechanics.HASTE_RATING_PER_HASTE_PERCENT * 100);
const meleeHasteBuffsMultiplier = (playerClass == Class.ClassHunter) ? this.getPseudoStat(PseudoStat.PseudoStatRangedSpeedMultiplier) : this.getPseudoStat(PseudoStat.PseudoStatMeleeSpeedMultiplier);
const meleeHasteBuffsMultiplier =
playerClass == Class.ClassHunter
? this.getPseudoStat(PseudoStat.PseudoStatRangedSpeedMultiplier)
: this.getPseudoStat(PseudoStat.PseudoStatMeleeSpeedMultiplier);
const baseSpellHasteMultiplier = 1 + this.getStat(Stat.StatSpellHaste) / (Mechanics.HASTE_RATING_PER_HASTE_PERCENT * 100);
const spellHasteBuffsMultiplier = this.getPseudoStat(PseudoStat.PseudoStatCastSpeedMultiplier);
return [baseMeleeHasteMultiplier, meleeHasteBuffsMultiplier, baseSpellHasteMultiplier, spellHasteBuffsMultiplier];
Expand All @@ -209,7 +212,7 @@ export class Stats {

// Assumes that Haste multipliers have already been applied to both Stats arrays
computeStatCapsDelta(statCaps: Stats, playerClass: Class): Stats {
const [finalMeleeHasteMulti, meleeHasteBuffsMulti, finalSpellHasteMulti, spellHasteBuffsMulti] = this.getHasteMultipliers(playerClass);
const [_finalMeleeHasteMulti, meleeHasteBuffsMulti, _finalSpellHasteMulti, spellHasteBuffsMulti] = this.getHasteMultipliers(playerClass);
return new Stats(
this.stats.map((value, stat) => {
if (statCaps.stats[stat] > 0) {
Expand Down
33 changes: 7 additions & 26 deletions ui/mage/arcane/presets.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,6 @@
import * as PresetUtils from '../../core/preset_utils';
import {
Conjured,
Consumes,
Debuffs,
Flask,
Food,
Glyphs,
Potions,
Profession,
RaidBuffs,
Spec,
Stat,
TinkerHands,
UnitReference,
} from '../../core/proto/common';
import {
ArcaneMage_Options as MageOptions,
MageMajorGlyph as MajorGlyph,
MageMinorGlyph as MinorGlyph,
MagePrimeGlyph as PrimeGlyph,
} from '../../core/proto/mage';
import { Consumes, Debuffs, Flask, Food, Glyphs, Potions, Profession, RaidBuffs, Stat, TinkerHands, UnitReference } from '../../core/proto/common';
import { ArcaneMage_Options as MageOptions, MageMajorGlyph as MajorGlyph, MagePrimeGlyph as PrimeGlyph } from '../../core/proto/mage';
import { SavedTalents } from '../../core/proto/ui';
import { Stats } from '../../core/proto_utils/stats';
import ArcaneApl from './apls/arcane.apl.json';
Expand All @@ -46,12 +27,12 @@ export const ARCANE_ROTATION_PRESET_DEFAULT = PresetUtils.makePresetAPLRotation(
export const P1_EP_PRESET = PresetUtils.makePresetEpWeights(
'Arcane P1',
Stats.fromMap({
[Stat.StatIntellect]: 1.74,
[Stat.StatIntellect]: 1.76,
[Stat.StatSpellPower]: 1,
[Stat.StatSpellHit]: 1.27,
[Stat.StatSpellCrit]: 0.5,
[Stat.StatSpellHaste]: 0.25,
[Stat.StatMastery]: 0.56,
[Stat.StatSpellHit]: 1.31,
[Stat.StatSpellCrit]: 0.53,
[Stat.StatSpellHaste]: 0.49,
[Stat.StatMastery]: 0.57,
}),
);

Expand Down
21 changes: 20 additions & 1 deletion ui/mage/arcane/sim.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CharacterStats } from '../../core/components/character_stats';
import * as OtherInputs from '../../core/components/inputs/other_inputs';
import { ReforgeOptimizer } from '../../core/components/suggest_reforges_action';
import * as Mechanics from '../../core/constants/mechanics';
Expand All @@ -7,6 +8,7 @@ import { PlayerClasses } from '../../core/player_classes';
import { Mage } from '../../core/player_classes/mage';
import { APLRotation } from '../../core/proto/apl';
import { Faction, IndividualBuffs, PartyBuffs, Race, Spec, Stat } from '../../core/proto/common';
import { StatCapType } from '../../core/proto/ui';
import { Stats } from '../../core/proto_utils/stats';
import * as ArcaneInputs from './inputs';
import * as Presets from './presets';
Expand Down Expand Up @@ -53,6 +55,21 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecArcaneMage, {
statCaps: (() => {
return new Stats().withStat(Stat.StatSpellHit, 17 * Mechanics.SPELL_HIT_RATING_PER_HIT_CHANCE);
})(),
// Default soft caps for the Reforge optimizer
softCapBreakpoints: (() => {
// Sources:
// https://www.icy-veins.com/cataclysm-classic/arcane-mage-pve-stat-priority
// https://www.wowhead.com/cata/guide/classes/mage/arcane/dps-stat-priority-attributes-pve
const breakpoints = [2497];
const hasteSoftCapConfig = {
stat: Stat.StatSpellHaste,
breakpoints,
capType: StatCapType.TypeSoftCap,
postCapEPs: [0.56],
};

return [hasteSoftCapConfig];
})(),
// Default consumes settings.
consumes: Presets.DefaultArcaneConsumes,
// Default talents.
Expand Down Expand Up @@ -210,7 +227,9 @@ export class ArcaneMageSimUI extends IndividualSimUI<Spec.SpecArcaneMage> {
super(parentElem, player, SPEC_CONFIG);

player.sim.waitForInit().then(() => {
new ReforgeOptimizer(this, { experimental: true });
new ReforgeOptimizer(this, {
experimental: true,
});
});
}
}
12 changes: 6 additions & 6 deletions ui/mage/fire/presets.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as PresetUtils from '../../core/preset_utils';
import { Conjured, Consumes, Debuffs, Flask, Food, Glyphs, Potions, Profession, RaidBuffs, Spec, Stat } from '../../core/proto/common';
import { Consumes, Debuffs, Flask, Food, Glyphs, Potions, Profession, RaidBuffs, Spec, Stat } from '../../core/proto/common';
import {
FireMage_Options as MageOptions,
MageMajorGlyph as MajorGlyph,
Expand Down Expand Up @@ -32,12 +32,12 @@ export const FIRE_ROTATION_PRESET_DEFAULT = PresetUtils.makePresetAPLRotation('F
export const P1_EP_PRESET = PresetUtils.makePresetEpWeights(
'Fire P1',
Stats.fromMap({
[Stat.StatIntellect]: 1.32,
[Stat.StatIntellect]: 1.33,
[Stat.StatSpellPower]: 1.0,
[Stat.StatSpellHit]: 1.05,
[Stat.StatSpellCrit]: 0.56,
[Stat.StatSpellHaste]: 0.64,
[Stat.StatMastery]: 0.47,
[Stat.StatSpellHit]: 1.09,
[Stat.StatSpellCrit]: 0.61,
[Stat.StatSpellHaste]: 0.69,
[Stat.StatMastery]: 0.46,
}),
);

Expand Down
49 changes: 48 additions & 1 deletion ui/mage/fire/sim.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CharacterStats } from '../../core/components/character_stats';
import * as OtherInputs from '../../core/components/inputs/other_inputs';
import { ReforgeOptimizer } from '../../core/components/suggest_reforges_action';
import * as Mechanics from '../../core/constants/mechanics';
Expand All @@ -6,6 +7,7 @@ import { Player } from '../../core/player';
import { PlayerClasses } from '../../core/player_classes';
import { APLRotation } from '../../core/proto/apl';
import { Faction, IndividualBuffs, PartyBuffs, Race, Spec, Stat } from '../../core/proto/common';
import { StatCapType } from '../../core/proto/ui';
import { Stats } from '../../core/proto_utils/stats';
import * as FireInputs from './inputs';
import * as Presets from './presets';
Expand Down Expand Up @@ -53,6 +55,49 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecFireMage, {
statCaps: (() => {
return new Stats().withStat(Stat.StatSpellHit, 17 * Mechanics.SPELL_HIT_RATING_PER_HIT_CHANCE);
})(),
// Default soft caps for the Reforge optimizer
softCapBreakpoints: (() => {
// Picked from Mage Discord
// Sources:
// https://docs.google.com/spreadsheets/d/17cJJUReg2uz-XxBB3oDWb1kCncdH_-X96mSb0HAu4Ko/edit?gid=0#gid=0
// https://docs.google.com/spreadsheets/d/1WLOZ1YevGPw_WZs0JhGzVVy906W5y0i9UqHa3ejyBkE/htmlview?gid=19
const breakpoints = [
1602, // 12.51% - 5-tick LvB + Pyro
1922, // 15.01% - 12-tick Combust
3212, // 25.08% - 13-tick Combust
4488, // 35.04% - 14-tick Combust
4805, // 37.52% - 6-tick LvB + Pyro
5767, // 45.03% - 15-tick Combust
7033, // 54.92% - 16-tick Combust
// 8000, // 62.47% - 7-tick LvB + Pyro
// 8309, // 64.88% - 17-tick Combust
// 9602, // 74.98% - 18-tick Combust
// 10887, // 85.01% - 19-tick Combust
// 11198, // 87.44% - 8-tick LvB + Pyro
// 12182, // 95.12% - 20-tick Combust
// 13463,
// 14412,
// 14704,
// 16004,
// 17290,
// 17600,
// 18543,
// 19821,
// 20820,
// 21117,
// 22424,
// 23730,
// 24010,
];
const hasteSoftCapConfig = {
stat: Stat.StatSpellHaste,
breakpoints,
capType: StatCapType.TypeSoftCap,
postCapEPs: [0.96, 0.86, 0.77, 0.77, 1.17, 0.76, 0.65],
};

return [hasteSoftCapConfig];
})(),
// Default consumes settings.
consumes: Presets.DefaultFireConsumes,
// Default talents.
Expand Down Expand Up @@ -143,7 +188,9 @@ export class FireMageSimUI extends IndividualSimUI<Spec.SpecFireMage> {
super(parentElem, player, SPEC_CONFIG);

player.sim.waitForInit().then(() => {
new ReforgeOptimizer(this, { experimental: true });
new ReforgeOptimizer(this, {
experimental: true,
});
});
}
}

0 comments on commit 7431e0b

Please sign in to comment.