diff --git a/proto/common.proto b/proto/common.proto
index fad3f8e2fa..f035c890b5 100644
--- a/proto/common.proto
+++ b/proto/common.proto
@@ -121,6 +121,7 @@ enum PseudoStat {
PseudoStatMainHandDps = 0;
PseudoStatOffHandDps = 1;
PseudoStatRangedDps = 2;
+ PseudoStatBlockValueMultiplier = 3;
}
message UnitStats {
diff --git a/sim/core/attack.go b/sim/core/attack.go
index 9090b6ccb4..d19bc4c4e9 100644
--- a/sim/core/attack.go
+++ b/sim/core/attack.go
@@ -27,6 +27,14 @@ type Weapon struct {
SpellSchool SpellSchool
}
+func (w Weapon) DPS() float64 {
+ if w.SwingSpeed == 0 {
+ return 0
+ } else {
+ return (w.BaseDamageMin + w.BaseDamageMax) / 2.0 / w.SwingSpeed
+ }
+}
+
func (w Weapon) WithBonusDPS(bonusDps float64) Weapon {
newWeapon := w
bonusSwingDamage := bonusDps * w.SwingSpeed
diff --git a/sim/core/character.go b/sim/core/character.go
index 6842af76a7..50a7d45ab5 100644
--- a/sim/core/character.go
+++ b/sim/core/character.go
@@ -538,7 +538,12 @@ func (character *Character) doneIteration(sim *Simulation) {
}
func (character *Character) GetPseudoStatsProto() []float64 {
- return nil
+ vals := make([]float64, stats.PseudoStatsLen)
+ vals[proto.PseudoStat_PseudoStatMainHandDps] = character.WeaponFromMainHand(0).DPS()
+ vals[proto.PseudoStat_PseudoStatOffHandDps] = character.WeaponFromOffHand(0).DPS()
+ vals[proto.PseudoStat_PseudoStatRangedDps] = character.WeaponFromRanged(0).DPS()
+ vals[proto.PseudoStat_PseudoStatBlockValueMultiplier] = character.PseudoStats.BlockValueMultiplier
+ return vals
}
func (character *Character) GetMetricsProto() *proto.UnitMetrics {
diff --git a/ui/core/components/character_stats.ts b/ui/core/components/character_stats.ts
index 5db251e91a..761cdd3bd6 100644
--- a/ui/core/components/character_stats.ts
+++ b/ui/core/components/character_stats.ts
@@ -1,4 +1,4 @@
-import { Stat, Class } from '..//proto/common.js';
+import { Stat, Class, PseudoStat } from '..//proto/common.js';
import { TristateEffect } from '..//proto/common.js'
import { getClassStatName, statOrder } from '..//proto_utils/names.js';
import { Stats } from '..//proto_utils/stats.js';
@@ -87,13 +87,13 @@ export class CharacterStats extends Component {
this.stats.forEach((stat, idx) => {
let fragment = document.createElement('fragment');
fragment.innerHTML = `
- ${this.statDisplayString(finalStats, stat)}
+ ${this.statDisplayString(finalStats, finalStats, stat)}
`
let valueElem = fragment.children[0] as HTMLElement;
this.valueElems[idx].querySelector('.stat-value-link')?.remove()
this.valueElems[idx].prepend(valueElem);
- let bonusStatValue = player.getBonusStats().getStat(stat);
+ let bonusStatValue = bonusStats.getStat(stat);
if (bonusStatValue == 0) {
valueElem.classList.remove('text-success', 'text-danger');
@@ -109,39 +109,39 @@ export class CharacterStats extends Component {
valueElem.setAttribute('data-bs-title', `
Base:
- ${this.statDisplayString(baseDelta, stat)}
+ ${this.statDisplayString(baseStats, baseDelta, stat)}
Gear:
- ${this.statDisplayString(gearDelta, stat)}
+ ${this.statDisplayString(gearStats, gearDelta, stat)}
Talents:
- ${this.statDisplayString(talentsDelta, stat)}
+ ${this.statDisplayString(talentsStats, talentsDelta, stat)}
Buffs:
- ${this.statDisplayString(buffsDelta, stat)}
+ ${this.statDisplayString(buffsStats, buffsDelta, stat)}
Consumes:
- ${this.statDisplayString(consumesDelta, stat)}
+ ${this.statDisplayString(consumesStats, consumesDelta, stat)}
${debuffStats.getStat(stat) == 0 ? '' : `
Debuffs:
- ${this.statDisplayString(debuffStats, stat)}
+ ${this.statDisplayString(debuffStats, debuffStats, stat)}
`}
${bonusStatValue == 0 ? '' : `
Bonus:
- ${this.statDisplayString(this.player.getBonusStats(), stat)}
+ ${this.statDisplayString(bonusStats, bonusStats, stat)}
`}
Total:
- ${this.statDisplayString(finalStats, stat)}
+ ${this.statDisplayString(finalStats, finalStats, stat)}
`);
@@ -149,8 +149,13 @@ export class CharacterStats extends Component {
});
}
- private statDisplayString(stats: Stats, stat: Stat): string {
- const rawValue = stats.getStat(stat);
+ private statDisplayString(stats: Stats, deltaStats: Stats, stat: Stat): string {
+ let rawValue = deltaStats.getStat(stat);
+
+ if (stat == Stat.StatBlockValue) {
+ rawValue *= stats.getPseudoStat(PseudoStat.PseudoStatBlockValueMultiplier) || 1;
+ }
+
let displayStr = String(Math.round(rawValue));
if (stat == Stat.StatMeleeHit) {
diff --git a/ui/core/components/exporters.ts b/ui/core/components/exporters.ts
index 2b7be8cff8..00ec1416b1 100644
--- a/ui/core/components/exporters.ts
+++ b/ui/core/components/exporters.ts
@@ -181,7 +181,6 @@ export class Individual80UEPExporter extends Exporter {
}
static pseudoStatNames: Partial> = {
[PseudoStat.PseudoStatMainHandDps]: 'dps',
- [PseudoStat.PseudoStatOffHandDps]: '',
[PseudoStat.PseudoStatRangedDps]: 'rangedDps',
}
}
@@ -270,7 +269,6 @@ export class IndividualPawnEPExporter extends Exporter {
}
static pseudoStatNames: Partial> = {
[PseudoStat.PseudoStatMainHandDps]: 'MeleeDps',
- [PseudoStat.PseudoStatOffHandDps]: '',
[PseudoStat.PseudoStatRangedDps]: 'RangedDps',
}
}
diff --git a/ui/core/components/stat_weights_action.ts b/ui/core/components/stat_weights_action.ts
index 975c0ccc90..f3cc7336e5 100644
--- a/ui/core/components/stat_weights_action.ts
+++ b/ui/core/components/stat_weights_action.ts
@@ -203,8 +203,7 @@ class EpWeightsMenu extends Popup {
this.tableBody.innerHTML = '';
this.tableBody.appendChild(this.tableHeader);
- const allUnitStats = UnitStat.getAll();
- allUnitStats.forEach(stat => {
+ EpWeightsMenu.epUnitStats.forEach(stat => {
const row = this.makeTableRow(stat, iterations, result);
if ((stat.isStat() && !this.epStats.includes(stat.getStat())) || (stat.isPseudoStat() && !this.epPseudoStats.includes(stat.getPseudoStat()))) {
row.classList.add('non-ep-stat');
@@ -538,6 +537,18 @@ class EpWeightsMenu extends Popup {
bestGemEP: bestGemEP,
};
}
+
+ private static epUnitStats: Array = UnitStat.getAll().filter(stat => {
+ if (stat.isStat()) {
+ return true;
+ } else {
+ return [
+ PseudoStat.PseudoStatMainHandDps,
+ PseudoStat.PseudoStatOffHandDps,
+ PseudoStat.PseudoStatRangedDps,
+ ].includes(stat.getPseudoStat());
+ }
+ });
}
interface BestGemsResult {
diff --git a/ui/core/proto_utils/names.ts b/ui/core/proto_utils/names.ts
index 131adf12e0..f91ef9a392 100644
--- a/ui/core/proto_utils/names.ts
+++ b/ui/core/proto_utils/names.ts
@@ -186,11 +186,13 @@ export const pseudoStatOrder: Array = [
PseudoStat.PseudoStatMainHandDps,
PseudoStat.PseudoStatOffHandDps,
PseudoStat.PseudoStatRangedDps,
+ PseudoStat.PseudoStatBlockValueMultiplier,
];
export const pseudoStatNames: Record = {
[PseudoStat.PseudoStatMainHandDps]: 'Main Hand DPS',
[PseudoStat.PseudoStatOffHandDps]: 'Off Hand DPS',
[PseudoStat.PseudoStatRangedDps]: 'Ranged DPS',
+ [PseudoStat.PseudoStatBlockValueMultiplier]: 'Block Value Multiplier',
};
export function getClassStatName(stat: Stat, playerClass: Class): string {