Browse Source

clean up

main
Stephanie Gredell 4 months ago
parent
commit
63ca75ea78
  1. 28
      src/data/cards.js
  2. 6
      src/engine/battle.js
  3. 74
      src/engine/events.js
  4. 92
      src/input/InputManager.js
  5. 64
      src/ui/render.js
  6. 19
      style.css

28
src/data/cards.js

@ -132,12 +132,12 @@ export const CARDS = {
ctx.log("Recursion activates and strikes again!"); ctx.log("Recursion activates and strikes again!");
ctx.enemy.hp = 1; ctx.enemy.hp = 1;
ctx.deal(ctx.enemy, ctx.scalarFromWeak(5)); ctx.deal(ctx.enemy, ctx.scalarFromWeak(5));
// Check for battle end after second attack // Check for battle end after second attack
if (ctx.enemy.hp <= 0) { if (ctx.enemy.hp <= 0) {
ctx.enemy.hp = 0; ctx.enemy.hp = 0;
ctx.onWin(); ctx.onWin();
return; return;
} }
} }
} }
@ -167,13 +167,13 @@ export const CARDS = {
ctx.log("No cards left in deck to review."); ctx.log("No cards left in deck to review.");
return; return;
} }
// Store selection state for modal // Store selection state for modal
ctx.root._codeReviewCards = topCards; ctx.root._codeReviewCards = topCards;
ctx.root._codeReviewCallback = (selectedIndex) => { ctx.root._codeReviewCallback = (selectedIndex) => {
// Get the selected card // Get the selected card
const selectedCard = topCards[selectedIndex]; const selectedCard = topCards[selectedIndex];
// Remove the peeked cards from draw pile (they were only peeked) // Remove the peeked cards from draw pile (they were only peeked)
topCards.forEach((card, i) => { topCards.forEach((card, i) => {
const drawIndex = ctx.root.player.draw.findIndex(id => id === card.id); const drawIndex = ctx.root.player.draw.findIndex(id => id === card.id);
@ -181,21 +181,21 @@ export const CARDS = {
ctx.root.player.draw.splice(drawIndex, 1); ctx.root.player.draw.splice(drawIndex, 1);
} }
}); });
// Add selected card to hand // Add selected card to hand
ctx.addToHand(selectedCard); ctx.addToHand(selectedCard);
// Put remaining cards on bottom of deck // Put remaining cards on bottom of deck
topCards.forEach((card, i) => { topCards.forEach((card, i) => {
if (i !== selectedIndex) { if (i !== selectedIndex) {
ctx.putOnBottom(card.id); ctx.putOnBottom(card.id);
} }
}); });
ctx.log(`Code review complete. Added ${selectedCard.name} to hand.`); ctx.log(`Code review complete. Added ${selectedCard.name} to hand.`);
ctx.render(); ctx.render();
}; };
// Show selection modal // Show selection modal
if (window.gameModules?.render?.renderCodeReviewSelection) { if (window.gameModules?.render?.renderCodeReviewSelection) {
window.gameModules.render.renderCodeReviewSelection(ctx.root, topCards); window.gameModules.render.renderCodeReviewSelection(ctx.root, topCards);
@ -291,12 +291,10 @@ export const CARDS = {
}, },
rubber_duck: { rubber_duck: {
id: "rubber_duck", name: "Rubber Duck Debug", cost: 0, type: "skill", text: "Draw 1. Reveal enemy intent.", id: "rubber_duck", name: "Rubber Duck Debug", cost: 0, type: "skill", text: "Draw 1.",
art: "Monk_31.png", art: "Monk_31.png",
effect: (ctx) => { effect: (ctx) => {
ctx.draw(1); ctx.draw(1);
const intent = ctx.enemy.intent;
ctx.log(`Rubber duck reveals: Enemy will ${intent.type} for ${intent.value || 'unknown'} next turn.`);
} }
}, },
@ -370,7 +368,7 @@ export const CARDS = {
export const STARTER_DECK = [ export const STARTER_DECK = [
"strike", "strike", "defend", "defend", "strike", "strike", "defend", "defend",
"segfault", "coffee_rush", "skill_issue", "git_commit", "segfault", "coffee_rush", "skill_issue", "git_commit",
"stack_trace", "stack_trace" "stack_trace", "stack_trace", "git_push_force", "code_review"
]; ];
export const CARD_POOL = [ export const CARD_POOL = [

6
src/engine/battle.js

@ -125,6 +125,12 @@ export function playCard(ctx, handIndex) {
if (ctx.enemy.hp <= 0) { ctx.enemy.hp = 0; ctx.onWin(); return; } if (ctx.enemy.hp <= 0) { ctx.enemy.hp = 0; ctx.onWin(); return; }
if (ctx.player.hp <= 0) { ctx.onLose(); return; } if (ctx.player.hp <= 0) { ctx.onLose(); return; }
// Don't render if Code Review modal is active
if (ctx.root._codeReviewCards) {
return;
}
ctx.render(); ctx.render();
} }

74
src/engine/events.js

@ -10,7 +10,7 @@ export class EventHandler {
this.keyHandlers = new Map(); // Track keyboard shortcuts this.keyHandlers = new Map(); // Track keyboard shortcuts
this.globalHandlers = new Set(); // Track global event handlers this.globalHandlers = new Set(); // Track global event handlers
this.currentScreen = null; this.currentScreen = null;
this.setupGlobalEvents(); this.setupGlobalEvents();
} }
@ -26,17 +26,17 @@ export class EventHandler {
handler(e); handler(e);
} }
}; };
document.addEventListener('keydown', this.globalKeyHandler); document.addEventListener('keydown', this.globalKeyHandler);
this.globalHandlers.add('keydown'); this.globalHandlers.add('keydown');
// Global escape handler for modals // Global escape handler for modals
this.globalEscapeHandler = (e) => { this.globalEscapeHandler = (e) => {
if (e.key === 'Escape') { if (e.key === 'Escape') {
this.closeTopModal(); this.closeTopModal();
} }
}; };
document.addEventListener('keydown', this.globalEscapeHandler); document.addEventListener('keydown', this.globalEscapeHandler);
this.globalHandlers.add('escape'); this.globalHandlers.add('escape');
} }
@ -55,7 +55,7 @@ export class EventHandler {
*/ */
on(element, event, handler, options = {}) { on(element, event, handler, options = {}) {
if (!element) return; if (!element) return;
const wrappedHandler = (e) => { const wrappedHandler = (e) => {
try { try {
handler(e); handler(e);
@ -63,9 +63,9 @@ export class EventHandler {
console.error(`Event handler error (${event}):`, error); console.error(`Event handler error (${event}):`, error);
} }
}; };
element.addEventListener(event, wrappedHandler, options); element.addEventListener(event, wrappedHandler, options);
// Track for cleanup // Track for cleanup
if (!this.listeners.has(element)) { if (!this.listeners.has(element)) {
this.listeners.set(element, []); this.listeners.set(element, []);
@ -92,7 +92,7 @@ export class EventHandler {
*/ */
setupBattleEvents() { setupBattleEvents() {
this.switchScreen('battle'); this.switchScreen('battle');
// Card play events // Card play events
this.root.app.querySelectorAll("[data-play]").forEach(btn => { this.root.app.querySelectorAll("[data-play]").forEach(btn => {
this.on(btn, "mouseenter", () => { this.on(btn, "mouseenter", () => {
@ -141,11 +141,11 @@ export class EventHandler {
this.addKeyHandler(i.toString(), (e) => { this.addKeyHandler(i.toString(), (e) => {
const cardIndex = i - 1; const cardIndex = i - 1;
const hand = this.root.player.hand; const hand = this.root.player.hand;
if (cardIndex >= hand.length) return; if (cardIndex >= hand.length) return;
const card = hand[cardIndex]; const card = hand[cardIndex];
if (this.root.selectedCardIndex === cardIndex) { if (this.root.selectedCardIndex === cardIndex) {
// Second press - play the card // Second press - play the card
if (this.root.player.energy >= card.cost) { if (this.root.player.energy >= card.cost) {
@ -168,11 +168,11 @@ export class EventHandler {
*/ */
setupMapEvents() { setupMapEvents() {
this.switchScreen('map'); this.switchScreen('map');
// Node navigation // Node navigation
this.root.app.querySelectorAll("[data-node]").forEach(el => { this.root.app.querySelectorAll("[data-node]").forEach(el => {
if (!el.dataset.node) return; if (!el.dataset.node) return;
this.on(el, "click", () => this.root.go(el.dataset.node)); this.on(el, "click", () => this.root.go(el.dataset.node));
}); });
@ -206,14 +206,14 @@ export class EventHandler {
*/ */
setupRewardEvents(choices) { setupRewardEvents(choices) {
this.switchScreen('reward'); this.switchScreen('reward');
this.root.app.querySelectorAll("[data-pick]").forEach(btn => { this.root.app.querySelectorAll("[data-pick]").forEach(btn => {
this.on(btn, "click", () => { this.on(btn, "click", () => {
const idx = parseInt(btn.dataset.pick, 10); const idx = parseInt(btn.dataset.pick, 10);
this.root.takeReward(idx); this.root.takeReward(idx);
}); });
}); });
const skipBtn = this.root.app.querySelector("[data-skip]"); const skipBtn = this.root.app.querySelector("[data-skip]");
if (skipBtn) { if (skipBtn) {
this.on(skipBtn, "click", () => this.root.skipReward()); this.on(skipBtn, "click", () => this.root.skipReward());
@ -225,7 +225,7 @@ export class EventHandler {
this.root.takeReward(i - 1); this.root.takeReward(i - 1);
}, `Select Reward ${i}`); }, `Select Reward ${i}`);
} }
this.addKeyHandler('s', () => this.root.skipReward(), 'Skip Reward'); this.addKeyHandler('s', () => this.root.skipReward(), 'Skip Reward');
} }
@ -234,10 +234,10 @@ export class EventHandler {
*/ */
setupRestEvents() { setupRestEvents() {
this.switchScreen('rest'); this.switchScreen('rest');
const healBtn = this.root.app.querySelector("[data-act='heal']"); const healBtn = this.root.app.querySelector("[data-act='heal']");
const upgradeBtn = this.root.app.querySelector("[data-act='upgrade']"); const upgradeBtn = this.root.app.querySelector("[data-act='upgrade']");
if (healBtn) { if (healBtn) {
this.on(healBtn, "click", () => { this.on(healBtn, "click", () => {
const heal = Math.floor(this.root.player.maxHp * 0.2); const heal = Math.floor(this.root.player.maxHp * 0.2);
@ -246,7 +246,7 @@ export class EventHandler {
this.root.afterNode(); this.root.afterNode();
}); });
} }
if (upgradeBtn) { if (upgradeBtn) {
this.on(upgradeBtn, "click", () => { this.on(upgradeBtn, "click", () => {
// Import and call renderUpgrade // Import and call renderUpgrade
@ -266,7 +266,7 @@ export class EventHandler {
*/ */
setupShopEvents(shopCards = [], shopRelic = null) { setupShopEvents(shopCards = [], shopRelic = null) {
this.switchScreen('shop'); this.switchScreen('shop');
// Card purchase events // Card purchase events
this.root.app.querySelectorAll("[data-buy-card]").forEach(btn => { this.root.app.querySelectorAll("[data-buy-card]").forEach(btn => {
this.on(btn, "click", () => { this.on(btn, "click", () => {
@ -278,7 +278,7 @@ export class EventHandler {
this.root.log(`Bought ${card.name} for 50 gold.`); this.root.log(`Bought ${card.name} for 50 gold.`);
btn.disabled = true; btn.disabled = true;
btn.textContent = "SOLD"; btn.textContent = "SOLD";
this.updateGoldDisplay(); this.updateGoldDisplay();
this.updateShopAffordability(); this.updateShopAffordability();
} else { } else {
@ -303,7 +303,7 @@ export class EventHandler {
relicBtn.disabled = true; relicBtn.disabled = true;
relicBtn.textContent = "SOLD"; relicBtn.textContent = "SOLD";
this.updateGoldDisplay(); this.updateGoldDisplay();
this.updateShopAffordability(); this.updateShopAffordability();
} else { } else {
@ -329,7 +329,7 @@ export class EventHandler {
*/ */
setupEventEvents(event) { setupEventEvents(event) {
this.switchScreen('event'); this.switchScreen('event');
this.root.app.querySelectorAll("[data-choice]").forEach(btn => { this.root.app.querySelectorAll("[data-choice]").forEach(btn => {
this.on(btn, "click", () => { this.on(btn, "click", () => {
const idx = parseInt(btn.dataset.choice, 10); const idx = parseInt(btn.dataset.choice, 10);
@ -352,7 +352,7 @@ export class EventHandler {
*/ */
setupRelicSelectionEvents(relicChoices) { setupRelicSelectionEvents(relicChoices) {
this.switchScreen('relic-selection'); this.switchScreen('relic-selection');
this.root.app.querySelectorAll("[data-relic]").forEach(btn => { this.root.app.querySelectorAll("[data-relic]").forEach(btn => {
this.on(btn, "click", () => { this.on(btn, "click", () => {
const relicId = btn.dataset.relic; const relicId = btn.dataset.relic;
@ -369,11 +369,11 @@ export class EventHandler {
// Keyboard shortcuts // Keyboard shortcuts
for (let i = 1; i <= relicChoices.length; i++) { for (let i = 1; i <= relicChoices.length; i++) {
this.addKeyHandler(i.toString(), () => { this.addKeyHandler(i.toString(), () => {
const relicBtn = this.root.app.querySelector(`[data-relic="${relicChoices[i-1]}"]`); const relicBtn = this.root.app.querySelector(`[data-relic="${relicChoices[i - 1]}"]`);
relicBtn?.click(); relicBtn?.click();
}, `Select Relic ${i}`); }, `Select Relic ${i}`);
} }
this.addKeyHandler('m', () => this.showMessagesModal(), 'Show Messages'); this.addKeyHandler('m', () => this.showMessagesModal(), 'Show Messages');
} }
@ -382,15 +382,15 @@ export class EventHandler {
*/ */
setupEndGameEvents() { setupEndGameEvents() {
this.switchScreen('endgame'); this.switchScreen('endgame');
const replayBtn = this.root.app.querySelector("[data-replay]"); const replayBtn = this.root.app.querySelector("[data-replay]");
const restartAct2Btn = this.root.app.querySelector("[data-restart-act2]"); const restartAct2Btn = this.root.app.querySelector("[data-restart-act2]");
const menuBtn = this.root.app.querySelector("[data-menu]"); const menuBtn = this.root.app.querySelector("[data-menu]");
if (replayBtn) { if (replayBtn) {
this.on(replayBtn, "click", () => this.root.reset()); this.on(replayBtn, "click", () => this.root.reset());
} }
if (restartAct2Btn) { if (restartAct2Btn) {
this.on(restartAct2Btn, "click", async () => { this.on(restartAct2Btn, "click", async () => {
if (this.root.loadAct2Checkpoint()) { if (this.root.loadAct2Checkpoint()) {
@ -401,7 +401,7 @@ export class EventHandler {
} }
}); });
} }
if (menuBtn) { if (menuBtn) {
this.on(menuBtn, "click", () => this.root.reset()); this.on(menuBtn, "click", () => this.root.reset());
} }
@ -455,10 +455,10 @@ export class EventHandler {
showTooltip(event) { showTooltip(event) {
const tooltip = document.getElementById('custom-tooltip'); const tooltip = document.getElementById('custom-tooltip');
if (!tooltip) return; if (!tooltip) return;
const node = event.target.closest('.spire-node'); const node = event.target.closest('.spire-node');
if (!node) return; if (!node) return;
const content = node.dataset.tooltip; const content = node.dataset.tooltip;
const avatarPath = node.dataset.avatar; const avatarPath = node.dataset.avatar;
@ -514,7 +514,7 @@ export class EventHandler {
modal.innerHTML = ` modal.innerHTML = `
<div class="messages-modal"> <div class="messages-modal">
<div class="messages-modal-header"> <div class="messages-modal-header">
<h2>Messages for Prime</h2> <h2>Inbox</h2>
<button class="messages-close-btn" aria-label="Close">×</button> <button class="messages-close-btn" aria-label="Close">×</button>
</div> </div>
<div class="messages-modal-content"> <div class="messages-modal-content">
@ -565,7 +565,7 @@ export class EventHandler {
} }
} }
this.listeners.clear(); this.listeners.clear();
// Clear keyboard handlers // Clear keyboard handlers
this.keyHandlers.clear(); this.keyHandlers.clear();
} }
@ -575,7 +575,7 @@ export class EventHandler {
*/ */
destroy() { destroy() {
this.cleanup(); this.cleanup();
// Remove global handlers // Remove global handlers
if (this.globalHandlers.has('keydown')) { if (this.globalHandlers.has('keydown')) {
document.removeEventListener('keydown', this.globalKeyHandler); document.removeEventListener('keydown', this.globalKeyHandler);
@ -583,7 +583,7 @@ export class EventHandler {
if (this.globalHandlers.has('escape')) { if (this.globalHandlers.has('escape')) {
document.removeEventListener('keydown', this.globalEscapeHandler); document.removeEventListener('keydown', this.globalEscapeHandler);
} }
this.globalHandlers.clear(); this.globalHandlers.clear();
} }
} }

92
src/input/InputManager.js

@ -18,7 +18,7 @@ export class InputManager {
this.root = gameRoot; this.root = gameRoot;
this.activeHandlers = new Map(); // Track active event listeners for cleanup this.activeHandlers = new Map(); // Track active event listeners for cleanup
this.globalHandlers = new Set(); // Track global document listeners this.globalHandlers = new Set(); // Track global document listeners
// Bind methods to preserve 'this' context // Bind methods to preserve 'this' context
this.handleGlobalKeydown = this.handleGlobalKeydown.bind(this); this.handleGlobalKeydown = this.handleGlobalKeydown.bind(this);
this.handleGlobalClick = this.handleGlobalClick.bind(this); this.handleGlobalClick = this.handleGlobalClick.bind(this);
@ -31,7 +31,7 @@ export class InputManager {
// Global keyboard handling // Global keyboard handling
document.addEventListener('keydown', this.handleGlobalKeydown); document.addEventListener('keydown', this.handleGlobalKeydown);
this.globalHandlers.add('keydown'); this.globalHandlers.add('keydown');
// Global click handling for data attributes // Global click handling for data attributes
document.addEventListener('click', this.handleGlobalClick); document.addEventListener('click', this.handleGlobalClick);
this.globalHandlers.add('click'); this.globalHandlers.add('click');
@ -45,7 +45,7 @@ export class InputManager {
if (event.key === 'Escape') { if (event.key === 'Escape') {
this.handleEscapeKey(event); this.handleEscapeKey(event);
} }
// Handle number keys for code review selection // Handle number keys for code review selection
if (this.root._codeReviewCards && event.key >= '1' && event.key <= '3') { if (this.root._codeReviewCards && event.key >= '1' && event.key <= '3') {
const selectedIndex = parseInt(event.key, 10) - 1; const selectedIndex = parseInt(event.key, 10) - 1;
@ -58,7 +58,7 @@ export class InputManager {
} }
} }
} }
// Add other global shortcuts here as needed // Add other global shortcuts here as needed
} }
@ -67,90 +67,90 @@ export class InputManager {
*/ */
handleGlobalClick(event) { handleGlobalClick(event) {
const target = event.target; const target = event.target;
// Event delegation for game interactions // Event delegation for game interactions
// Handle clicks on elements with data attributes (check both direct and parent elements) // Handle clicks on elements with data attributes (check both direct and parent elements)
// Check for card play (battle-card with data-play) // Check for card play (battle-card with data-play)
const cardElement = target.closest('[data-play]'); const cardElement = target.closest('[data-play]');
if (cardElement) { if (cardElement) {
this.handleCardPlay(cardElement, event); this.handleCardPlay(cardElement, event);
return; // Early return to avoid duplicate handling return; // Early return to avoid duplicate handling
} }
// Check for other interactive elements (using closest to handle child elements) // Check for other interactive elements (using closest to handle child elements)
const actionElement = target.closest('[data-action]'); const actionElement = target.closest('[data-action]');
if (actionElement) { if (actionElement) {
this.handleActionButton(actionElement, event); this.handleActionButton(actionElement, event);
return; return;
} }
const actElement = target.closest('[data-act]'); const actElement = target.closest('[data-act]');
if (actElement) { if (actElement) {
this.handleRestAction(actElement, event); this.handleRestAction(actElement, event);
return; return;
} }
const pickElement = target.closest('[data-pick]'); const pickElement = target.closest('[data-pick]');
if (pickElement) { if (pickElement) {
this.handleRewardPick(pickElement, event); this.handleRewardPick(pickElement, event);
return; return;
} }
const choiceElement = target.closest('[data-choice]'); const choiceElement = target.closest('[data-choice]');
if (choiceElement) { if (choiceElement) {
this.handleEventChoice(choiceElement, event); this.handleEventChoice(choiceElement, event);
return; return;
} }
const upgradeElement = target.closest('[data-upgrade]'); const upgradeElement = target.closest('[data-upgrade]');
if (upgradeElement) { if (upgradeElement) {
this.handleCardUpgrade(upgradeElement, event); this.handleCardUpgrade(upgradeElement, event);
return; return;
} }
const buyCardElement = target.closest('[data-buy-card]'); const buyCardElement = target.closest('[data-buy-card]');
if (buyCardElement) { if (buyCardElement) {
this.handleShopCardBuy(buyCardElement, event); this.handleShopCardBuy(buyCardElement, event);
return; return;
} }
const buyRelicElement = target.closest('[data-buy-relic]'); const buyRelicElement = target.closest('[data-buy-relic]');
if (buyRelicElement) { if (buyRelicElement) {
this.handleShopRelicBuy(buyRelicElement, event); this.handleShopRelicBuy(buyRelicElement, event);
return; return;
} }
const leaveElement = target.closest('[data-leave]'); const leaveElement = target.closest('[data-leave]');
if (leaveElement) { if (leaveElement) {
this.handleLeaveShop(leaveElement, event); this.handleLeaveShop(leaveElement, event);
return; return;
} }
const relicElement = target.closest('[data-relic]'); const relicElement = target.closest('[data-relic]');
if (relicElement) { if (relicElement) {
this.handleRelicSelection(relicElement, event); this.handleRelicSelection(relicElement, event);
return; return;
} }
const codeReviewElement = target.closest('[data-code-review-pick]'); const codeReviewElement = target.closest('[data-code-review-pick]');
if (codeReviewElement) { if (codeReviewElement) {
this.handleCodeReviewPick(codeReviewElement, event); this.handleCodeReviewPick(codeReviewElement, event);
return; return;
} }
// Check for direct data attributes on target (fallback) // Check for direct data attributes on target (fallback)
if (target.dataset.node !== undefined) { if (target.dataset.node !== undefined) {
this.handleMapNodeClick(target, event); this.handleMapNodeClick(target, event);
} }
// Handle spire node clicks (check if clicked element is inside a spire-node) // Handle spire node clicks (check if clicked element is inside a spire-node)
const spireNode = target.closest('.spire-node'); const spireNode = target.closest('.spire-node');
if (spireNode && spireNode.dataset.node) { if (spireNode && spireNode.dataset.node) {
this.handleMapNodeClick(spireNode, event); this.handleMapNodeClick(spireNode, event);
} }
// Handle other specific buttons // Handle other specific buttons
this.handleSpecificButtons(target, event); this.handleSpecificButtons(target, event);
} }
@ -160,21 +160,21 @@ export class InputManager {
*/ */
handleCardPlay(element, event) { handleCardPlay(element, event) {
if (!element.classList.contains('playable')) return; if (!element.classList.contains('playable')) return;
const index = parseInt(element.dataset.play, 10); const index = parseInt(element.dataset.play, 10);
const card = this.root.player.hand[index]; const card = this.root.player.hand[index];
if (!card) return; if (!card) return;
if (this.root.player.energy < card.cost) return; if (this.root.player.energy < card.cost) return;
try { try {
// Play sound // Play sound
this.playSound('played-card.mp3'); this.playSound('played-card.mp3');
// Create and execute PlayCardCommand // Create and execute PlayCardCommand
const command = new PlayCardCommand(this.root, index); const command = new PlayCardCommand(this.root, index);
const success = this.root.commandInvoker.execute(command); const success = this.root.commandInvoker.execute(command);
if (success) { if (success) {
// Clear card selection // Clear card selection
this.root.selectedCardIndex = null; this.root.selectedCardIndex = null;
@ -192,7 +192,7 @@ export class InputManager {
*/ */
handleMapNodeClick(element, event) { handleMapNodeClick(element, event) {
if (!element.dataset.node) return; if (!element.dataset.node) return;
try { try {
// Create and execute MapMoveCommand // Create and execute MapMoveCommand
const command = new MapMoveCommand(this.root, element.dataset.node); const command = new MapMoveCommand(this.root, element.dataset.node);
@ -207,7 +207,7 @@ export class InputManager {
*/ */
handleRewardPick(element, event) { handleRewardPick(element, event) {
const idx = parseInt(element.dataset.pick, 10); const idx = parseInt(element.dataset.pick, 10);
try { try {
// Create and execute RewardPickCommand // Create and execute RewardPickCommand
const command = new RewardPickCommand(this.root, idx); const command = new RewardPickCommand(this.root, idx);
@ -235,7 +235,7 @@ export class InputManager {
handleCardUpgrade(element, event) { handleCardUpgrade(element, event) {
const deckIndex = parseInt(element.dataset.upgrade, 10); const deckIndex = parseInt(element.dataset.upgrade, 10);
const oldCardId = this.root.player.deck[deckIndex]; const oldCardId = this.root.player.deck[deckIndex];
// Find the upgraded version and replace it // Find the upgraded version and replace it
const { CARDS } = window.gameModules?.cards || {}; const { CARDS } = window.gameModules?.cards || {};
if (CARDS && CARDS[oldCardId]?.upgrades) { if (CARDS && CARDS[oldCardId]?.upgrades) {
@ -259,16 +259,16 @@ export class InputManager {
this.root.log(`Bought ${card.name} for 50 gold.`); this.root.log(`Bought ${card.name} for 50 gold.`);
element.disabled = true; element.disabled = true;
element.textContent = "SOLD"; element.textContent = "SOLD";
// Update gold display // Update gold display
const goldDisplay = this.root.app.querySelector('.gold-amount'); const goldDisplay = this.root.app.querySelector('.gold-amount');
if (goldDisplay) { if (goldDisplay) {
goldDisplay.textContent = this.root.player.gold; goldDisplay.textContent = this.root.player.gold;
} }
// Update affordability of remaining items // Update affordability of remaining items
this.updateShopAffordability(); this.updateShopAffordability();
// Save immediately to persist purchase // Save immediately to persist purchase
this.root.save(); this.root.save();
} else { } else {
@ -293,7 +293,7 @@ export class InputManager {
const newRelicIds = [...currentRelicIds, relic.id]; const newRelicIds = [...currentRelicIds, relic.id];
attachRelics(this.root, newRelicIds); attachRelics(this.root, newRelicIds);
}); });
element.disabled = true; element.disabled = true;
element.textContent = "SOLD"; element.textContent = "SOLD";
@ -305,7 +305,7 @@ export class InputManager {
// Update affordability of remaining items // Update affordability of remaining items
this.updateShopAffordability(); this.updateShopAffordability();
// Save immediately to persist purchase // Save immediately to persist purchase
this.root.save(); this.root.save();
} else { } else {
@ -388,12 +388,12 @@ export class InputManager {
*/ */
handleCodeReviewPick(element, event) { handleCodeReviewPick(element, event) {
const selectedIndex = parseInt(element.dataset.codeReviewPick, 10); const selectedIndex = parseInt(element.dataset.codeReviewPick, 10);
if (this.root._codeReviewCallback && this.root._codeReviewCards) { if (this.root._codeReviewCallback && this.root._codeReviewCards) {
try { try {
// Execute the callback with selected index // Execute the callback with selected index
this.root._codeReviewCallback(selectedIndex); this.root._codeReviewCallback(selectedIndex);
// Clean up state // Clean up state
this.root._codeReviewCards = null; this.root._codeReviewCards = null;
this.root._codeReviewCallback = null; this.root._codeReviewCallback = null;
@ -408,7 +408,7 @@ export class InputManager {
*/ */
handleActionButton(element, event) { handleActionButton(element, event) {
const action = element.dataset.action; const action = element.dataset.action;
switch (action) { switch (action) {
case 'show-messages': case 'show-messages':
this.handleShowMessages(); this.handleShowMessages();
@ -426,7 +426,7 @@ export class InputManager {
*/ */
handleRestAction(element, event) { handleRestAction(element, event) {
const action = element.dataset.act; const action = element.dataset.act;
try { try {
// Create and execute RestActionCommand // Create and execute RestActionCommand
const command = new RestActionCommand(this.root, action); const command = new RestActionCommand(this.root, action);
@ -444,7 +444,7 @@ export class InputManager {
// Create and execute EndTurnCommand // Create and execute EndTurnCommand
const command = new EndTurnCommand(this.root); const command = new EndTurnCommand(this.root);
const success = this.root.commandInvoker.execute(command); const success = this.root.commandInvoker.execute(command);
if (success) { if (success) {
// Clear card selection // Clear card selection
this.root.selectedCardIndex = null; this.root.selectedCardIndex = null;
@ -507,7 +507,7 @@ export class InputManager {
if (this.root.currentShopRelic && this.root.player.gold >= 100) { if (this.root.currentShopRelic && this.root.player.gold >= 100) {
this.root.player.gold -= 100; this.root.player.gold -= 100;
this.root.log(`Bought ${this.root.currentShopRelic.name} for 100 gold.`); this.root.log(`Bought ${this.root.currentShopRelic.name} for 100 gold.`);
// Add relic logic here // Add relic logic here
element.disabled = true; element.disabled = true;
element.textContent = "SOLD"; element.textContent = "SOLD";
@ -536,7 +536,7 @@ export class InputManager {
this.root.render(); this.root.render();
return; return;
} }
// Close any open modals // Close any open modals
const modals = document.querySelectorAll('.messages-modal-overlay'); const modals = document.querySelectorAll('.messages-modal-overlay');
modals.forEach(modal => modal.remove()); modals.forEach(modal => modal.remove());
@ -555,7 +555,7 @@ export class InputManager {
modal.innerHTML = ` modal.innerHTML = `
<div class="messages-modal"> <div class="messages-modal">
<div class="messages-modal-header"> <div class="messages-modal-header">
<h2>Messages for Prime</h2> <h2>Messages</h2>
<button class="messages-close-btn" aria-label="Close">×</button> <button class="messages-close-btn" aria-label="Close">×</button>
</div> </div>
<div class="messages-modal-content"> <div class="messages-modal-content">
@ -576,10 +576,10 @@ export class InputManager {
// Close functionality // Close functionality
const closeModal = () => modal.remove(); const closeModal = () => modal.remove();
const closeBtn = modal.querySelector('.messages-close-btn'); const closeBtn = modal.querySelector('.messages-close-btn');
closeBtn.addEventListener('click', closeModal); closeBtn.addEventListener('click', closeModal);
// Close on overlay click // Close on overlay click
modal.addEventListener('click', (e) => { modal.addEventListener('click', (e) => {
if (e.target === modal) closeModal(); if (e.target === modal) closeModal();
@ -615,7 +615,7 @@ export class InputManager {
try { try {
const audio = new Audio(`assets/sounds/${soundFile}`); const audio = new Audio(`assets/sounds/${soundFile}`);
audio.volume = 0.3; audio.volume = 0.3;
audio.play().catch(() => {}); // Ignore audio play failures audio.play().catch(() => { }); // Ignore audio play failures
} catch (e) { } catch (e) {
// Ignore audio errors // Ignore audio errors
} }
@ -632,7 +632,7 @@ export class InputManager {
if (this.globalHandlers.has('click')) { if (this.globalHandlers.has('click')) {
document.removeEventListener('click', this.handleGlobalClick); document.removeEventListener('click', this.handleGlobalClick);
} }
this.globalHandlers.clear(); this.globalHandlers.clear();
this.activeHandlers.clear(); this.activeHandlers.clear();
} }
@ -643,7 +643,7 @@ function playSound(soundFile) {
try { try {
const audio = new Audio(`assets/sounds/${soundFile}`); const audio = new Audio(`assets/sounds/${soundFile}`);
audio.volume = 0.3; audio.volume = 0.3;
audio.play().catch(() => {}); // Ignore failures in restrictive environments audio.play().catch(() => { }); // Ignore failures in restrictive environments
} catch (e) { } catch (e) {
// Audio not supported or file missing, ignore // Audio not supported or file missing, ignore
} }

64
src/ui/render.js

@ -10,62 +10,8 @@ function playSound(soundFile) {
} }
} }
async function showMessagesModal() {
const { getAllMessages } = await import("../data/messages.js");
const messages = getAllMessages();
const modal = document.createElement('div');
modal.className = 'messages-modal-overlay';
modal.innerHTML = `
<div class="messages-modal">
<div class="messages-modal-header">
<h2>Messages for Prime</h2>
<button class="messages-close-btn" aria-label="Close">×</button>
</div>
<div class="messages-modal-content">
${messages.length > 0 ? messages.map((msg, index) => `
<div class="message-item">
<div class="message-from">From: ${msg.from}</div>
<div class="message-text">${msg.message}</div>
</div>
`).join('') : `
<div class="no-messages-placeholder">
<p>No messages added yet!</p>
<p>Add your birthday messages to <code>src/data/messages.js</code></p>
</div>
`}
</div>
</div>
`;
// Close functionality
const closeModal = () => {
modal.remove();
};
const closeBtn = modal.querySelector('.messages-close-btn');
closeBtn.addEventListener('click', closeModal);
// Close on overlay click
modal.addEventListener('click', (e) => {
if (e.target === modal) closeModal();
});
// Close on Escape key
const handleEscape = (e) => {
if (e.key === 'Escape') {
closeModal();
document.removeEventListener('keydown', handleEscape);
}
};
document.addEventListener('keydown', handleEscape);
// Add to DOM
document.body.appendChild(modal);
}
export function showDamageNumber(damage, target, isPlayer = false) { export function showDamageNumber(damage, target, isPlayer = false) {
console.log('this is shown - damage number')
const targetElement = isPlayer ? const targetElement = isPlayer ?
document.querySelector('.player-battle-zone') : document.querySelector('.player-battle-zone') :
document.querySelector('.enemy-battle-zone'); document.querySelector('.enemy-battle-zone');
@ -1459,7 +1405,7 @@ export async function renderWin(root) {
export async function renderCodeReviewSelection(root, cards) { export async function renderCodeReviewSelection(root, cards) {
const { CARDS } = await import("../data/cards.js"); const { CARDS } = await import("../data/cards.js");
if (!cards || cards.length === 0) { if (!cards || cards.length === 0) {
root.log("No cards available for code review."); root.log("No cards available for code review.");
return; return;
@ -1475,8 +1421,8 @@ export async function renderCodeReviewSelection(root, cards) {
<div class="code-review-cards-container"> <div class="code-review-cards-container">
${cards.map((card, index) => { ${cards.map((card, index) => {
const cardType = card.type === 'attack' ? 'attack' : card.type === 'skill' ? 'skill' : 'power'; const cardType = card.type === 'attack' ? 'attack' : card.type === 'skill' ? 'skill' : 'power';
return ` return `
<div class="code-review-card" data-code-review-pick="${index}"> <div class="code-review-card" data-code-review-pick="${index}">
<div class="battle-card ${cardType} playable"> <div class="battle-card ${cardType} playable">
<div class="card-glow"></div> <div class="card-glow"></div>
@ -1499,7 +1445,7 @@ export async function renderCodeReviewSelection(root, cards) {
<div class="code-review-card-label">Click to choose</div> <div class="code-review-card-label">Click to choose</div>
</div> </div>
`; `;
}).join('')} }).join('')}
</div> </div>
<div class="code-review-footer"> <div class="code-review-footer">

19
style.css

@ -1734,8 +1734,8 @@ h3 {
.battle-card.playable:hover { .battle-card.playable:hover {
transform: translateY(-80px) scale(1.15) rotate(0deg) !important; transform: translateY(0px) scale(1.05) rotate(0deg) !important;
z-index: 300; z-index: 900;
margin-left: -60px; margin-left: -60px;
margin-right: 40px; margin-right: 40px;
} }
@ -2116,7 +2116,8 @@ h3 {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
pointer-events: none; /* Allow clicks to pass through to elements behind */ pointer-events: none;
/* Allow clicks to pass through to elements behind */
color: #dc3545; color: #dc3545;
font-size: 12px; font-size: 12px;
font-weight: bold; font-weight: bold;
@ -4211,7 +4212,8 @@ h3 {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border-radius: 16px; border-radius: 16px;
pointer-events: none; /* Allow clicks to pass through to elements behind */ pointer-events: none;
/* Allow clicks to pass through to elements behind */
color: #ff6b6b; color: #ff6b6b;
font-weight: bold; font-weight: bold;
font-size: 14px; font-size: 14px;
@ -5648,7 +5650,7 @@ h3 {
.messages-modal { .messages-modal {
background: var(--panel); background: var(--panel);
border: 3px solid var(--accent); border: 3px solid var(--accent);
border-radius: 16px; border-radius: 6px;
max-width: 700px; max-width: 700px;
width: 90%; width: 90%;
max-height: 80vh; max-height: 80vh;
@ -5676,7 +5678,6 @@ h3 {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
border-radius: 12px 12px 0 0;
} }
.messages-modal-header h2 { .messages-modal-header h2 {
@ -5942,16 +5943,16 @@ h3 {
width: 95%; width: 95%;
max-height: 90vh; max-height: 90vh;
} }
.code-review-cards-container { .code-review-cards-container {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
} }
.code-review-card .battle-card { .code-review-card .battle-card {
transform: scale(0.8); transform: scale(0.8);
} }
.code-review-header h2 { .code-review-header h2 {
font-size: 1.5em; font-size: 1.5em;
} }

Loading…
Cancel
Save