Show max-roll effect value in totals
This commit is contained in:
@@ -78,6 +78,10 @@ All additive bonuses are evaluated against the current total. For example, if th
|
||||
|
||||
Each item effect row displays its actual current DPS contribution and the marginal DPS gain from the next `+1%` upgrade.
|
||||
|
||||
The effect totals panel also shows what adding one more maximum-roll effect would do right now. For example, if the build already has `+100% melee damage`, another max `+50% melee damage` changes the multiplier from `2.0x` to `2.5x`, so it displays `+25% DPS`.
|
||||
|
||||
Defensive effect totals use an estimated survivability value based on health, block chance, health regen, and lifesteal. This is meant for comparison between effects, not as an exact combat simulator.
|
||||
|
||||
## Strategy profile
|
||||
|
||||
The first version includes an adjustable strategy profile:
|
||||
|
||||
@@ -157,6 +157,7 @@ function updateAnalysis() {
|
||||
const profile = PROFILES[state.profile];
|
||||
const totals = calculateTotals();
|
||||
const dps = calculateDps(totals, state.combatStyle);
|
||||
const survivability = calculateSurvivability(totals);
|
||||
const pieceScores = state.pieces.map((piece) => scorePiece(piece, profile, totals));
|
||||
|
||||
$("#totalDamage").textContent = fmt(totals.baseDamage);
|
||||
@@ -169,7 +170,7 @@ function updateAnalysis() {
|
||||
});
|
||||
updateEffectImpactHints(totals, dps);
|
||||
|
||||
renderEffectTotals(totals.effects);
|
||||
renderEffectTotals(totals, dps, survivability);
|
||||
renderUpgrades(profile, totals, dps);
|
||||
renderSacrifices(pieceScores);
|
||||
saveDraft();
|
||||
@@ -202,6 +203,16 @@ function calculateDps(totals, combatStyle = "melee") {
|
||||
return baseDamage * globalDamage * styleDamage * attackSpeed * doubleHit * crit * cooldown;
|
||||
}
|
||||
|
||||
function calculateSurvivability(totals) {
|
||||
const effects = totals.effects;
|
||||
const baseHealth = Math.max(0, totals.baseHealth);
|
||||
const health = baseHealth * (1 + effects.health / 100);
|
||||
const blockMultiplier = 1 / Math.max(0.05, 1 - effects.blockChance / 100);
|
||||
const regenMultiplier = 1 + effects.healthRegen / 100;
|
||||
const lifestealMultiplier = 1 + effects.lifesteal / 100;
|
||||
return health * blockMultiplier * regenMultiplier * lifestealMultiplier;
|
||||
}
|
||||
|
||||
function getStyleDamageBonus(effects, combatStyle) {
|
||||
if (combatStyle === "ranged") return effects.rangedDamage;
|
||||
if (combatStyle === "skill") return effects.skillDamage;
|
||||
@@ -219,6 +230,16 @@ function cloneTotalsWithEffectDelta(totals, effectType, delta) {
|
||||
};
|
||||
}
|
||||
|
||||
function describeMaxRollGain(effect, totals, currentDps, currentSurvivability) {
|
||||
const withMaxRoll = cloneTotalsWithEffectDelta(totals, effect.id, effect.cap);
|
||||
const dpsGain = percentChange(currentDps, calculateDps(withMaxRoll, state.combatStyle));
|
||||
const survivabilityGain = percentChange(currentSurvivability, calculateSurvivability(withMaxRoll));
|
||||
const parts = [];
|
||||
if (Math.abs(dpsGain) >= 0.05) parts.push(`+${fmt(dpsGain)}% DPS`);
|
||||
if (Math.abs(survivabilityGain) >= 0.05) parts.push(`+${fmt(survivabilityGain)}% survivability`);
|
||||
return parts.length ? parts.join(" / ") : "no effect for current style";
|
||||
}
|
||||
|
||||
function percentChange(from, to) {
|
||||
if (from <= 0) return to > 0 ? 100 : 0;
|
||||
return ((to / from) - 1) * 100;
|
||||
@@ -253,9 +274,13 @@ function scorePiece(piece, profile, totals) {
|
||||
return { piece, score, reasons: [...new Set(reasons)] };
|
||||
}
|
||||
|
||||
function renderEffectTotals(effectTotals) {
|
||||
function renderEffectTotals(totals, currentDps, currentSurvivability) {
|
||||
$("#effectTotals").innerHTML = EFFECTS.map((effect) => `
|
||||
<div class="effect-pill"><span>${effect.label}</span><strong>${fmt(effectTotals[effect.id])}%</strong></div>
|
||||
<div class="effect-pill">
|
||||
<span>${effect.label}</span>
|
||||
<strong>${fmt(totals.effects[effect.id])}%</strong>
|
||||
<small>Max roll +${effect.cap}%: ${describeMaxRollGain(effect, totals, currentDps, currentSurvivability)}</small>
|
||||
</div>
|
||||
`).join("");
|
||||
}
|
||||
|
||||
|
||||
+10
-5
@@ -152,18 +152,23 @@ h3 { margin-bottom: 0; }
|
||||
|
||||
.effect-totals {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.effect-pill {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: 1rem;
|
||||
align-items: start;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 999px;
|
||||
padding: 0.55rem 0.8rem;
|
||||
border-radius: 1rem;
|
||||
padding: 0.75rem 0.85rem;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
.effect-pill small {
|
||||
grid-column: 1 / -1;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.section-heading { margin: 1.5rem 0 1rem; }
|
||||
.section-heading p { color: var(--muted); }
|
||||
|
||||
Reference in New Issue
Block a user