From 6e13dd36c216e2c1cd3e92f643c2b7e2004d8a64 Mon Sep 17 00:00:00 2001 From: Stephanie Gredell Date: Mon, 1 Sep 2025 19:38:49 -0700 Subject: [PATCH] cleaning up excessive objects, adding more enemies, cleaning up enemies --- ...{merge_conflict_enemy.png => codegirl.png} | Bin src/data/enemies.js | 61 +- src/data/maps.js | 24 +- src/engine/battle.js | 7 - src/ui/render.js | 1142 ++++++++--------- 5 files changed, 597 insertions(+), 637 deletions(-) rename assets/avatars/{merge_conflict_enemy.png => codegirl.png} (100%) diff --git a/assets/avatars/merge_conflict_enemy.png b/assets/avatars/codegirl.png similarity index 100% rename from assets/avatars/merge_conflict_enemy.png rename to assets/avatars/codegirl.png diff --git a/src/data/enemies.js b/src/data/enemies.js index ea24af7..6baffb1 100644 --- a/src/data/enemies.js +++ b/src/data/enemies.js @@ -1,64 +1,49 @@ export const ENEMIES = { - chat_gremlin: { - id: "chat_gremlin", name: "Old Man Judo", maxHp: 40, + old_man_judo: { + id: "old_man_judo", name: "Old Man Judo", maxHp: 40, avatar: "assets/avatars/13.png", // Young person with cap background: "assets/backgrounds/terrace.png", ai: (turn) => turn % 3 === 0 ? { type: "block", value: 6 } : { type: "attack", value: turn % 2 === 0 ? 7 : 8 } }, - type_checker: { - id: "type_checker", name: "Type Checker", maxHp: 45, - avatar: "assets/avatars/type_checker.png", // Scholar/mage with glasses - background: "assets/backgrounds/castle.png", - ai: (turn) => (turn % 3 === 0) ? { type: "debuff", value: 1 } : { type: "attack", value: 8 }, - onDebuff: (ctx) => ctx.applyWeak(ctx.player, 1) - }, - js_blob: { - id: "js_blob", name: "JS Blob", maxHp: 60, - avatar: "assets/avatars/js_blob.png", // Mysterious hooded figure - background: "assets/backgrounds/dead forest.png", - ai: (turn) => (turn % 2 === 0) ? { type: "attack", value: 12 } : { type: "block", value: 6 }, - onBlock: (ctx, val) => ctx.enemy.block += val - }, - infinite_loop: { - id: "infinite_loop", name: "Beastco", maxHp: 35, + beastco: { + id: "beastco", name: "Beastco", maxHp: 35, avatar: "assets/avatars/2.png", // Dizzy/confused character background: "assets/backgrounds/throne room.png", ai: (turn) => ({ type: "attack", value: 4 }), }, - merge_conflict_enemy: { - id: "merge_conflict_enemy", name: "Codegirl", maxHp: 50, - avatar: "assets/avatars/merge_conflict_enemy.png", // Warrior with conflicted expression + codegirl: { + id: "codegirl", name: "Codegirl", maxHp: 50, + avatar: "assets/avatars/codegirl.png", // Warrior with conflicted expression background: "assets/backgrounds/terrace.png", // Repeat background ai: (turn) => turn <= 4 ? { type: "attack", value: 8 } : { type: "debuff", value: 1 }, onDebuff: (ctx) => { - ctx.enemy.hp = Math.min(ctx.enemy.maxHp, ctx.enemy.hp + 8); ctx.log("Codegirl resolves the merge conflict and heals 8 HP!"); } }, - bug_404: { - id: "bug_404", name: "404 Bug", maxHp: 45, - avatar: "assets/avatars/bug_404.png", // Elusive character - background: "assets/backgrounds/castle.png", // Repeat background - ai: (turn) => ({ type: "attack", value: 10 }), - - }, - elite_ts_demon: { - id: "elite_ts_demon", name: "Nightshadedude", maxHp: 85, + nightshadedude: { + id: "nightshadedude", name: "Nightshadedude", maxHp: 85, avatar: "assets/avatars/11.png", // Powerful demon/witch background: "assets/backgrounds/dead forest.png", // Repeat background ai: (turn) => turn % 3 === 1 ? { type: "debuff", value: 1 } : { type: "attack", value: 14 }, onDebuff: (ctx) => ctx.applyVulnerable(ctx.player, 1) }, - elite_refactor: { - id: "elite_refactor", name: "Refactor Dragon (Elite)", maxHp: 90, - avatar: "assets/avatars/elite_refactor.png", // Regal/noble character - background: "assets/backgrounds/throne room.png", // Repeat background - ai: (turn) => ({ type: "attack", value: 10 + Math.floor(turn * 1.5) }) + defyusall: { + id: "defyusall", name: "Defyusall", maxHp: 65, + avatar: "assets/avatars/bug_404.png", // Elusive character + background: "assets/backgrounds/castle.png", + ai: (turn) => turn % 3 === 0 ? { type: "block", value: 8 } : { type: "attack", value: 10 }, + }, + lithium: { + id: "lithium", name: "Lithium", maxHp: 55, + avatar: "assets/avatars/type_checker.png", // Scholar/mage with glasses + background: "assets/backgrounds/dead forest.png", + ai: (turn) => (turn % 2 === 0) ? { type: "debuff", value: 1 } : { type: "attack", value: 12 }, + onDebuff: (ctx) => ctx.applyWeak(ctx.player, 1) }, - boss_birthday_bug: { - id: "boss_birthday_bug", name: "Teej", maxHp: 120, + teej: { + id: "teej", name: "Teej", maxHp: 120, avatar: "assets/avatars/boss_birthday_bug.png", // Demanding/angry character background: "assets/backgrounds/throne room.png", // Repeat background - fitting for boss ai: (turn) => { diff --git a/src/data/maps.js b/src/data/maps.js index 27bed66..d125856 100644 --- a/src/data/maps.js +++ b/src/data/maps.js @@ -3,16 +3,20 @@ export const MAPS = { act1: { id: "act1", name: "Birthday Spire β€” Act I", nodes: [ - { id: "n1", kind: "start", next: ["n2", "n3"] }, - { id: "n2", kind: "battle", enemy: "chat_gremlin", next: ["n4"] }, - { id: "n3", kind: "event", next: ["n4"] }, - { id: "n4", kind: "battle", enemy: "infinite_loop", next: ["n5", "n6"] }, - { id: "n5", kind: "rest", next: ["n7"] }, - { id: "n6", kind: "shop", next: ["n7"] }, - { id: "n7", kind: "battle", enemy: "merge_conflict_enemy", next: ["n8"] }, - { id: "n8", kind: "elite", enemy: "elite_ts_demon", next: ["n9"] }, - { id: "n9", kind: "rest", next: ["n10"] }, - { id: "n10", kind: "boss", enemy: "boss_birthday_bug", next: [] }, + { id: "n1", kind: "start", next: ["n2", "n3"], x: 500, y: 760 }, + { id: "n2", kind: "battle", enemy: "old_man_judo", next: ["n4"], x: 350, y: 695 }, + { id: "n3", kind: "event", next: ["n4"], x: 650, y: 695 }, + { id: "n4", kind: "battle", enemy: "beastco", next: ["n5", "n6"], x: 500, y: 600 }, + { id: "n5", kind: "rest", next: ["n7"], x: 350, y: 525 }, + { id: "n6", kind: "shop", next: ["n7"], x: 650, y: 525 }, + { id: "n7", kind: "battle", enemy: "codegirl", next: ["n8"], x: 500, y: 460 }, + { id: "n8", kind: "battle", enemy: "defyusall", next: ["n9", "n10"], x: 500, y: 395 }, + { id: "n9", kind: "rest", next: ["n11"], x: 350, y: 330 }, + { id: "n10", kind: "shop", next: ["n11"], x: 650, y: 330 }, + { id: "n11", kind: "battle", enemy: "lithium", next: ["n12"], x: 500, y: 280 }, + { id: "n12", kind: "elite", enemy: "nightshadedude", next: ["n13"], x: 500, y: 205 }, + { id: "n13", kind: "rest", next: ["n14"], x: 500, y: 125 }, + { id: "n14", kind: "boss", enemy: "teej", next: [], x: 500, y: 40 }, ] } }; diff --git a/src/engine/battle.js b/src/engine/battle.js index b3f02fb..dd3c47f 100644 --- a/src/engine/battle.js +++ b/src/engine/battle.js @@ -125,13 +125,6 @@ export function enemyTurn(ctx) { } function applyDamage(ctx, target, raw, label) { - - if (target === ctx.enemy && ctx.enemy.id === "bug_404" && ctx.enemy.turn % 3 === 0) { - ctx.log(`${ctx.enemy.name} phases out and dodges your attack completely!`); - return; - } - - let dmg = raw; for (const r of ctx.relicStates) { if (r.hooks?.onDamageTaken && target === ctx.player) dmg = r.hooks.onDamageTaken(ctx, dmg); diff --git a/src/ui/render.js b/src/ui/render.js index 49b4b1d..58af47e 100644 --- a/src/ui/render.js +++ b/src/ui/render.js @@ -1,13 +1,13 @@ // Simple audio utility function playSound(soundFile) { - try { - const audio = new Audio(`assets/sounds/${soundFile}`); - audio.volume = 0.3; - audio.play().catch(e => {}); // Silently fail if no audio - } catch (e) { - // Silently fail if audio not available - } + try { + const audio = new Audio(`assets/sounds/${soundFile}`); + audio.volume = 0.3; + audio.play().catch(e => { }); // Silently fail if no audio + } catch (e) { + // Silently fail if audio not available + } } export function showDamageNumber(damage, target, isPlayer = false) { @@ -42,8 +42,8 @@ export function showDamageNumber(damage, target, isPlayer = false) { } export async function renderBattle(root) { - const app = root.app; - const p = root.player, e = root.enemy; + const app = root.app; + const p = root.player, e = root.enemy; const { ENEMIES } = await import("../data/enemies.js"); @@ -51,13 +51,13 @@ export async function renderBattle(root) { const backgroundImage = enemyData?.background || null; - const intentInfo = { + const intentInfo = { attack: { emoji: '', text: `Will attack for ${e.intent.value} damage`, color: 'danger' }, block: { emoji: '', text: `Will gain ${e.intent.value} block`, color: 'info' }, debuff: { emoji: '', text: 'Will apply a debuff', color: 'warning' } }[e.intent.type] || { emoji: '', text: 'Unknown intent', color: 'neutral' }; - app.innerHTML = ` + app.innerHTML = `
@@ -183,12 +183,12 @@ export async function renderBattle(root) {
- ${p.hand.length === 0 ? - '
🎴 No cards in hand - End turn to draw new cards
' : - p.hand.map((card, i) => { - const canPlay = p.energy >= card.cost; - const cardType = card.type === 'attack' ? 'attack' : card.type === 'skill' ? 'skill' : 'power'; - return ` + ${p.hand.length === 0 ? + '
🎴 No cards in hand - End turn to draw new cards
' : + p.hand.map((card, i) => { + const canPlay = p.energy >= card.cost; + const cardType = card.type === 'attack' ? 'attack' : card.type === 'skill' ? 'skill' : 'power'; + return `
@@ -209,8 +209,8 @@ export async function renderBattle(root) { ${!canPlay ? `
Need ${card.cost} energy
` : ''}
`; - }).join('') - } + }).join('') + }
@@ -235,96 +235,96 @@ export async function renderBattle(root) {
`; - app.querySelectorAll("[data-play]").forEach(btn => { - btn.addEventListener("mouseenter", () => { - if (btn.classList.contains('playable')) { - playSound('swipe.mp3'); - root.selectedCardIndex = null; - updateCardSelection(root); - } - }); - - btn.addEventListener("click", () => { - const index = parseInt(btn.dataset.play, 10); - const card = p.hand[index]; - if (p.energy >= card.cost) { - root.play(index); - // Clear selection when card is played via mouse - root.selectedCardIndex = null; - updateCardSelection(root); - } - }); + app.querySelectorAll("[data-play]").forEach(btn => { + btn.addEventListener("mouseenter", () => { + if (btn.classList.contains('playable')) { + playSound('swipe.mp3'); + root.selectedCardIndex = null; + updateCardSelection(root); + } }); - - const endTurnBtn = app.querySelector("[data-action='end']"); - if (endTurnBtn) { - endTurnBtn.addEventListener("click", () => { - - try { - root.end(); - } catch (error) { - console.error("Error ending turn:", error); - } - }); - } - - // Initialize card selection state if not exists - if (!root.selectedCardIndex) { + btn.addEventListener("click", () => { + const index = parseInt(btn.dataset.play, 10); + const card = p.hand[index]; + if (p.energy >= card.cost) { + root.play(index); + // Clear selection when card is played via mouse root.selectedCardIndex = null; + updateCardSelection(root); + } + }); + }); + + const endTurnBtn = app.querySelector("[data-action='end']"); + if (endTurnBtn) { + + endTurnBtn.addEventListener("click", () => { + + try { + root.end(); + } catch (error) { + console.error("Error ending turn:", error); + } + }); + } + + // Initialize card selection state if not exists + if (!root.selectedCardIndex) { + root.selectedCardIndex = null; + } + + window.onkeydown = (e) => { + if (e.key.toLowerCase() === "e") { + try { + root.end(); + } catch (error) { + console.error("Error ending turn via keyboard:", error); + } } - window.onkeydown = (e) => { - if (e.key.toLowerCase() === "e") { - try { - root.end(); - } catch (error) { - console.error("Error ending turn via keyboard:", error); - } - } - - const n = parseInt(e.key, 10); - if (n >= 1 && n <= p.hand.length) { - const cardIndex = n - 1; - const card = p.hand[cardIndex]; - - if (root.selectedCardIndex === cardIndex) { - // Second press of same key - play the card - if (p.energy >= card.cost) { - root.play(cardIndex); - root.selectedCardIndex = null; // Clear selection - updateCardSelection(root); - } - } else { - // First press or different key - select the card - root.selectedCardIndex = cardIndex; - updateCardSelection(root); - playSound('swipe.mp3'); // Play swipe sound on keyboard selection - } + const n = parseInt(e.key, 10); + if (n >= 1 && n <= p.hand.length) { + const cardIndex = n - 1; + const card = p.hand[cardIndex]; + + if (root.selectedCardIndex === cardIndex) { + // Second press of same key - play the card + if (p.energy >= card.cost) { + root.play(cardIndex); + root.selectedCardIndex = null; // Clear selection + updateCardSelection(root); } - }; + } else { + // First press or different key - select the card + root.selectedCardIndex = cardIndex; + updateCardSelection(root); + playSound('swipe.mp3'); // Play swipe sound on keyboard selection + } + } + }; // Auto-scroll fight log to bottom const logContent = document.getElementById('fight-log-content'); if (logContent) { logContent.scrollTop = logContent.scrollHeight; - } - - // Apply initial card selection visual state - updateCardSelection(root); + } + + // Apply initial card selection visual state + updateCardSelection(root); } export async function renderMap(root) { - const { CARDS } = await import("../data/cards.js"); - const { ENEMIES } = await import("../data/enemies.js"); - const m = root.map; - const currentId = root.nodeId; + const { CARDS } = await import("../data/cards.js"); + const { ENEMIES } = await import("../data/enemies.js"); + const m = root.map; + const currentId = root.nodeId; const currentNode = m.nodes.find(n => n.id === currentId); const nextIds = currentNode ? currentNode.next : []; - const getNodeEmoji = (kind) => { - const emojis = { + const getNodeEmoji = (kind) => { + const emojis = { start: 'Start', battle: 'Battle', elite: 'Battle', @@ -332,49 +332,49 @@ export async function renderMap(root) { rest: 'Rest', shop: 'Shop', event: 'Event' - }; - return emojis[kind] || '❓'; }; + return emojis[kind] || '❓'; + }; - const getNodeDescription = (node) => { + const getNodeDescription = (node) => { switch (node.kind) { - case 'start': + case 'start': return 'Starting Point\nBegin your journey up ThePrimeagen Spire'; - case 'battle': - const enemy = ENEMIES[node.enemy]; + case 'battle': + const enemy = ENEMIES[node.enemy]; return `Battle\nFight: ${enemy?.name || 'Unknown Enemy'}\nHP: ${enemy?.maxHp || '?'}`; - case 'elite': - const elite = ENEMIES[node.enemy]; + case 'elite': + const elite = ENEMIES[node.enemy]; return `Elite Battle\nFight: ${elite?.name || 'Unknown Elite'}\nHP: ${elite?.maxHp || '?'}\nTough enemy with better rewards`; - case 'boss': - const boss = ENEMIES[node.enemy]; + case 'boss': + const boss = ENEMIES[node.enemy]; return `Boss Battle\nFight: ${boss?.name || 'Unknown Boss'}\nHP: ${boss?.maxHp || '?'}\nFinal challenge of the act`; - case 'rest': + case 'rest': return 'Rest Site\nHeal up to 30% max HP\nor upgrade a card'; - case 'shop': + case 'shop': return 'Shop\nSpend your hard-earned gold'; - case 'event': + case 'event': return 'Random Event\nBirthday-themed encounter\nUnknown outcome\nPotential rewards or challenges'; - default: + default: return 'Unknown\nMysterious node'; - } - }; + } + }; - const getNodeTooltipData = (node) => { - const description = getNodeDescription(node); - let avatarPath = null; - - if (['battle', 'elite', 'boss'].includes(node.kind) && node.enemy) { - const enemy = ENEMIES[node.enemy]; - if (enemy?.avatar) { - avatarPath = enemy.avatar; - } - } - - return { description, avatarPath }; - }; + const getNodeTooltipData = (node) => { + const description = getNodeDescription(node); + let avatarPath = null; - root.app.innerHTML = ` + if (['battle', 'elite', 'boss'].includes(node.kind) && node.enemy) { + const enemy = ENEMIES[node.enemy]; + if (enemy?.avatar) { + avatarPath = enemy.avatar; + } + } + + return { description, avatarPath }; + }; + + root.app.innerHTML = `
@@ -588,17 +568,17 @@ May this birthday bring joy in each moment you’ve got.

${Object.entries( - root.player.deck.reduce((acc, cardId) => { - acc[cardId] = (acc[cardId] || 0) + 1; - return acc; - }, {}) + root.player.deck.reduce((acc, cardId) => { + acc[cardId] = (acc[cardId] || 0) + 1; + return acc; + }, {}) ).map(([cardId, count], index) => { - const card = CARDS[cardId]; - if (!card) return ''; - - const cardType = card.type === 'attack' ? 'attack' : card.type === 'skill' ? 'skill' : 'power'; - - return ` + const card = CARDS[cardId]; + if (!card) return ''; + + const cardType = card.type === 'attack' ? 'attack' : card.type === 'skill' ? 'skill' : 'power'; + + return `
@@ -613,7 +593,7 @@ May this birthday bring joy in each moment you’ve got.

`; - }).join('')} + }).join('')}
@@ -622,20 +602,20 @@ May this birthday bring joy in each moment you’ve got.

`; - root.app.querySelectorAll("[data-node]").forEach(el => { - if (!el.dataset.node) return; - el.addEventListener("click", () => root.go(el.dataset.node)); - }); + root.app.querySelectorAll("[data-node]").forEach(el => { + if (!el.dataset.node) return; + el.addEventListener("click", () => root.go(el.dataset.node)); + }); window.showTooltip = function (event) { - const tooltip = document.getElementById('custom-tooltip'); - const node = event.target.closest('.spire-node'); - const content = node.dataset.tooltip; - const avatarPath = node.dataset.avatar; - - let tooltipHTML = ''; - if (avatarPath) { - tooltipHTML = ` + const tooltip = document.getElementById('custom-tooltip'); + const node = event.target.closest('.spire-node'); + const content = node.dataset.tooltip; + const avatarPath = node.dataset.avatar; + + let tooltipHTML = ''; + if (avatarPath) { + tooltipHTML = `
Enemy Avatar

${content}
`; - } else { - tooltipHTML = content; - } - - tooltip.innerHTML = tooltipHTML; - tooltip.style.display = 'block'; - + } else { + tooltipHTML = content; + } - const rect = node.getBoundingClientRect(); - tooltip.style.left = (rect.right + 15) + 'px'; - tooltip.style.top = (rect.top + rect.height / 2 - tooltip.offsetHeight / 2) + 'px'; - + tooltip.innerHTML = tooltipHTML; + tooltip.style.display = 'block'; - const tooltipRect = tooltip.getBoundingClientRect(); - if (tooltipRect.right > window.innerWidth) { - tooltip.style.left = (rect.left - tooltip.offsetWidth - 15) + 'px'; - } - if (tooltipRect.top < 0) { - tooltip.style.top = '10px'; - } - if (tooltipRect.bottom > window.innerHeight) { - tooltip.style.top = (window.innerHeight - tooltip.offsetHeight - 10) + 'px'; - } - }; + + const rect = node.getBoundingClientRect(); + tooltip.style.left = (rect.right + 15) + 'px'; + tooltip.style.top = (rect.top + rect.height / 2 - tooltip.offsetHeight / 2) + 'px'; + + + const tooltipRect = tooltip.getBoundingClientRect(); + if (tooltipRect.right > window.innerWidth) { + tooltip.style.left = (rect.left - tooltip.offsetWidth - 15) + 'px'; + } + if (tooltipRect.top < 0) { + tooltip.style.top = '10px'; + } + if (tooltipRect.bottom > window.innerHeight) { + tooltip.style.top = (window.innerHeight - tooltip.offsetHeight - 10) + 'px'; + } + }; window.hideTooltip = function () { - const tooltip = document.getElementById('custom-tooltip'); - tooltip.style.display = 'none'; - }; + const tooltip = document.getElementById('custom-tooltip'); + tooltip.style.display = 'none'; + }; - const resetBtn = root.app.querySelector("[data-reset]"); - resetBtn.addEventListener("click", () => { - root.clearSave(); - root.reset(); - }); + const resetBtn = root.app.querySelector("[data-reset]"); + resetBtn.addEventListener("click", () => { + root.clearSave(); + root.reset(); + }); } export function renderReward(root, choices) { - root.app.innerHTML = ` + root.app.innerHTML = `

Choose a Card

@@ -720,17 +700,17 @@ export function renderReward(root, choices) {
`; - root.app.querySelectorAll("[data-pick]").forEach(btn => { - btn.addEventListener("click", () => { - const idx = parseInt(btn.dataset.pick, 10); - root.takeReward(idx); - }); + root.app.querySelectorAll("[data-pick]").forEach(btn => { + btn.addEventListener("click", () => { + const idx = parseInt(btn.dataset.pick, 10); + root.takeReward(idx); }); - root.app.querySelector("[data-skip]").addEventListener("click", () => root.skipReward()); + }); + root.app.querySelector("[data-skip]").addEventListener("click", () => root.skipReward()); } export function renderRest(root) { - root.app.innerHTML = ` + root.app.innerHTML = `

Rest and Recover

@@ -760,35 +740,35 @@ export function renderRest(root) {
`; - root.app.querySelector("[data-act='heal']").addEventListener("click", () => { - const heal = Math.floor(root.player.maxHp * 0.2); - root.player.hp = Math.min(root.player.maxHp, root.player.hp + heal); - root.log(`Rested: +${heal} HP`); - root.afterNode(); - }); - root.app.querySelector("[data-act='upgrade']").addEventListener("click", () => { - renderUpgrade(root); - }); + root.app.querySelector("[data-act='heal']").addEventListener("click", () => { + const heal = Math.floor(root.player.maxHp * 0.2); + root.player.hp = Math.min(root.player.maxHp, root.player.hp + heal); + root.log(`Rested: +${heal} HP`); + root.afterNode(); + }); + root.app.querySelector("[data-act='upgrade']").addEventListener("click", () => { + renderUpgrade(root); + }); } export function renderUpgrade(root) { - import("../data/cards.js").then(({ CARDS }) => { - const upgradableCards = root.player.deck - .map((cardId, index) => ({ cardId, index })) + import("../data/cards.js").then(({ CARDS }) => { + const upgradableCards = root.player.deck + .map((cardId, index) => ({ cardId, index })) .filter(({ cardId }) => { const card = CARDS[cardId]; return card?.upgrades && !cardId.endsWith('+'); }) - .slice(0, 3); // Show max 3 options - - if (upgradableCards.length === 0) { - root.log("No cards can be upgraded."); - root.afterNode(); - return; - } + .slice(0, 3); // Show max 3 options + + if (upgradableCards.length === 0) { + root.log("No cards can be upgraded."); + root.afterNode(); + return; + } - root.app.innerHTML = ` + root.app.innerHTML = `

⬆️ Upgrade a Card

@@ -797,14 +777,14 @@ export function renderUpgrade(root) {
${upgradableCards.map(({ cardId, index }) => { - const card = CARDS[cardId]; - const upgradedCard = CARDS[card.upgrades]; + const card = CARDS[cardId]; + const upgradedCard = CARDS[card.upgrades]; if (!upgradedCard) { return ''; // Skip if no upgrade found } - return ` + return `
@@ -860,7 +840,7 @@ export function renderUpgrade(root) {
`; - }).join("")} + }).join("")}
@@ -869,23 +849,23 @@ export function renderUpgrade(root) {
`; - root.app.querySelectorAll("[data-upgrade]").forEach(btn => { - btn.addEventListener("click", () => { - const deckIndex = parseInt(btn.dataset.upgrade, 10); - const oldCardId = root.player.deck[deckIndex]; - const newCardId = CARDS[oldCardId].upgrades; - root.player.deck[deckIndex] = newCardId; - root.log(`Upgraded ${CARDS[oldCardId].name} β†’ ${CARDS[newCardId].name}`); + root.app.querySelectorAll("[data-upgrade]").forEach(btn => { + btn.addEventListener("click", () => { + const deckIndex = parseInt(btn.dataset.upgrade, 10); + const oldCardId = root.player.deck[deckIndex]; + const newCardId = CARDS[oldCardId].upgrades; + root.player.deck[deckIndex] = newCardId; + root.log(`Upgraded ${CARDS[oldCardId].name} β†’ ${CARDS[newCardId].name}`); root.afterNode(); - }); - }); - root.app.querySelector("[data-skip]").addEventListener("click", () => root.afterNode()); + }); }); + root.app.querySelector("[data-skip]").addEventListener("click", () => root.afterNode()); + }); } export function renderShop(root) { - import("../data/cards.js").then(({ CARDS, CARD_POOL }) => { - import("../data/relics.js").then(({ RELICS, START_RELIC_CHOICES }) => { + import("../data/cards.js").then(({ CARDS, CARD_POOL }) => { + import("../data/relics.js").then(({ RELICS, START_RELIC_CHOICES }) => { const availableCards = CARD_POOL.filter(cardId => { @@ -898,9 +878,9 @@ export function renderShop(root) { const shopCards = shuffle(cardsToShow.slice()).slice(0, 3).map(id => CARDS[id]); const ownedRelicIds = root.relicStates.map(r => r.id); const availableRelics = START_RELIC_CHOICES.filter(id => !ownedRelicIds.includes(id)); - const shopRelic = availableRelics.length > 0 ? RELICS[availableRelics[0]] : null; - - root.app.innerHTML = ` + const shopRelic = availableRelics.length > 0 ? RELICS[availableRelics[0]] : null; + + root.app.innerHTML = `

Merchant's Shop

@@ -989,140 +969,140 @@ export function renderShop(root) {
`; - if (!root.player.gold) root.player.gold = 100; - - root.app.querySelectorAll("[data-buy-card]").forEach(btn => { - btn.addEventListener("click", () => { - const idx = parseInt(btn.dataset.buyCard, 10); - const card = shopCards[idx]; - if (root.player.gold >= 50) { - root.player.gold -= 50; - root.player.deck.push(card.id); - root.log(`Bought ${card.name} for 50 gold.`); - btn.disabled = true; - btn.textContent = "SOLD"; - - // Update gold display - const goldDisplay = root.app.querySelector('.gold-amount'); - if (goldDisplay) { - goldDisplay.textContent = root.player.gold; - } - - // Update affordability of remaining items - updateShopAffordability(root); - } else { - root.log("Not enough gold!"); - } - }); - }); + if (!root.player.gold) root.player.gold = 100; + root.app.querySelectorAll("[data-buy-card]").forEach(btn => { + btn.addEventListener("click", () => { + const idx = parseInt(btn.dataset.buyCard, 10); + const card = shopCards[idx]; + if (root.player.gold >= 50) { + root.player.gold -= 50; + root.player.deck.push(card.id); + root.log(`Bought ${card.name} for 50 gold.`); + btn.disabled = true; + btn.textContent = "SOLD"; + + // Update gold display + const goldDisplay = root.app.querySelector('.gold-amount'); + if (goldDisplay) { + goldDisplay.textContent = root.player.gold; + } + + // Update affordability of remaining items + updateShopAffordability(root); + } else { + root.log("Not enough gold!"); + } + }); + }); - if (shopRelic) { - root.app.querySelector("[data-buy-relic]").addEventListener("click", () => { - if (root.player.gold >= 100) { - root.player.gold -= 100; - root.log(`Bought ${shopRelic.name} for 100 gold.`); - import("../engine/battle.js").then(({ attachRelics }) => { + if (shopRelic) { + root.app.querySelector("[data-buy-relic]").addEventListener("click", () => { + if (root.player.gold >= 100) { + root.player.gold -= 100; + root.log(`Bought ${shopRelic.name} for 100 gold.`); + + import("../engine/battle.js").then(({ attachRelics }) => { const currentRelicIds = root.relicStates.map(r => r.id); const newRelicIds = [...currentRelicIds, shopRelic.id]; attachRelics(root, newRelicIds); - }); - root.app.querySelector("[data-buy-relic]").disabled = true; - root.app.querySelector("[data-buy-relic]").textContent = "SOLD"; - - // Update gold display - const goldDisplay = root.app.querySelector('.gold-amount'); - if (goldDisplay) { - goldDisplay.textContent = root.player.gold; - } - - // Update affordability of remaining items - updateShopAffordability(root); - } else { - root.log("Not enough gold!"); - } - }); + }); + root.app.querySelector("[data-buy-relic]").disabled = true; + root.app.querySelector("[data-buy-relic]").textContent = "SOLD"; + + // Update gold display + const goldDisplay = root.app.querySelector('.gold-amount'); + if (goldDisplay) { + goldDisplay.textContent = root.player.gold; } - root.app.querySelector("[data-leave]").addEventListener("click", () => root.afterNode()); + // Update affordability of remaining items + updateShopAffordability(root); + } else { + root.log("Not enough gold!"); + } }); + } + + root.app.querySelector("[data-leave]").addEventListener("click", () => root.afterNode()); }); + }); } function updateCardSelection(root) { - // Remove selection from all cards - root.app.querySelectorAll('.battle-card').forEach(card => { - card.classList.remove('card-selected'); - }); - - // Add selection to currently selected card - if (root.selectedCardIndex !== null) { - const selectedCard = root.app.querySelector(`[data-play="${root.selectedCardIndex}"]`); - if (selectedCard) { - selectedCard.classList.add('card-selected'); - } + // Remove selection from all cards + root.app.querySelectorAll('.battle-card').forEach(card => { + card.classList.remove('card-selected'); + }); + + // Add selection to currently selected card + if (root.selectedCardIndex !== null) { + const selectedCard = root.app.querySelector(`[data-play="${root.selectedCardIndex}"]`); + if (selectedCard) { + selectedCard.classList.add('card-selected'); } + } } function updateShopAffordability(root) { - // Update card affordability - root.app.querySelectorAll("[data-buy-card]").forEach(btn => { - if (!btn.disabled) { - const cardContainer = btn.closest('.shop-card-container'); - const overlay = cardContainer.querySelector('.card-disabled-overlay'); - - if (root.player.gold < 50) { - btn.classList.remove('playable'); - btn.classList.add('unplayable'); - if (!overlay) { - const newOverlay = document.createElement('div'); - newOverlay.className = 'card-disabled-overlay'; - newOverlay.innerHTML = 'Need 50 gold'; - cardContainer.appendChild(newOverlay); - } - } else { - btn.classList.remove('unplayable'); - btn.classList.add('playable'); - if (overlay) { - overlay.remove(); - } - } + // Update card affordability + root.app.querySelectorAll("[data-buy-card]").forEach(btn => { + if (!btn.disabled) { + const cardContainer = btn.closest('.shop-card-container'); + const overlay = cardContainer.querySelector('.card-disabled-overlay'); + + if (root.player.gold < 50) { + btn.classList.remove('playable'); + btn.classList.add('unplayable'); + if (!overlay) { + const newOverlay = document.createElement('div'); + newOverlay.className = 'card-disabled-overlay'; + newOverlay.innerHTML = 'Need 50 gold'; + cardContainer.appendChild(newOverlay); } - }); - - // Update relic affordability - const relicBtn = root.app.querySelector("[data-buy-relic]"); - if (relicBtn && !relicBtn.disabled) { - const relicContainer = relicBtn.closest('.shop-relic-container'); - const overlay = relicContainer.querySelector('.relic-disabled-overlay'); - - if (root.player.gold < 100) { - relicBtn.classList.remove('affordable'); - relicBtn.classList.add('unaffordable'); - if (!overlay) { - const newOverlay = document.createElement('div'); - newOverlay.className = 'relic-disabled-overlay'; - newOverlay.innerHTML = 'Need 100 gold'; - relicContainer.appendChild(newOverlay); - } - } else { - relicBtn.classList.remove('unaffordable'); - relicBtn.classList.add('affordable'); - if (overlay) { - overlay.remove(); - } + } else { + btn.classList.remove('unplayable'); + btn.classList.add('playable'); + if (overlay) { + overlay.remove(); } + } } + }); + + // Update relic affordability + const relicBtn = root.app.querySelector("[data-buy-relic]"); + if (relicBtn && !relicBtn.disabled) { + const relicContainer = relicBtn.closest('.shop-relic-container'); + const overlay = relicContainer.querySelector('.relic-disabled-overlay'); + + if (root.player.gold < 100) { + relicBtn.classList.remove('affordable'); + relicBtn.classList.add('unaffordable'); + if (!overlay) { + const newOverlay = document.createElement('div'); + newOverlay.className = 'relic-disabled-overlay'; + newOverlay.innerHTML = 'Need 100 gold'; + relicContainer.appendChild(newOverlay); + } + } else { + relicBtn.classList.remove('unaffordable'); + relicBtn.classList.add('affordable'); + if (overlay) { + overlay.remove(); + } + } + } } function shuffle(array) { - for (let i = array.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [array[i], array[j]] = [array[j], array[i]]; - } - return array; + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } + return array; } function getRelicEmoji(relicId) { @@ -1138,124 +1118,122 @@ function getRelicEmoji(relicId) { } function getRelicName(relicId) { - const names = { + const names = { mech_kb: 'Kinesis', standing_desk: 'Motions', prime_hat: 'VS Code', - coffee_thermos: 'Coffee Thermos', - cpp_compiler: 'C++ Compiler', - chat_mod_sword: 'Chat Mod Sword' - }; - return names[relicId] || relicId; + coffee_thermos: 'Coffee Thermos', + cpp_compiler: 'C++ Compiler', + chat_mod_sword: 'Chat Mod Sword' + }; + return names[relicId] || relicId; } function getRelicText(relicId) { - const texts = { - mech_kb: '+1 card draw each turn.', - standing_desk: '+10 Max HP.', - prime_hat: '-10% damage taken.', - coffee_thermos: 'Start each fight with Coffee Rush.', - cpp_compiler: 'First attack each turn deals double.', - chat_mod_sword: 'Start fights with 1 Weak on all enemies.' - }; - return texts[relicId] || 'Unknown relic'; + const texts = { + mech_kb: '+1 card draw each turn.', + standing_desk: '+10 Max HP.', + prime_hat: '-10% damage taken.', + coffee_thermos: 'Start each fight with Coffee Rush.', + cpp_compiler: 'First attack each turn deals double.', + chat_mod_sword: 'Start fights with 1 Weak on all enemies.' + }; + return texts[relicId] || 'Unknown relic'; } function getCardArt(cardId) { - const artMappings = { + const artMappings = { + + strike: 'Monk_1.png', + 'strike+': 'Monk_2.png', + + defend: 'Monk_3.png', + 'defend+': 'Monk_4.png', + + coffee_rush: 'Monk_5.png', // Energy boost + 'coffee_rush+': 'Monk_6.png', // Upgraded energy + macro: 'Monk_7.png', // Replay magic + refactor: 'Monk_8.png', // Refactoring tool + type_safety: 'Monk_9.png', // Protection + chat_ban: 'Monk_10.png', // Ban/restriction + + segfault: 'Monk_11.png', // Powerful attack + null_pointer: 'Monk_12.png', // Precise strike + recursion: 'Monk_13.png', // Repetition + merge_conflict: 'Monk_14.png', // Dual attack + hotfix: 'Monk_15.png', // Emergency fix + production_deploy: 'Monk_16.png', // High risk/reward + + gc: 'Monk_17.png', // Cleanup + async_await: 'Monk_18.png', // Time manipulation + stack_overflow: 'Monk_19.png', // Knowledge overflow + infinite_vim: 'Monk_20.png', // Infinite power + debug_print: 'Monk_21.png', // Information + git_commit: 'Monk_22.png', // Recording + memory_leak: 'Monk_23.png', // Draining effect + code_review: 'Monk_24.png', // Investigation + pair_programming: 'Monk_25.png', // Cooperation + rubber_duck: 'Monk_26.png', // Helpful companion + unit_test: 'Monk_27.png', // Testing/verification + + sugar_crash: 'Monk_28.png' // Negative effect + }; - strike: 'Monk_1.png', - 'strike+': 'Monk_2.png', - - defend: 'Monk_3.png', - 'defend+': 'Monk_4.png', - - coffee_rush: 'Monk_5.png', // Energy boost - 'coffee_rush+': 'Monk_6.png', // Upgraded energy - macro: 'Monk_7.png', // Replay magic - refactor: 'Monk_8.png', // Refactoring tool - type_safety: 'Monk_9.png', // Protection - chat_ban: 'Monk_10.png', // Ban/restriction - - segfault: 'Monk_11.png', // Powerful attack - null_pointer: 'Monk_12.png', // Precise strike - recursion: 'Monk_13.png', // Repetition - merge_conflict: 'Monk_14.png', // Dual attack - hotfix: 'Monk_15.png', // Emergency fix - production_deploy: 'Monk_16.png', // High risk/reward - - gc: 'Monk_17.png', // Cleanup - async_await: 'Monk_18.png', // Time manipulation - stack_overflow: 'Monk_19.png', // Knowledge overflow - infinite_vim: 'Monk_20.png', // Infinite power - debug_print: 'Monk_21.png', // Information - git_commit: 'Monk_22.png', // Recording - memory_leak: 'Monk_23.png', // Draining effect - code_review: 'Monk_24.png', // Investigation - pair_programming: 'Monk_25.png', // Cooperation - rubber_duck: 'Monk_26.png', // Helpful companion - unit_test: 'Monk_27.png', // Testing/verification - - sugar_crash: 'Monk_28.png' // Negative effect - }; - - const imagePath = artMappings[cardId]; - if (imagePath) { - return `${cardId} + const imagePath = artMappings[cardId]; + if (imagePath) { + return `${cardId} `; - } - - return getCardArtFallback(cardId); + } + + return getCardArtFallback(cardId); } function getCardArtFallback(cardId) { - const fallbacks = { - strike: 'πŸ‘Š', defend: 'πŸ›‘οΈ', coffee_rush: 'β˜•', macro: 'πŸ”„', - refactor: '⚑', type_safety: 'πŸ”’', chat_ban: '🚫', segfault: 'πŸ’₯', - gc: 'πŸ—‘οΈ', async_await: '⏳', stack_overflow: 'πŸ“š', infinite_vim: '♾️', - debug_print: 'πŸ›', null_pointer: '❌', recursion: 'πŸ”', git_commit: 'πŸ“', - memory_leak: 'πŸ•³οΈ', code_review: 'πŸ‘€', pair_programming: 'πŸ‘₯', hotfix: '🚨', - rubber_duck: 'πŸ¦†', merge_conflict: 'βš”οΈ', unit_test: 'βœ…', production_deploy: 'πŸš€', - sugar_crash: '🍰' - }; - return fallbacks[cardId] || 'πŸƒ'; + const fallbacks = { + strike: 'πŸ‘Š', defend: 'πŸ›‘οΈ', coffee_rush: 'β˜•', macro: 'πŸ”„', + refactor: '⚑', type_safety: 'πŸ”’', chat_ban: '🚫', segfault: 'πŸ’₯', + gc: 'πŸ—‘οΈ', async_await: '⏳', stack_overflow: 'πŸ“š', infinite_vim: '♾️', + debug_print: 'πŸ›', null_pointer: '❌', recursion: 'πŸ”', git_commit: 'πŸ“', + memory_leak: 'πŸ•³οΈ', code_review: 'πŸ‘€', pair_programming: 'πŸ‘₯', hotfix: '🚨', + rubber_duck: 'πŸ¦†', merge_conflict: 'βš”οΈ', unit_test: 'βœ…', production_deploy: 'πŸš€', + sugar_crash: '🍰' + }; + return fallbacks[cardId] || 'πŸƒ'; } function getEnemyArt(enemyId, ENEMIES = null) { const enemyData = ENEMIES?.[enemyId]; const avatarPath = enemyData?.avatar || `assets/avatars/${enemyId}.png`; - return `${enemyId} + return `${enemyId} `; } function getEnemyFallbackEmoji(enemyId) { - const arts = { - chat_gremlin: 'πŸ‘Ή', - type_checker: 'πŸ€–', - js_blob: '🟒', - infinite_loop: 'πŸŒ€', - merge_conflict_enemy: 'βš”οΈ', - bug_404: '❌', - elite_ts_demon: '😈', - elite_refactor: 'πŸ‰', - boss_birthday_bug: 'πŸŽ‚πŸ‘Ύ' + const arts = { + old_man_judo: 'πŸ‘Ή', + beastco: 'πŸŒ€', + codegirl: 'βš”οΈ', + defyusall: '🚫', + lithium: '⚑', + nightshadedude: '😈', + teej: 'πŸŽ‚πŸ‘Ύ' }; - return arts[enemyId] || 'πŸ‘Ύ'; + return arts[enemyId] || 'πŸ‘Ύ'; } function getEnemyType(enemyId) { - if (enemyId.includes('boss_')) return 'BOSS'; - if (enemyId.includes('elite_')) return 'ELITE'; - return 'ENEMY'; + if (enemyId.includes('boss_')) return 'BOSS'; + if (enemyId.includes('elite_')) return 'ELITE'; + return 'ENEMY'; } export function renderRelicSelection(root) { - import("../data/relics.js").then(({ RELICS, START_RELIC_CHOICES }) => { - const relicChoices = START_RELIC_CHOICES.slice(0, 3); // Show first 3 relics - - root.app.innerHTML = ` + import("../data/relics.js").then(({ RELICS, START_RELIC_CHOICES }) => { + const relicChoices = START_RELIC_CHOICES.slice(0, 3); // Show first 3 relics + + root.app.innerHTML = `
`; - root.app.querySelectorAll("[data-relic]").forEach(btn => { - btn.addEventListener("click", () => { - const relicId = btn.dataset.relic; - root.selectStartingRelic(relicId); - }); - }); + root.app.querySelectorAll("[data-relic]").forEach(btn => { + btn.addEventListener("click", () => { + const relicId = btn.dataset.relic; + root.selectStartingRelic(relicId); + }); }); + }); } export function renderEvent(root) { - const events = [ - { + const events = [ + { title: "Birthday Cake", - text: "You find a delicious birthday cake! But it looks suspicious...", + text: "You find a delicious birthday cake! But it looks suspicious...", artwork: "assets/card-art/bread.png", - choices: [ - { + choices: [ + { text: "Eat the whole cake (+15 HP, gain Sugar Crash curse)", icon: "assets/card-art/apple.png", risk: "high", - effect: () => { - root.player.hp = Math.min(root.player.maxHp, root.player.hp + 15); - root.player.deck.push("sugar_crash"); - root.log("Ate cake: +15 HP, added Sugar Crash curse"); - } - }, - { - text: "Take a small bite (+8 HP)", + effect: () => { + root.player.hp = Math.min(root.player.maxHp, root.player.hp + 15); + root.player.deck.push("sugar_crash"); + root.log("Ate cake: +15 HP, added Sugar Crash curse"); + } + }, + { + text: "Take a small bite (+8 HP)", icon: "assets/card-art/heart.png", risk: "low", - effect: () => { - root.player.hp = Math.min(root.player.maxHp, root.player.hp + 8); - root.log("Small bite: +8 HP"); - } - }, - { - text: "Leave it alone (gain 25 gold)", - icon: "assets/card-art/bag_of_gold.png", - risk: "none", - effect: () => { - root.player.gold += 25; - root.log("Resisted temptation: +25 gold"); - } - } - ] + effect: () => { + root.player.hp = Math.min(root.player.maxHp, root.player.hp + 8); + root.log("Small bite: +8 HP"); + } }, { + text: "Leave it alone (gain 25 gold)", + icon: "assets/card-art/bag_of_gold.png", + risk: "none", + effect: () => { + root.player.gold += 25; + root.log("Resisted temptation: +25 gold"); + } + } + ] + }, + { title: "Birthday Present", - text: "A mysterious gift box sits before you. What could be inside?", + text: "A mysterious gift box sits before you. What could be inside?", artwork: "assets/card-art/chest_closed.png", - choices: [ - { - text: "Open it eagerly (Random card or lose 10 HP)", + choices: [ + { + text: "Open it eagerly (Random card or lose 10 HP)", icon: "assets/card-art/key.png", risk: "high", - effect: () => { - if (Math.random() < 0.7) { - import("../data/cards.js").then(({ CARDS, CARD_POOL }) => { - const randomCard = CARD_POOL[Math.floor(Math.random() * CARD_POOL.length)]; - root.player.deck.push(randomCard); - root.log(`Found ${CARDS[randomCard].name}!`); - }); - } else { - root.player.hp = Math.max(1, root.player.hp - 10); - root.log("It was a trap! -10 HP"); - } - } - }, - { - text: "Open it carefully (+5 Max HP)", + effect: () => { + if (Math.random() < 0.7) { + import("../data/cards.js").then(({ CARDS, CARD_POOL }) => { + const randomCard = CARD_POOL[Math.floor(Math.random() * CARD_POOL.length)]; + root.player.deck.push(randomCard); + root.log(`Found ${CARDS[randomCard].name}!`); + }); + } else { + root.player.hp = Math.max(1, root.player.hp - 10); + root.log("It was a trap! -10 HP"); + } + } + }, + { + text: "Open it carefully (+5 Max HP)", icon: "assets/card-art/potion_heal.png", risk: "low", - effect: () => { - root.player.maxHp += 5; - root.player.hp += 5; - root.log("Careful approach: +5 Max HP"); - } - }, - { - text: "Don't touch it (gain 30 gold)", - icon: "assets/card-art/bag_of_gold.png", - risk: "none", - effect: () => { - root.player.gold += 30; - root.log("Played it safe: +30 gold"); - } - } - ] + effect: () => { + root.player.maxHp += 5; + root.player.hp += 5; + root.log("Careful approach: +5 Max HP"); + } }, { + text: "Don't touch it (gain 30 gold)", + icon: "assets/card-art/bag_of_gold.png", + risk: "none", + effect: () => { + root.player.gold += 30; + root.log("Played it safe: +30 gold"); + } + } + ] + }, + { title: "Birthday Balloons", - text: "Colorful balloons float by. One has a note attached: 'Pop me for a surprise!'", + text: "Colorful balloons float by. One has a note attached: 'Pop me for a surprise!'", artwork: "assets/card-art/feather.png", - choices: [ - { + choices: [ + { text: "Pop the balloon (Remove a random basic card from deck)", icon: "assets/card-art/scroll.png", risk: "medium", - effect: () => { - const basicCards = root.player.deck.filter(id => id === "strike" || id === "defend"); - if (basicCards.length > 0) { - const toRemove = basicCards[0]; - const index = root.player.deck.indexOf(toRemove); - root.player.deck.splice(index, 1); - root.log(`Removed ${toRemove} from deck`); - } else { - root.log("No basic cards to remove"); - } - } - }, - { - text: "Collect the balloons (+1 Energy next 3 fights)", + effect: () => { + const basicCards = root.player.deck.filter(id => id === "strike" || id === "defend"); + if (basicCards.length > 0) { + const toRemove = basicCards[0]; + const index = root.player.deck.indexOf(toRemove); + root.player.deck.splice(index, 1); + root.log(`Removed ${toRemove} from deck`); + } else { + root.log("No basic cards to remove"); + } + } + }, + { + text: "Collect the balloons (+1 Energy next 3 fights)", icon: "assets/card-art/magic_sphere.png", risk: "low", - effect: () => { - root.flags.bonusEnergyFights = 3; - root.log("Collected balloons: +1 Energy next 3 fights"); - } - }, - { - text: "Ignore them (heal 12 HP)", + effect: () => { + root.flags.bonusEnergyFights = 3; + root.log("Collected balloons: +1 Energy next 3 fights"); + } + }, + { + text: "Ignore them (heal 12 HP)", icon: "assets/card-art/heart.png", risk: "none", - effect: () => { - root.player.hp = Math.min(root.player.maxHp, root.player.hp + 12); - root.log("Focused on rest: +12 HP"); - } - } - ] + effect: () => { + root.player.hp = Math.min(root.player.maxHp, root.player.hp + 12); + root.log("Focused on rest: +12 HP"); + } } - ]; + ] + } + ]; - const event = events[Math.floor(Math.random() * events.length)]; - - root.app.innerHTML = ` + const event = events[Math.floor(Math.random() * events.length)]; + + root.app.innerHTML = `

${event.title}

@@ -1504,13 +1482,13 @@ export function renderEvent(root) {
`; - root.app.querySelectorAll("[data-choice]").forEach(btn => { - btn.addEventListener("click", () => { - const idx = parseInt(btn.dataset.choice, 10); - event.choices[idx].effect(); - root.afterNode(); - }); + root.app.querySelectorAll("[data-choice]").forEach(btn => { + btn.addEventListener("click", () => { + const idx = parseInt(btn.dataset.choice, 10); + event.choices[idx].effect(); + root.afterNode(); }); + }); } export function renderWin(root) { @@ -1524,7 +1502,7 @@ export function renderWin(root) { relicsCollected: root.relicStates.length }; - root.app.innerHTML = ` + root.app.innerHTML = `
@@ -1609,7 +1587,7 @@ export function renderWin(root) {
`; - root.app.querySelector("[data-replay]").addEventListener("click", () => root.reset()); + root.app.querySelector("[data-replay]").addEventListener("click", () => root.reset()); } export function renderLose(root) { @@ -1624,7 +1602,7 @@ export function renderLose(root) { nodeId: root.nodeId || 'unknown' }; - root.app.innerHTML = ` + root.app.innerHTML = `

You Failed!