Skip to content

Commit

Permalink
Merge pull request #75 from wowsims/WeaponSkillUI
Browse files Browse the repository at this point in the history
First pass at handedness crit cap for Classic
  • Loading branch information
sanguinerarogue authored Jan 11, 2025
2 parents 03c9537 + ff7897b commit 502fa7d
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 102 deletions.
178 changes: 111 additions & 67 deletions ui/core/components/character_stats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ref } from 'tsx-vanilla';

import * as Mechanics from '../constants/mechanics.js';
import { Player } from '../player.js';
import { PseudoStat, Spec, Stat } from '../proto/common.js';
import { HandType, ItemSlot, PseudoStat, Spec, Stat, WeaponType } from '../proto/common.js';
import { Stats, UnitStat } from '../proto_utils/stats.js';
import { EventID, TypedEvent } from '../typed_event.js';
import { Component } from './component.js';
Expand All @@ -12,13 +12,7 @@ import { NumberPicker } from './number_picker';
export type StatMods = { talents?: Stats; buffs?: Stats };

const statGroups = new Map<string, Array<UnitStat>>([
[
'Primary',
[
UnitStat.fromStat(Stat.StatHealth),
UnitStat.fromStat(Stat.StatMana),
],
],
['Primary', [UnitStat.fromStat(Stat.StatHealth), UnitStat.fromStat(Stat.StatMana)]],
[
'Attributes',
[
Expand All @@ -27,7 +21,7 @@ const statGroups = new Map<string, Array<UnitStat>>([
UnitStat.fromStat(Stat.StatStamina),
UnitStat.fromStat(Stat.StatIntellect),
UnitStat.fromStat(Stat.StatSpirit),
]
],
],
[
'Physical',
Expand All @@ -40,7 +34,7 @@ const statGroups = new Map<string, Array<UnitStat>>([
UnitStat.fromStat(Stat.StatMeleeCrit),
UnitStat.fromStat(Stat.StatMeleeHaste),
UnitStat.fromPseudoStat(PseudoStat.BonusPhysicalDamage),
]
],
],
[
'Spell',
Expand All @@ -58,7 +52,7 @@ const statGroups = new Map<string, Array<UnitStat>>([
UnitStat.fromStat(Stat.StatSpellHaste),
UnitStat.fromStat(Stat.StatSpellPenetration),
UnitStat.fromStat(Stat.StatMP5),
]
],
],
[
'Defense',
Expand All @@ -70,7 +64,7 @@ const statGroups = new Map<string, Array<UnitStat>>([
UnitStat.fromStat(Stat.StatParry),
UnitStat.fromStat(Stat.StatBlock),
UnitStat.fromStat(Stat.StatBlockValue),
]
],
],
[
'Resistance',
Expand All @@ -80,13 +74,10 @@ const statGroups = new Map<string, Array<UnitStat>>([
UnitStat.fromStat(Stat.StatFrostResistance),
UnitStat.fromStat(Stat.StatNatureResistance),
UnitStat.fromStat(Stat.StatShadowResistance),
]
],
[
'Misc',
[]
],
],
])
['Misc', []],
]);

