Compact gear cards and add dual pet stats

This commit is contained in:
Jan Bader
2026-06-16 15:19:30 +02:00
parent befcba3442
commit 01fbc347d3
3 changed files with 51 additions and 35 deletions
+42 -17
View File
@@ -83,6 +83,8 @@ function createDefaultPiece(slot, index) {
name: slot.fixed?.name || slot.title,
baseType: slot.fixed?.baseType || (index % 3 === 1 ? "health" : "damage"),
baseValue: slot.fixed?.baseValue || 0,
baseDamage: slot.fixed?.baseType === "damage" ? slot.fixed.baseValue : 0,
baseHealth: slot.fixed?.baseType === "health" ? slot.fixed.baseValue : 0,
fixedBase: Boolean(slot.fixed),
effects: [{ type: "", value: 1 }, { type: "", value: 1 }],
};
@@ -96,6 +98,8 @@ function enforceFixedPieceFields(piece, index) {
name: fixed.name,
baseType: fixed.baseType,
baseValue: fixed.baseValue,
baseDamage: fixed.baseType === "damage" ? fixed.baseValue : 0,
baseHealth: fixed.baseType === "health" ? fixed.baseValue : 0,
fixedBase: true,
};
}
@@ -111,6 +115,8 @@ function normalizeState(candidate) {
pieces: fallback.pieces.map((piece, index) => enforceFixedPieceFields({
...piece,
...(candidate?.pieces?.[index] || {}),
baseDamage: Number(candidate?.pieces?.[index]?.baseDamage ?? (candidate?.pieces?.[index]?.baseType === "damage" ? candidate?.pieces?.[index]?.baseValue : piece.baseDamage) ?? 0),
baseHealth: Number(candidate?.pieces?.[index]?.baseHealth ?? (candidate?.pieces?.[index]?.baseType === "health" ? candidate?.pieces?.[index]?.baseValue : piece.baseHealth) ?? 0),
effects: [0, 1].map((effectIndex) => ({
type: candidate?.pieces?.[index]?.effects?.[effectIndex]?.type || "",
value: clampEffectValue(
@@ -150,19 +156,34 @@ function renderGear() {
state.pieces.forEach((piece, pieceIndex) => {
const node = template.content.firstElementChild.cloneNode(true);
node.dataset.index = pieceIndex;
node.querySelector("h3").textContent = piece.fixedBase ? `${piece.title} · ${piece.name}` : piece.title;
node.querySelector("h3").textContent = piece.name;
node.querySelector('[data-field="name"]').value = piece.name;
node.querySelector('[data-field="baseType"]').value = piece.baseType;
node.querySelector('[data-field="baseValue"]').value = piece.baseValue;
const baseFields = node.querySelector(".inline-fields");
if (piece.fixedBase) {
node.classList.add("fixed-piece");
node.querySelector('[data-field="name"]').readOnly = true;
node.querySelector('[data-field="baseType"]').disabled = true;
node.querySelector('[data-field="baseValue"]').readOnly = true;
const badge = document.createElement("span");
badge.className = "fixed-badge";
badge.textContent = "Fixed base";
node.querySelector(".gear-title-row").appendChild(badge);
baseFields.innerHTML = `
<label>Base stat
<select data-field="baseType" disabled>
<option value="damage">Damage</option>
<option value="health">Health</option>
</select>
</label>
<label>Value
<input data-field="baseValue" type="number" min="0" step="1" readonly />
</label>`;
baseFields.querySelector('[data-field="baseType"]').value = piece.baseType;
baseFields.querySelector('[data-field="baseValue"]').value = piece.baseValue;
} else {
baseFields.innerHTML = `
<label>Damage base
<input data-field="baseDamage" type="number" min="0" step="1" />
</label>
<label>Health base
<input data-field="baseHealth" type="number" min="0" step="1" />
</label>`;
baseFields.querySelector('[data-field="baseDamage"]').value = piece.baseDamage || 0;
baseFields.querySelector('[data-field="baseHealth"]').value = piece.baseHealth || 0;
}
const effects = node.querySelector(".effects");
@@ -216,9 +237,8 @@ function updateAnalysis() {
function calculateTotals() {
const totals = { baseDamage: 0, baseHealth: 0, effects: Object.fromEntries(EFFECTS.map((effect) => [effect.id, 0])) };
for (const piece of state.pieces) {
const baseValue = Number(piece.baseValue || 0);
if (piece.baseType === "health") totals.baseHealth += baseValue;
else totals.baseDamage += baseValue;
totals.baseDamage += Number(piece.baseDamage || 0);
totals.baseHealth += Number(piece.baseHealth || 0);
for (const effect of piece.effects) {
if (effect.type) totals.effects[effect.type] += clampEffectValue(effect.type, Number(effect.value));
}
@@ -290,9 +310,10 @@ function calculateGroupScore(totals, profile, groups, baseKind) {
}
function scorePiece(piece, profile, totals) {
const baseWeight = piece.baseType === "health" ? profile.baseHealth : profile.baseDamage;
const baseAverage = Math.max(1, piece.baseType === "health" ? totals.baseHealth / 6 : totals.baseDamage / 6);
let score = (Number(piece.baseValue || 0) / baseAverage) * 35 * baseWeight;
const baseAverageDamage = Math.max(1, totals.baseDamage / 6);
const baseAverageHealth = Math.max(1, totals.baseHealth / 6);
let score = (Number(piece.baseDamage || 0) / baseAverageDamage) * 35 * profile.baseDamage
+ (Number(piece.baseHealth || 0) / baseAverageHealth) * 35 * profile.baseHealth;
const reasons = [];
for (const effect of piece.effects) {
@@ -307,7 +328,7 @@ function scorePiece(piece, profile, totals) {
if (profile.weights[effect.type] <= 3) reasons.push(`${def.label} is low priority for this profile`);
}
if (!Number(piece.baseValue || 0)) reasons.push("no base stat entered");
if (!Number(piece.baseDamage || 0) && !Number(piece.baseHealth || 0)) reasons.push("no base stat entered");
return { piece, score, reasons: [...new Set(reasons)] };
}
@@ -555,7 +576,11 @@ function handleGearInput(event) {
return;
}
const field = event.target.dataset.field;
piece[field] = field === "baseValue" ? Math.max(0, Number(event.target.value || 0)) : event.target.value;
piece[field] = ["baseValue", "baseDamage", "baseHealth"].includes(field) ? Math.max(0, Number(event.target.value || 0)) : event.target.value;
if (field === "baseDamage" || field === "baseHealth") {
piece.baseType = Number(piece.baseHealth || 0) > Number(piece.baseDamage || 0) ? "health" : "damage";
piece.baseValue = Math.max(Number(piece.baseDamage || 0), Number(piece.baseHealth || 0));
}
}
if (event.target.dataset.effectType !== undefined) {
const index = Number(event.target.dataset.effectType);