|
|
|
@@ -71,6 +71,7 @@ function createDefaultNewItem() {
|
|
|
|
|
baseDamage: 0,
|
|
|
|
|
baseHealth: 0,
|
|
|
|
|
effects: [{ type: "", value: 1 }, { type: "", value: 1 }],
|
|
|
|
|
lastSwap: null,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -93,13 +94,14 @@ function createDefaultPiece(slot, index) {
|
|
|
|
|
function enforceFixedPieceFields(piece, index) {
|
|
|
|
|
const fixed = FIXED_ITEMS[index];
|
|
|
|
|
if (!fixed || piece.type !== "item") return piece;
|
|
|
|
|
const baseValue = Number(piece.baseValue || (fixed.baseType === "damage" ? piece.baseDamage : piece.baseHealth) || fixed.baseValue);
|
|
|
|
|
return {
|
|
|
|
|
...piece,
|
|
|
|
|
name: fixed.name,
|
|
|
|
|
baseType: fixed.baseType,
|
|
|
|
|
baseValue: fixed.baseValue,
|
|
|
|
|
baseDamage: fixed.baseType === "damage" ? fixed.baseValue : 0,
|
|
|
|
|
baseHealth: fixed.baseType === "health" ? fixed.baseValue : 0,
|
|
|
|
|
baseValue,
|
|
|
|
|
baseDamage: fixed.baseType === "damage" ? baseValue : 0,
|
|
|
|
|
baseHealth: fixed.baseType === "health" ? baseValue : 0,
|
|
|
|
|
fixedBase: true,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
@@ -147,6 +149,7 @@ function normalizeNewItem(candidate) {
|
|
|
|
|
baseDamage: Math.max(0, Number(candidate?.baseDamage || 0)),
|
|
|
|
|
baseHealth: Math.max(0, Number(candidate?.baseHealth || 0)),
|
|
|
|
|
effects,
|
|
|
|
|
lastSwap: candidate?.lastSwap || null,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -296,6 +299,7 @@ function renderNewItemComparison() {
|
|
|
|
|
</label>
|
|
|
|
|
<label>Percent
|
|
|
|
|
<input data-new-item-effect-value="${effectIndex}" type="number" min="1" step="0.1" value="${effect.value}" />
|
|
|
|
|
<span class="cap-hint"></span>
|
|
|
|
|
</label>`;
|
|
|
|
|
row.querySelector("select").value = effect.type;
|
|
|
|
|
effects.appendChild(row);
|
|
|
|
@@ -343,6 +347,29 @@ function renderReplacementComparison(totals, currentDps, currentRegen, pieceScor
|
|
|
|
|
$("#replacementDpsDelta").className = deltaClass(dpsDelta);
|
|
|
|
|
$("#replacementRegenDelta").textContent = formatDelta(regenDelta, "%");
|
|
|
|
|
$("#replacementRegenDelta").className = deltaClass(regenDelta);
|
|
|
|
|
updateNewItemImpactHints(replacementTotals, replacementDps);
|
|
|
|
|
updateSwapControls();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateNewItemImpactHints(replacementTotals, replacementDps) {
|
|
|
|
|
document.querySelectorAll("#newItemEffects .effect-row").forEach((row, effectIndex) => {
|
|
|
|
|
const effect = state.newItem.effects[effectIndex];
|
|
|
|
|
const hint = row.querySelector(".cap-hint");
|
|
|
|
|
if (!effect?.type) {
|
|
|
|
|
hint.textContent = "Ignored";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const current = clampEffectValue(effect.type, Number(effect.value));
|
|
|
|
|
const withoutThis = calculateDps(cloneTotalsWithEffectDelta(replacementTotals, effect.type, -current), state.combatStyle);
|
|
|
|
|
const currentGain = percentChange(withoutThis, replacementDps);
|
|
|
|
|
hint.textContent = `Actual: ${fmt(currentGain)}% DPS`;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateSwapControls() {
|
|
|
|
|
const hasSwap = Boolean(state.newItem.lastSwap);
|
|
|
|
|
$("#swapBackNewItem").disabled = !hasSwap;
|
|
|
|
|
$("#swapStatus").textContent = hasSwap ? `Can swap back ${state.newItem.lastSwap.piece?.name || "previous item"}.` : "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function calculateTotals() {
|
|
|
|
@@ -404,6 +431,68 @@ function createNewItemCandidate(target) {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createPieceFromCandidate(existingPiece, candidate) {
|
|
|
|
|
const baseDamage = Number(candidate.baseDamage || 0);
|
|
|
|
|
const baseHealth = Number(candidate.baseHealth || 0);
|
|
|
|
|
return enforceFixedPieceFields({
|
|
|
|
|
...existingPiece,
|
|
|
|
|
baseDamage,
|
|
|
|
|
baseHealth,
|
|
|
|
|
baseType: existingPiece.fixedBase ? existingPiece.baseType : baseHealth > baseDamage ? "health" : "damage",
|
|
|
|
|
baseValue: existingPiece.fixedBase
|
|
|
|
|
? (existingPiece.baseType === "health" ? baseHealth : baseDamage)
|
|
|
|
|
: Math.max(baseDamage, baseHealth),
|
|
|
|
|
effects: candidate.effects.map((effect) => ({ ...effect })),
|
|
|
|
|
}, state.pieces.indexOf(existingPiece));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function clonePiece(piece) {
|
|
|
|
|
return {
|
|
|
|
|
...piece,
|
|
|
|
|
effects: (piece.effects || []).map((effect) => ({ ...effect })),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function cloneNewItem(newItem) {
|
|
|
|
|
return {
|
|
|
|
|
...newItem,
|
|
|
|
|
effects: newItem.effects.map((effect) => ({ ...effect })),
|
|
|
|
|
lastSwap: null,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function swapInNewItem() {
|
|
|
|
|
const profile = PROFILES[state.profile];
|
|
|
|
|
const totals = calculateTotals();
|
|
|
|
|
const pieceScores = state.pieces.map((piece) => scorePiece(piece, profile, totals));
|
|
|
|
|
const targetPiece = getReplacementTargetPiece(state.newItem.target, pieceScores);
|
|
|
|
|
if (!targetPiece) return;
|
|
|
|
|
const index = state.pieces.indexOf(targetPiece);
|
|
|
|
|
const candidate = createNewItemCandidate(state.newItem.target);
|
|
|
|
|
const previousPiece = clonePiece(targetPiece);
|
|
|
|
|
const previousNewItem = cloneNewItem(state.newItem);
|
|
|
|
|
state.pieces[index] = createPieceFromCandidate(targetPiece, candidate);
|
|
|
|
|
state.newItem = {
|
|
|
|
|
...previousNewItem,
|
|
|
|
|
lastSwap: {
|
|
|
|
|
index,
|
|
|
|
|
target: previousNewItem.target,
|
|
|
|
|
piece: previousPiece,
|
|
|
|
|
newItem: previousNewItem,
|
|
|
|
|
swappedAt: new Date().toISOString(),
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
render();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function swapBackNewItem() {
|
|
|
|
|
const lastSwap = state.newItem.lastSwap;
|
|
|
|
|
if (!lastSwap || !state.pieces[lastSwap.index]) return;
|
|
|
|
|
state.pieces[lastSwap.index] = clonePiece(lastSwap.piece);
|
|
|
|
|
state.newItem = cloneNewItem(lastSwap.newItem);
|
|
|
|
|
render();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatDelta(value, suffix = "") {
|
|
|
|
|
const sign = value > 0 ? "+" : "";
|
|
|
|
|
return `${sign}${fmt(value)}${suffix}`;
|
|
|
|
@@ -747,6 +836,8 @@ function bindEvents() {
|
|
|
|
|
renderNewItemComparison();
|
|
|
|
|
updateAnalysis();
|
|
|
|
|
});
|
|
|
|
|
$("#swapNewItem").addEventListener("click", swapInNewItem);
|
|
|
|
|
$("#swapBackNewItem").addEventListener("click", swapBackNewItem);
|
|
|
|
|
$(".new-item-card").addEventListener("input", handleNewItemInput);
|
|
|
|
|
$(".new-item-card").addEventListener("change", handleNewItemInput);
|
|
|
|
|
}
|
|
|
|
|