export class CharacterStats extends Component {
readonly stats: Array<UnitStat>;
Expand Down Expand Up @@ -247,46 +238,51 @@ export class CharacterStats extends Component {
<div className="ps-2">
<div className="character-stats-tooltip-row">
<span>Axes</span>
<span>
{this.weaponSkillDisplayString(gearStats, PseudoStat.PseudoStatAxesSkill)} /{' '}
{this.weaponSkillDisplayString(gearStats, PseudoStat.PseudoStatTwoHandedAxesSkill)}
</span>
<span>{this.weaponSkillDisplayString(talentsStats, PseudoStat.PseudoStatAxesSkill)}</span>
</div>
<div className="character-stats-tooltip-row">
<span>2H Axes</span>
<span>{this.weaponSkillDisplayString(talentsStats, PseudoStat.PseudoStatTwoHandedAxesSkill)}</span>
</div>
<div className="character-stats-tooltip-row">
<span>Daggers</span>
<span>{this.weaponSkillDisplayString(gearStats, PseudoStat.PseudoStatDaggersSkill)}</span>
<span>{this.weaponSkillDisplayString(talentsStats, PseudoStat.PseudoStatDaggersSkill)}</span>
</div>
{/* Commenting out feral combat skill since not present in Classic.
{player.spec === Spec.SpecFeralDruid && (
<div className="character-stats-tooltip-row">
<span>Feral Combat</span>
<span>{this.weaponSkillDisplayString(gearStats, PseudoStat.PseudoStatFeralCombatSkill)}</span>
<span>{this.weaponSkillDisplayString(talentsStats, PseudoStat.PseudoStatFeralCombatSkill)}</span>
</div>
)}
*/}
<div className="character-stats-tooltip-row">
<span>Maces</span>
<span>
{this.weaponSkillDisplayString(gearStats, PseudoStat.PseudoStatMacesSkill)} /{' '}
{this.weaponSkillDisplayString(gearStats, PseudoStat.PseudoStatTwoHandedMacesSkill)}
</span>
<span>{this.weaponSkillDisplayString(talentsStats, PseudoStat.PseudoStatMacesSkill)}</span>
</div>
<div className="character-stats-tooltip-row">
<span>2H Maces</span>
<span>{this.weaponSkillDisplayString(talentsStats, PseudoStat.PseudoStatTwoHandedMacesSkill)}</span>
</div>
<div className="character-stats-tooltip-row">
<span>Polearms</span>
<span>{this.weaponSkillDisplayString(gearStats, PseudoStat.PseudoStatPolearmsSkill)}</span>
<span>{this.weaponSkillDisplayString(talentsStats, PseudoStat.PseudoStatPolearmsSkill)}</span>
</div>
<div className="character-stats-tooltip-row">
<span>Staves</span>
<span>{this.weaponSkillDisplayString(gearStats, PseudoStat.PseudoStatStavesSkill)}</span>
<span>{this.weaponSkillDisplayString(talentsStats, PseudoStat.PseudoStatStavesSkill)}</span>
</div>
<div className="character-stats-tooltip-row">
<span>Swords</span>
<span>
{this.weaponSkillDisplayString(gearStats, PseudoStat.PseudoStatSwordsSkill)} /{' '}
{this.weaponSkillDisplayString(gearStats, PseudoStat.PseudoStatTwoHandedSwordsSkill)}
</span>
<span>{this.weaponSkillDisplayString(talentsStats, PseudoStat.PseudoStatSwordsSkill)}</span>
</div>
<div className="character-stats-tooltip-row">
<span>2H Swords</span>
<span>{this.weaponSkillDisplayString(talentsStats, PseudoStat.PseudoStatTwoHandedSwordsSkill)}</span>
</div>
<div className="character-stats-tooltip-row">
<span>Unarmed</span>
<span>{this.weaponSkillDisplayString(gearStats, PseudoStat.PseudoStatUnarmedSkill)}</span>
<span>{this.weaponSkillDisplayString(talentsStats, PseudoStat.PseudoStatUnarmedSkill)}</span>
</div>
</div>,
);
Expand Down Expand Up @@ -327,15 +323,27 @@ export class CharacterStats extends Component {
});

