From f17ef711da0e41053e77f6a1e5f960a86c9ec353 Mon Sep 17 00:00:00 2001 From: dnviti Date: Mon, 22 Dec 2025 17:11:49 +0100 Subject: [PATCH] feat: Add manual draw card action, interactive mana pool controls, and reorganize game view layout. --- src/client/src/modules/game/GameView.tsx | 57 ++++++++++++++++-------- src/server/game/RulesEngine.ts | 31 ++++++++++++- src/server/managers/GameManager.ts | 9 ++++ 3 files changed, 78 insertions(+), 19 deletions(-) diff --git a/src/client/src/modules/game/GameView.tsx b/src/client/src/modules/game/GameView.tsx index a56ea14..280d4d2 100644 --- a/src/client/src/modules/game/GameView.tsx +++ b/src/client/src/modules/game/GameView.tsx @@ -797,10 +797,10 @@ export const GameView: React.FC = ({ gameState, currentPlayerId } {/* Bottom Area: Controls & Hand */} -
+
- {/* Left Controls: Library/Grave */} -
+ {/* Left Controls: Library/Grave/Exile */} +
{/* Phase Strip Integration */}
@@ -839,6 +839,13 @@ export const GameView: React.FC = ({ gameState, currentPlayerId }
+ + +
handleContextMenu(e, 'zone', undefined, 'exile')}> + Exile + {myExile.length} +
+
{/* Hand Area & Smart Button */} @@ -890,7 +897,7 @@ export const GameView: React.FC = ({ gameState, currentPlayerId }
{/* Right Controls: Exile / Life */} -
+
Your Life
-
+
{myPlayer?.life}
-
+
{/* Mana Pool Display */} -
+
{['W', 'U', 'B', 'R', 'G', 'C'].map(color => { const count = myPlayer?.manaPool?.[color] || 0; const icons: Record = { @@ -941,20 +948,34 @@ export const GameView: React.FC = ({ gameState, currentPlayerId } }; return ( -
0 ? 'opacity-100 scale-110 font-bold' : 'opacity-30'} transition-all`}> -
{icons[color]}
-
{count}
+
+
+ {icons[color]} +
+ +
+ + 0 ? 'text-white font-bold' : 'text-slate-500'}`}> + {count} + + +
); })}
- -
handleContextMenu(e, 'zone', undefined, 'exile')}> - Exile Drop Zone - {myExile.length} -
-
diff --git a/src/server/game/RulesEngine.ts b/src/server/game/RulesEngine.ts index 1beaadd..29f5661 100644 --- a/src/server/game/RulesEngine.ts +++ b/src/server/game/RulesEngine.ts @@ -454,9 +454,38 @@ export class RulesEngine { // 2. Draw Step if (step === 'draw') { + const player = this.state.players[activePlayerId]; if (this.state.turnCount > 1 || this.state.turnOrder.length > 2) { - this.drawCard(activePlayerId); + // If Bot: Auto Draw + if (player && player.isBot) { + console.log(`[Auto] Bot ${player.name} drawing card.`); + this.drawCard(activePlayerId); + // After draw, AP priority + this.resetPriority(activePlayerId); + } else { + // If Human: Wait for Manual Action + console.log(`[Manual] Waiting for Human ${player?.name} to draw.`); + // We do NOT call drawCard here. + // We DO reset priority to them so they can take the action? + // Actually, if we are in 'draw' step, strict rules say AP gets priority. + // Yet, the "Turn Based Action" of drawing usually happens *immediately* at start of step, BEFORE priority. + // 504.1. First, the active player draws a card. This turn-based action doesn't use the stack. + // 504.2. Second, the active player gets priority. + // So for "Manual" feeling, we pause BEFORE 504.1 is considered "done"? + // Effectively, we treat the "DRAW_CARD" action as the completion of 504.1. + + // Ensure they are the priority player so the UI lets them act (if we key off priority) + // But strict action validation for DRAW_CARD will check if they are AP and in Draw step. + if (this.state.priorityPlayerId !== activePlayerId) { + this.state.priorityPlayerId = activePlayerId; + } + } + } else { + // Skip draw (Turn 1 in 2p game) + console.log("Skipping Draw (Turn 1 2P)."); + this.resetPriority(activePlayerId); } + return; } // 3. Cleanup Step diff --git a/src/server/managers/GameManager.ts b/src/server/managers/GameManager.ts index ce08809..1c6f2cb 100644 --- a/src/server/managers/GameManager.ts +++ b/src/server/managers/GameManager.ts @@ -126,6 +126,15 @@ export class GameManager { case 'MULLIGAN_DECISION': engine.resolveMulligan(actorId, action.keep, action.cardsToBottom); break; + case 'DRAW_CARD': + // Strict validation: Must be Draw step, Must be Active Player + if (game.step !== 'draw') throw new Error("Can only draw in Draw Step."); + if (game.activePlayerId !== actorId) throw new Error("Only Active Player can draw."); + + engine.drawCard(actorId); + // After drawing, 504.2 says AP gets priority. + engine.resetPriority(actorId); + break; // TODO: Activate Ability default: console.warn(`Unknown strict action: ${action.type}`);