11 changed files with 337 additions and 29 deletions
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
/** |
||||
* Base Command class following the Command Pattern |
||||
* All game actions should extend this class |
||||
*/ |
||||
export class Command { |
||||
constructor() { |
||||
this.executed = false; |
||||
this.timestamp = Date.now(); |
||||
} |
||||
|
||||
/** |
||||
* Execute the command |
||||
* @returns {boolean} true if successful, false otherwise |
||||
*/ |
||||
execute() { |
||||
throw new Error("Command.execute() must be implemented by subclass"); |
||||
} |
||||
|
||||
/** |
||||
* Get a description of this command for logging/debugging |
||||
* @returns {string} |
||||
*/ |
||||
getDescription() { |
||||
return this.constructor.name; |
||||
} |
||||
} |
||||
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
/** |
||||
* CommandInvoker manages command execution |
||||
* Follows the Command Pattern for centralized action handling |
||||
*/ |
||||
export class CommandInvoker { |
||||
constructor() { |
||||
this.history = []; |
||||
this.maxHistorySize = 50; // Prevent memory bloat
|
||||
} |
||||
|
||||
/** |
||||
* Execute a command and add it to history |
||||
* @param {Command} command - The command to execute |
||||
* @returns {boolean} true if successful |
||||
*/ |
||||
execute(command) { |
||||
try { |
||||
const success = command.execute(); |
||||
|
||||
if (success) { |
||||
// Add command to history for debugging
|
||||
this.history.push(command); |
||||
|
||||
// Trim history if it gets too long
|
||||
if (this.history.length > this.maxHistorySize) { |
||||
this.history.shift(); |
||||
} |
||||
|
||||
command.executed = true; |
||||
console.log(`Executed: ${command.getDescription()}`); |
||||
} |
||||
|
||||
return success; |
||||
} catch (error) { |
||||
console.error(`Command execution failed: ${command.getDescription()}`, error); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Get command history for debugging |
||||
* @returns {Array<string>} |
||||
*/ |
||||
getHistory() { |
||||
return this.history.map(cmd => cmd.getDescription()); |
||||
} |
||||
|
||||
/** |
||||
* Clear command history |
||||
*/ |
||||
clear() { |
||||
this.history = []; |
||||
} |
||||
} |
||||
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
import { Command } from './Command.js'; |
||||
|
||||
/** |
||||
* Command for ending the player's turn in battle |
||||
* Wraps the existing root.end() method |
||||
*/ |
||||
export class EndTurnCommand extends Command { |
||||
constructor(gameRoot) { |
||||
super(); |
||||
this.gameRoot = gameRoot; |
||||
} |
||||
|
||||
execute() { |
||||
try { |
||||
// Use existing root.end method (which now creates proper battle context)
|
||||
this.gameRoot.end(); |
||||
return true; |
||||
} catch (error) { |
||||
console.error("EndTurnCommand execution failed:", error); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
getDescription() { |
||||
return "End Turn"; |
||||
} |
||||
} |
||||
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
import { Command } from './Command.js'; |
||||
|
||||
/** |
||||
* Command for moving to a node on the map |
||||
* Wraps the existing root.go() method |
||||
*/ |
||||
export class MapMoveCommand extends Command { |
||||
constructor(gameRoot, nodeId) { |
||||
super(); |
||||
this.gameRoot = gameRoot; |
||||
this.nodeId = nodeId; |
||||
} |
||||
|
||||
execute() { |
||||
try { |
||||
// Use existing root.go method
|
||||
this.gameRoot.go(this.nodeId); |
||||
return true; |
||||
} catch (error) { |
||||
console.error("MapMoveCommand execution failed:", error); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
getDescription() { |
||||
return `Move to Node: ${this.nodeId}`; |
||||
} |
||||
} |
||||
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
import { Command } from './Command.js'; |
||||
|
||||
/** |
||||
* Command for picking a reward card |
||||
* Not undoable as it modifies deck permanently |
||||
*/ |
||||
export class PickRewardCommand extends Command { |
||||
constructor(gameRoot, rewardIndex) { |
||||
super(); |
||||
this.gameRoot = gameRoot; |
||||
this.rewardIndex = rewardIndex; |
||||
} |
||||
|
||||
execute() { |
||||
if (this.gameRoot.screen !== 'reward') { |
||||
console.warn("Cannot pick reward - not on reward screen"); |
||||
return false; |
||||
} |
||||
|
||||
try { |
||||
// Use existing reward selection logic
|
||||
this.gameRoot.takeReward(this.rewardIndex); |
||||
return true; |
||||
} catch (error) { |
||||
console.error("PickRewardCommand execution failed:", error); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
canUndo() { |
||||
// Reward picks are not undoable as they modify deck
|
||||
return false; |
||||
} |
||||
|
||||
getDescription() { |
||||
const reward = this.gameRoot.rewards?.[this.rewardIndex]; |
||||
const cardName = reward?.name || 'Unknown Card'; |
||||
return `Pick Reward: ${cardName}`; |
||||
} |
||||
} |
||||
@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
|
||||
import { Command } from './Command.js'; |
||||
|
||||
/** |
||||
* Command for playing a card in battle |
||||
* Wraps the existing root.play() method |
||||
*/ |
||||
export class PlayCardCommand extends Command { |
||||
constructor(gameRoot, cardIndex) { |
||||
super(); |
||||
this.gameRoot = gameRoot; |
||||
this.cardIndex = cardIndex; |
||||
} |
||||
|
||||
execute() { |
||||
try { |
||||
// Use existing root.play method (which now creates proper battle context)
|
||||
this.gameRoot.play(this.cardIndex); |
||||
return true; |
||||
} catch (error) { |
||||
console.error("PlayCardCommand execution failed:", error); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
getDescription() { |
||||
const card = this.gameRoot.player.hand[this.cardIndex]; |
||||
const cardName = card?.name || 'Unknown Card'; |
||||
return `Play Card: ${cardName}`; |
||||
} |
||||
} |
||||
@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
|
||||
import { Command } from './Command.js'; |
||||
|
||||
/** |
||||
* Command for rest site actions (heal, upgrade) |
||||
* Wraps the existing rest action logic |
||||
*/ |
||||
export class RestActionCommand extends Command { |
||||
constructor(gameRoot, action) { |
||||
super(); |
||||
this.gameRoot = gameRoot; |
||||
this.action = action; // 'heal' or 'upgrade'
|
||||
} |
||||
|
||||
execute() { |
||||
try { |
||||
if (this.action === 'heal') { |
||||
// Heal 20% of max HP (same as current logic)
|
||||
const heal = Math.floor(this.gameRoot.player.maxHp * 0.2); |
||||
this.gameRoot.player.hp = Math.min(this.gameRoot.player.maxHp, this.gameRoot.player.hp + heal); |
||||
this.gameRoot.log(`Healed for ${heal} HP.`); |
||||
this.gameRoot.afterNode(); |
||||
} else if (this.action === 'upgrade') { |
||||
// Show upgrade selection (same as current logic)
|
||||
if (window.gameModules?.render?.renderUpgrade) { |
||||
window.gameModules.render.renderUpgrade(this.gameRoot); |
||||
} |
||||
} else { |
||||
console.warn(`Unknown rest action: ${this.action}`); |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} catch (error) { |
||||
console.error("RestActionCommand execution failed:", error); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
getDescription() { |
||||
return `Rest Action: ${this.action}`; |
||||
} |
||||
} |
||||
@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
|
||||
import { Command } from './Command.js'; |
||||
|
||||
/** |
||||
* Command for picking a reward card |
||||
* Wraps the existing root.takeReward() method |
||||
*/ |
||||
export class RewardPickCommand extends Command { |
||||
constructor(gameRoot, rewardIndex) { |
||||
super(); |
||||
this.gameRoot = gameRoot; |
||||
this.rewardIndex = rewardIndex; |
||||
} |
||||
|
||||
execute() { |
||||
try { |
||||
// Use existing root.takeReward method
|
||||
this.gameRoot.takeReward(this.rewardIndex); |
||||
return true; |
||||
} catch (error) { |
||||
console.error("RewardPickCommand execution failed:", error); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
getDescription() { |
||||
const reward = this.gameRoot.rewards?.[this.rewardIndex]; |
||||
const cardName = reward?.name || 'Unknown Card'; |
||||
return `Pick Reward: ${cardName}`; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue