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.
|
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
|
## Strategy profile
|
||||||
|
|
||||||
The first version includes an adjustable strategy profile:
|
The first version includes an adjustable strategy profile:
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ function updateAnalysis() {
|
|||||||
const profile = PROFILES[state.profile];
|
const profile = PROFILES[state.profile];
|
||||||
const totals = calculateTotals();
|
const totals = calculateTotals();
|
||||||
const dps = calculateDps(totals, state.combatStyle);
|
const dps = calculateDps(totals, state.combatStyle);
|
||||||
|
const survivability = calculateSurvivability(totals);
|
||||||
const pieceScores = state.pieces.map((piece) => scorePiece(piece, profile, totals));
|
const pieceScores = state.pieces.map((piece) => scorePiece(piece, profile, totals));
|
||||||
|
|
||||||
$("#totalDamage").textContent = fmt(totals.baseDamage);
|
$("#totalDamage").textContent = fmt(totals.baseDamage);
|
||||||
@@ -169,7 +170,7 @@ function updateAnalysis() {
|
|||||||
});
|
});
|
||||||
updateEffectImpactHints(totals, dps);
|
updateEffectImpactHints(totals, dps);
|
||||||
|
|
||||||
renderEffectTotals(totals.effects);
|
renderEffectTotals(totals, dps, survivability);
|
||||||
renderUpgrades(profile, totals, dps);
|
renderUpgrades(profile, totals, dps);
|
||||||
renderSacrifices(pieceScores);
|
renderSacrifices(pieceScores);
|
||||||
saveDraft();
|
saveDraft();
|
||||||
@@ -202,6 +203,16 @@ function calculateDps(totals, combatStyle = "melee") {
|
|||||||
return baseDamage * globalDamage * styleDamage * attackSpeed * doubleHit * crit * cooldown;
|
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) {
|
function getStyleDamageBonus(effects, combatStyle) {
|
||||||
if (combatStyle === "ranged") return effects.rangedDamage;
|
if (combatStyle === "ranged") return effects.rangedDamage;
|
||||||
if (combatStyle === "skill") return effects.skillDamage;
|
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) {
|
function percentChange(from, to) {
|
||||||
if (from <= 0) return to > 0 ? 100 : 0;
|
if (from <= 0) return to > 0 ? 100 : 0;
|
||||||
return ((to / from) - 1) * 100;
|
return ((to / from) - 1) * 100;
|
||||||
@@ -253,9 +274,13 @@ function scorePiece(piece, profile, totals) {
|
|||||||
return { piece, score, reasons: [...new Set(reasons)] };
|
return { piece, score, reasons: [...new Set(reasons)] };
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderEffectTotals(effectTotals) {
|
function renderEffectTotals(totals, currentDps, currentSurvivability) {
|
||||||
$("#effectTotals").innerHTML = EFFECTS.map((effect) => `
|
$("#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("");
|
`).join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-5
@@ -152,18 +152,23 @@ h3 { margin-bottom: 0; }
|
|||||||
|
|
||||||
.effect-totals {
|
.effect-totals {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
}
|
}
|
||||||
.effect-pill {
|
.effect-pill {
|
||||||
display: flex;
|
display: grid;
|
||||||
justify-content: space-between;
|
grid-template-columns: 1fr auto;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
align-items: start;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 999px;
|
border-radius: 1rem;
|
||||||
padding: 0.55rem 0.8rem;
|
padding: 0.75rem 0.85rem;
|
||||||
background: rgba(255, 255, 255, 0.05);
|
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 { margin: 1.5rem 0 1rem; }
|
||||||
.section-heading p { color: var(--muted); }
|
.section-heading p { color: var(--muted); }
|
||||||
|
|||||||
Reference in New Issue
Block a user