Remove direct upgrade recommendations
This commit is contained in:
@@ -54,20 +54,14 @@ Mount and pet base stats remain editable, and each has separate damage and healt
|
|||||||
|
|
||||||
## Optimizer goals
|
## Optimizer goals
|
||||||
|
|
||||||
The app lets you enter the current state of all equipment and then recommends:
|
The app lets you enter the current state of all equipment and then evaluates:
|
||||||
|
|
||||||
1. **What to increase next**
|
1. **What is best to sacrifice instead**
|
||||||
- Prioritizes effects by actual DPS gain for the selected combat style.
|
|
||||||
- Accounts for each effect's remaining room to grow before cap.
|
|
||||||
- Shows the DPS gain from the next `+1%` and from upgrading to cap.
|
|
||||||
- Crit damage has no DPS value until crit chance is above `0%`.
|
|
||||||
|
|
||||||
2. **What is best to sacrifice instead**
|
|
||||||
- Scores every equipped piece from its base stat and effects.
|
- Scores every equipped piece from its base stat and effects.
|
||||||
- Identifies the lowest-value pieces as likely sacrifice candidates.
|
- Identifies the lowest-value pieces as likely sacrifice candidates.
|
||||||
- Explains why a piece is weak, such as missing effects, low base stat, or low-impact effects.
|
- Explains why a piece is weak, such as low base stat or low-impact effects.
|
||||||
|
|
||||||
3. **Overall build summary**
|
2. **Overall build summary**
|
||||||
- Totals base damage and health.
|
- Totals base damage and health.
|
||||||
- Totals effect percentages across all items, mount, and pets.
|
- Totals effect percentages across all items, mount, and pets.
|
||||||
- Shows calculated DPS and estimated regen to compare save slots.
|
- Shows calculated DPS and estimated regen to compare save slots.
|
||||||
@@ -80,12 +74,14 @@ The app calculates DPS from entered base damage and offensive effects:
|
|||||||
DPS = base damage
|
DPS = base damage
|
||||||
* (1 + damage%)
|
* (1 + damage%)
|
||||||
* (1 + selected style damage%)
|
* (1 + selected style damage%)
|
||||||
* (1 + attack speed%)
|
* ((1 + attack speed%) / 2)
|
||||||
* (1 + double chance%)
|
* (1 + double chance%)
|
||||||
* (1 + crit chance% * crit damage%)
|
* (1 + crit chance% * crit damage%)
|
||||||
* skill cooldown multiplier, only for Skill style
|
* skill cooldown multiplier, only for Skill style
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The attack speed term assumes the base `100%` attack speed is about one hit every two seconds, or `0.5` hits per second.
|
||||||
|
|
||||||
The selected combat style controls which specialized damage bonus is used:
|
The selected combat style controls which specialized damage bonus is used:
|
||||||
|
|
||||||
- Melee uses melee damage.
|
- Melee uses melee damage.
|
||||||
@@ -94,7 +90,7 @@ The selected combat style controls which specialized damage bonus is used:
|
|||||||
|
|
||||||
All additive bonuses are evaluated against the current total. For example, if the build already has `+100% melee damage`, adding another `+50% melee damage` increases the melee multiplier from `2.0x` to `2.5x`, which is an actual `+25%` DPS increase rather than `+50%`.
|
All additive bonuses are evaluated against the current total. For example, if the build already has `+100% melee damage`, adding another `+50% melee damage` increases the melee multiplier from `2.0x` to `2.5x`, which is an actual `+25%` DPS increase rather than `+50%`.
|
||||||
|
|
||||||
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. Direct effect upgrading is not modeled because players roll replacement items rather than adjust an existing effect in-place.
|
||||||
|
|
||||||
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`.
|
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`.
|
||||||
|
|
||||||
|
|||||||
@@ -236,7 +236,6 @@ function updateAnalysis() {
|
|||||||
updateEffectImpactHints(totals, dps);
|
updateEffectImpactHints(totals, dps);
|
||||||
|
|
||||||
renderEffectTotals(totals, dps, survivability);
|
renderEffectTotals(totals, dps, survivability);
|
||||||
renderUpgrades(profile, totals, dps);
|
|
||||||
renderSacrifices(pieceScores);
|
renderSacrifices(pieceScores);
|
||||||
saveDraft();
|
saveDraft();
|
||||||
}
|
}
|
||||||
@@ -263,7 +262,7 @@ function calculateDamageDetails(totals, combatStyle = "melee") {
|
|||||||
const globalDamage = 1 + effects.damage / 100;
|
const globalDamage = 1 + effects.damage / 100;
|
||||||
const styleDamage = 1 + getStyleDamageBonus(effects, combatStyle) / 100;
|
const styleDamage = 1 + getStyleDamageBonus(effects, combatStyle) / 100;
|
||||||
const singleHit = baseDamage * globalDamage * styleDamage;
|
const singleHit = baseDamage * globalDamage * styleDamage;
|
||||||
const attackSpeed = 1 + effects.attackSpeed / 100;
|
const attacksPerSecond = (1 + effects.attackSpeed / 100) / 2;
|
||||||
const doubleHit = 1 + effects.doubleChance / 100;
|
const doubleHit = 1 + effects.doubleChance / 100;
|
||||||
const critChance = effects.critChance / 100;
|
const critChance = effects.critChance / 100;
|
||||||
const critDamage = effects.critDamage / 100;
|
const critDamage = effects.critDamage / 100;
|
||||||
@@ -273,7 +272,7 @@ function calculateDamageDetails(totals, combatStyle = "melee") {
|
|||||||
return {
|
return {
|
||||||
singleHit,
|
singleHit,
|
||||||
critHit,
|
critHit,
|
||||||
dps: singleHit * attackSpeed * doubleHit * crit * cooldown,
|
dps: singleHit * attacksPerSecond * doubleHit * crit * cooldown,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,45 +403,11 @@ function updateEffectImpactHints(totals, currentDps) {
|
|||||||
const current = clampEffectValue(effect.type, Number(effect.value));
|
const current = clampEffectValue(effect.type, Number(effect.value));
|
||||||
const withoutThis = calculateDps(cloneTotalsWithEffectDelta(totals, effect.type, -current), state.combatStyle);
|
const withoutThis = calculateDps(cloneTotalsWithEffectDelta(totals, effect.type, -current), state.combatStyle);
|
||||||
const currentGain = percentChange(withoutThis, currentDps);
|
const currentGain = percentChange(withoutThis, currentDps);
|
||||||
const remaining = Math.max(0, def.cap - current);
|
hint.textContent = `Actual: ${fmt(currentGain)}% DPS`;
|
||||||
const plusOne = Math.min(1, remaining);
|
|
||||||
const plusOneGain = plusOne > 0
|
|
||||||
? percentChange(currentDps, calculateDps(cloneTotalsWithEffectDelta(totals, effect.type, plusOne), state.combatStyle))
|
|
||||||
: 0;
|
|
||||||
hint.textContent = `Cap: ${def.cap}%. Actual now: ${fmt(currentGain)}% DPS. Next +${fmt(plusOne)}%: ${fmt(plusOneGain)}% DPS.`;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderUpgrades(profile, totals, currentDps) {
|
|
||||||
const upgrades = [];
|
|
||||||
for (const piece of state.pieces) {
|
|
||||||
for (const effect of piece.effects) {
|
|
||||||
if (!effect.type) continue;
|
|
||||||
const def = effectById(effect.type);
|
|
||||||
const current = clampEffectValue(effect.type, Number(effect.value));
|
|
||||||
const remaining = def.cap - current;
|
|
||||||
if (remaining <= 0) continue;
|
|
||||||
const plusOne = Math.min(1, remaining);
|
|
||||||
const onePointDps = calculateDps(cloneTotalsWithEffectDelta(totals, effect.type, plusOne), state.combatStyle);
|
|
||||||
const capDps = calculateDps(cloneTotalsWithEffectDelta(totals, effect.type, remaining), state.combatStyle);
|
|
||||||
const onePointGain = percentChange(currentDps, onePointDps);
|
|
||||||
const capGain = percentChange(currentDps, capDps);
|
|
||||||
const fallback = profile.weights[effect.type] * 0.001;
|
|
||||||
const efficiency = capGain || onePointGain || fallback;
|
|
||||||
upgrades.push({ piece, def, current, remaining, efficiency, onePointGain, capGain });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
upgrades.sort((a, b) => b.efficiency - a.efficiency);
|
|
||||||
const count = state.recommendationCount;
|
|
||||||
$("#upgradeList").innerHTML = upgrades.slice(0, count).map((item) => `
|
|
||||||
<li><strong>${item.def.label}</strong> on ${escapeHtml(item.piece.name || item.piece.title)}
|
|
||||||
<small>${fmt(item.current)}% now, ${fmt(item.remaining)}% room to cap. Next +1% gives ${fmt(item.onePointGain)}% DPS; to cap gives ${fmt(item.capGain)}% DPS.</small>
|
|
||||||
</li>
|
|
||||||
`).join("") || "<li>No upgradeable effects entered yet.</li>";
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderSacrifices(pieceScores) {
|
function renderSacrifices(pieceScores) {
|
||||||
const weakest = pieceScores
|
const weakest = pieceScores
|
||||||
.filter(({ piece }) => !piece.noEffects)
|
.filter(({ piece }) => !piece.noEffects)
|
||||||
|
|||||||
@@ -78,10 +78,6 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="results">
|
<section class="results">
|
||||||
<article class="card">
|
|
||||||
<h2>Best upgrades</h2>
|
|
||||||
<ol id="upgradeList" class="recommendation-list"></ol>
|
|
||||||
</article>
|
|
||||||
<article class="card">
|
<article class="card">
|
||||||
<h2>Best sacrifice candidates</h2>
|
<h2>Best sacrifice candidates</h2>
|
||||||
<ol id="sacrificeList" class="recommendation-list"></ol>
|
<ol id="sacrificeList" class="recommendation-list"></ol>
|
||||||
|
|||||||
+1
-1
@@ -175,7 +175,7 @@ h3 { margin-bottom: 0; }
|
|||||||
.metric strong { font-size: 1.8rem; }
|
.metric strong { font-size: 1.8rem; }
|
||||||
.metric small { display: block; margin-top: 0.4rem; color: var(--muted); line-height: 1.35; }
|
.metric small { display: block; margin-top: 0.4rem; color: var(--muted); line-height: 1.35; }
|
||||||
|
|
||||||
.results { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-bottom: 1rem; }
|
.results { grid-template-columns: 1fr; margin-bottom: 1rem; }
|
||||||
.recommendation-list { display: grid; gap: 0.75rem; padding-left: 1.5rem; }
|
.recommendation-list { display: grid; gap: 0.75rem; padding-left: 1.5rem; }
|
||||||
.recommendation-list li { padding-left: 0.25rem; }
|
.recommendation-list li { padding-left: 0.25rem; }
|
||||||
.recommendation-list strong { color: var(--accent); }
|
.recommendation-list strong { color: var(--accent); }
|
||||||
|
|||||||
Reference in New Issue
Block a user