From 84959d2e8b210c9f6438e07331c791b8bcb3e36c Mon Sep 17 00:00:00 2001 From: Jan Bader Date: Wed, 17 Jun 2026 22:34:43 +0200 Subject: [PATCH] Introduce constants for repeated ids --- app.js | 53 +++++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/app.js b/app.js index 5952c8c..36ca223 100644 --- a/app.js +++ b/app.js @@ -18,14 +18,19 @@ const EFFECTS = [ { id: "health", label: "Health", cap: 15, group: "defense" }, ]; +const PROFILE_IDS = Object.freeze({ LIFESTEAL: "lifesteal", REGEN: "regen" }); +const COMBAT_STYLES = Object.freeze({ MELEE: "melee", RANGED: "ranged", SKILL: "skill" }); +const PIECE_TYPES = Object.freeze({ ITEM: "item", MOUNT: "mount", PET: "pet", SKILL: "skill" }); +const REPLACEMENT_TARGETS = Object.freeze({ MOUNT: "mount", PET: "pet" }); + const PROFILES = { - lifesteal: { + [PROFILE_IDS.LIFESTEAL]: { label: "Lifesteal (DPS + Lifesteal)", weights: { critChance: 10, critDamage: 6, blockChance: 2, healthRegen: 2, lifesteal: 10, doubleChance: 10, damage: 10, meleeDamage: 6, rangedDamage: 6, attackSpeed: 9, skillDamage: 7, skillCooldown: 7, health: 3 }, baseDamage: 1.25, baseHealth: 0.45, }, - regen: { + [PROFILE_IDS.REGEN]: { label: "Regen (Health + Regen)", weights: { critChance: 4, critDamage: 2, blockChance: 7, healthRegen: 12, lifesteal: 5, doubleChance: 4, damage: 4, meleeDamage: 2, rangedDamage: 2, attackSpeed: 3, skillDamage: 3, skillCooldown: 3, health: 12 }, baseDamage: 0.45, @@ -47,10 +52,10 @@ const FIXED_ITEMS = [ { name: "Belt", baseType: "health", baseValue: 1150000 }, ]; const slots = [ - ...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}` })), - { type: "skill", title: "Skill", noEffects: true }, + ...FIXED_ITEMS.map((item, i) => ({ type: PIECE_TYPES.ITEM, title: `Item ${i + 1}`, fixed: item })), + { type: PIECE_TYPES.MOUNT, title: "Mount" }, + ...Array.from({ length: 3 }, (_, i) => ({ type: PIECE_TYPES.PET, title: `Pet ${i + 1}` })), + { type: PIECE_TYPES.SKILL, title: "Skill", noEffects: true }, ]; const $ = (selector) => document.querySelector(selector); @@ -65,8 +70,8 @@ let state = loadDraft() || createDefaultState(); function createDefaultState() { return { - profile: "lifesteal", - combatStyle: "melee", + profile: PROFILE_IDS.LIFESTEAL, + combatStyle: COMBAT_STYLES.MELEE, recommendationCount: 8, pieces: slots.map((slot, index) => createDefaultPiece(slot, index)), newItem: createDefaultNewItem(), @@ -101,7 +106,7 @@ function createDefaultPiece(slot, index) { function enforceFixedPieceFields(piece, index) { const fixed = FIXED_ITEMS[index]; - if (!fixed || piece.type !== "item") return piece; + if (!fixed || piece.type !== PIECE_TYPES.ITEM) return piece; const baseValue = Number(piece.baseValue || (fixed.baseType === "damage" ? piece.baseDamage : piece.baseHealth) || fixed.baseValue); return { ...piece, @@ -121,7 +126,7 @@ function normalizeState(candidate) { ...fallback, ...candidate, profile, - combatStyle: ["melee", "ranged", "skill"].includes(candidate?.combatStyle) ? candidate.combatStyle : "melee", + combatStyle: Object.values(COMBAT_STYLES).includes(candidate?.combatStyle) ? candidate.combatStyle : COMBAT_STYLES.MELEE, recommendationCount: Math.min(20, Math.max(3, Number(candidate?.recommendationCount || 8))), newItem: normalizeNewItem(candidate?.newItem), pieces: fallback.pieces.map((piece, index) => normalizePiece(piece, candidate?.pieces?.[index], index)), @@ -167,9 +172,9 @@ function normalizeEffects(candidateEffects) { function normalizeProfile(profile) { if (PROFILES[profile]) return profile; - if (["damage", "skill", "balanced"].includes(profile)) return "lifesteal"; - if (profile === "survival") return "regen"; - return "lifesteal"; + if (["damage", "skill", "balanced"].includes(profile)) return PROFILE_IDS.LIFESTEAL; + if (profile === "survival") return PROFILE_IDS.REGEN; + return PROFILE_IDS.LIFESTEAL; } function clampEffectValue(type, value) { @@ -193,8 +198,8 @@ function render() { function getReplacementOptions() { return [ ...FIXED_ITEMS.map((item, index) => ({ value: `item-${index}`, label: item.name })), - { value: "mount", label: "Mount" }, - { value: "pet", label: "Pet (worst current pet)" }, + { value: REPLACEMENT_TARGETS.MOUNT, label: "Mount" }, + { value: REPLACEMENT_TARGETS.PET, label: "Pet (worst current pet)" }, ]; } @@ -430,10 +435,10 @@ function getReplacementTargetPiece(target, pieceScores) { const pinned = state.pieces.find((piece) => piece.id === state.newItem.targetPieceId); if (pinned) return pinned; } - if (target === "mount") return state.pieces.find((piece) => piece.type === "mount") || null; - if (target === "pet") { + if (target === REPLACEMENT_TARGETS.MOUNT) return state.pieces.find((piece) => piece.type === PIECE_TYPES.MOUNT) || null; + if (target === REPLACEMENT_TARGETS.PET) { return [...pieceScores] - .filter(({ piece }) => piece.type === "pet") + .filter(({ piece }) => piece.type === PIECE_TYPES.PET) .sort((a, b) => a.score - b.score)[0]?.piece || null; } const fixed = /^item-(\d+)$/.exec(target || ""); @@ -446,7 +451,7 @@ function createNewItemCandidate(target) { const fixedBaseHealth = fixed?.baseType === "health" ? Number(state.newItem.baseHealth || fixed.baseValue) : 0; return { id: "new-item", - type: target === "mount" ? "mount" : target === "pet" ? "pet" : "item", + type: target === REPLACEMENT_TARGETS.MOUNT ? PIECE_TYPES.MOUNT : target === REPLACEMENT_TARGETS.PET ? PIECE_TYPES.PET : PIECE_TYPES.ITEM, name: "New item", baseDamage: fixed ? fixedBaseDamage : Number(state.newItem.baseDamage || 0), baseHealth: fixed ? fixedBaseHealth : Number(state.newItem.baseHealth || 0), @@ -524,11 +529,11 @@ function deltaClass(value) { return "delta-neutral"; } -function calculateDps(totals, combatStyle = "melee") { +function calculateDps(totals, combatStyle = COMBAT_STYLES.MELEE) { return calculateDamageDetails(totals, combatStyle).dps; } -function calculateDamageDetails(totals, combatStyle = "melee") { +function calculateDamageDetails(totals, combatStyle = COMBAT_STYLES.MELEE) { const effects = totals.effects; const baseDamage = Math.max(0, totals.baseDamage); const globalDamage = 1 + effects.damage / 100; @@ -540,7 +545,7 @@ function calculateDamageDetails(totals, combatStyle = "melee") { const critDamage = effects.critDamage / 100; const crit = 1 + critChance * critDamage; const critHit = singleHit * (1 + critDamage); - const cooldown = combatStyle === "skill" ? 1 / Math.max(0.05, 1 - effects.skillCooldown / 100) : 1; + const cooldown = combatStyle === COMBAT_STYLES.SKILL ? 1 / Math.max(0.05, 1 - effects.skillCooldown / 100) : 1; return { singleHit, critHit, @@ -570,8 +575,8 @@ function calculateRegenDetails(totals, dps) { } function getStyleDamageBonus(effects, combatStyle) { - if (combatStyle === "ranged") return effects.rangedDamage; - if (combatStyle === "skill") return effects.skillDamage; + if (combatStyle === COMBAT_STYLES.RANGED) return effects.rangedDamage; + if (combatStyle === COMBAT_STYLES.SKILL) return effects.skillDamage; return effects.meleeDamage; }