Add reversible new item swap
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user