if (this.meleeCritCapValueElem) {
const meleeCritCapInfo = player.getMeleeCritCapInfo();
const has2hWeapon = player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.handType == HandType.HandTypeTwoHand;
const mhWeapon: WeaponType = player.getGear().getEquippedItem(ItemSlot.ItemSlotMainHand)?.item.weaponType as WeaponType;
const ohWeapon: WeaponType = player.getGear().getEquippedItem(ItemSlot.ItemSlotOffHand)?.item.weaponType as WeaponType;
const mhCritCapInfo = player.getMeleeCritCapInfo(mhWeapon, has2hWeapon);
const ohCritCapInfo = player.getMeleeCritCapInfo(ohWeapon, has2hWeapon);

const playerCritCapDelta = mhCritCapInfo.playerCritCapDelta;

if (playerCritCapDelta === 0.0) {
const prefix = 'Exact';
}

const prefix = playerCritCapDelta > 0 ? 'Over by ' : 'Under by ';

const valueElem = (
<a href="javascript:void(0)" className="stat-value-link" attributes={{ role: 'button' }}>
{`${this.meleeCritCapDisplayString(player, finalStats)} `}
{`${prefix} ${Math.abs(playerCritCapDelta).toFixed(2)}%`}
</a>
);

const capDelta = meleeCritCapInfo.playerCritCapDelta;
const capDelta = mhCritCapInfo.playerCritCapDelta;
if (capDelta === 0) {
valueElem.classList.add('text-white');
} else if (capDelta > 0) {
Expand All @@ -349,41 +357,90 @@ export class CharacterStats extends Component {

const tooltipContent = (
<div>
<div className="character-stats-tooltip-row">
<span>Main Hand</span>
<span></span>
</div>
<hr />
<div className="character-stats-tooltip-row">
<span>Glancing:</span>
<span>{`${meleeCritCapInfo.glancing.toFixed(2)}%`}</span>
<span>{`${mhCritCapInfo.glancing.toFixed(2)}%`}</span>
</div>
<div className="character-stats-tooltip-row">
<span>Suppression:</span>
<span>{`${meleeCritCapInfo.suppression.toFixed(2)}%`}</span>
<span>{`${mhCritCapInfo.suppression.toFixed(2)}%`}</span>
</div>
<div className="character-stats-tooltip-row">
<span>To Hit Cap:</span>
<span>{`${meleeCritCapInfo.remainingMeleeHitCap.toFixed(2)}%`}</span>
<span>White Miss:</span>
<span>{`${mhCritCapInfo.remainingMeleeHitCap.toFixed(2)}%`}</span>
</div>
<div className="character-stats-tooltip-row">
<span>To Exp Cap:</span>
<span>{`${meleeCritCapInfo.remainingExpertiseCap.toFixed(2)}%`}</span>
<span>Dodge:</span>
<span>{`${mhCritCapInfo.dodgeCap.toFixed(2)}%`}</span>
</div>
<div className="character-stats-tooltip-row">
<span>Debuffs:</span>
<span>{`${meleeCritCapInfo.debuffCrit.toFixed(2)}%`}</span>
<span>Parry:</span>
<span>{`${mhCritCapInfo.parryCap.toFixed(2)}%`}</span>
</div>
{meleeCritCapInfo.specSpecificOffset != 0 && (
{mhCritCapInfo.specSpecificOffset != 0 && (
<div className="character-stats-tooltip-row">
<span>Spec Offsets:</span>
<span>{`${meleeCritCapInfo.specSpecificOffset.toFixed(2)}%`}</span>
<span>{`${mhCritCapInfo.specSpecificOffset.toFixed(2)}%`}</span>
</div>
)}
<div className="character-stats-tooltip-row">
<span>Final Crit Cap:</span>
<span>{`${meleeCritCapInfo.baseCritCap.toFixed(2)}%`}</span>
<span>{`${mhCritCapInfo.baseCritCap.toFixed(2)}%`}</span>
</div>
<hr />
<div className="character-stats-tooltip-row">
<span>Can Raise By:</span>
<span>{`${(meleeCritCapInfo.remainingExpertiseCap + meleeCritCapInfo.remainingMeleeHitCap).toFixed(2)}%`}</span>
<span>{`${mhCritCapInfo.remainingMeleeHitCap.toFixed(2)}%`}</span>
</div>
{!has2hWeapon && (
<div>
<hr />

<div className="character-stats-tooltip-row">
<span>Off Hand</span>
<span></span>
</div>
<hr />
<div className="character-stats-tooltip-row">
<span>Glancing:</span>
<span>{`${ohCritCapInfo.glancing.toFixed(2)}%`}</span>
</div>
<div className="character-stats-tooltip-row">
<span>Suppression:</span>
<span>{`${ohCritCapInfo.suppression.toFixed(2)}%`}</span>
</div>
<div className="character-stats-tooltip-row">
<span>White Miss:</span>
<span>{`${ohCritCapInfo.remainingMeleeHitCap.toFixed(2)}%`}</span>
</div>
<div className="character-stats-tooltip-row">
<span>Dodge:</span>
<span>{`${ohCritCapInfo.dodgeCap.toFixed(2)}%`}</span>
</div>
<div className="character-stats-tooltip-row">
<span>Parry:</span>
<span>{`${ohCritCapInfo.parryCap.toFixed(2)}%`}</span>
</div>
{ohCritCapInfo.specSpecificOffset != 0 && (
<div className="character-stats-tooltip-row">
<span>Spec Offsets:</span>
<span>{`${ohCritCapInfo.specSpecificOffset.toFixed(2)}%`}</span>
</div>
)}
<div className="character-stats-tooltip-row">
<span>Final Crit Cap:</span>
<span>{`${ohCritCapInfo.baseCritCap.toFixed(2)}%`}</span>
</div>
<div className="character-stats-tooltip-row">
<span>Can Raise By:</span>
<span>{`${ohCritCapInfo.remainingMeleeHitCap.toFixed(2)}%`}</span>
</div>
</div>
)}
</div>
);

Expand All @@ -403,7 +460,7 @@ export class CharacterStats extends Component {
if (stat === Stat.StatBlockValue) {
const mult = stats.getPseudoStat(PseudoStat.PseudoStatBlockValueMultiplier) || 1;
const perStr = Math.max(0, stats.getPseudoStat(PseudoStat.PseudoStatBlockValuePerStrength) * deltaStats.getStat(Stat.StatStrength) - 1);
displayStr = String(Math.round((rawValue * mult) + perStr));
displayStr = String(Math.round(rawValue * mult + perStr));
} else if (stat === Stat.StatMeleeHit) {
displayStr = `${(rawValue / Mechanics.MELEE_HIT_RATING_PER_HIT_CHANCE).toFixed(2)}%`;
} else if (stat === Stat.StatSpellHit) {
Expand Down Expand Up @@ -447,7 +504,7 @@ export class CharacterStats extends Component {
displayStr = `${rawValue} (${(rawValue / Mechanics.RESILIENCE_RATING_PER_CRIT_REDUCTION_CHANCE).toFixed(2)}%)`;
}
}

if (!displayStr) displayStr = String(Math.round(rawValue));

return displayStr;
Expand Down Expand Up @@ -516,19 +573,6 @@ export class CharacterStats extends Component {
}

private shouldShowMeleeCritCap(player: Player<any>): boolean {
// TODO: Disabled for now while we fix displayed crit cap
return false;
// return [Spec.SpecEnhancementShaman, Spec.SpecRetributionPaladin, Spec.SpecRogue, Spec.SpecWarrior, Spec.SpecHunter].includes(player.spec);
}

private meleeCritCapDisplayString(player: Player<any>, _: Stats): string {
const playerCritCapDelta = player.getMeleeCritCap();

if (playerCritCapDelta === 0.0) {
return 'Exact';
}

const prefix = playerCritCapDelta > 0 ? 'Over by ' : 'Under by ';
return `${prefix} ${Math.abs(playerCritCapDelta).toFixed(2)}%`;
return [Spec.SpecEnhancementShaman, Spec.SpecRetributionPaladin, Spec.SpecRogue, Spec.SpecWarrior, Spec.SpecHunter].includes(player.spec);
}
}
12 changes: 0 additions & 12 deletions ui/core/components/encounter_picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,18 +320,6 @@ class TargetPicker extends Input<Encounter, TargetProto> {
{ name: '62', value: 62 },
{ name: '61', value: 61 },
{ name: '60', value: 60 },
{ name: '53', value: 53 },
{ name: '52', value: 52 },
{ name: '51', value: 51 },
{ name: '50', value: 50 },
{ name: '43', value: 43 },
{ name: '42', value: 42 },
{ name: '41', value: 41 },
{ name: '40', value: 40 },
{ name: '28', value: 28 },
{ name: '27', value: 27 },
{ name: '26', value: 26 },
{ name: '25', value: 25 },
],
changedEvent: () => encounter.targetsChangeEmitter,
getValue: () => this.getTarget().level,
Expand Down
Loading

0 comments on commit 502fa7d

Please sign in to comment.