Fix core item base stats
This commit is contained in:
@@ -16,6 +16,23 @@ Each equipped piece has:
|
||||
|
||||
Effects start at `1%` and can be upgraded up to their cap.
|
||||
|
||||
## Fixed item base stats
|
||||
|
||||
The 8 core item slots are fixed and cannot be renamed or have their base stat edited in the app:
|
||||
|
||||
| Slot | Item | Base stat |
|
||||
| --- | --- | ---: |
|
||||
| Item 1 | Helmet | 1,930,000 health |
|
||||
| Item 2 | Armor | 1,890,000 health |
|
||||
| Item 3 | Gloves | 237,000 damage |
|
||||
| Item 4 | Collar | 237,000 damage |
|
||||
| Item 5 | Ring | 244,000 damage |
|
||||
| Item 6 | Weapon | 398,000 damage |
|
||||
| Item 7 | Boots | 1,850,000 health |
|
||||
| Item 8 | Belt | 1,150,000 health |
|
||||
|
||||
Mount and pet base stats remain editable.
|
||||
|
||||
## Supported effects
|
||||
|
||||
| Effect | Maximum |
|
||||
|
||||
@@ -44,8 +44,18 @@ const PROFILES = {
|
||||
const STORAGE_KEY = "forgeMasterOptimizer.v1";
|
||||
const DRAFT_KEY = "forgeMasterOptimizer.draft.v1";
|
||||
const EXPORT_VERSION = 1;
|
||||
const FIXED_ITEMS = [
|
||||
{ name: "Helmet", baseType: "health", baseValue: 1930000 },
|
||||
{ name: "Armor", baseType: "health", baseValue: 1890000 },
|
||||
{ name: "Gloves", baseType: "damage", baseValue: 237000 },
|
||||
{ name: "Collar", baseType: "damage", baseValue: 237000 },
|
||||
{ name: "Ring", baseType: "damage", baseValue: 244000 },
|
||||
{ name: "Weapon", baseType: "damage", baseValue: 398000 },
|
||||
{ name: "Boots", baseType: "health", baseValue: 1850000 },
|
||||
{ name: "Belt", baseType: "health", baseValue: 1150000 },
|
||||
];
|
||||
const slots = [
|
||||
...Array.from({ length: 8 }, (_, i) => ({ type: "item", title: `Item ${i + 1}` })),
|
||||
...FIXED_ITEMS.map((item, i) => ({ type: "item", title: `Item ${i + 1}`, fixed: item })),
|
||||
{ type: "mount", title: "Mount" },
|
||||
...Array.from({ length: 3 }, (_, i) => ({ type: "pet", title: `Pet ${i + 1}` })),
|
||||
];
|
||||
@@ -61,15 +71,32 @@ function createDefaultState() {
|
||||
profile: "balanced",
|
||||
combatStyle: "melee",
|
||||
recommendationCount: 8,
|
||||
pieces: slots.map((slot, index) => ({
|
||||
pieces: slots.map((slot, index) => createDefaultPiece(slot, index)),
|
||||
};
|
||||
}
|
||||
|
||||
function createDefaultPiece(slot, index) {
|
||||
return {
|
||||
id: `${slot.type}-${index}`,
|
||||
type: slot.type,
|
||||
title: slot.title,
|
||||
name: slot.title,
|
||||
baseType: index % 3 === 1 ? "health" : "damage",
|
||||
baseValue: 0,
|
||||
name: slot.fixed?.name || slot.title,
|
||||
baseType: slot.fixed?.baseType || (index % 3 === 1 ? "health" : "damage"),
|
||||
baseValue: slot.fixed?.baseValue || 0,
|
||||
fixedBase: Boolean(slot.fixed),
|
||||
effects: [{ type: "", value: 1 }, { type: "", value: 1 }],
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
function enforceFixedPieceFields(piece, index) {
|
||||
const fixed = FIXED_ITEMS[index];
|
||||
if (!fixed || piece.type !== "item") return piece;
|
||||
return {
|
||||
...piece,
|
||||
name: fixed.name,
|
||||
baseType: fixed.baseType,
|
||||
baseValue: fixed.baseValue,
|
||||
fixedBase: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -81,7 +108,7 @@ function normalizeState(candidate) {
|
||||
profile: PROFILES[candidate?.profile] ? candidate.profile : "balanced",
|
||||
combatStyle: ["melee", "ranged", "skill"].includes(candidate?.combatStyle) ? candidate.combatStyle : "melee",
|
||||
recommendationCount: Math.min(20, Math.max(3, Number(candidate?.recommendationCount || 8))),
|
||||
pieces: fallback.pieces.map((piece, index) => ({
|
||||
pieces: fallback.pieces.map((piece, index) => enforceFixedPieceFields({
|
||||
...piece,
|
||||
...(candidate?.pieces?.[index] || {}),
|
||||
effects: [0, 1].map((effectIndex) => ({
|
||||
@@ -91,7 +118,7 @@ function normalizeState(candidate) {
|
||||
Number(candidate?.pieces?.[index]?.effects?.[effectIndex]?.value || 1),
|
||||
),
|
||||
})),
|
||||
})),
|
||||
}, index)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -123,10 +150,20 @@ function renderGear() {
|
||||
state.pieces.forEach((piece, pieceIndex) => {
|
||||
const node = template.content.firstElementChild.cloneNode(true);
|
||||
node.dataset.index = pieceIndex;
|
||||
node.querySelector("h3").textContent = piece.title;
|
||||
node.querySelector("h3").textContent = piece.fixedBase ? `${piece.title} · ${piece.name}` : piece.title;
|
||||
node.querySelector('[data-field="name"]').value = piece.name;
|
||||
node.querySelector('[data-field="baseType"]').value = piece.baseType;
|
||||
node.querySelector('[data-field="baseValue"]').value = piece.baseValue;
|
||||
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);
|
||||
}
|
||||
|
||||
const effects = node.querySelector(".effects");
|
||||
piece.effects.forEach((effect, effectIndex) => {
|
||||
@@ -513,6 +550,10 @@ function handleGearInput(event) {
|
||||
const piece = state.pieces[Number(card.dataset.index)];
|
||||
|
||||
if (event.target.dataset.field) {
|
||||
if (piece.fixedBase && ["name", "baseType", "baseValue"].includes(event.target.dataset.field)) {
|
||||
event.target.value = piece[event.target.dataset.field];
|
||||
return;
|
||||
}
|
||||
const field = event.target.dataset.field;
|
||||
piece[field] = field === "baseValue" ? Math.max(0, Number(event.target.value || 0)) : event.target.value;
|
||||
}
|
||||
|
||||
+15
@@ -49,6 +49,11 @@ button:hover { filter: brightness(1.1); }
|
||||
button.secondary { background: var(--panel-2); }
|
||||
button.danger { border-color: rgba(251, 113, 133, 0.5); background: rgba(251, 113, 133, 0.16); }
|
||||
textarea { width: 100%; min-height: 9rem; resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
|
||||
input[readonly], select:disabled {
|
||||
cursor: not-allowed;
|
||||
color: var(--muted);
|
||||
background: rgba(255, 255, 255, 0.035);
|
||||
}
|
||||
|
||||
label {
|
||||
display: grid;
|
||||
@@ -176,6 +181,16 @@ h3 { margin-bottom: 0; }
|
||||
.gear-card { display: grid; gap: 0.85rem; }
|
||||
.gear-title-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
|
||||
.piece-score { color: var(--accent); font-weight: 800; }
|
||||
.fixed-badge {
|
||||
border: 1px solid rgba(125, 211, 252, 0.35);
|
||||
border-radius: 999px;
|
||||
color: var(--accent);
|
||||
background: rgba(125, 211, 252, 0.08);
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 800;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.inline-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; }
|
||||
.effects { display: grid; gap: 0.75rem; }
|
||||
.effect-row { display: grid; grid-template-columns: 1fr 110px; gap: 0.75rem; align-items: end; }
|
||||
|
||||
Reference in New Issue
Block a user