diff --git a/src/data/cards.js b/src/data/cards.js
index f503713..05ae8dd 100644
--- a/src/data/cards.js
+++ b/src/data/cards.js
@@ -132,12 +132,12 @@ export const CARDS = {
ctx.log("Recursion activates and strikes again!");
ctx.enemy.hp = 1;
ctx.deal(ctx.enemy, ctx.scalarFromWeak(5));
-
+
// Check for battle end after second attack
- if (ctx.enemy.hp <= 0) {
- ctx.enemy.hp = 0;
- ctx.onWin();
- return;
+ if (ctx.enemy.hp <= 0) {
+ ctx.enemy.hp = 0;
+ ctx.onWin();
+ return;
}
}
}
@@ -167,13 +167,13 @@ export const CARDS = {
ctx.log("No cards left in deck to review.");
return;
}
-
+
// Store selection state for modal
ctx.root._codeReviewCards = topCards;
ctx.root._codeReviewCallback = (selectedIndex) => {
// Get the selected card
const selectedCard = topCards[selectedIndex];
-
+
// Remove the peeked cards from draw pile (they were only peeked)
topCards.forEach((card, i) => {
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);
}
});
-
+
// Add selected card to hand
ctx.addToHand(selectedCard);
-
+
// Put remaining cards on bottom of deck
topCards.forEach((card, i) => {
if (i !== selectedIndex) {
ctx.putOnBottom(card.id);
}
});
-
+
ctx.log(`Code review complete. Added ${selectedCard.name} to hand.`);
ctx.render();
};
-
+
// Show selection modal
if (window.gameModules?.render?.renderCodeReviewSelection) {
window.gameModules.render.renderCodeReviewSelection(ctx.root, topCards);
@@ -291,12 +291,10 @@ export const CARDS = {
},
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",
effect: (ctx) => {
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 = [
"strike", "strike", "defend", "defend",
"segfault", "coffee_rush", "skill_issue", "git_commit",
- "stack_trace", "stack_trace"
+ "stack_trace", "stack_trace", "git_push_force", "code_review"
];
export const CARD_POOL = [
diff --git a/src/engine/battle.js b/src/engine/battle.js
index 67e9c78..8748e67 100644
--- a/src/engine/battle.js
+++ b/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.player.hp <= 0) { ctx.onLose(); return; }
+
+ // Don't render if Code Review modal is active
+ if (ctx.root._codeReviewCards) {
+ return;
+ }
+
ctx.render();
}
diff --git a/src/engine/events.js b/src/engine/events.js
index 169e085..6a08d5d 100644
--- a/src/engine/events.js
+++ b/src/engine/events.js
@@ -10,7 +10,7 @@ export class EventHandler {
this.keyHandlers = new Map(); // Track keyboard shortcuts
this.globalHandlers = new Set(); // Track global event handlers
this.currentScreen = null;
-
+
this.setupGlobalEvents();
}
@@ -26,17 +26,17 @@ export class EventHandler {
handler(e);
}
};
-
+
document.addEventListener('keydown', this.globalKeyHandler);
this.globalHandlers.add('keydown');
-
+
// Global escape handler for modals
this.globalEscapeHandler = (e) => {
if (e.key === 'Escape') {
this.closeTopModal();
}
};
-
+
document.addEventListener('keydown', this.globalEscapeHandler);
this.globalHandlers.add('escape');
}
@@ -55,7 +55,7 @@ export class EventHandler {
*/
on(element, event, handler, options = {}) {
if (!element) return;
-
+
const wrappedHandler = (e) => {
try {
handler(e);
@@ -63,9 +63,9 @@ export class EventHandler {
console.error(`Event handler error (${event}):`, error);
}
};
-
+
element.addEventListener(event, wrappedHandler, options);
-
+
// Track for cleanup
if (!this.listeners.has(element)) {
this.listeners.set(element, []);
@@ -92,7 +92,7 @@ export class EventHandler {
*/
setupBattleEvents() {
this.switchScreen('battle');
-
+
// Card play events
this.root.app.querySelectorAll("[data-play]").forEach(btn => {
this.on(btn, "mouseenter", () => {
@@ -141,11 +141,11 @@ export class EventHandler {
this.addKeyHandler(i.toString(), (e) => {
const cardIndex = i - 1;
const hand = this.root.player.hand;
-
+
if (cardIndex >= hand.length) return;
-
+
const card = hand[cardIndex];
-
+
if (this.root.selectedCardIndex === cardIndex) {
// Second press - play the card
if (this.root.player.energy >= card.cost) {
@@ -168,11 +168,11 @@ export class EventHandler {
*/
setupMapEvents() {
this.switchScreen('map');
-
+
// Node navigation
this.root.app.querySelectorAll("[data-node]").forEach(el => {
if (!el.dataset.node) return;
-
+
this.on(el, "click", () => this.root.go(el.dataset.node));
});
@@ -206,14 +206,14 @@ export class EventHandler {
*/
setupRewardEvents(choices) {
this.switchScreen('reward');
-
+
this.root.app.querySelectorAll("[data-pick]").forEach(btn => {
this.on(btn, "click", () => {
const idx = parseInt(btn.dataset.pick, 10);
this.root.takeReward(idx);
});
});
-
+
const skipBtn = this.root.app.querySelector("[data-skip]");
if (skipBtn) {
this.on(skipBtn, "click", () => this.root.skipReward());
@@ -225,7 +225,7 @@ export class EventHandler {
this.root.takeReward(i - 1);
}, `Select Reward ${i}`);
}
-
+
this.addKeyHandler('s', () => this.root.skipReward(), 'Skip Reward');
}
@@ -234,10 +234,10 @@ export class EventHandler {
*/
setupRestEvents() {
this.switchScreen('rest');
-
+
const healBtn = this.root.app.querySelector("[data-act='heal']");
const upgradeBtn = this.root.app.querySelector("[data-act='upgrade']");
-
+
if (healBtn) {
this.on(healBtn, "click", () => {
const heal = Math.floor(this.root.player.maxHp * 0.2);
@@ -246,7 +246,7 @@ export class EventHandler {
this.root.afterNode();
});
}
-
+
if (upgradeBtn) {
this.on(upgradeBtn, "click", () => {
// Import and call renderUpgrade
@@ -266,7 +266,7 @@ export class EventHandler {
*/
setupShopEvents(shopCards = [], shopRelic = null) {
this.switchScreen('shop');
-
+
// Card purchase events
this.root.app.querySelectorAll("[data-buy-card]").forEach(btn => {
this.on(btn, "click", () => {
@@ -278,7 +278,7 @@ export class EventHandler {
this.root.log(`Bought ${card.name} for 50 gold.`);
btn.disabled = true;
btn.textContent = "SOLD";
-
+
this.updateGoldDisplay();
this.updateShopAffordability();
} else {
@@ -303,7 +303,7 @@ export class EventHandler {
relicBtn.disabled = true;
relicBtn.textContent = "SOLD";
-
+
this.updateGoldDisplay();
this.updateShopAffordability();
} else {
@@ -329,7 +329,7 @@ export class EventHandler {
*/
setupEventEvents(event) {
this.switchScreen('event');
-
+
this.root.app.querySelectorAll("[data-choice]").forEach(btn => {
this.on(btn, "click", () => {
const idx = parseInt(btn.dataset.choice, 10);
@@ -352,7 +352,7 @@ export class EventHandler {
*/
setupRelicSelectionEvents(relicChoices) {
this.switchScreen('relic-selection');
-
+
this.root.app.querySelectorAll("[data-relic]").forEach(btn => {
this.on(btn, "click", () => {
const relicId = btn.dataset.relic;
@@ -369,11 +369,11 @@ export class EventHandler {
// Keyboard shortcuts
for (let i = 1; i <= relicChoices.length; i++) {
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();
}, `Select Relic ${i}`);
}
-
+
this.addKeyHandler('m', () => this.showMessagesModal(), 'Show Messages');
}
@@ -382,15 +382,15 @@ export class EventHandler {
*/
setupEndGameEvents() {
this.switchScreen('endgame');
-
+
const replayBtn = this.root.app.querySelector("[data-replay]");
const restartAct2Btn = this.root.app.querySelector("[data-restart-act2]");
const menuBtn = this.root.app.querySelector("[data-menu]");
-
+
if (replayBtn) {
this.on(replayBtn, "click", () => this.root.reset());
}
-
+
if (restartAct2Btn) {
this.on(restartAct2Btn, "click", async () => {
if (this.root.loadAct2Checkpoint()) {
@@ -401,7 +401,7 @@ export class EventHandler {
}
});
}
-
+
if (menuBtn) {
this.on(menuBtn, "click", () => this.root.reset());
}
@@ -455,10 +455,10 @@ export class EventHandler {
showTooltip(event) {
const tooltip = document.getElementById('custom-tooltip');
if (!tooltip) return;
-
+
const node = event.target.closest('.spire-node');
if (!node) return;
-
+
const content = node.dataset.tooltip;
const avatarPath = node.dataset.avatar;
@@ -514,7 +514,7 @@ export class EventHandler {
modal.innerHTML = `
@@ -565,7 +565,7 @@ export class EventHandler {
}
}
this.listeners.clear();
-
+
// Clear keyboard handlers
this.keyHandlers.clear();
}
@@ -575,7 +575,7 @@ export class EventHandler {
*/
destroy() {
this.cleanup();
-
+
// Remove global handlers
if (this.globalHandlers.has('keydown')) {
document.removeEventListener('keydown', this.globalKeyHandler);
@@ -583,7 +583,7 @@ export class EventHandler {
if (this.globalHandlers.has('escape')) {
document.removeEventListener('keydown', this.globalEscapeHandler);
}
-
+
this.globalHandlers.clear();
}
-}
\ No newline at end of file
+}
diff --git a/src/input/InputManager.js b/src/input/InputManager.js
index d05df91..b3deebe 100644
--- a/src/input/InputManager.js
+++ b/src/input/InputManager.js
@@ -18,7 +18,7 @@ export class InputManager {
this.root = gameRoot;
this.activeHandlers = new Map(); // Track active event listeners for cleanup
this.globalHandlers = new Set(); // Track global document listeners
-
+
// Bind methods to preserve 'this' context
this.handleGlobalKeydown = this.handleGlobalKeydown.bind(this);
this.handleGlobalClick = this.handleGlobalClick.bind(this);
@@ -31,7 +31,7 @@ export class InputManager {
// Global keyboard handling
document.addEventListener('keydown', this.handleGlobalKeydown);
this.globalHandlers.add('keydown');
-
+
// Global click handling for data attributes
document.addEventListener('click', this.handleGlobalClick);
this.globalHandlers.add('click');
@@ -45,7 +45,7 @@ export class InputManager {
if (event.key === 'Escape') {
this.handleEscapeKey(event);
}
-
+
// Handle number keys for code review selection
if (this.root._codeReviewCards && event.key >= '1' && event.key <= '3') {
const selectedIndex = parseInt(event.key, 10) - 1;
@@ -58,7 +58,7 @@ export class InputManager {
}
}
}
-
+
// Add other global shortcuts here as needed
}
@@ -67,90 +67,90 @@ export class InputManager {
*/
handleGlobalClick(event) {
const target = event.target;
-
+
// Event delegation for game interactions
-
+
// Handle clicks on elements with data attributes (check both direct and parent elements)
-
+
// Check for card play (battle-card with data-play)
const cardElement = target.closest('[data-play]');
if (cardElement) {
this.handleCardPlay(cardElement, event);
return; // Early return to avoid duplicate handling
}
-
+
// Check for other interactive elements (using closest to handle child elements)
const actionElement = target.closest('[data-action]');
if (actionElement) {
this.handleActionButton(actionElement, event);
return;
}
-
+
const actElement = target.closest('[data-act]');
if (actElement) {
this.handleRestAction(actElement, event);
return;
}
-
+
const pickElement = target.closest('[data-pick]');
if (pickElement) {
this.handleRewardPick(pickElement, event);
return;
}
-
+
const choiceElement = target.closest('[data-choice]');
if (choiceElement) {
this.handleEventChoice(choiceElement, event);
return;
}
-
+
const upgradeElement = target.closest('[data-upgrade]');
if (upgradeElement) {
this.handleCardUpgrade(upgradeElement, event);
return;
}
-
+
const buyCardElement = target.closest('[data-buy-card]');
if (buyCardElement) {
this.handleShopCardBuy(buyCardElement, event);
return;
}
-
+
const buyRelicElement = target.closest('[data-buy-relic]');
if (buyRelicElement) {
this.handleShopRelicBuy(buyRelicElement, event);
return;
}
-
+
const leaveElement = target.closest('[data-leave]');
if (leaveElement) {
this.handleLeaveShop(leaveElement, event);
return;
}
-
+
const relicElement = target.closest('[data-relic]');
if (relicElement) {
this.handleRelicSelection(relicElement, event);
return;
}
-
+
const codeReviewElement = target.closest('[data-code-review-pick]');
if (codeReviewElement) {
this.handleCodeReviewPick(codeReviewElement, event);
return;
}
-
+
// Check for direct data attributes on target (fallback)
if (target.dataset.node !== undefined) {
this.handleMapNodeClick(target, event);
}
-
+
// Handle spire node clicks (check if clicked element is inside a spire-node)
const spireNode = target.closest('.spire-node');
if (spireNode && spireNode.dataset.node) {
this.handleMapNodeClick(spireNode, event);
}
-
+
// Handle other specific buttons
this.handleSpecificButtons(target, event);
}
@@ -160,21 +160,21 @@ export class InputManager {
*/
handleCardPlay(element, event) {
if (!element.classList.contains('playable')) return;
-
+
const index = parseInt(element.dataset.play, 10);
const card = this.root.player.hand[index];
-
+
if (!card) return;
if (this.root.player.energy < card.cost) return;
-
+
try {
// Play sound
this.playSound('played-card.mp3');
-
+
// Create and execute PlayCardCommand
const command = new PlayCardCommand(this.root, index);
const success = this.root.commandInvoker.execute(command);
-
+
if (success) {
// Clear card selection
this.root.selectedCardIndex = null;
@@ -192,7 +192,7 @@ export class InputManager {
*/
handleMapNodeClick(element, event) {
if (!element.dataset.node) return;
-
+
try {
// Create and execute MapMoveCommand
const command = new MapMoveCommand(this.root, element.dataset.node);
@@ -207,7 +207,7 @@ export class InputManager {
*/
handleRewardPick(element, event) {
const idx = parseInt(element.dataset.pick, 10);
-
+
try {
// Create and execute RewardPickCommand
const command = new RewardPickCommand(this.root, idx);
@@ -235,7 +235,7 @@ export class InputManager {
handleCardUpgrade(element, event) {
const deckIndex = parseInt(element.dataset.upgrade, 10);
const oldCardId = this.root.player.deck[deckIndex];
-
+
// Find the upgraded version and replace it
const { CARDS } = window.gameModules?.cards || {};
if (CARDS && CARDS[oldCardId]?.upgrades) {
@@ -259,16 +259,16 @@ export class InputManager {
this.root.log(`Bought ${card.name} for 50 gold.`);
element.disabled = true;
element.textContent = "SOLD";
-
+
// Update gold display
const goldDisplay = this.root.app.querySelector('.gold-amount');
if (goldDisplay) {
goldDisplay.textContent = this.root.player.gold;
}
-
+
// Update affordability of remaining items
this.updateShopAffordability();
-
+
// Save immediately to persist purchase
this.root.save();
} else {
@@ -293,7 +293,7 @@ export class InputManager {
const newRelicIds = [...currentRelicIds, relic.id];
attachRelics(this.root, newRelicIds);
});
-
+
element.disabled = true;
element.textContent = "SOLD";
@@ -305,7 +305,7 @@ export class InputManager {
// Update affordability of remaining items
this.updateShopAffordability();
-
+
// Save immediately to persist purchase
this.root.save();
} else {
@@ -388,12 +388,12 @@ export class InputManager {
*/
handleCodeReviewPick(element, event) {
const selectedIndex = parseInt(element.dataset.codeReviewPick, 10);
-
+
if (this.root._codeReviewCallback && this.root._codeReviewCards) {
try {
// Execute the callback with selected index
this.root._codeReviewCallback(selectedIndex);
-
+
// Clean up state
this.root._codeReviewCards = null;
this.root._codeReviewCallback = null;
@@ -408,7 +408,7 @@ export class InputManager {
*/
handleActionButton(element, event) {
const action = element.dataset.action;
-
+
switch (action) {
case 'show-messages':
this.handleShowMessages();
@@ -426,7 +426,7 @@ export class InputManager {
*/
handleRestAction(element, event) {
const action = element.dataset.act;
-
+
try {
// Create and execute RestActionCommand
const command = new RestActionCommand(this.root, action);
@@ -444,7 +444,7 @@ export class InputManager {
// Create and execute EndTurnCommand
const command = new EndTurnCommand(this.root);
const success = this.root.commandInvoker.execute(command);
-
+
if (success) {
// Clear card selection
this.root.selectedCardIndex = null;
@@ -507,7 +507,7 @@ export class InputManager {
if (this.root.currentShopRelic && this.root.player.gold >= 100) {
this.root.player.gold -= 100;
this.root.log(`Bought ${this.root.currentShopRelic.name} for 100 gold.`);
-
+
// Add relic logic here
element.disabled = true;
element.textContent = "SOLD";
@@ -536,7 +536,7 @@ export class InputManager {
this.root.render();
return;
}
-
+
// Close any open modals
const modals = document.querySelectorAll('.messages-modal-overlay');
modals.forEach(modal => modal.remove());
@@ -555,7 +555,7 @@ export class InputManager {
modal.innerHTML = `
@@ -576,10 +576,10 @@ export class InputManager {
// 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();
@@ -615,7 +615,7 @@ export class InputManager {
try {
const audio = new Audio(`assets/sounds/${soundFile}`);
audio.volume = 0.3;
- audio.play().catch(() => {}); // Ignore audio play failures
+ audio.play().catch(() => { }); // Ignore audio play failures
} catch (e) {
// Ignore audio errors
}
@@ -632,7 +632,7 @@ export class InputManager {
if (this.globalHandlers.has('click')) {
document.removeEventListener('click', this.handleGlobalClick);
}
-
+
this.globalHandlers.clear();
this.activeHandlers.clear();
}
@@ -643,7 +643,7 @@ function playSound(soundFile) {
try {
const audio = new Audio(`assets/sounds/${soundFile}`);
audio.volume = 0.3;
- audio.play().catch(() => {}); // Ignore failures in restrictive environments
+ audio.play().catch(() => { }); // Ignore failures in restrictive environments
} catch (e) {
// Audio not supported or file missing, ignore
}
diff --git a/src/ui/render.js b/src/ui/render.js
index 040a8a4..095a849 100644
--- a/src/ui/render.js
+++ b/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 = `
-
-
-
- ${messages.length > 0 ? messages.map((msg, index) => `
-
-
From: ${msg.from}
-
${msg.message}
-
- `).join('') : `
-
-
No messages added yet!
-
Add your birthday messages to src/data/messages.js
-
- `}
-
-
- `;
-
- // 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) {
+ console.log('this is shown - damage number')
const targetElement = isPlayer ?
document.querySelector('.player-battle-zone') :
document.querySelector('.enemy-battle-zone');
@@ -1459,7 +1405,7 @@ export async function renderWin(root) {
export async function renderCodeReviewSelection(root, cards) {
const { CARDS } = await import("../data/cards.js");
-
+
if (!cards || cards.length === 0) {
root.log("No cards available for code review.");
return;
@@ -1475,8 +1421,8 @@ export async function renderCodeReviewSelection(root, cards) {
${cards.map((card, index) => {
- const cardType = card.type === 'attack' ? 'attack' : card.type === 'skill' ? 'skill' : 'power';
- return `
+ const cardType = card.type === 'attack' ? 'attack' : card.type === 'skill' ? 'skill' : 'power';
+ return `
@@ -1499,7 +1445,7 @@ export async function renderCodeReviewSelection(root, cards) {
Click to choose
`;
- }).join('')}
+ }).join('')}